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
+55 -87
View File
@@ -1,31 +1,39 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 11.
#
# For more information, see:
#
# Documentation/dev-tools/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
--- ---
Language: Cpp AccessModifierOffset: -4
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left AlignEscapedNewlines: Left
AlignOperands: true AlignOperands: true
AlignTrailingComments: true AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: true AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: true AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes AlwaysBreakTemplateDeclarations: false
BinPackArguments: true BinPackArguments: true
BinPackParameters: true BinPackParameters: true
BraceWrapping: BraceWrapping:
AfterClass: false AfterClass: false
AfterControlStatement: false AfterControlStatement: false
AfterEnum: false AfterEnum: false
AfterFunction: false AfterFunction: true
AfterNamespace: false AfterNamespace: true
AfterObjCDeclaration: false AfterObjCDeclaration: false
AfterStruct: false AfterStruct: false
AfterUnion: false AfterUnion: false
@@ -37,43 +45,33 @@ BraceWrapping:
SplitEmptyRecord: true SplitEmptyRecord: true
SplitEmptyNamespace: true SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true BreakStringLiterals: false
ColumnLimit: 120 ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:' CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
Cpp11BracedListStyle: true Cpp11BracedListStyle: false
DerivePointerAlignment: true DerivePointerAlignment: false
DisableFormat: false DisableFormat: false
ExperimentalAutoDetectBinPacking: false ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve IncludeBlocks: Preserve
IncludeCategories: IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*' - Regex: '.*'
Priority: 3 Priority: 1
IncludeIsMainRegex: '([-_](test|unittest))?$' IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: true IndentCaseLabels: false
IndentGotoLabels: false
IndentPPDirectives: None IndentPPDirectives: None
IndentWidth: 2 IndentWidth: 4
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave JavaScriptQuotes: Leave
JavaScriptWrapImports: true JavaScriptWrapImports: true
@@ -82,67 +80,37 @@ MacroBlockBegin: ''
MacroBlockEnd: '' MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: None NamespaceIndentation: None
ObjCBinPackProtocolList: Never ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2 ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1 # Taken from git's rules
PenaltyBreakComment: 300 PenaltyBreakAssignment: 10
PenaltyBreakFirstLessLess: 120 PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakString: 1000 PenaltyBreakComment: 10
PenaltyBreakTemplateDeclaration: 10 PenaltyBreakFirstLessLess: 0
PenaltyExcessCharacter: 1000000 PenaltyBreakString: 10
PenaltyReturnTypeOnItsOwnLine: 200 PenaltyExcessCharacter: 100
PointerAlignment: Left PenaltyReturnTypeOnItsOwnLine: 60
RawStringFormats:
- Language: Cpp PointerAlignment: Right
Delimiters: ReflowComments: false
- cc SortIncludes: false
- CC SortUsingDeclarations: false
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2 SpacesBeforeTrailingComments: 1
SpacesInAngles: false SpacesInAngles: false
SpacesInContainerLiterals: true SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false SpacesInCStyleCastParentheses: false
SpacesInParentheses: false SpacesInParentheses: false
SpacesInSquareBrackets: false SpacesInSquareBrackets: false
Standard: Auto TabWidth: 4
TabWidth: 8 UseTab: Always
UseTab: Never
... ...
+147
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'
+57 -28
View File
@@ -28,7 +28,8 @@
#include "config/config.h" #include "config/config.h"
namespace olive { namespace olive
{
AudioManager *AudioManager::instance_ = nullptr; AudioManager *AudioManager::instance_ = nullptr;
@@ -55,35 +56,45 @@ void AudioManager::SetOutputNotifyInterval(int n)
output_buffer_->set_notify_interval(n); output_buffer_->set_notify_interval(n);
} }
int OutputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) int OutputCallback(const void *input, void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{ {
PreviewAudioDevice *device = static_cast<PreviewAudioDevice *>(userData); PreviewAudioDevice *device = static_cast<PreviewAudioDevice *>(userData);
qint64 max_read = frameCount * device->bytes_per_frame(); qint64 max_read = frameCount * device->bytes_per_frame();
qint64 read_count = device->read(reinterpret_cast<char*>(output), max_read); qint64 read_count =
device->read(reinterpret_cast<char *>(output), max_read);
if (read_count < max_read) { if (read_count < max_read) {
memset(reinterpret_cast<uint8_t*>(output) + read_count, 0, max_read - read_count); memset(reinterpret_cast<uint8_t *>(output) + read_count, 0,
max_read - read_count);
} }
return paContinue; return paContinue;
} }
int InputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) int InputCallback(const void *input, void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{ {
FFmpegEncoder *f = static_cast<FFmpegEncoder *>(userData); FFmpegEncoder *f = static_cast<FFmpegEncoder *>(userData);
AudioParams our_params = f->params().audio_params(); AudioParams our_params = f->params().audio_params();
our_params.set_format(f->params().audio_params().format().to_packed_equivalent()); our_params.set_format(
f->params().audio_params().format().to_packed_equivalent());
f->WriteAudioData(our_params, reinterpret_cast<const uint8_t**>(&input), frameCount); f->WriteAudioData(our_params, reinterpret_cast<const uint8_t **>(&input),
frameCount);
return paContinue; return paContinue;
} }
bool AudioManager::PushToOutput(const AudioParams &params, const QByteArray &samples, QString *error) bool AudioManager::PushToOutput(const AudioParams &params,
const QByteArray &samples, QString *error)
{ {
if (output_device_ == paNoDevice) { if (output_device_ == paNoDevice) {
if (error) *error = tr("No output device is set"); if (error)
*error = tr("No output device is set");
return false; return false;
} }
@@ -94,11 +105,15 @@ bool AudioManager::PushToOutput(const AudioParams &params, const QByteArray &sam
PaStreamParameters p = GetPortAudioParams(params, output_device_); PaStreamParameters p = GetPortAudioParams(params, output_device_);
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_); PaError r = Pa_OpenStream(&output_stream_, nullptr, &p,
output_params_.sample_rate(),
paFramesPerBufferUnspecified, paNoFlag,
OutputCallback, output_buffer_);
if (r != paNoError) { if (r != paNoError) {
// Unhandled error // Unhandled error
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r); //qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
if (error) *error = Pa_GetErrorText(r); if (error)
*error = Pa_GetErrorText(r);
return false; return false;
} }
@@ -171,7 +186,8 @@ void AudioManager::SetOutputDevice(PaDeviceIndex device)
if (device == paNoDevice) { if (device == paNoDevice) {
qInfo() << "No output device found"; qInfo() << "No output device found";
} else { } else {
qInfo() << "Setting output audio device to" << Pa_GetDeviceInfo(device)->name; qInfo() << "Setting output audio device to"
<< Pa_GetDeviceInfo(device)->name;
} }
output_device_ = device; output_device_ = device;
@@ -184,7 +200,8 @@ void AudioManager::SetInputDevice(PaDeviceIndex device)
if (device == paNoDevice) { if (device == paNoDevice) {
qInfo() << "No input device found"; qInfo() << "No input device found";
} else { } else {
qInfo() << "Setting input audio device to" << Pa_GetDeviceInfo(device)->name; qInfo() << "Setting input audio device to"
<< Pa_GetDeviceInfo(device)->name;
} }
input_device_ = device; input_device_ = device;
@@ -197,7 +214,8 @@ void AudioManager::HardReset()
Pa_Initialize(); Pa_Initialize();
} }
bool AudioManager::StartRecording(const EncodingParams &params, QString *error_str) bool AudioManager::StartRecording(const EncodingParams &params,
QString *error_str)
{ {
if (input_device_ == paNoDevice) { if (input_device_ == paNoDevice) {
return false; return false;
@@ -209,9 +227,13 @@ bool AudioManager::StartRecording(const EncodingParams &params, QString *error_s
return false; return false;
} }
PaStreamParameters p = GetPortAudioParams(params.audio_params(), input_device_); PaStreamParameters p =
GetPortAudioParams(params.audio_params(), input_device_);
PaError r = Pa_OpenStream(&input_stream_, &p, nullptr, params.audio_params().sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_); PaError r = Pa_OpenStream(&input_stream_, &p, nullptr,
params.audio_params().sample_rate(),
paFramesPerBufferUnspecified, paNoFlag,
InputCallback, input_encoder_);
if (r == paNoError) { if (r == paNoError) {
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_); //const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
r = Pa_StartStream(input_stream_); r = Pa_StartStream(input_stream_);
@@ -248,28 +270,34 @@ void AudioManager::StopRecording()
PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device) PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device)
{ {
QString entry = is_output_device ? QStringLiteral("AudioOutput") : QStringLiteral("AudioInput"); QString entry = is_output_device ? QStringLiteral("AudioOutput") :
QStringLiteral("AudioInput");
return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(), is_output_device); return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(),
is_output_device);
} }
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s, bool is_output_device) PaDeviceIndex AudioManager::FindDeviceByName(const QString &s,
bool is_output_device)
{ {
if (!s.isEmpty()) { if (!s.isEmpty()) {
for (PaDeviceIndex i = 0, end = Pa_GetDeviceCount(); i < end; i++) { for (PaDeviceIndex i = 0, end = Pa_GetDeviceCount(); i < end; i++) {
const PaDeviceInfo *device = Pa_GetDeviceInfo(i); const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
if (((is_output_device && device->maxOutputChannels) || (!is_output_device && device->maxInputChannels)) if (((is_output_device && device->maxOutputChannels) ||
&& !s.compare(device->name)) { (!is_output_device && device->maxInputChannels)) &&
!s.compare(device->name)) {
return i; return i;
} }
} }
} }
return is_output_device ? Pa_GetDefaultOutputDevice() : Pa_GetDefaultInputDevice(); return is_output_device ? Pa_GetDefaultOutputDevice() :
Pa_GetDefaultInputDevice();
} }
PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams &params, PaDeviceIndex device) PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams &params,
PaDeviceIndex device)
{ {
PaStreamParameters p; PaStreamParameters p;
@@ -282,10 +310,10 @@ PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams &params, P
return p; return p;
} }
AudioManager::AudioManager() : AudioManager::AudioManager()
output_stream_(nullptr), : output_stream_(nullptr)
input_stream_(nullptr), , input_stream_(nullptr)
input_encoder_(nullptr) , input_encoder_(nullptr)
{ {
#ifdef PA_HAS_JACK #ifdef PA_HAS_JACK
// PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not // PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not
@@ -304,7 +332,8 @@ AudioManager::AudioManager() :
output_buffer_ = new PreviewAudioDevice(this); output_buffer_ = new PreviewAudioDevice(this);
output_buffer_->open(PreviewAudioDevice::ReadWrite); output_buffer_->open(PreviewAudioDevice::ReadWrite);
connect(output_buffer_, &PreviewAudioDevice::Notify, this, &AudioManager::OutputNotify); connect(output_buffer_, &PreviewAudioDevice::Notify, this,
&AudioManager::OutputNotify);
} }
AudioManager::~AudioManager() AudioManager::~AudioManager()
+11 -8
View File
@@ -33,7 +33,8 @@
#include "render/audioplaybackcache.h" #include "render/audioplaybackcache.h"
#include "render/previewaudiodevice.h" #include "render/previewaudiodevice.h"
namespace olive { namespace olive
{
/** /**
* @brief Audio input and output management class * @brief Audio input and output management class
@@ -41,8 +42,7 @@ namespace olive {
* Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to * Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to
* the rest of the system. * the rest of the system.
*/ */
class AudioManager : public QObject class AudioManager : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
static void CreateInstance(); static void CreateInstance();
@@ -52,7 +52,8 @@ public:
void SetOutputNotifyInterval(int n); void SetOutputNotifyInterval(int n);
bool PushToOutput(const AudioParams &params, const QByteArray& samples, QString *error = nullptr); bool PushToOutput(const AudioParams &params, const QByteArray &samples,
QString *error = nullptr);
void ClearBufferedOutput(); void ClearBufferedOutput();
@@ -74,14 +75,17 @@ public:
void HardReset(); void HardReset();
bool StartRecording(const EncodingParams &params, QString *error_str = nullptr); bool StartRecording(const EncodingParams &params,
QString *error_str = nullptr);
void StopRecording(); void StopRecording();
static PaDeviceIndex FindConfigDeviceByName(bool is_output_device); static PaDeviceIndex FindConfigDeviceByName(bool is_output_device);
static PaDeviceIndex FindDeviceByName(const QString &s, bool is_output_device); static PaDeviceIndex FindDeviceByName(const QString &s,
bool is_output_device);
static PaStreamParameters GetPortAudioParams(const AudioParams &p, PaDeviceIndex device); static PaStreamParameters GetPortAudioParams(const AudioParams &p,
PaDeviceIndex device);
signals: signals:
void OutputNotify(); void OutputNotify();
@@ -108,7 +112,6 @@ private:
PaStream *input_stream_; PaStream *input_stream_;
FFmpegEncoder *input_encoder_; FFmpegEncoder *input_encoder_;
}; };
} }
+47 -28
View File
@@ -29,7 +29,8 @@ extern "C" {
#include "common/ffmpegutils.h" #include "common/ffmpegutils.h"
namespace olive { namespace olive
{
AudioProcessor::AudioProcessor() AudioProcessor::AudioProcessor()
{ {
@@ -43,7 +44,8 @@ AudioProcessor::~AudioProcessor()
Close(); Close();
} }
bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double tempo) bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to,
double tempo)
{ {
if (filter_graph_) { if (filter_graph_) {
qWarning() << "Tried to open a processor that was already open"; qWarning() << "Tried to open a processor that was already open";
@@ -61,17 +63,18 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
// Set up audio buffer args // Set up audio buffer args
char filter_args[200]; char filter_args[200];
snprintf(filter_args, 200, "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64, snprintf(
1, filter_args, 200,
from.sample_rate(), "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
from.sample_rate(), 1, from.sample_rate(), from.sample_rate(), from_fmt_,
from_fmt_,
from.channel_layout().u.mask); from.channel_layout().u.mask);
int r; int r;
// Create buffersrc (input) // Create buffersrc (input)
r = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("abuffer"), "in", filter_args, nullptr, filter_graph_); r = avfilter_graph_create_filter(&buffersrc_ctx_,
avfilter_get_by_name("abuffer"), "in",
filter_args, nullptr, filter_graph_);
if (r < 0) { if (r < 0) {
qCritical() << "Failed to create buffersrc:" << r; qCritical() << "Failed to create buffersrc:" << r;
Close(); Close();
@@ -96,16 +99,16 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
speed_log -= whole; speed_log -= whole;
for (int i = 0; i <= whole; i++) { for (int i = 0; i <= whole; i++) {
double filter_tempo = (i == whole) ? std::pow(base, speed_log) : base; double filter_tempo = (i == whole) ? std::pow(base, speed_log) :
base;
/* /*
if (qFuzzyCompare(filter_tempo, 1.0)) { if (qFuzzyCompare(filter_tempo, 1.0)) {
// This filter would do nothing // This filter would do nothing
continue; continue;
}*/ }*/
previous_filter = CreateTempoFilter(filter_graph_, previous_filter =
previous_filter, CreateTempoFilter(filter_graph_, previous_filter, filter_tempo);
filter_tempo);
if (!previous_filter) { if (!previous_filter) {
qCritical() << "Failed to create audio tempo filter"; qCritical() << "Failed to create audio tempo filter";
@@ -118,19 +121,24 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
// Create conversion filter // Create conversion filter
auto ch1 = from.channel_layout(); auto ch1 = from.channel_layout();
auto ch2 = to.channel_layout(); auto ch2 = to.channel_layout();
if (from.sample_rate() != to.sample_rate() || av_channel_layout_compare(&ch1, &ch2) || from.format() != to.format() if (from.sample_rate() != to.sample_rate() ||
|| (to.format().is_planar() && create_tempo)) { // Tempo processor automatically converts to packed, av_channel_layout_compare(&ch1, &ch2) || from.format() != to.format() ||
(to.format().is_planar() &&
create_tempo)) { // Tempo processor automatically converts to packed,
// so if the desired output is planar, it'll need // so if the desired output is planar, it'll need
// to be converted // to be converted
snprintf(filter_args, 200, "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64, snprintf(filter_args, 200,
av_get_sample_fmt_name(to_fmt_), "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
to.sample_rate(), av_get_sample_fmt_name(to_fmt_), to.sample_rate(),
to.channel_layout().u.mask); to.channel_layout().u.mask);
AVFilterContext *c; AVFilterContext *c;
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"), "fmt", filter_args, nullptr, filter_graph_); r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"),
"fmt", filter_args, nullptr,
filter_graph_);
if (r < 0) { if (r < 0) {
qCritical() << "Failed to create format conversion filter:" << r << filter_args; qCritical() << "Failed to create format conversion filter:" << r
<< filter_args;
Close(); Close();
return false; return false;
} }
@@ -146,7 +154,9 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
} }
// Create buffersink (output) // Create buffersink (output)
r = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("abuffersink"), "out", nullptr, nullptr, filter_graph_); r = avfilter_graph_create_filter(&buffersink_ctx_,
avfilter_get_by_name("abuffersink"), "out",
nullptr, nullptr, filter_graph_);
if (r < 0) { if (r < 0) {
qCritical() << "Failed to create buffersink:" << r; qCritical() << "Failed to create buffersink:" << r;
Close(); Close();
@@ -214,7 +224,8 @@ void AudioProcessor::Close()
} }
} }
int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output) int AudioProcessor::Convert(float **in, int nb_in_samples,
AudioProcessor::Buffer *output)
{ {
if (!IsOpen()) { if (!IsOpen()) {
qCritical() << "Tried to convert on closed processor"; qCritical() << "Tried to convert on closed processor";
@@ -231,7 +242,8 @@ int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffe
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples); in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
} }
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_, AV_BUFFERSRC_FLAG_KEEP_REF); r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_,
AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) { if (r < 0) {
qCritical() << "Failed to add frame to buffersrc:" << r; qCritical() << "Failed to add frame to buffersrc:" << r;
return r; return r;
@@ -263,14 +275,16 @@ int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffe
break; break;
} }
int nb_bytes = out_frame_->nb_samples * to_.bytes_per_sample_per_channel(); int nb_bytes =
out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
if (to_.format().is_packed()) { if (to_.format().is_packed()) {
nb_bytes *= to_.channel_count(); nb_bytes *= to_.channel_count();
} }
for (int i = 0; i < nb_channels; i++) { for (int i = 0; i < nb_channels; i++) {
result[i].resize(byte_offset + nb_bytes); result[i].resize(byte_offset + nb_bytes);
memcpy(result[i].data() + byte_offset, out_frame_->data[i], nb_bytes); memcpy(result[i].data() + byte_offset, out_frame_->data[i],
nb_bytes);
} }
byte_offset += nb_bytes; byte_offset += nb_bytes;
} }
@@ -282,13 +296,16 @@ int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffe
void AudioProcessor::Flush() void AudioProcessor::Flush()
{ {
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF); int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr,
AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) { if (r < 0) {
qCritical() << "Failed to flush:" << r; qCritical() << "Failed to flush:" << r;
} }
} }
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilterContext* link, const double &tempo) AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph *graph,
AVFilterContext *link,
const double &tempo)
{ {
// Set up tempo param, which is taken as a C string // Set up tempo param, which is taken as a C string
char speed_param[20]; char speed_param[20];
@@ -296,8 +313,10 @@ AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilte
AVFilterContext *tempo_ctx = nullptr; AVFilterContext *tempo_ctx = nullptr;
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"), "atempo", speed_param, nullptr, graph) >= 0 if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"),
&& avfilter_link(link, 0, tempo_ctx, 0) == 0) { "atempo", speed_param, nullptr,
graph) >= 0 &&
avfilter_link(link, 0, tempo_ctx, 0) == 0) {
return tempo_ctx; return tempo_ctx;
} }
+20 -9
View File
@@ -31,12 +31,12 @@ extern "C" {
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
using namespace core; using namespace core;
class AudioProcessor class AudioProcessor {
{
public: public:
AudioProcessor(); AudioProcessor();
@@ -44,22 +44,34 @@ public:
DISABLE_COPY_MOVE(AudioProcessor) DISABLE_COPY_MOVE(AudioProcessor)
bool Open(const AudioParams &from, const AudioParams &to, double tempo = 1.0); bool Open(const AudioParams &from, const AudioParams &to,
double tempo = 1.0);
void Close(); void Close();
bool IsOpen() const { return filter_graph_; } bool IsOpen() const
{
return filter_graph_;
}
using Buffer = QVector<QByteArray>; using Buffer = QVector<QByteArray>;
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output); int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
void Flush(); void Flush();
const AudioParams &from() const { return from_; } const AudioParams &from() const
const AudioParams &to() const { return to_; } {
return from_;
}
const AudioParams &to() const
{
return to_;
}
private: private:
static AVFilterContext* CreateTempoFilter(AVFilterGraph *graph, AVFilterContext *link, const double& tempo); static AVFilterContext *CreateTempoFilter(AVFilterGraph *graph,
AVFilterContext *link,
const double &tempo);
AVFilterGraph *filter_graph_; AVFilterGraph *filter_graph_;
@@ -76,7 +88,6 @@ private:
AVFrame *in_frame_; AVFrame *in_frame_;
AVFrame *out_frame_; AVFrame *out_frame_;
}; };
} }
+116 -60
View File
@@ -25,23 +25,30 @@
#include "config/config.h" #include "config/config.h"
namespace olive { namespace olive
{
const rational AudioVisualWaveform::kMinimumSampleRate = rational(1, 8); const rational AudioVisualWaveform::kMinimumSampleRate = rational(1, 8);
const rational AudioVisualWaveform::kMaximumSampleRate = 1024; const rational AudioVisualWaveform::kMaximumSampleRate = 1024;
AudioVisualWaveform::AudioVisualWaveform() : AudioVisualWaveform::AudioVisualWaveform()
channels_(0) : channels_(0)
{ {
for (rational i = kMinimumSampleRate; i <= kMaximumSampleRate; i *= 2) { for (rational i = kMinimumSampleRate; i <= kMaximumSampleRate; i *= 2) {
mipmapped_data_.insert({ i, Sample() }); mipmapped_data_.insert({ i, Sample() });
} }
} }
void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational &start, double target_rate, Sample& data, size_t &start_index, size_t &samples_length) void AudioVisualWaveform::OverwriteSamplesFromBuffer(
const SampleBuffer &samples, int sample_rate, const rational &start,
double target_rate, Sample &data, size_t &start_index,
size_t &samples_length)
{ {
start_index = time_to_samples(start, target_rate); start_index = time_to_samples(start, target_rate);
samples_length = time_to_samples(static_cast<double>(samples.sample_count()) / static_cast<double>(sample_rate), target_rate); samples_length =
time_to_samples(static_cast<double>(samples.sample_count()) /
static_cast<double>(sample_rate),
target_rate);
size_t end_index = start_index + samples_length; size_t end_index = start_index + samples_length;
if (data.size() < end_index) { if (data.size() < end_index) {
@@ -52,22 +59,26 @@ void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples
for (size_t i = 0; i < samples_length; i += channels_) { for (size_t i = 0; i < samples_length; i += channels_) {
size_t src_start = qRound((double(i) * chunk_size)) / channels_; size_t src_start = qRound((double(i) * chunk_size)) / channels_;
size_t src_end = qMin(size_t(qRound64((double(i + channels_) * chunk_size))) / channels_, samples.sample_count()); size_t src_end = qMin(
size_t(qRound64((double(i + channels_) * chunk_size))) / channels_,
samples.sample_count());
Sample summary = SumSamples(samples, Sample summary = SumSamples(samples, src_start, src_end - src_start);
src_start,
src_end - src_start);
memcpy(&data.data()[i + start_index], memcpy(&data.data()[i + start_index], summary.data(),
summary.data(),
summary.size() * sizeof(SamplePerChannel)); summary.size() * sizeof(SamplePerChannel));
} }
} }
void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::Sample &input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational &start, double output_rate, AudioVisualWaveform::Sample &output_data) void AudioVisualWaveform::OverwriteSamplesFromMipmap(
const AudioVisualWaveform::Sample &input, double input_sample_rate,
size_t &input_start, size_t &input_length, const rational &start,
double output_rate, AudioVisualWaveform::Sample &output_data)
{ {
size_t start_index = time_to_samples(start, output_rate); size_t start_index = time_to_samples(start, output_rate);
size_t samples_length = time_to_samples(static_cast<double>(input_length / channels_) / input_sample_rate, output_rate); size_t samples_length = time_to_samples(
static_cast<double>(input_length / channels_) / input_sample_rate,
output_rate);
size_t end_index = start_index + samples_length; size_t end_index = start_index + samples_length;
if (output_data.size() < end_index) { if (output_data.size() < end_index) {
@@ -78,10 +89,11 @@ void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::
size_t chunk_size = input_sample_rate / output_rate; size_t chunk_size = input_sample_rate / output_rate;
for (size_t i = 0; i < samples_length; i += channels_) { for (size_t i = 0; i < samples_length; i += channels_) {
Sample summary = ReSumSamples(&input.data()[input_start + (i*chunk_size)], chunk_size * channels_, channels_); Sample summary =
ReSumSamples(&input.data()[input_start + (i * chunk_size)],
chunk_size * channels_, channels_);
memcpy(&output_data.data()[i + start_index], memcpy(&output_data.data()[i + start_index], summary.data(),
summary.data(),
summary.size() * sizeof(SamplePerChannel)); summary.size() * sizeof(SamplePerChannel));
} }
@@ -98,7 +110,9 @@ void AudioVisualWaveform::ValidateVirtualStart(const rational &new_start)
} }
} }
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational &start) void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples,
int sample_rate,
const rational &start)
{ {
if (!channels_) { if (!channels_) {
qWarning() << "Failed to write samples - channel count is zero"; qWarning() << "Failed to write samples - channel count is zero";
@@ -110,7 +124,10 @@ void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int samp
// Process the largest mipmap directly for the samples // Process the largest mipmap directly for the samples
auto current_mipmap = mipmapped_data_.rbegin(); auto current_mipmap = mipmapped_data_.rbegin();
size_t input_start, input_length; size_t input_start, input_length;
OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_, current_mipmap->first.toDouble(), current_mipmap->second, input_start, input_length); OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_,
current_mipmap->first.toDouble(),
current_mipmap->second, input_start,
input_length);
while (true) { while (true) {
// For each smaller mipmap, we just process from the mipmap before it, making each one // For each smaller mipmap, we just process from the mipmap before it, making each one
@@ -121,16 +138,20 @@ void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int samp
break; break;
} }
OverwriteSamplesFromMipmap(previous_mipmap->second, previous_mipmap->first.toDouble(), OverwriteSamplesFromMipmap(
input_start, input_length, start - virtual_start_, current_mipmap->first.toDouble(), previous_mipmap->second, previous_mipmap->first.toDouble(),
current_mipmap->second); input_start, input_length, start - virtual_start_,
current_mipmap->first.toDouble(), current_mipmap->second);
} }
rational sample_length(samples.sample_count(), sample_rate); rational sample_length(samples.sample_count(), sample_rate);
length_ = qMax(length_, start + sample_length); length_ = qMax(length_, start + sample_length);
} }
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const rational &dest, const rational& offset, const rational& length) void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums,
const rational &dest,
const rational &offset,
const rational &length)
{ {
ValidateVirtualStart(dest); ValidateVirtualStart(dest);
@@ -143,7 +164,8 @@ void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const r
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
// Get our destination sample // Get our destination sample
size_t our_start_index = time_to_samples(dest - virtual_start_, rate_dbl); size_t our_start_index =
time_to_samples(dest - virtual_start_, rate_dbl);
// Get our source sample // Get our source sample
size_t their_start_index = time_to_samples(offset, rate_dbl); size_t their_start_index = time_to_samples(offset, rate_dbl);
@@ -166,15 +188,19 @@ void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const r
our_arr.resize(end_index); our_arr.resize(end_index);
} }
memcpy(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel), memcpy(reinterpret_cast<char *>(our_arr.data()) +
reinterpret_cast<const char*>(their_arr.data()) + their_start_index * sizeof(SamplePerChannel), our_start_index * sizeof(SamplePerChannel),
reinterpret_cast<const char *>(their_arr.data()) +
their_start_index * sizeof(SamplePerChannel),
copy_len * sizeof(SamplePerChannel)); copy_len * sizeof(SamplePerChannel));
} }
length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset : length)); length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset :
length));
} }
void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational &length) void AudioVisualWaveform::OverwriteSilence(const rational &start,
const rational &length)
{ {
ValidateVirtualStart(start); ValidateVirtualStart(start);
@@ -186,7 +212,8 @@ void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
// Get our destination sample // Get our destination sample
size_t our_start_index = time_to_samples(start - virtual_start_, rate_dbl); size_t our_start_index =
time_to_samples(start - virtual_start_, rate_dbl);
size_t our_length_index = time_to_samples(length, rate_dbl); size_t our_length_index = time_to_samples(length, rate_dbl);
size_t our_end_index = our_start_index + our_length_index; size_t our_end_index = our_start_index + our_length_index;
@@ -194,7 +221,9 @@ void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational
our_arr.resize(our_end_index); our_arr.resize(our_end_index);
} }
memset(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel), 0, our_length_index * sizeof(SamplePerChannel)); memset(reinterpret_cast<char *>(our_arr.data()) +
our_start_index * sizeof(SamplePerChannel),
0, our_length_index * sizeof(SamplePerChannel));
} }
length_ = qMax(length_, start + length); length_ = qMax(length_, start + length);
@@ -242,7 +271,8 @@ AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const
return mid; return mid;
} }
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset, const rational &length) const AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset,
const rational &length) const
{ {
AudioVisualWaveform mid = *this; AudioVisualWaveform mid = *this;
@@ -276,7 +306,9 @@ void AudioVisualWaveform::TrimRange(const rational &in, const rational &length)
Resize(length); Resize(length);
} }
AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const rational &start, const rational &length) const AudioVisualWaveform::Sample
AudioVisualWaveform::GetSummaryFromTime(const rational &start,
const rational &length) const
{ {
// Find mipmap that requires // Find mipmap that requires
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble()); auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());
@@ -294,14 +326,16 @@ AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const ration
// Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the // Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the
// array and nothing can be returned. // array and nothing can be returned.
if (sample_length > 0) { if (sample_length > 0) {
return ReSumSamples(&mipmap_data.data()[start_sample], sample_length, channels_); return ReSumSamples(&mipmap_data.data()[start_sample], sample_length,
channels_);
} }
// Return null samples // Return null samples
return AudioVisualWaveform::Sample(channel_count(), { 0, 0 }); return AudioVisualWaveform::Sample(channel_count(), { 0, 0 });
} }
void ExpandMinMaxChannel(const float *a, size_t length, float &min_val, float &max_val) void ExpandMinMaxChannel(const float *a, size_t length, float &min_val,
float &max_val)
{ {
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM) #if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
// SSE optimized // SSE optimized
@@ -344,13 +378,18 @@ void ExpandMinMaxChannel(const float *a, size_t length, float &min_val, float &m
#endif #endif
} }
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index, size_t length) AudioVisualWaveform::Sample
AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index,
size_t length)
{ {
int channels = samples.audio_params().channel_count(); int channels = samples.audio_params().channel_count();
AudioVisualWaveform::Sample summed_samples(channels); AudioVisualWaveform::Sample summed_samples(channels);
for (int channel=0; channel<samples.audio_params().channel_count(); channel++) { for (int channel = 0; channel < samples.audio_params().channel_count();
ExpandMinMaxChannel(samples.data(channel) + start_index, length, summed_samples[channel].min, summed_samples[channel].max); channel++) {
ExpandMinMaxChannel(samples.data(channel) + start_index, length,
summed_samples[channel].min,
summed_samples[channel].max);
} }
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip // for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
@@ -361,15 +400,16 @@ AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &
return summed_samples; return summed_samples;
} }
AudioVisualWaveform::Sample AudioVisualWaveform::ReSumSamples(const SamplePerChannel* samples, AudioVisualWaveform::Sample
size_t nb_samples, AudioVisualWaveform::ReSumSamples(const SamplePerChannel *samples,
int nb_channels) size_t nb_samples, int nb_channels)
{ {
AudioVisualWaveform::Sample summed_samples(nb_channels); AudioVisualWaveform::Sample summed_samples(nb_channels);
for (size_t i = 0; i < nb_samples; i += nb_channels) { for (size_t i = 0; i < nb_samples; i += nb_channels) {
for (int j = 0; j < nb_channels; j++) { for (int j = 0; j < nb_channels; j++) {
const AudioVisualWaveform::SamplePerChannel& sample = samples[i + j]; const AudioVisualWaveform::SamplePerChannel &sample =
samples[i + j];
if (sample.min < summed_samples[j].min) { if (sample.min < summed_samples[j].min) {
summed_samples[j].min = sample.min; summed_samples[j].min = sample.min;
@@ -384,13 +424,13 @@ AudioVisualWaveform::Sample AudioVisualWaveform::ReSumSamples(const SamplePerCha
return summed_samples; return summed_samples;
} }
template <typename T> template <typename T> inline int round_away_from_zero(T t)
inline int round_away_from_zero(T t)
{ {
return (t < 0) ? std::floor(t) : std::ceil(t); return (t < 0) ? std::floor(t) : std::ceil(t);
} }
void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample& sample, int x, int y, int height, bool rectified) void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample &sample,
int x, int y, int height, bool rectified)
{ {
if (sample.empty()) { if (sample.empty()) {
return; return;
@@ -408,24 +448,29 @@ void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample& sample, in
int diff = round_away_from_zero((max - min) * channel_half_height); int diff = round_away_from_zero((max - min) * channel_half_height);
painter->drawLine(x, painter->drawLine(x, channel_bottom - diff, x, channel_bottom);
channel_bottom - diff,
x,
channel_bottom);
} else { } else {
int channel_mid = y + channel_height * i + channel_half_height; int channel_mid = y + channel_height * i + channel_half_height;
// We subtract the sample so that positive Y values go up on the screen rather than down, // We subtract the sample so that positive Y values go up on the screen rather than down,
// which is how waveforms are usually rendered // which is how waveforms are usually rendered
painter->drawLine(x, painter->drawLine(
channel_mid - round_away_from_zero(min * static_cast<float>(channel_half_height)),
x, x,
channel_mid - round_away_from_zero(max * static_cast<float>(channel_half_height))); channel_mid -
round_away_from_zero(
min * static_cast<float>(channel_half_height)),
x,
channel_mid -
round_away_from_zero(
max * static_cast<float>(channel_half_height)));
} }
} }
} }
void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, const double& scale, const AudioVisualWaveform &samples, const rational& start_time) void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect &rect,
const double &scale,
const AudioVisualWaveform &samples,
const rational &start_time)
{ {
if (samples.mipmapped_data_.empty()) { if (samples.mipmapped_data_.empty()) {
return; return;
@@ -437,7 +482,8 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
const Sample &arr = using_mipmap->second; const Sample &arr = using_mipmap->second;
size_t start_sample_index = samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl); size_t start_sample_index =
samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl);
if (start_sample_index >= arr.size()) { if (start_sample_index >= arr.size()) {
return; return;
@@ -464,12 +510,18 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
break; break;
} }
next_sample_index = std::min(arr.size(), next_sample_index = std::min(
size_t(start_sample_index + std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) / scale) * samples.channel_count())); arr.size(),
size_t(start_sample_index +
std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) /
scale) *
samples.channel_count()));
if (summary_index != sample_index) { if (summary_index != sample_index) {
summary = AudioVisualWaveform::ReSumSamples(&arr.at(sample_index), summary = AudioVisualWaveform::ReSumSamples(
qMax(size_t(samples.channel_count()), next_sample_index - sample_index), &arr.at(sample_index),
qMax(size_t(samples.channel_count()),
next_sample_index - sample_index),
samples.channel_count()); samples.channel_count());
summary_index = sample_index; summary_index = sample_index;
} }
@@ -478,20 +530,24 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
} }
} }
size_t AudioVisualWaveform::time_to_samples(const rational &time, double sample_rate) const size_t AudioVisualWaveform::time_to_samples(const rational &time,
double sample_rate) const
{ {
return time_to_samples(time.toDouble(), sample_rate); return time_to_samples(time.toDouble(), sample_rate);
} }
size_t AudioVisualWaveform::time_to_samples(const double &time, double sample_rate) const size_t AudioVisualWaveform::time_to_samples(const double &time,
double sample_rate) const
{ {
return std::floor(time * sample_rate) * channels_; return std::floor(time * sample_rate) * channels_;
} }
std::map<rational, AudioVisualWaveform::Sample>::const_iterator AudioVisualWaveform::GetMipmapForScale(double scale) const std::map<rational, AudioVisualWaveform::Sample>::const_iterator
AudioVisualWaveform::GetMipmapForScale(double scale) const
{ {
// Find largest mipmap for this scale (or the largest if we don't find one sufficient) // Find largest mipmap for this scale (or the largest if we don't find one sufficient)
for (auto it=mipmapped_data_.cbegin(); it!=mipmapped_data_.cend(); it++) { for (auto it = mipmapped_data_.cbegin(); it != mipmapped_data_.cend();
it++) {
if (it->first.toDouble() >= scale) { if (it->first.toDouble() >= scale) {
return it; return it;
} }
+32 -13
View File
@@ -25,7 +25,8 @@
#include <QPainter> #include <QPainter>
#include <QVector> #include <QVector>
namespace olive { namespace olive
{
using namespace core; using namespace core;
@@ -66,7 +67,8 @@ public:
* *
* Starting at `start`, writes samples over anything in the buffer, expanding it if necessary. * Starting at `start`, writes samples over anything in the buffer, expanding it if necessary.
*/ */
void OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational& start = 0); void OverwriteSamples(const SampleBuffer &samples, int sample_rate,
const rational &start = 0);
/** /**
* @brief Replaces sums at a certain range in this visual waveform * @brief Replaces sums at a certain range in this visual waveform
@@ -87,42 +89,60 @@ public:
* *
* Maximum length of `sums` to overwrite with. * Maximum length of `sums` to overwrite with.
*/ */
void OverwriteSums(const AudioVisualWaveform& sums, const rational& dest, const rational& offset = 0, const rational &length = 0); void OverwriteSums(const AudioVisualWaveform &sums, const rational &dest,
const rational &offset = 0, const rational &length = 0);
void OverwriteSilence(const rational &start, const rational &length); void OverwriteSilence(const rational &start, const rational &length);
void TrimIn(rational length); void TrimIn(rational length);
AudioVisualWaveform Mid(const rational &offset) const; AudioVisualWaveform Mid(const rational &offset) const;
AudioVisualWaveform Mid(const rational &offset, const rational &length) const; AudioVisualWaveform Mid(const rational &offset,
const rational &length) const;
void Resize(const rational &length); void Resize(const rational &length);
void TrimRange(const rational &in, const rational &length); void TrimRange(const rational &in, const rational &length);
Sample GetSummaryFromTime(const rational& start, const rational& length) const; Sample GetSummaryFromTime(const rational &start,
const rational &length) const;
static Sample SumSamples(const SampleBuffer &samples, size_t start_index, size_t length); static Sample SumSamples(const SampleBuffer &samples, size_t start_index,
size_t length);
static Sample ReSumSamples(const SamplePerChannel *samples, size_t nb_samples, int nb_channels); static Sample ReSumSamples(const SamplePerChannel *samples,
size_t nb_samples, int nb_channels);
static void DrawSample(QPainter* painter, const Sample &sample, int x, int y, int height, bool rectified); static void DrawSample(QPainter *painter, const Sample &sample, int x,
int y, int height, bool rectified);
static void DrawWaveform(QPainter* painter, const QRect &rect, const double &scale, const AudioVisualWaveform& samples, const rational &start_time); static void DrawWaveform(QPainter *painter, const QRect &rect,
const double &scale,
const AudioVisualWaveform &samples,
const rational &start_time);
// Must be a power of 2 // Must be a power of 2
static const rational kMinimumSampleRate; static const rational kMinimumSampleRate;
static const rational kMaximumSampleRate; static const rational kMaximumSampleRate;
private: private:
void OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational& start, double target_rate, Sample &data, size_t &start_index, size_t &samples_length); void OverwriteSamplesFromBuffer(const SampleBuffer &samples,
int sample_rate, const rational &start,
double target_rate, Sample &data,
size_t &start_index,
size_t &samples_length);
void OverwriteSamplesFromMipmap(const Sample& input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational& start, double output_rate, Sample &output_data); void OverwriteSamplesFromMipmap(const Sample &input,
double input_sample_rate,
size_t &input_start, size_t &input_length,
const rational &start, double output_rate,
Sample &output_data);
size_t time_to_samples(const rational &time, double sample_rate) const; size_t time_to_samples(const rational &time, double sample_rate) const;
size_t time_to_samples(const double &time, double sample_rate) const; size_t time_to_samples(const double &time, double sample_rate) const;
std::map<rational, Sample>::const_iterator GetMipmapForScale(double scale) const; std::map<rational, Sample>::const_iterator
GetMipmapForScale(double scale) const;
void ValidateVirtualStart(const rational &new_start); void ValidateVirtualStart(const rational &new_start);
@@ -133,7 +153,6 @@ private:
std::map<rational, Sample> mipmapped_data_; std::map<rational, Sample> mipmapped_data_;
rational length_; rational length_;
}; };
} }
+2 -2
View File
@@ -20,11 +20,11 @@
#include "cliexportmanager.h" #include "cliexportmanager.h"
namespace olive { namespace olive
{
CLIExportManager::CLIExportManager() CLIExportManager::CLIExportManager()
{ {
} }
} }
+3 -3
View File
@@ -23,10 +23,10 @@
#include "task/export/export.h" #include "task/export/export.h"
namespace olive { namespace olive
class CLIExportManager : public QObject
{ {
class CLIExportManager : public QObject {
public: public:
CLIExportManager(); CLIExportManager();
}; };
+9 -7
View File
@@ -22,13 +22,14 @@
#include <iostream> #include <iostream>
namespace olive { namespace olive
{
CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) : CLIProgressDialog::CLIProgressDialog(const QString &title, QObject *parent)
QObject(parent), : QObject(parent)
title_(title), , title_(title)
progress_(-1), , progress_(-1)
drawn_(false) , drawn_(false)
{ {
SetProgress(0); SetProgress(0);
} }
@@ -51,7 +52,8 @@ void CLIProgressDialog::Update()
QString sized_title = title_; QString sized_title = title_;
if (title_.size() > title_columns) { if (title_.size() > title_columns) {
sized_title = title_.left(title_columns - 3).append(QStringLiteral("...")); sized_title =
title_.left(title_columns - 3).append(QStringLiteral("..."));
} else { } else {
sized_title = title_; sized_title = title_;
} }
+3 -4
View File
@@ -26,10 +26,10 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
class CLIProgressDialog : public QObject
{ {
class CLIProgressDialog : public QObject {
public: public:
CLIProgressDialog(const QString &title, QObject *parent = nullptr); CLIProgressDialog(const QString &title, QObject *parent = nullptr);
@@ -44,7 +44,6 @@ private:
double progress_; double progress_;
bool drawn_; bool drawn_;
}; };
} }
+5 -4
View File
@@ -20,11 +20,12 @@
#include "clitaskdialog.h" #include "clitaskdialog.h"
namespace olive { namespace olive
{
CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) : CLITaskDialog::CLITaskDialog(Task *task, QObject *parent)
CLIProgressDialog(task->GetTitle(), parent), : CLIProgressDialog(task->GetTitle(), parent)
task_(task) , task_(task)
{ {
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress); connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
} }
+3 -4
View File
@@ -24,10 +24,10 @@
#include "cli/cliprogress/cliprogressdialog.h" #include "cli/cliprogress/cliprogressdialog.h"
#include "task/task.h" #include "task/task.h"
namespace olive { namespace olive
class CLITaskDialog : public CLIProgressDialog
{ {
class CLITaskDialog : public CLIProgressDialog {
Q_OBJECT Q_OBJECT
public: public:
CLITaskDialog(Task *task, QObject *parent = nullptr); CLITaskDialog(Task *task, QObject *parent = nullptr);
@@ -36,7 +36,6 @@ public:
private: private:
Task *task_; Task *task_;
}; };
} }
+23 -9
View File
@@ -4,17 +4,21 @@
#include "task/taskmanager.h" #include "task/taskmanager.h"
namespace olive { namespace olive
{
ConformManager *ConformManager::instance_ = nullptr; ConformManager *ConformManager::instance_ = nullptr;
ConformManager::Conform ConformManager::GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params, bool wait) ConformManager::Conform ConformManager::GetConformState(
const QString &decoder_id, const QString &cache_path,
const Decoder::CodecStream &stream, const AudioParams &params, bool wait)
{ {
// Mutex because we'll need to check the status of a conform task // Mutex because we'll need to check the status of a conform task
QMutexLocker locker(&mutex_); QMutexLocker locker(&mutex_);
// Return existing conform if exists // Return existing conform if exists
QVector<QString> filenames = GetConformedFilename(cache_path, stream, params); QVector<QString> filenames =
GetConformedFilename(cache_path, stream, params);
if (AllConformsExist(filenames)) { if (AllConformsExist(filenames)) {
return { kConformExists, filenames, nullptr }; return { kConformExists, filenames, nullptr };
} }
@@ -39,12 +43,17 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i
working_filenames[i].append(QStringLiteral(".working")); working_filenames[i].append(QStringLiteral(".working"));
} }
conforming_task = new ConformTask(decoder_id, stream, params, working_filenames); conforming_task =
connect(conforming_task, &ConformTask::Finished, this, &ConformManager::ConformTaskFinished); new ConformTask(decoder_id, stream, params, working_filenames);
connect(conforming_task, &ConformTask::Finished, this,
&ConformManager::ConformTaskFinished);
conforming_task->moveToThread(TaskManager::instance()->thread()); conforming_task->moveToThread(TaskManager::instance()->thread());
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask", Qt::QueuedConnection, Q_ARG(Task *, conforming_task)); QMetaObject::invokeMethod(TaskManager::instance(), "AddTask",
Qt::QueuedConnection,
Q_ARG(Task *, conforming_task));
conforming_.append({stream, params, conforming_task, working_filenames, filenames}); conforming_.append(
{ stream, params, conforming_task, working_filenames, filenames });
} }
if (wait) { if (wait) {
@@ -57,12 +66,17 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i
return { kConformGenerating, QVector<QString>(), conforming_task }; return { kConformGenerating, QVector<QString>(), conforming_task };
} }
QVector<QString> ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params) QVector<QString>
ConformManager::GetConformedFilename(const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params)
{ {
QVector<QString> filenames(params.channel_count()); QVector<QString> filenames(params.channel_count());
for (int i = 0; i < filenames.size(); i++) { for (int i = 0; i < filenames.size(); i++) {
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.%6.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()), QString index_fn =
QStringLiteral("%1-%2.%3.%4.%5.%6.pcm")
.arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
QString::number(stream.stream()), QString::number(stream.stream()),
QString::number(params.sample_rate()), QString::number(params.sample_rate()),
QString::number(params.format()), QString::number(params.format()),
+13 -11
View File
@@ -7,10 +7,10 @@
#include "decoder.h" #include "decoder.h"
#include "task/conform/conform.h" #include "task/conform/conform.h"
namespace olive { namespace olive
class ConformManager : public QObject
{ {
class ConformManager : public QObject {
Q_OBJECT Q_OBJECT
public: public:
static void CreateInstance() static void CreateInstance()
@@ -31,10 +31,7 @@ public:
return instance_; return instance_;
} }
enum ConformState { enum ConformState { kConformExists, kConformGenerating };
kConformExists,
kConformGenerating
};
struct Conform { struct Conform {
ConformState state; ConformState state;
@@ -47,7 +44,10 @@ public:
* *
* Thread-safe. * Thread-safe.
*/ */
Conform GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params, bool wait); Conform GetConformState(const QString &decoder_id,
const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params, bool wait);
signals: signals:
void ConformReady(); void ConformReady();
@@ -74,15 +74,17 @@ private:
/** /**
* @brief Get the destination filename of an audio stream conformed to a set of parameters * @brief Get the destination filename of an audio stream conformed to a set of parameters
*/ */
static QVector<QString> GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params); static QVector<QString>
GetConformedFilename(const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params);
static bool AllConformsExist(const QVector<QString> &filenames); static bool AllConformsExist(const QVector<QString> &filenames);
private slots: private slots:
void ConformTaskFinished(Task *task, bool succeeded); void ConformTaskFinished(Task *task, bool succeeded);
}; };
} } // namespace olive
#endif // CONFORMMANAGER_H #endif // CONFORMMANAGER_H
+55 -25
View File
@@ -33,12 +33,13 @@
#include "node/project.h" #include "node/project.h"
#include "task/taskmanager.h" #include "task/taskmanager.h"
namespace olive { namespace olive
{
const rational Decoder::kAnyTimecode = RATIONAL_MIN; const rational Decoder::kAnyTimecode = RATIONAL_MIN;
Decoder::Decoder() : Decoder::Decoder()
cached_texture_(nullptr) : cached_texture_(nullptr)
{ {
UpdateLastAccessed(); UpdateLastAccessed();
} }
@@ -59,7 +60,8 @@ bool Decoder::Open(const CodecStream &stream)
if (stream_ == stream) { if (stream_ == stream) {
return true; return true;
} else { } else {
qWarning() << "Tried to open a decoder that was already open with another stream"; qWarning()
<< "Tried to open a decoder that was already open with another stream";
return false; return false;
} }
} else { } else {
@@ -84,7 +86,8 @@ bool Decoder::Open(const CodecStream &stream)
return true; return true;
} else { } else {
// Unset stream // Unset stream
qCritical() << "Failed to open" << stream_.filename() << "stream" << stream_.stream(); qCritical() << "Failed to open" << stream_.filename() << "stream"
<< stream_.stream();
CloseInternal(); CloseInternal();
stream_.Reset(); stream_.Reset();
return false; return false;
@@ -112,7 +115,8 @@ TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
return nullptr; return nullptr;
} }
if (cached_texture_ && cached_time_ == p.time && cached_divider_ == p.divider) { if (cached_texture_ && cached_time_ == p.time &&
cached_divider_ == p.divider) {
return cached_texture_; return cached_texture_;
} }
@@ -123,7 +127,10 @@ TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
return cached_texture_; return cached_texture_;
} }
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams &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_);
@@ -140,14 +147,17 @@ Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const Ti
} }
// Get conform state from ConformManager // Get conform state from ConformManager
ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, params, (mode == RenderMode::kOnline)); ConformManager::Conform conform =
ConformManager::instance()->GetConformState(
id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
if (conform.state == ConformManager::kConformGenerating) { if (conform.state == ConformManager::kConformGenerating) {
// If we need the task, it's available in `conform.task` // If we need the task, it's available in `conform.task`
return kWaitingForConform; return kWaitingForConform;
} }
// See if we got the conform // See if we got the conform
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode, params)) { if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode,
params)) {
return kOK; return kOK;
} else { } else {
return kUnknownError; return kUnknownError;
@@ -175,7 +185,8 @@ void Decoder::Close()
} }
} }
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams &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);
} }
@@ -217,11 +228,13 @@ DecoderPtr Decoder::CreateFromID(const QString &id)
void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration) void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration)
{ {
if (duration != AV_NOPTS_VALUE && duration != 0) { if (duration != AV_NOPTS_VALUE && duration != 0) {
emit IndexProgress(static_cast<double>(ts) / static_cast<double>(duration)); emit IndexProgress(static_cast<double>(ts) /
static_cast<double>(duration));
} }
} }
QString Decoder::TransformImageSequenceFileName(const QString &filename, const int64_t& number) QString Decoder::TransformImageSequenceFileName(const QString &filename,
const int64_t &number)
{ {
int digit_count = GetImageSequenceDigitCount(filename); int digit_count = GetImageSequenceDigitCount(filename);
@@ -229,10 +242,13 @@ QString Decoder::TransformImageSequenceFileName(const QString &filename, const i
QString original_basename = file_info.completeBaseName(); QString original_basename = file_info.completeBaseName();
QString new_basename = original_basename.left(original_basename.size() - digit_count) QString new_basename =
.append(QStringLiteral("%1").arg(number, digit_count, 10, QChar('0'))); original_basename.left(original_basename.size() - digit_count)
.append(
QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
return file_info.dir().filePath(file_info.fileName().replace(original_basename, new_basename)); return file_info.dir().filePath(
file_info.fileName().replace(original_basename, new_basename));
} }
int Decoder::GetImageSequenceDigitCount(const QString &filename) int Decoder::GetImageSequenceDigitCount(const QString &filename)
@@ -261,7 +277,8 @@ int64_t Decoder::GetImageSequenceIndex(const QString &filename)
QString original_basename = file_info.completeBaseName(); QString original_basename = file_info.completeBaseName();
QString number_only = original_basename.mid(original_basename.size() - digit_count); QString number_only =
original_basename.mid(original_basename.size() - digit_count);
return number_only.toLongLong(); return number_only.toLongLong();
} }
@@ -272,7 +289,9 @@ TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
return nullptr; return nullptr;
} }
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, CancelAtom *cancelled) bool Decoder::ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled)
{ {
Q_UNUSED(filenames) Q_UNUSED(filenames)
Q_UNUSED(cancelled) Q_UNUSED(cancelled)
@@ -280,17 +299,22 @@ bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const Audi
return false; return false;
} }
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &input_params) bool Decoder::RetrieveAudioFromConform(
SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames,
TimeRange range, LoopMode loop_mode, const AudioParams &input_params)
{ {
PlanarFileDevice input; PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) { if (input.open(conform_filenames, QFile::ReadOnly)) {
// Offset range by audio start offset // Offset range by audio start offset
range -= GetAudioStartOffset(); range -= GetAudioStartOffset();
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count(); qint64 read_index = input_params.time_to_bytes(range.in()) /
input_params.channel_count();
qint64 write_index = 0; qint64 write_index = 0;
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel(); const qint64 buffer_length_in_bytes =
sample_buffer.sample_count() *
input_params.bytes_per_sample_per_channel();
while (write_index < buffer_length_in_bytes) { while (write_index < buffer_length_in_bytes) {
if (loop_mode == LoopMode::kLoopModeLoop) { if (loop_mode == LoopMode::kLoopModeLoop) {
@@ -308,15 +332,20 @@ bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVecto
if (read_index < 0) { if (read_index < 0) {
// Reading before 0, write silence here until audio data would actually start // Reading before 0, write silence here until audio data would actually start
write_count = qMin(-read_index, buffer_length_in_bytes); write_count = qMin(-read_index, buffer_length_in_bytes);
sample_buffer.silence_bytes(write_index, write_index + write_count); sample_buffer.silence_bytes(write_index,
write_index + write_count);
} else if (read_index >= input.size()) { } else if (read_index >= input.size()) {
// Reading after data length, write silence until the end of the buffer // Reading after data length, write silence until the end of the buffer
write_count = buffer_length_in_bytes - write_index; write_count = buffer_length_in_bytes - write_index;
sample_buffer.silence_bytes(write_index, write_index + write_count); sample_buffer.silence_bytes(write_index,
write_index + write_count);
} else { } else {
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index); write_count = qMin(input.size() - read_index,
buffer_length_in_bytes - write_index);
input.seek(read_index); input.seek(read_index);
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index); input.read(reinterpret_cast<char **>(
sample_buffer.to_raw_ptrs().data()),
write_count, write_index);
} }
read_index += write_count; read_index += write_count;
@@ -338,7 +367,8 @@ void Decoder::UpdateLastAccessed()
uint qHash(Decoder::CodecStream stream, uint seed) uint qHash(Decoder::CodecStream stream, uint seed)
{ {
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^ qHash(stream.block(), seed); return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^
qHash(stream.block(), seed);
} }
} }
+48 -31
View File
@@ -36,12 +36,17 @@ extern "C" {
#include "render/cancelatom.h" #include "render/cancelatom.h"
#include "render/rendermodes.h" #include "render/rendermodes.h"
namespace olive { namespace olive
{
class Decoder; class Decoder;
using DecoderPtr = std::shared_ptr<Decoder>; using DecoderPtr = std::shared_ptr<Decoder>;
#define DECODER_DEFAULT_DESTRUCTOR(x) virtual ~x() override {CloseInternal();} #define DECODER_DEFAULT_DESTRUCTOR(x) \
virtual ~x() override \
{ \
CloseInternal(); \
}
/** /**
* @brief A decoder's is the main class for bringing external media into Olive * @brief A decoder's is the main class for bringing external media into Olive
@@ -59,15 +64,10 @@ using DecoderPtr = std::shared_ptr<Decoder>;
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService * A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService
* to be utilized in the rest of the rendering pipeline. * to be utilized in the rest of the rendering pipeline.
*/ */
class Decoder : public QObject class Decoder : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
enum RetrieveState { enum RetrieveState { kReady, kFailedToOpen, kIndexUnavailable };
kReady,
kFailedToOpen,
kIndexUnavailable
};
Decoder(); Decoder();
@@ -76,24 +76,29 @@ public:
*/ */
virtual QString id() const = 0; virtual QString id() const = 0;
virtual bool SupportsVideo(){return false;} virtual bool SupportsVideo()
virtual bool SupportsAudio(){return false;} {
return false;
}
virtual bool SupportsAudio()
{
return false;
}
void IncrementAccessTime(qint64 t); void IncrementAccessTime(qint64 t);
class CodecStream class CodecStream {
{
public: public:
CodecStream() : CodecStream()
stream_(-1), : stream_(-1)
block_(nullptr) , block_(nullptr)
{ {
} }
CodecStream(const QString& filename, int stream, Block *block) : CodecStream(const QString &filename, int stream, Block *block)
filename_(filename), : filename_(filename)
stream_(stream), , stream_(stream)
block_(block) , block_(block)
{ {
} }
@@ -138,7 +143,6 @@ public:
int stream_; int stream_;
Block *block_; Block *block_;
}; };
/** /**
@@ -154,8 +158,7 @@ public:
static const rational kAnyTimecode; static const rational kAnyTimecode;
struct RetrieveVideoParams struct RetrieveVideoParams {
{
Renderer *renderer = nullptr; Renderer *renderer = nullptr;
rational time; rational time;
int divider = 1; int divider = 1;
@@ -192,7 +195,10 @@ public:
* *
* This function is thread safe and can only run while the decoder is open. \see Open() * This function is thread safe and can only run while the decoder is open. \see Open()
*/ */
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, LoopMode loop_mode, RenderMode::Mode mode); RetrieveAudioStatus
RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
const AudioParams &params, const QString &cache_path,
LoopMode loop_mode, RenderMode::Mode mode);
/** /**
* @brief Determine the last time this decoder instance was used in any way * @brief Determine the last time this decoder instance was used in any way
@@ -210,7 +216,8 @@ public:
* *
* This function is re-entrant. * This function is re-entrant.
*/ */
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const = 0; virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const = 0;
/** /**
* @brief Closes media/deallocates memory * @brief Closes media/deallocates memory
@@ -222,7 +229,9 @@ public:
/** /**
* @brief Conform audio stream * @brief Conform audio stream
*/ */
bool ConformAudio(const QVector<QString> &output_filenames, const AudioParams &params, CancelAtom *cancelled = nullptr); bool ConformAudio(const QVector<QString> &output_filenames,
const AudioParams &params,
CancelAtom *cancelled = nullptr);
/** /**
* @brief Create a Decoder instance using a Decoder ID * @brief Create a Decoder instance using a Decoder ID
@@ -233,7 +242,8 @@ public:
*/ */
static DecoderPtr CreateFromID(const QString &id); static DecoderPtr CreateFromID(const QString &id);
static QString TransformImageSequenceFileName(const QString& filename, const int64_t& number); static QString TransformImageSequenceFileName(const QString &filename,
const int64_t &number);
static int GetImageSequenceDigitCount(const QString &filename); static int GetImageSequenceDigitCount(const QString &filename);
@@ -272,7 +282,9 @@ protected:
*/ */
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams &p); virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams &p);
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &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);
@@ -286,7 +298,10 @@ protected:
return stream_; return stream_;
} }
virtual rational GetAudioStartOffset() const { return 0; } virtual rational GetAudioStartOffset() const
{
return 0;
}
signals: signals:
/** /**
@@ -298,7 +313,10 @@ signals:
private: private:
void UpdateLastAccessed(); void UpdateLastAccessed();
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &params); bool RetrieveAudioFromConform(SampleBuffer &sample_buffer,
const QVector<QString> &conform_filenames,
TimeRange range, LoopMode loop_mode,
const AudioParams &params);
CodecStream stream_; CodecStream stream_;
@@ -309,7 +327,6 @@ private:
TexturePtr cached_texture_; TexturePtr cached_texture_;
rational cached_time_; rational cached_time_;
int cached_divider_; int cached_divider_;
}; };
uint qHash(Decoder::CodecStream stream, uint seed = 0); uint qHash(Decoder::CodecStream stream, uint seed = 0);
+161 -85
View File
@@ -26,13 +26,16 @@
#include "ffmpeg/ffmpegencoder.h" #include "ffmpeg/ffmpegencoder.h"
#include "oiio/oiioencoder.h" #include "oiio/oiioencoder.h"
namespace olive { namespace olive
{
const QRegularExpression Encoder::kImageSequenceContainsDigits = QRegularExpression(QStringLiteral("\\[[#]+\\]")); const QRegularExpression Encoder::kImageSequenceContainsDigits =
const QRegularExpression Encoder::kImageSequenceRemoveDigits = QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]")); QRegularExpression(QStringLiteral("\\[[#]+\\]"));
const QRegularExpression Encoder::kImageSequenceRemoveDigits =
QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
Encoder::Encoder(const EncodingParams &params) : Encoder::Encoder(const EncodingParams &params)
params_(params) : params_(params)
{ {
} }
@@ -45,9 +48,11 @@ QString Encoder::GetFilenameForFrame(const rational &frame)
{ {
if (params().video_is_image_sequence()) { if (params().video_is_image_sequence()) {
// Transform! // Transform!
int64_t frame_index = Timecode::time_to_timestamp(frame, params().video_params().frame_rate_as_time_base()); int64_t frame_index = Timecode::time_to_timestamp(
frame, params().video_params().frame_rate_as_time_base());
int digits = GetImageSequencePlaceholderDigitCount(params().filename()); int digits = GetImageSequencePlaceholderDigitCount(params().filename());
QString frame_index_str = QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0')); QString frame_index_str =
QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
QString f = params_.filename(); QString f = params_.filename();
f.replace(kImageSequenceContainsDigits, frame_index_str); f.replace(kImageSequenceContainsDigits, frame_index_str);
@@ -82,26 +87,27 @@ QString Encoder::FilenameRemoveDigitPlaceholder(QString filename)
return filename.remove(kImageSequenceRemoveDigits); return filename.remove(kImageSequenceRemoveDigits);
} }
EncodingParams::EncodingParams() : EncodingParams::EncodingParams()
video_enabled_(false), : video_enabled_(false)
video_bit_rate_(0), , video_bit_rate_(0)
video_min_bit_rate_(0), , video_min_bit_rate_(0)
video_max_bit_rate_(0), , video_max_bit_rate_(0)
video_buffer_size_(0), , video_buffer_size_(0)
video_threads_(0), , video_threads_(0)
video_is_image_sequence_(false), , video_is_image_sequence_(false)
audio_enabled_(false), , audio_enabled_(false)
audio_bit_rate_(0), , audio_bit_rate_(0)
subtitles_enabled_(false), , subtitles_enabled_(false)
subtitles_are_sidecar_(false), , subtitles_are_sidecar_(false)
video_scaling_method_(kStretch), , video_scaling_method_(kStretch)
has_custom_range_(false) , has_custom_range_(false)
{ {
} }
QDir EncodingParams::GetPresetPath() QDir EncodingParams::GetPresetPath()
{ {
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("exportpresets")); return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("exportpresets"));
} }
QStringList EncodingParams::GetListOfPresets() QStringList EncodingParams::GetListOfPresets()
@@ -110,14 +116,16 @@ QStringList EncodingParams::GetListOfPresets()
return d.entryList(QDir::Files); return d.entryList(QDir::Files);
} }
void EncodingParams::EnableVideo(const VideoParams &video_params, const ExportCodec::Codec &vcodec) void EncodingParams::EnableVideo(const VideoParams &video_params,
const ExportCodec::Codec &vcodec)
{ {
video_enabled_ = true; video_enabled_ = true;
video_params_ = video_params; video_params_ = video_params;
video_codec_ = vcodec; video_codec_ = vcodec;
} }
void EncodingParams::EnableAudio(const AudioParams &audio_params, const ExportCodec::Codec &acodec) void EncodingParams::EnableAudio(const AudioParams &audio_params,
const ExportCodec::Codec &acodec)
{ {
audio_enabled_ = true; audio_enabled_ = true;
audio_params_ = audio_params; audio_params_ = audio_params;
@@ -130,7 +138,8 @@ void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
subtitles_codec_ = scodec; subtitles_codec_ = scodec;
} }
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec) void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
const ExportCodec::Codec &scodec)
{ {
subtitles_enabled_ = true; subtitles_enabled_ = true;
subtitles_are_sidecar_ = true; subtitles_are_sidecar_ = true;
@@ -159,7 +168,8 @@ bool EncodingParams::Load(QXmlStreamReader *reader)
if (reader->name() == QStringLiteral("export")) { if (reader->name() == QStringLiteral("export")) {
int version = 0; int version = 0;
XMLAttributeLoop(reader, attr) { XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("version")) { if (attr.name() == QStringLiteral("version")) {
version = attr.value().toInt(); version = attr.value().toInt();
} }
@@ -195,46 +205,73 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
writer->writeStartElement(QStringLiteral("export")); writer->writeStartElement(QStringLiteral("export"));
writer->writeAttribute(QStringLiteral("version"), QString::number(kEncoderParamsVersion)); writer->writeAttribute(QStringLiteral("version"),
QString::number(kEncoderParamsVersion));
writer->writeTextElement(QStringLiteral("filename"), filename_); writer->writeTextElement(QStringLiteral("filename"), filename_);
writer->writeTextElement(QStringLiteral("format"), QString::number(format_)); writer->writeTextElement(QStringLiteral("format"),
QString::number(format_));
writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_)); writer->writeTextElement(QStringLiteral("range"),
writer->writeTextElement(QStringLiteral("customrangein"), QString::fromStdString(custom_range_.in().toString())); QString::number(has_custom_range_));
writer->writeTextElement(QStringLiteral("customrangeout"), QString::fromStdString(custom_range_.out().toString())); writer->writeTextElement(
QStringLiteral("customrangein"),
QString::fromStdString(custom_range_.in().toString()));
writer->writeTextElement(
QStringLiteral("customrangeout"),
QString::fromStdString(custom_range_.out().toString()));
writer->writeStartElement(QStringLiteral("video")); writer->writeStartElement(QStringLiteral("video"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(video_enabled_)); writer->writeAttribute(QStringLiteral("enabled"),
QString::number(video_enabled_));
if (video_enabled_) { if (video_enabled_) {
writer->writeTextElement(QStringLiteral("codec"), QString::number(video_codec_)); writer->writeTextElement(QStringLiteral("codec"),
writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width())); QString::number(video_codec_));
writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height())); writer->writeTextElement(QStringLiteral("width"),
writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format())); QString::number(video_params_.width()));
writer->writeTextElement(QStringLiteral("pixelaspect"), QString::fromStdString(video_params_.pixel_aspect_ratio().toString())); writer->writeTextElement(QStringLiteral("height"),
writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(video_params_.time_base().toString())); QString::number(video_params_.height()));
writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params_.divider())); writer->writeTextElement(QStringLiteral("format"),
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(video_bit_rate_)); QString::number(video_params_.format()));
writer->writeTextElement(QStringLiteral("minbitrate"), QString::number(video_min_bit_rate_)); writer->writeTextElement(
writer->writeTextElement(QStringLiteral("maxbitrate"), QString::number(video_max_bit_rate_)); QStringLiteral("pixelaspect"),
writer->writeTextElement(QStringLiteral("bufsize"), QString::number(video_buffer_size_)); QString::fromStdString(
writer->writeTextElement(QStringLiteral("threads"), QString::number(video_threads_)); video_params_.pixel_aspect_ratio().toString()));
writer->writeTextElement(
QStringLiteral("timebase"),
QString::fromStdString(video_params_.time_base().toString()));
writer->writeTextElement(QStringLiteral("divider"),
QString::number(video_params_.divider()));
writer->writeTextElement(QStringLiteral("bitrate"),
QString::number(video_bit_rate_));
writer->writeTextElement(QStringLiteral("minbitrate"),
QString::number(video_min_bit_rate_));
writer->writeTextElement(QStringLiteral("maxbitrate"),
QString::number(video_max_bit_rate_));
writer->writeTextElement(QStringLiteral("bufsize"),
QString::number(video_buffer_size_));
writer->writeTextElement(QStringLiteral("threads"),
QString::number(video_threads_));
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_); writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_);
writer->writeTextElement(QStringLiteral("imgseq"), QString::number(video_is_image_sequence_)); writer->writeTextElement(QStringLiteral("imgseq"),
QString::number(video_is_image_sequence_));
writer->writeStartElement(QStringLiteral("color")); writer->writeStartElement(QStringLiteral("color"));
writer->writeTextElement(QStringLiteral("output"), color_transform_.output()); writer->writeTextElement(QStringLiteral("output"),
color_transform_.output());
writer->writeEndElement(); // colortransform writer->writeEndElement(); // colortransform
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_)); writer->writeTextElement(QStringLiteral("vscale"),
QString::number(video_scaling_method_));
if (!video_opts_.isEmpty()) { if (!video_opts_.isEmpty()) {
writer->writeStartElement(QStringLiteral("opts")); writer->writeStartElement(QStringLiteral("opts"));
QHash<QString, QString>::const_iterator i; QHash<QString, QString>::const_iterator i;
for (i=video_opts_.constBegin(); i!=video_opts_.constEnd(); i++) { for (i = video_opts_.constBegin(); i != video_opts_.constEnd();
i++) {
writer->writeStartElement(QStringLiteral("entry")); writer->writeStartElement(QStringLiteral("entry"));
writer->writeTextElement(QStringLiteral("key"), i.key()); writer->writeTextElement(QStringLiteral("key"), i.key());
@@ -251,27 +288,38 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
writer->writeStartElement(QStringLiteral("audio")); writer->writeStartElement(QStringLiteral("audio"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(audio_enabled_)); writer->writeAttribute(QStringLiteral("enabled"),
QString::number(audio_enabled_));
if (audio_enabled_) { if (audio_enabled_) {
writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_)); writer->writeTextElement(QStringLiteral("codec"),
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate())); QString::number(audio_codec_));
writer->writeTextElement(QStringLiteral("samplerate"),
QString::number(audio_params_.sample_rate()));
writer->writeTextElement(QStringLiteral("channellayout"), writer->writeTextElement(
QStringLiteral("channellayout"),
QString::number(audio_params().channel_layout().u.mask)); QString::number(audio_params().channel_layout().u.mask));
writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(audio_params_.format().to_string())); writer->writeTextElement(
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_)); QStringLiteral("format"),
QString::fromStdString(audio_params_.format().to_string()));
writer->writeTextElement(QStringLiteral("bitrate"),
QString::number(audio_bit_rate_));
} }
writer->writeStartElement(QStringLiteral("subtitles")); writer->writeStartElement(QStringLiteral("subtitles"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(subtitles_enabled_)); writer->writeAttribute(QStringLiteral("enabled"),
QString::number(subtitles_enabled_));
if (subtitles_enabled_) { if (subtitles_enabled_) {
writer->writeTextElement(QStringLiteral("sidecar"), QString::number(subtitles_are_sidecar_)); writer->writeTextElement(QStringLiteral("sidecar"),
writer->writeTextElement(QStringLiteral("sidecarformat"), QString::number(subtitle_sidecar_fmt_)); QString::number(subtitles_are_sidecar_));
writer->writeTextElement(QStringLiteral("sidecarformat"),
QString::number(subtitle_sidecar_fmt_));
writer->writeTextElement(QStringLiteral("codec"), QString::number(subtitles_codec_)); writer->writeTextElement(QStringLiteral("codec"),
QString::number(subtitles_codec_));
} }
writer->writeEndElement(); // subtitles writer->writeEndElement(); // subtitles
@@ -324,7 +372,8 @@ Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
return kEncoderTypeNone; return kEncoderTypeNone;
} }
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams &params) Encoder *Encoder::CreateFromFormat(ExportFormat::Format f,
const EncodingParams &params)
{ {
return CreateFromID(GetTypeFromFormat(f), params); return CreateFromID(GetTypeFromFormat(f), params);
} }
@@ -339,12 +388,14 @@ QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
return QStringList(); return QStringList();
} }
std::vector<SampleFormat> Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const std::vector<SampleFormat>
Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
{ {
return std::vector<SampleFormat>(); return std::vector<SampleFormat>();
} }
QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method, QMatrix4x4
EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
int source_width, int source_height, int source_width, int source_height,
int dest_width, int dest_height) int dest_width, int dest_height)
{ {
@@ -354,8 +405,10 @@ QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod met
return preview_matrix; return preview_matrix;
} }
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height); float export_ar =
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height); static_cast<float>(dest_width) / static_cast<float>(dest_height);
float source_ar =
static_cast<float>(source_width) / static_cast<float>(source_height);
if (qFuzzyCompare(export_ar, source_ar)) { if (qFuzzyCompare(export_ar, source_ar)) {
return preview_matrix; return preview_matrix;
@@ -378,15 +431,19 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
if (reader->name() == QStringLiteral("filename")) { if (reader->name() == QStringLiteral("filename")) {
filename_ = reader->readElementText(); filename_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("format")) { } else if (reader->name() == QStringLiteral("format")) {
format_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt()); format_ = static_cast<ExportFormat::Format>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("range")) { } else if (reader->name() == QStringLiteral("range")) {
has_custom_range_ = reader->readElementText().toInt(); has_custom_range_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("customrangein")) { } else if (reader->name() == QStringLiteral("customrangein")) {
custom_range_in = rational::fromString(reader->readElementText().toStdString()); custom_range_in =
rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("customrangeout")) { } else if (reader->name() == QStringLiteral("customrangeout")) {
custom_range_out = rational::fromString(reader->readElementText().toStdString()); custom_range_out =
rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("video")) { } else if (reader->name() == QStringLiteral("video")) {
XMLAttributeLoop(reader, attr) { XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) { if (attr.name() == QStringLiteral("enabled")) {
video_enabled_ = attr.value().toInt(); video_enabled_ = attr.value().toInt();
} }
@@ -394,25 +451,32 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) { if (reader->name() == QStringLiteral("codec")) {
video_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt()); video_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("width")) { } else if (reader->name() == QStringLiteral("width")) {
video_params_.set_width(reader->readElementText().toInt()); video_params_.set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) { } else if (reader->name() == QStringLiteral("height")) {
video_params_.set_height(reader->readElementText().toInt()); video_params_.set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("format")) { } else if (reader->name() == QStringLiteral("format")) {
video_params_.set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt())); video_params_.set_format(static_cast<PixelFormat::Format>(
reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("pixelaspect")) { } else if (reader->name() == QStringLiteral("pixelaspect")) {
video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString())); video_params_.set_pixel_aspect_ratio(rational::fromString(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("timebase")) { } else if (reader->name() == QStringLiteral("timebase")) {
video_params_.set_time_base(rational::fromString(reader->readElementText().toStdString())); video_params_.set_time_base(rational::fromString(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("divider")) { } else if (reader->name() == QStringLiteral("divider")) {
video_params_.set_divider(reader->readElementText().toInt()); video_params_.set_divider(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("bitrate")) { } else if (reader->name() == QStringLiteral("bitrate")) {
video_bit_rate_ = reader->readElementText().toLongLong(); video_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("minbitrate")) { } else if (reader->name() == QStringLiteral("minbitrate")) {
video_min_bit_rate_ = reader->readElementText().toLongLong(); video_min_bit_rate_ =
reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("maxbitrate")) { } else if (reader->name() == QStringLiteral("maxbitrate")) {
video_max_bit_rate_ = reader->readElementText().toLongLong(); video_max_bit_rate_ =
reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("bufsize")) { } else if (reader->name() == QStringLiteral("bufsize")) {
video_buffer_size_ = reader->readElementText().toLongLong(); video_buffer_size_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("threads")) { } else if (reader->name() == QStringLiteral("threads")) {
@@ -420,7 +484,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
} else if (reader->name() == QStringLiteral("pixfmt")) { } else if (reader->name() == QStringLiteral("pixfmt")) {
video_pix_fmt_ = reader->readElementText(); video_pix_fmt_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("imgseq")) { } else if (reader->name() == QStringLiteral("imgseq")) {
video_is_image_sequence_ = reader->readElementText().toInt(); video_is_image_sequence_ =
reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("color")) { } else if (reader->name() == QStringLiteral("color")) {
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("output")) { if (reader->name() == QStringLiteral("output")) {
@@ -430,7 +495,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
} }
} }
} else if (reader->name() == QStringLiteral("vscale")) { } else if (reader->name() == QStringLiteral("vscale")) {
video_scaling_method_ = static_cast<VideoScalingMethod>(reader->readElementText().toInt()); video_scaling_method_ = static_cast<VideoScalingMethod>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("opts")) { } else if (reader->name() == QStringLiteral("opts")) {
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("entry")) { if (reader->name() == QStringLiteral("entry")) {
@@ -438,7 +504,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) { if (reader->name() == QStringLiteral("key")) {
key = reader->readElementText(); key = reader->readElementText();
} else if (reader->name() == QStringLiteral("value")) { } else if (reader->name() ==
QStringLiteral("value")) {
value = reader->readElementText(); value = reader->readElementText();
} else { } else {
reader->skipCurrentElement(); reader->skipCurrentElement();
@@ -459,7 +526,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
video_params_.set_pixel_aspect_ratio(1); video_params_.set_pixel_aspect_ratio(1);
} }
} else if (reader->name() == QStringLiteral("audio")) { } else if (reader->name() == QStringLiteral("audio")) {
XMLAttributeLoop(reader, attr) { XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) { if (attr.name() == QStringLiteral("enabled")) {
audio_enabled_ = attr.value().toInt(); audio_enabled_ = attr.value().toInt();
} }
@@ -467,16 +535,21 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) { if (reader->name() == QStringLiteral("codec")) {
audio_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt()); audio_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("samplerate")) { } else if (reader->name() == QStringLiteral("samplerate")) {
audio_params_.set_sample_rate(reader->readElementText().toInt()); audio_params_.set_sample_rate(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("channellayout")) { } else if (reader->name() == QStringLiteral("channellayout")) {
AVChannelLayout av_channel_layout; AVChannelLayout av_channel_layout;
av_channel_layout_from_mask(&av_channel_layout, reader->readElementText().toLongLong()); av_channel_layout_from_mask(
&av_channel_layout,
reader->readElementText().toLongLong());
audio_params_.set_channel_layout(av_channel_layout); audio_params_.set_channel_layout(av_channel_layout);
av_channel_layout_uninit(&av_channel_layout); av_channel_layout_uninit(&av_channel_layout);
} else if (reader->name() == QStringLiteral("format")) { } else if (reader->name() == QStringLiteral("format")) {
audio_params_.set_format(SampleFormat::from_string(reader->readElementText().toStdString())); audio_params_.set_format(SampleFormat::from_string(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("bitrate")) { } else if (reader->name() == QStringLiteral("bitrate")) {
audio_bit_rate_ = reader->readElementText().toLongLong(); audio_bit_rate_ = reader->readElementText().toLongLong();
} else { } else {
@@ -489,7 +562,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
audio_bit_rate_ = 320000; audio_bit_rate_ = 320000;
} }
} else if (reader->name() == QStringLiteral("subtitles")) { } else if (reader->name() == QStringLiteral("subtitles")) {
XMLAttributeLoop(reader, attr) { XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) { if (attr.name() == QStringLiteral("enabled")) {
subtitles_enabled_ = attr.value().toInt(); subtitles_enabled_ = attr.value().toInt();
} }
@@ -499,9 +573,11 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
if (reader->name() == QStringLiteral("sidecar")) { if (reader->name() == QStringLiteral("sidecar")) {
subtitles_are_sidecar_ = reader->readElementText().toInt(); subtitles_are_sidecar_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("sidecarformat")) { } else if (reader->name() == QStringLiteral("sidecarformat")) {
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt()); subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("codec")) { } else if (reader->name() == QStringLiteral("codec")) {
subtitles_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt()); subtitles_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else { } else {
reader->skipCurrentElement(); reader->skipCurrentElement();
} }
+187 -65
View File
@@ -34,19 +34,15 @@
#include "render/subtitleparams.h" #include "render/subtitleparams.h"
#include "render/videoparams.h" #include "render/videoparams.h"
//这个代码也许是导出编码视频用的? //这个代码也许是导出编码视频用的?
namespace olive { namespace olive
{
class Encoder; class Encoder;
using EncoderPtr = std::shared_ptr<Encoder>; using EncoderPtr = std::shared_ptr<Encoder>;
class EncodingParams class EncodingParams {
{
public: public:
enum VideoScalingMethod { enum VideoScalingMethod { kFit, kStretch, kCrop };
kFit,
kStretch,
kCrop
};
EncodingParams(); EncodingParams();
@@ -58,61 +54,178 @@ public:
return video_enabled_ || audio_enabled_ || subtitles_enabled_; return video_enabled_ || audio_enabled_ || subtitles_enabled_;
} }
void SetFilename(const QString& filename) { filename_ = filename; } void SetFilename(const QString &filename)
{
filename_ = filename;
}
void EnableVideo(const VideoParams& video_params, const ExportCodec::Codec& vcodec); void EnableVideo(const VideoParams &video_params,
void EnableAudio(const AudioParams& audio_params, const ExportCodec::Codec &acodec); const ExportCodec::Codec &vcodec);
void EnableAudio(const AudioParams &audio_params,
const ExportCodec::Codec &acodec);
void EnableSubtitles(const ExportCodec::Codec &scodec); void EnableSubtitles(const ExportCodec::Codec &scodec);
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec); void EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
const ExportCodec::Codec &scodec);
void DisableVideo(); void DisableVideo();
void DisableAudio(); void DisableAudio();
void DisableSubtitles(); void DisableSubtitles();
const ExportFormat::Format &format() const { return format_; } const ExportFormat::Format &format() const
void set_format(const ExportFormat::Format &format) { format_ = format; } {
return format_;
}
void set_format(const ExportFormat::Format &format)
{
format_ = format;
}
void set_video_option(const QString& key, const QString& value) { video_opts_.insert(key, value); } void set_video_option(const QString &key, const QString &value)
void set_video_bit_rate(const int64_t& rate) { video_bit_rate_ = rate; } {
void set_video_min_bit_rate(const int64_t& rate) { video_min_bit_rate_ = rate; } video_opts_.insert(key, value);
void set_video_max_bit_rate(const int64_t& rate) { video_max_bit_rate_ = rate; } }
void set_video_buffer_size(const int64_t& sz) { video_buffer_size_ = sz; } void set_video_bit_rate(const int64_t &rate)
void set_video_threads(const int& threads) { video_threads_ = threads; } {
void set_video_pix_fmt(const QString& s) { video_pix_fmt_ = s; } video_bit_rate_ = rate;
void set_video_is_image_sequence(bool s) { video_is_image_sequence_ = s; } }
void set_color_transform(const ColorTransform& color_transform) { color_transform_ = color_transform; } void set_video_min_bit_rate(const int64_t &rate)
{
video_min_bit_rate_ = rate;
}
void set_video_max_bit_rate(const int64_t &rate)
{
video_max_bit_rate_ = rate;
}
void set_video_buffer_size(const int64_t &sz)
{
video_buffer_size_ = sz;
}
void set_video_threads(const int &threads)
{
video_threads_ = threads;
}
void set_video_pix_fmt(const QString &s)
{
video_pix_fmt_ = s;
}
void set_video_is_image_sequence(bool s)
{
video_is_image_sequence_ = s;
}
void set_color_transform(const ColorTransform &color_transform)
{
color_transform_ = color_transform;
}
const QString& filename() const { return filename_; } const QString &filename() const
{
return filename_;
}
bool video_enabled() const { return video_enabled_; } bool video_enabled() const
const ExportCodec::Codec& video_codec() const { return video_codec_; } {
const VideoParams& video_params() const { return video_params_; } return video_enabled_;
const QHash<QString, QString>& video_opts() const { return video_opts_; } }
QString video_option(const QString &key) const { return video_opts_.value(key); } const ExportCodec::Codec &video_codec() const
bool has_video_opt(const QString &key) const { return video_opts_.contains(key); } {
const int64_t& video_bit_rate() const { return video_bit_rate_; } return video_codec_;
const int64_t& video_min_bit_rate() const { return video_min_bit_rate_; } }
const int64_t& video_max_bit_rate() const { return video_max_bit_rate_; } const VideoParams &video_params() const
const int64_t& video_buffer_size() const { return video_buffer_size_; } {
const int& video_threads() const { return video_threads_; } return video_params_;
const QString& video_pix_fmt() const { return video_pix_fmt_; } }
bool video_is_image_sequence() const { return video_is_image_sequence_; } const QHash<QString, QString> &video_opts() const
const ColorTransform& color_transform() const { return color_transform_; } {
return video_opts_;
}
QString video_option(const QString &key) const
{
return video_opts_.value(key);
}
bool has_video_opt(const QString &key) const
{
return video_opts_.contains(key);
}
const int64_t &video_bit_rate() const
{
return video_bit_rate_;
}
const int64_t &video_min_bit_rate() const
{
return video_min_bit_rate_;
}
const int64_t &video_max_bit_rate() const
{
return video_max_bit_rate_;
}
const int64_t &video_buffer_size() const
{
return video_buffer_size_;
}
const int &video_threads() const
{
return video_threads_;
}
const QString &video_pix_fmt() const
{
return video_pix_fmt_;
}
bool video_is_image_sequence() const
{
return video_is_image_sequence_;
}
const ColorTransform &color_transform() const
{
return color_transform_;
}
bool audio_enabled() const { return audio_enabled_; } bool audio_enabled() const
const ExportCodec::Codec &audio_codec() const { return audio_codec_; } {
const AudioParams& audio_params() const { return audio_params_; } return audio_enabled_;
const int64_t& audio_bit_rate() const { return audio_bit_rate_; } }
const ExportCodec::Codec &audio_codec() const
{
return audio_codec_;
}
const AudioParams &audio_params() const
{
return audio_params_;
}
const int64_t &audio_bit_rate() const
{
return audio_bit_rate_;
}
void set_audio_bit_rate(const int64_t& b) { audio_bit_rate_ = b; } void set_audio_bit_rate(const int64_t &b)
{
audio_bit_rate_ = b;
}
bool subtitles_enabled() const { return subtitles_enabled_; } bool subtitles_enabled() const
bool subtitles_are_sidecar() const { return subtitles_are_sidecar_; } {
ExportFormat::Format subtitle_sidecar_fmt() const { return subtitle_sidecar_fmt_; } return subtitles_enabled_;
ExportCodec::Codec subtitles_codec() const { return subtitles_codec_; } }
bool subtitles_are_sidecar() const
{
return subtitles_are_sidecar_;
}
ExportFormat::Format subtitle_sidecar_fmt() const
{
return subtitle_sidecar_fmt_;
}
ExportCodec::Codec subtitles_codec() const
{
return subtitles_codec_;
}
const rational& GetExportLength() const { return export_length_; } const rational &GetExportLength() const
void SetExportLength(const rational& export_length) { export_length_ = export_length; } {
return export_length_;
}
void SetExportLength(const rational &export_length)
{
export_length_ = export_length;
}
bool Load(QIODevice *device); bool Load(QIODevice *device);
bool Load(QXmlStreamReader *reader); bool Load(QXmlStreamReader *reader);
@@ -120,16 +233,29 @@ public:
void Save(QIODevice *device) const; void Save(QIODevice *device) const;
void Save(QXmlStreamWriter *writer) const; void Save(QXmlStreamWriter *writer) const;
bool has_custom_range() const { return has_custom_range_; } bool has_custom_range() const
const TimeRange& custom_range() const { return custom_range_; } {
return has_custom_range_;
}
const TimeRange &custom_range() const
{
return custom_range_;
}
void set_custom_range(const TimeRange &custom_range) void set_custom_range(const TimeRange &custom_range)
{ {
has_custom_range_ = true; has_custom_range_ = true;
custom_range_ = custom_range; custom_range_ = custom_range;
} }
const VideoScalingMethod& video_scaling_method() const { return video_scaling_method_; } const VideoScalingMethod &video_scaling_method() const
void set_video_scaling_method(const VideoScalingMethod& video_scaling_method) { video_scaling_method_ = video_scaling_method; } {
return video_scaling_method_;
}
void
set_video_scaling_method(const VideoScalingMethod &video_scaling_method)
{
video_scaling_method_ = video_scaling_method;
}
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method, static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
int source_width, int source_height, int source_width, int source_height,
@@ -171,20 +297,14 @@ private:
bool has_custom_range_; bool has_custom_range_;
TimeRange custom_range_; TimeRange custom_range_;
}; };
class Encoder : public QObject class Encoder : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
Encoder(const EncodingParams &params); Encoder(const EncodingParams &params);
enum Type { enum Type { kEncoderTypeNone = -1, kEncoderTypeFFmpeg, kEncoderTypeOIIO };
kEncoderTypeNone = -1,
kEncoderTypeFFmpeg,
kEncoderTypeOIIO
};
/** /**
* @brief Create a Encoder instance using a Encoder ID * @brief Create a Encoder instance using a Encoder ID
@@ -197,12 +317,14 @@ public:
static Type GetTypeFromFormat(ExportFormat::Format f); static Type GetTypeFromFormat(ExportFormat::Format f);
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams &params); static Encoder *CreateFromFormat(ExportFormat::Format f,
const EncodingParams &params);
static Encoder *CreateFromParams(const EncodingParams &params); static Encoder *CreateFromParams(const EncodingParams &params);
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const; virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const; virtual std::vector<SampleFormat>
GetSampleFormatsForCodec(ExportCodec::Codec c) const;
const EncodingParams &params() const; const EncodingParams &params() const;
@@ -229,7 +351,8 @@ public:
public slots: public slots:
virtual bool Open() = 0; virtual bool Open() = 0;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) = 0; virtual bool WriteFrame(olive::FramePtr frame,
olive::core::rational time) = 0;
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0; virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0; virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
@@ -245,7 +368,6 @@ private:
EncodingParams params_; EncodingParams params_;
QString error_; QString error_;
}; };
} }
+2 -1
View File
@@ -25,7 +25,8 @@ extern "C" {
#include <libavutil/pixdesc.h> #include <libavutil/pixdesc.h>
} }
namespace olive { namespace olive
{
QString ExportCodec::GetCodecName(ExportCodec::Codec c) QString ExportCodec::GetCodecName(ExportCodec::Codec c)
{ {
+3 -4
View File
@@ -27,10 +27,10 @@
#include "common/define.h" #include "common/define.h"
#include "render/subtitleparams.h" #include "render/subtitleparams.h"
namespace olive { namespace olive
class ExportCodec : public QObject
{ {
class ExportCodec : public QObject {
Q_OBJECT Q_OBJECT
public: public:
// Only append to this list (never insert) because indexes are used in serialized files // Only append to this list (never insert) because indexes are used in serialized files
@@ -63,7 +63,6 @@ public:
static bool IsCodecAStillImage(Codec c); static bool IsCodecAStillImage(Codec c);
static bool IsCodecLossless(Codec c); static bool IsCodecLossless(Codec c);
}; };
} }
+26 -13
View File
@@ -22,7 +22,8 @@
#include "encoder.h" #include "encoder.h"
namespace olive { namespace olive
{
QString ExportFormat::GetName(olive::ExportFormat::Format f) QString ExportFormat::GetName(olive::ExportFormat::Format f)
{ {
@@ -111,9 +112,11 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
case kFormatDNxHD: case kFormatDNxHD:
return { ExportCodec::kCodecDNxHD }; return { ExportCodec::kCodecDNxHD };
case kFormatMatroska: case kFormatMatroska:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9}; return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265, ExportCodec::kCodecVP9 };
case kFormatMPEG4Video: case kFormatMPEG4Video:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265}; return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265 };
case kFormatOpenEXR: case kFormatOpenEXR:
return { ExportCodec::kCodecOpenEXR }; return { ExportCodec::kCodecOpenEXR };
case kFormatPNG: case kFormatPNG:
@@ -121,7 +124,9 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
case kFormatTIFF: case kFormatTIFF:
return { ExportCodec::kCodecTIFF }; return { ExportCodec::kCodecTIFF };
case kFormatQuickTime: case kFormatQuickTime:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform}; return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265, ExportCodec::kCodecProRes,
ExportCodec::kCodecCineform };
case kFormatWebM: case kFormatWebM:
return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 }; return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 };
case kFormatOgg: case kFormatOgg:
@@ -145,14 +150,21 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
case kFormatDNxHD: case kFormatDNxHD:
return { ExportCodec::kCodecPCM }; return { ExportCodec::kCodecPCM };
case kFormatMatroska: case kFormatMatroska:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC}; return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM,
ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus,
ExportCodec::kCodecFLAC };
case kFormatMPEG4Video: case kFormatMPEG4Video:
case kFormatMPEG4Audio: case kFormatMPEG4Audio:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3}; return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3 };
case kFormatQuickTime: case kFormatQuickTime:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM}; return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM };
case kFormatWebM: case kFormatWebM:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis}; return { ExportCodec::kCodecOpus, ExportCodec::kCodecAAC,
ExportCodec::kCodecMP2, ExportCodec::kCodecMP3,
ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis };
// Audio only formats // Audio only formats
case kFormatWAV: case kFormatWAV:
@@ -164,7 +176,8 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
case kFormatFLAC: case kFormatFLAC:
return { ExportCodec::kCodecFLAC }; return { ExportCodec::kCodecFLAC };
case kFormatOgg: case kFormatOgg:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM}; return { ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis,
ExportCodec::kCodecPCM };
// Video only formats // Video only formats
case kFormatOpenEXR: case kFormatOpenEXR:
@@ -173,7 +186,6 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
case kFormatSRT: case kFormatSRT:
case kFormatCount: case kFormatCount:
break; break;
} }
return {}; return {};
@@ -205,7 +217,8 @@ QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
return {}; return {};
} }
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, ExportCodec::Codec c) QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f,
ExportCodec::Codec c)
{ {
Encoder *e = Encoder::CreateFromFormat(f, EncodingParams()); Encoder *e = Encoder::CreateFromFormat(f, EncodingParams());
QStringList list; QStringList list;
@@ -218,7 +231,8 @@ QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, Export
return list; return list;
} }
std::vector<SampleFormat> ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c) std::vector<SampleFormat>
ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
{ {
std::vector<SampleFormat> f; std::vector<SampleFormat> f;
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams()); Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
@@ -228,7 +242,6 @@ std::vector<SampleFormat> ExportFormat::GetSampleFormatsForCodec(Format format,
delete e; delete e;
} }
return f; return f;
} }
+5 -5
View File
@@ -27,10 +27,10 @@
#include "common/define.h" #include "common/define.h"
#include "exportcodec.h" #include "exportcodec.h"
namespace olive { namespace olive
class ExportFormat : public QObject
{ {
class ExportFormat : public QObject {
Q_OBJECT Q_OBJECT
public: public:
// Only append to this list (never insert) because indexes are used in serialized files // Only append to this list (never insert) because indexes are used in serialized files
@@ -61,8 +61,8 @@ public:
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f); static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c); static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
static std::vector<SampleFormat> GetSampleFormatsForCodec(Format f, ExportCodec::Codec c); static std::vector<SampleFormat>
GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
}; };
} }
+251 -175
View File
@@ -45,16 +45,17 @@ extern "C" {
#include "render/renderer.h" #include "render/renderer.h"
#include "render/subtitleparams.h" #include "render/subtitleparams.h"
namespace olive { namespace olive
{
QVariant Yuv2RgbShader; QVariant Yuv2RgbShader;
QVariant DeinterlaceShader; QVariant DeinterlaceShader;
FFmpegDecoder::FFmpegDecoder() : FFmpegDecoder::FFmpegDecoder()
sws_ctx_(nullptr), : sws_ctx_(nullptr)
working_packet_(nullptr), , working_packet_(nullptr)
cache_at_zero_(false), , cache_at_zero_(false)
cache_at_eof_(false) , cache_at_eof_(false)
{ {
} }
@@ -73,21 +74,22 @@ bool FFmpegDecoder::OpenInternal()
return false; return false;
} }
TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVideoParams &p, const AVFramePtr original) TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
const RetrieveVideoParams &p,
const AVFramePtr original)
{ {
// Determine native format // Determine native format
AVPixelFormat ideal_fmt = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(f->format)); AVPixelFormat ideal_fmt = FFmpegUtils::GetCompatiblePixelFormat(
static_cast<AVPixelFormat>(f->format));
PixelFormat native_fmt = GetNativePixelFormat(ideal_fmt); PixelFormat native_fmt = GetNativePixelFormat(ideal_fmt);
int native_channels = GetNativeChannelCount(ideal_fmt); int native_channels = GetNativeChannelCount(ideal_fmt);
// Set up video params // Set up video params
VideoParams vp(original->width, VideoParams vp(original->width, original->height, native_fmt,
original->height,
native_fmt,
native_channels, native_channels,
av_guess_sample_aspect_ratio(instance_.fmt_ctx(), instance_.avstream(), nullptr), av_guess_sample_aspect_ratio(instance_.fmt_ctx(),
VideoParams::kInterlaceNone, instance_.avstream(), nullptr),
p.divider); VideoParams::kInterlaceNone, p.divider);
// Create texture // Create texture
TexturePtr tex = p.renderer->CreateTexture(vp); TexturePtr tex = p.renderer->CreateTexture(vp);
@@ -101,12 +103,13 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
case AV_PIX_FMT_YUV444P10LE: case AV_PIX_FMT_YUV444P10LE:
case AV_PIX_FMT_YUV420P12LE: case AV_PIX_FMT_YUV420P12LE:
case AV_PIX_FMT_YUV422P12LE: case AV_PIX_FMT_YUV422P12LE:
case AV_PIX_FMT_YUV444P12LE: case AV_PIX_FMT_YUV444P12LE: {
{
// Run through YUV to RGB shader // Run through YUV to RGB shader
if (Yuv2RgbShader.isNull()) { if (Yuv2RgbShader.isNull()) {
// Compile shader // Compile shader
Yuv2RgbShader = p.renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/yuv2rgb.frag")))); Yuv2RgbShader = p.renderer->CreateNativeShader(
ShaderCode(FileFunctions::ReadFileAsString(
QStringLiteral(":/shaders/yuv2rgb.frag"))));
if (Yuv2RgbShader.isNull()) { if (Yuv2RgbShader.isNull()) {
return nullptr; return nullptr;
} }
@@ -142,7 +145,8 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
plane_params.set_channel_count(1); plane_params.set_channel_count(1);
plane_params.set_format(native_fmt); plane_params.set_format(native_fmt);
TexturePtr y_plane = p.renderer->CreateTexture(plane_params, hw_in->data[0], hw_in->linesize[0] / px_size); TexturePtr y_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[0], hw_in->linesize[0] / px_size);
switch (f->format) { switch (f->format) {
case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV420P:
@@ -163,21 +167,37 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
break; break;
} }
TexturePtr u_plane = p.renderer->CreateTexture(plane_params, hw_in->data[1], hw_in->linesize[1] / px_size); TexturePtr u_plane = p.renderer->CreateTexture(
TexturePtr v_plane = p.renderer->CreateTexture(plane_params, hw_in->data[2], hw_in->linesize[2] / px_size); plane_params, hw_in->data[1], hw_in->linesize[1] / px_size);
TexturePtr v_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[2], hw_in->linesize[2] / px_size);
ShaderJob job; ShaderJob job;
job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_plane))); job.Insert(QStringLiteral("y_channel"),
job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_plane))); NodeValue(NodeValue::kTexture,
job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_plane))); QVariant::fromValue(y_plane)));
job.Insert(QStringLiteral("bits_per_pixel"), NodeValue(NodeValue::kInt, bits_per_pixel)); job.Insert(QStringLiteral("u_channel"),
job.Insert(QStringLiteral("full_range"), NodeValue(NodeValue::kBoolean, hw_in->color_range == AVCOL_RANGE_JPEG)); NodeValue(NodeValue::kTexture,
QVariant::fromValue(u_plane)));
job.Insert(QStringLiteral("v_channel"),
NodeValue(NodeValue::kTexture,
QVariant::fromValue(v_plane)));
job.Insert(QStringLiteral("bits_per_pixel"),
NodeValue(NodeValue::kInt, bits_per_pixel));
job.Insert(QStringLiteral("full_range"),
NodeValue(NodeValue::kBoolean,
hw_in->color_range == AVCOL_RANGE_JPEG));
const int *yuv_coeffs = sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(hw_in->colorspace)); const int *yuv_coeffs = sws_getCoefficients(
job.Insert(QStringLiteral("yuv_crv"), NodeValue(NodeValue::kFloat, yuv_coeffs[0]/65536.0)); FFmpegUtils::GetSwsColorspaceFromAVColorSpace(hw_in->colorspace));
job.Insert(QStringLiteral("yuv_cgu"), NodeValue(NodeValue::kFloat, yuv_coeffs[2]/65536.0)); job.Insert(QStringLiteral("yuv_crv"),
job.Insert(QStringLiteral("yuv_cgv"), NodeValue(NodeValue::kFloat, yuv_coeffs[3]/65536.0)); NodeValue(NodeValue::kFloat, yuv_coeffs[0] / 65536.0));
job.Insert(QStringLiteral("yuv_cbu"), NodeValue(NodeValue::kFloat, yuv_coeffs[1]/65536.0)); job.Insert(QStringLiteral("yuv_cgu"),
NodeValue(NodeValue::kFloat, yuv_coeffs[2] / 65536.0));
job.Insert(QStringLiteral("yuv_cgv"),
NodeValue(NodeValue::kFloat, yuv_coeffs[3] / 65536.0));
job.Insert(QStringLiteral("yuv_cbu"),
NodeValue(NodeValue::kFloat, yuv_coeffs[1] / 65536.0));
tex = p.renderer->CreateTexture(vp); tex = p.renderer->CreateTexture(vp);
p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false); p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
@@ -194,13 +214,16 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
if (p.src_interlacing != VideoParams::kInterlaceNone) { if (p.src_interlacing != VideoParams::kInterlaceNone) {
if (DeinterlaceShader.isNull()) { if (DeinterlaceShader.isNull()) {
// Compile shader // Compile shader
DeinterlaceShader = p.renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/deinterlace2.frag")))); DeinterlaceShader = p.renderer->CreateNativeShader(
ShaderCode(FileFunctions::ReadFileAsString(
QStringLiteral(":/shaders/deinterlace2.frag"))));
if (DeinterlaceShader.isNull()) { if (DeinterlaceShader.isNull()) {
return nullptr; return nullptr;
} }
} }
rational frame_rate_tb = av_guess_frame_rate(instance_.fmt_ctx(), instance_.avstream(), original.get()); rational frame_rate_tb = av_guess_frame_rate(
instance_.fmt_ctx(), instance_.avstream(), original.get());
// Double frame rate for interlaced fields // Double frame rate for interlaced fields
frame_rate_tb *= 2; frame_rate_tb *= 2;
@@ -208,22 +231,30 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
// Flip frame rate so it can be used as a timebase // Flip frame rate so it can be used as a timebase
frame_rate_tb.flip(); frame_rate_tb.flip();
int64_t req = Timecode::time_to_timestamp(p.time + rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE), frame_rate_tb); int64_t req = Timecode::time_to_timestamp(
int64_t frm = Timecode::rescale_timestamp(original->pts, instance_.avstream()->time_base, frame_rate_tb); p.time + rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE),
frame_rate_tb);
int64_t frm = Timecode::rescale_timestamp(
original->pts, instance_.avstream()->time_base, frame_rate_tb);
bool first = (req == frm); bool first = (req == frm);
bool top_first = (p.src_interlacing == VideoParams::kInterlacedTopFirst); bool top_first =
(p.src_interlacing == VideoParams::kInterlacedTopFirst);
int interlacing = (first == top_first) ? 1 : 2; int interlacing = (first == top_first) ? 1 : 2;
TexturePtr deinterlaced = p.renderer->CreateTexture(tex->params()); TexturePtr deinterlaced = p.renderer->CreateTexture(tex->params());
ShaderJob job; ShaderJob job;
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, tex)); job.Insert(QStringLiteral("ove_maintex"),
job.Insert(QStringLiteral("interlacing"), NodeValue(NodeValue::kInt, interlacing)); NodeValue(NodeValue::kTexture, tex));
job.Insert(QStringLiteral("pixel_height"), NodeValue(NodeValue::kInt, original->height)); job.Insert(QStringLiteral("interlacing"),
NodeValue(NodeValue::kInt, interlacing));
job.Insert(QStringLiteral("pixel_height"),
NodeValue(NodeValue::kInt, original->height));
p.renderer->BlitToTexture(DeinterlaceShader, job, deinterlaced.get(), false); p.renderer->BlitToTexture(DeinterlaceShader, job, deinterlaced.get(),
false);
tex = deinterlaced; tex = deinterlaced;
} }
@@ -241,10 +272,13 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
AVFramePtr original = f; AVFramePtr original = f;
// Disregard "JPEG" pixel formats because we allow the user to override that // Disregard "JPEG" pixel formats because we allow the user to override that
f->format = FFmpegUtils::ConvertJPEGSpaceToRegularSpace(static_cast<AVPixelFormat>(f->format)); f->format = FFmpegUtils::ConvertJPEGSpaceToRegularSpace(
static_cast<AVPixelFormat>(f->format));
// Force frame's color range to whatever it's set to in Olive // Force frame's color range to whatever it's set to in Olive
f->color_range = p.force_range == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; f->color_range = p.force_range == VideoParams::kColorRangeFull ?
AVCOL_RANGE_JPEG :
AVCOL_RANGE_MPEG;
// Perform any CPU processing required // Perform any CPU processing required
f = PreProcessFrame(f, p); f = PreProcessFrame(f, p);
@@ -277,8 +311,10 @@ rational FFmpegDecoder::GetAudioStartOffset() const
{ {
auto f = instance_.fmt_ctx(); auto f = instance_.fmt_ctx();
if (f) { if (f) {
rational fmt_start = rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE); rational fmt_start =
rational str_start = rational(instance_.avstream()->time_base) * instance_.avstream()->start_time; rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE);
rational str_start = rational(instance_.avstream()->time_base) *
instance_.avstream()->start_time;
return str_start - fmt_start; return str_start - fmt_start;
} else { } else {
return 0; return 0;
@@ -290,7 +326,8 @@ QString FFmpegDecoder::id() const
return QStringLiteral("ffmpeg"); return QStringLiteral("ffmpeg");
} }
FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *cancelled) const FootageDescription FFmpegDecoder::Probe(const QString &filename,
CancelAtom *cancelled) const
{ {
// Return value // Return value
FootageDescription desc(id()); FootageDescription desc(id());
@@ -307,41 +344,42 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
// Handle format context error // Handle format context error
if (error_code == 0) { if (error_code == 0) {
// Retrieve metadata about the media // Retrieve metadata about the media
avformat_find_stream_info(fmt_ctx, nullptr); avformat_find_stream_info(fmt_ctx, nullptr);
int64_t footage_duration = fmt_ctx->duration; int64_t footage_duration = fmt_ctx->duration;
bool duration_guessed_from_bitrate = (fmt_ctx->duration_estimation_method == AVFMT_DURATION_FROM_BITRATE); bool duration_guessed_from_bitrate =
(fmt_ctx->duration_estimation_method ==
AVFMT_DURATION_FROM_BITRATE);
if (duration_guessed_from_bitrate) { if (duration_guessed_from_bitrate) {
qWarning() << "Unreliable duration detected - we will manually determine it ourselves (this may take some time)"; qWarning()
<< "Unreliable duration detected - we will manually determine it ourselves (this may take some time)";
} }
// Dump it into the Footage object // Dump it into the Footage object
int video_streams = 0, audio_streams = 0, still_streams = 0; int video_streams = 0, audio_streams = 0, still_streams = 0;
for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) { for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
// FFmpeg AVStream // FFmpeg AVStream
AVStream *avstream = fmt_ctx->streams[i]; AVStream *avstream = fmt_ctx->streams[i];
// Find decoder for this stream, if it exists we can proceed // Find decoder for this stream, if it exists we can proceed
const AVCodec* decoder = avcodec_find_decoder(avstream->codecpar->codec_id); const AVCodec *decoder =
avcodec_find_decoder(avstream->codecpar->codec_id);
if (decoder
&& (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|| avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO
|| avstream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
if (decoder &&
(avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
avstream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
AVPixelFormat compatible_pix_fmt = AV_PIX_FMT_NONE; AVPixelFormat compatible_pix_fmt = AV_PIX_FMT_NONE;
bool image_is_still = false; bool image_is_still = false;
rational pixel_aspect_ratio; rational pixel_aspect_ratio;
rational frame_rate; rational frame_rate;
VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone; VideoParams::Interlacing interlacing =
VideoParams::kInterlaceNone;
{ {
// Read at least two frames to get more information about this video stream // Read at least two frames to get more information about this video stream
@@ -357,21 +395,27 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
// Check if video is interlaced and what field dominance it has if so // Check if video is interlaced and what field dominance it has if so
if (frame->interlaced_frame) { if (frame->interlaced_frame) {
if (frame->top_field_first) { if (frame->top_field_first) {
interlacing = VideoParams::kInterlacedTopFirst; interlacing =
VideoParams::kInterlacedTopFirst;
} else { } else {
interlacing = VideoParams::kInterlacedBottomFirst; interlacing =
VideoParams::kInterlacedBottomFirst;
} }
} }
pixel_aspect_ratio = av_guess_sample_aspect_ratio(instance.fmt_ctx(), pixel_aspect_ratio =
instance.avstream(), av_guess_sample_aspect_ratio(
instance.fmt_ctx(), instance.avstream(),
frame); frame);
frame_rate = av_guess_frame_rate(instance.fmt_ctx(), frame_rate = av_guess_frame_rate(
instance.avstream(), instance.fmt_ctx(), instance.avstream(),
frame); frame);
compatible_pix_fmt = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(avstream->codecpar->format)); compatible_pix_fmt =
FFmpegUtils::GetCompatiblePixelFormat(
static_cast<AVPixelFormat>(
avstream->codecpar->format));
} }
// Read second frame // Read second frame
@@ -379,23 +423,30 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
if (ret >= 0) { if (ret >= 0) {
// Check if we need a manual duration // Check if we need a manual duration
if (avstream->duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) { if (avstream->duration == AV_NOPTS_VALUE ||
if (footage_duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) { duration_guessed_from_bitrate) {
if (footage_duration == AV_NOPTS_VALUE ||
duration_guessed_from_bitrate) {
// Manually read through file for duration // Manually read through file for duration
int64_t new_dur; int64_t new_dur;
do { do {
new_dur = frame->best_effort_timestamp; new_dur =
} while (instance.GetFrame(pkt, frame) >= 0 && (!cancelled || !cancelled->IsCancelled())); frame->best_effort_timestamp;
} while (instance.GetFrame(
pkt, frame) >= 0 &&
(!cancelled ||
!cancelled->IsCancelled()));
avstream->duration = new_dur; avstream->duration = new_dur;
} else { } else {
// Fallback to footage duration // Fallback to footage duration
avstream->duration = Timecode::rescale_timestamp_ceil(footage_duration, rational(1, AV_TIME_BASE), avstream->time_base); avstream->duration =
Timecode::rescale_timestamp_ceil(
footage_duration,
rational(1, AV_TIME_BASE),
avstream->time_base);
} }
} }
} else if (ret == AVERROR_EOF) { } else if (ret == AVERROR_EOF) {
@@ -414,16 +465,22 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
stream.set_stream_index(i); stream.set_stream_index(i);
stream.set_width(avstream->codecpar->width); stream.set_width(avstream->codecpar->width);
stream.set_height(avstream->codecpar->height); stream.set_height(avstream->codecpar->height);
stream.set_video_type((image_is_still) ? VideoParams::kVideoTypeStill : VideoParams::kVideoTypeVideo); stream.set_video_type((image_is_still) ?
VideoParams::kVideoTypeStill :
VideoParams::kVideoTypeVideo);
stream.set_format(GetNativePixelFormat(compatible_pix_fmt)); stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt)); stream.set_channel_count(
GetNativeChannelCount(compatible_pix_fmt));
stream.set_interlacing(interlacing); stream.set_interlacing(interlacing);
stream.set_pixel_aspect_ratio(pixel_aspect_ratio); stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
stream.set_frame_rate(frame_rate); stream.set_frame_rate(frame_rate);
stream.set_start_time(avstream->start_time); stream.set_start_time(avstream->start_time);
stream.set_time_base(avstream->time_base); stream.set_time_base(avstream->time_base);
stream.set_duration(avstream->duration); stream.set_duration(avstream->duration);
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG ? VideoParams::kColorRangeFull : VideoParams::kColorRangeLimited); stream.set_color_range(avstream->codecpar->color_range ==
AVCOL_RANGE_JPEG ?
VideoParams::kColorRangeFull :
VideoParams::kColorRangeLimited);
// Defaults to false, requires user intervention if incorrect // Defaults to false, requires user intervention if incorrect
stream.set_premultiplied_alpha(false); stream.set_premultiplied_alpha(false);
@@ -436,17 +493,22 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
video_streams++; video_streams++;
} }
} else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { } else if (avstream->codecpar->codec_type ==
AVMEDIA_TYPE_AUDIO) {
// Create an audio stream object // Create an audio stream object
AVChannelLayout &channel_layout = avstream->codecpar->ch_layout; AVChannelLayout &channel_layout =
avstream->codecpar->ch_layout;
if (!av_channel_layout_check(&channel_layout)) { if (!av_channel_layout_check(&channel_layout)) {
av_channel_layout_default(&channel_layout,avstream->codecpar->ch_layout.nb_channels); av_channel_layout_default(
&channel_layout,
avstream->codecpar->ch_layout.nb_channels);
} }
if (avstream->duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) { if (avstream->duration == AV_NOPTS_VALUE ||
duration_guessed_from_bitrate) {
// Loop through stream until we get the whole duration // Loop through stream until we get the whole duration
if (footage_duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) { if (footage_duration == AV_NOPTS_VALUE ||
duration_guessed_from_bitrate) {
Instance instance; Instance instance;
instance.Open(filename_c, avstream->index); instance.Open(filename_c, avstream->index);
@@ -457,7 +519,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
do { do {
new_dur = frame->best_effort_timestamp; new_dur = frame->best_effort_timestamp;
} while (instance.GetFrame(pkt, frame) >= 0 && (!cancelled || !cancelled->IsCancelled())); } while (instance.GetFrame(pkt, frame) >= 0 &&
(!cancelled || !cancelled->IsCancelled()));
avstream->duration = new_dur; avstream->duration = new_dur;
@@ -466,9 +529,10 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
instance.Close(); instance.Close();
} else { } else {
avstream->duration =
avstream->duration = Timecode::rescale_timestamp_ceil(footage_duration, rational(1, AV_TIME_BASE), avstream->time_base); Timecode::rescale_timestamp_ceil(
footage_duration, rational(1, AV_TIME_BASE),
avstream->time_base);
} }
} }
@@ -476,15 +540,17 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
stream.set_stream_index(i); stream.set_stream_index(i);
stream.set_channel_layout(channel_layout); stream.set_channel_layout(channel_layout);
stream.set_sample_rate(avstream->codecpar->sample_rate); stream.set_sample_rate(avstream->codecpar->sample_rate);
stream.set_format(FFmpegUtils::GetNativeSampleFormat(static_cast<AVSampleFormat>(avstream->codecpar->format))); stream.set_format(FFmpegUtils::GetNativeSampleFormat(
static_cast<AVSampleFormat>(
avstream->codecpar->format)));
stream.set_time_base(avstream->time_base); stream.set_time_base(avstream->time_base);
stream.set_duration(avstream->duration); stream.set_duration(avstream->duration);
desc.AddAudioStream(stream); desc.AddAudioStream(stream);
audio_streams++; audio_streams++;
} else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { } else if (avstream->codecpar->codec_type ==
AVMEDIA_TYPE_SUBTITLE) {
// Limit to SRT for now... // Limit to SRT for now...
if (avstream->codecpar->codec_id == AV_CODEC_ID_SUBRIP) { if (avstream->codecpar->codec_id == AV_CODEC_ID_SUBRIP) {
SubtitleParams sub; SubtitleParams sub;
@@ -495,10 +561,15 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
instance.Open(filename_c, avstream->index); instance.Open(filename_c, avstream->index);
while (instance.GetPacket(pkt) >= 0) { while (instance.GetPacket(pkt) >= 0) {
TimeRange time(Timecode::timestamp_to_time(pkt->pts, avstream->time_base), TimeRange time(Timecode::timestamp_to_time(
Timecode::timestamp_to_time(pkt->pts + pkt->duration, avstream->time_base)); pkt->pts,
avstream->time_base),
Timecode::timestamp_to_time(
pkt->pts + pkt->duration,
avstream->time_base));
QString text = QString::fromUtf8((const char *) pkt->data, pkt->size); QString text = QString::fromUtf8(
(const char *)pkt->data, pkt->size);
sub.push_back(Subtitle(time, text)); sub.push_back(Subtitle(time, text));
} }
@@ -509,11 +580,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
desc.AddSubtitleStream(sub); desc.AddSubtitleStream(sub);
} }
} }
} }
} }
desc.SetStreamCount(fmt_ctx->nb_streams); desc.SetStreamCount(fmt_ctx->nb_streams);
@@ -527,7 +595,6 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
vp.set_enabled(false); vp.set_enabled(false);
} }
} }
} }
// Free all memory // Free all memory
@@ -543,7 +610,9 @@ QString FFmpegDecoder::FFmpegError(int error_code)
return QStringLiteral("%1 %2").arg(QString::number(error_code), err); return QStringLiteral("%1 %2").arg(QString::number(error_code), err);
} }
bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, CancelAtom *cancelled) bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled)
{ {
// Iterate through each audio frame and extract the PCM data // Iterate through each audio frame and extract the PCM data
@@ -551,23 +620,22 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
instance_.Seek(0); instance_.Seek(0);
// Handle NULL channel layout // Handle NULL channel layout
AVChannelLayout channel_layout = ValidateChannelLayout(instance_.avstream()); AVChannelLayout channel_layout =
ValidateChannelLayout(instance_.avstream());
if (!av_channel_layout_check(&channel_layout)) { if (!av_channel_layout_check(&channel_layout)) {
qCritical() << "Failed to determine channel layout of audio file, could not conform"; qCritical()
<< "Failed to determine channel layout of audio file, could not conform";
return false; return false;
} }
// Create resampling context // Create resampling context
AVChannelLayout layout = params.channel_layout(); AVChannelLayout layout = params.channel_layout();
SwrContext *resampler; SwrContext *resampler;
swr_alloc_set_opts2(&resampler, swr_alloc_set_opts2(
&layout, &resampler, &layout,
FFmpegUtils::GetFFmpegSampleFormat(params.format()), FFmpegUtils::GetFFmpegSampleFormat(params.format()),
params.sample_rate(), params.sample_rate(), &channel_layout,
&channel_layout,
static_cast<AVSampleFormat>(instance_.avstream()->codecpar->format), static_cast<AVSampleFormat>(instance_.avstream()->codecpar->format),
instance_.avstream()->codecpar->sample_rate, instance_.avstream()->codecpar->sample_rate, 0, nullptr);
0,
nullptr);
av_channel_layout_uninit(&layout); av_channel_layout_uninit(&layout);
swr_init(resampler); swr_init(resampler);
@@ -582,7 +650,9 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
duration = instance_.fmt_ctx()->duration; duration = instance_.fmt_ctx()->duration;
if (!(duration == 0 || duration == AV_NOPTS_VALUE)) { if (!(duration == 0 || duration == AV_NOPTS_VALUE)) {
// Rescale from AVFormatContext timebase to AVStream timebase // Rescale from AVFormatContext timebase to AVStream timebase
duration = av_rescale_q_rnd(duration, {1, AV_TIME_BASE}, instance_.avstream()->time_base, AV_ROUND_UP); duration = av_rescale_q_rnd(duration, { 1, AV_TIME_BASE },
instance_.avstream()->time_base,
AV_ROUND_UP);
} }
} }
@@ -601,7 +671,6 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
ret = instance_.GetFrame(pkt, frame); ret = instance_.GetFrame(pkt, frame);
if (ret < 0) { if (ret < 0) {
if (ret == AVERROR_EOF) { if (ret == AVERROR_EOF) {
success = true; success = true;
} else { } else {
@@ -610,29 +679,33 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
qWarning() << "Failed to conform:" << ret << err_str; qWarning() << "Failed to conform:" << ret << err_str;
} }
break; break;
} }
// Allocate buffers // Allocate buffers
int nb_samples = swr_get_out_samples(resampler, frame->nb_samples); int nb_samples = swr_get_out_samples(resampler, frame->nb_samples);
int nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels; int nb_bytes_per_channel =
params.samples_to_bytes(nb_samples) / nb_channels;
data.set_sample_count(nb_bytes_per_channel); data.set_sample_count(nb_bytes_per_channel);
data.allocate(); data.allocate();
// Resample audio to our destination parameters // Resample audio to our destination parameters
nb_samples = swr_convert(resampler, nb_samples = swr_convert(
resampler,
reinterpret_cast<uint8_t **>(data.to_raw_ptrs().data()), reinterpret_cast<uint8_t **>(data.to_raw_ptrs().data()),
nb_samples, nb_samples, const_cast<const uint8_t **>(frame->data),
const_cast<const uint8_t**>(frame->data),
frame->nb_samples); frame->nb_samples);
// If no error, write to files // If no error, write to files
if (nb_samples > 0) { if (nb_samples > 0) {
// Update byte count for the number of samples we actually received // Update byte count for the number of samples we actually received
nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels; nb_bytes_per_channel =
params.samples_to_bytes(nb_samples) / nb_channels;
// Write to files // Write to files
wave_out.write(const_cast<const char**>(reinterpret_cast<char**>(data.to_raw_ptrs().data())), nb_bytes_per_channel); wave_out.write(
const_cast<const char **>(
reinterpret_cast<char **>(data.to_raw_ptrs().data())),
nb_bytes_per_channel);
} }
// Free buffer // Free buffer
@@ -642,7 +715,8 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
if (nb_samples < 0) { if (nb_samples < 0) {
char err_str[512]; char err_str[512];
av_strerror(nb_samples, err_str, 512); av_strerror(nb_samples, err_str, 512);
qWarning() << "libswresample failed with error:" << nb_samples << err_str; qWarning() << "libswresample failed with error:" << nb_samples
<< err_str;
break; break;
} }
@@ -700,7 +774,8 @@ AVChannelLayout FFmpegDecoder::ValidateChannelLayout(AVStream* stream)
return layout; return layout;
} }
const char *FFmpegDecoder::GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing) const char *
FFmpegDecoder::GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing)
{ {
if (interlacing == VideoParams::kInterlacedTopFirst) { if (interlacing == VideoParams::kInterlacedTopFirst) {
return "tff"; return "tff";
@@ -740,13 +815,15 @@ void FFmpegDecoder::ClearFrameCache()
} }
} }
AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p) AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
const RetrieveVideoParams &p)
{ {
// In pre-processing, we try to achieve the following: // In pre-processing, we try to achieve the following:
// - If a divider is being used, scale down the image // - If a divider is being used, scale down the image
// - If a pixel format is not compatible with the GLSL shader, convert it to RGBA ourselves // - If a pixel format is not compatible with the GLSL shader, convert it to RGBA ourselves
if (p.divider == 1 && IsPixelFormatGLSLCompatible(static_cast<AVPixelFormat>(f->format))) { if (p.divider == 1 &&
IsPixelFormatGLSLCompatible(static_cast<AVPixelFormat>(f->format))) {
// No CPU processing required, the user wants this in full resolution and the pixel format can // No CPU processing required, the user wants this in full resolution and the pixel format can
// be converted on the GPU // be converted on the GPU
return f; return f;
@@ -766,8 +843,10 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParam
dest->height = VideoParams::GetScaledDimension(dest->height, p.divider); dest->height = VideoParams::GetScaledDimension(dest->height, p.divider);
} }
if (!IsPixelFormatGLSLCompatible(static_cast<AVPixelFormat>(dest->format))) { if (!IsPixelFormatGLSLCompatible(
dest->format = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(dest->format), p.maximum_format); static_cast<AVPixelFormat>(dest->format))) {
dest->format = FFmpegUtils::GetCompatiblePixelFormat(
static_cast<AVPixelFormat>(dest->format), p.maximum_format);
} }
int r = av_frame_get_buffer(dest.get(), 0); int r = av_frame_get_buffer(dest.get(), 0);
@@ -776,15 +855,11 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParam
return nullptr; return nullptr;
} }
if (!sws_ctx_ if (!sws_ctx_ || sws_src_width_ != f->width ||
|| sws_src_width_ != f->width sws_src_height_ != f->height || sws_src_format_ != f->format ||
|| sws_src_height_ != f->height sws_dst_width_ != dest->width || sws_dst_height_ != dest->height ||
|| sws_src_format_ != f->format sws_dst_format_ != dest->format || sws_colrange_ != dest->color_range ||
|| sws_dst_width_ != dest->width sws_colspace_ != dest->colorspace) {
|| sws_dst_height_ != dest->height
|| sws_dst_format_ != dest->format
|| sws_colrange_ != dest->color_range
|| sws_colspace_ != dest->colorspace) {
// SwsContext must be recreated, destroy current if it exists // SwsContext must be recreated, destroy current if it exists
FreeScaler(); FreeScaler();
@@ -799,27 +874,24 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParam
sws_colspace_ = dest->colorspace; sws_colspace_ = dest->colorspace;
// Create new scaler // Create new scaler
sws_ctx_ = sws_getContext(sws_src_width_, sws_ctx_ = sws_getContext(sws_src_width_, sws_src_height_,
sws_src_height_, sws_src_format_, sws_dst_width_,
sws_src_format_, sws_dst_height_, sws_dst_format_, SWS_POINT,
sws_dst_width_, nullptr, nullptr, nullptr);
sws_dst_height_,
sws_dst_format_,
SWS_POINT,
nullptr,
nullptr,
nullptr);
// Set swscale's colorspace details // Set swscale's colorspace details
sws_setColorspaceDetails(sws_ctx_, sws_setColorspaceDetails(
sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(dest->colorspace)), sws_ctx_,
sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(
dest->colorspace)),
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0,
sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(dest->colorspace)), sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, dest->colorspace)),
0, 0x10000, 0x10000); dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, 0, 0x10000, 0x10000);
} }
r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data, dest->linesize); r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data,
dest->linesize);
if (r < 0) { if (r < 0) {
FFmpegError(r); FFmpegError(r);
return nullptr; return nullptr;
@@ -828,12 +900,16 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParam
return dest; return dest;
} }
AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancelled) AVFramePtr FFmpegDecoder::RetrieveFrame(const rational &time,
CancelAtom *cancelled)
{ {
int64_t target_ts = Timecode::time_to_timestamp(time, instance_.avstream()->time_base); int64_t target_ts =
Timecode::time_to_timestamp(time, instance_.avstream()->time_base);
if (instance_.fmt_ctx()->start_time != AV_NOPTS_VALUE) { if (instance_.fmt_ctx()->start_time != AV_NOPTS_VALUE) {
target_ts += av_rescale_q(instance_.fmt_ctx()->start_time, {1, AV_TIME_BASE}, instance_.avstream()->time_base); target_ts += av_rescale_q(instance_.fmt_ctx()->start_time,
{ 1, AV_TIME_BASE },
instance_.avstream()->time_base);
} }
const int64_t min_seek = 0; const int64_t min_seek = 0;
@@ -842,8 +918,9 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
if (time != kAnyTimecode) { if (time != kAnyTimecode) {
// If the frame wasn't in the frame cache, see if this frame cache is too old to use // If the frame wasn't in the frame cache, see if this frame cache is too old to use
if (cached_frames_.empty() if (cached_frames_.empty() ||
|| (target_ts < cached_frames_.front()->pts || target_ts > cached_frames_.back()->pts + 2*second_ts_)) { (target_ts < cached_frames_.front()->pts ||
target_ts > cached_frames_.back()->pts + 2 * second_ts_)) {
ClearFrameCache(); ClearFrameCache();
instance_.Seek(seek_ts); instance_.Seek(seek_ts);
@@ -891,8 +968,9 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
if (still_seeking) { if (still_seeking) {
// Handle a failure to seek (occurs on some media) // Handle a failure to seek (occurs on some media)
// We'll only be here if the frame cache was emptied earlier // We'll only be here if the frame cache was emptied earlier
if (!cache_at_zero_ && (ret == AVERROR_EOF || filtered->best_effort_timestamp > target_ts)) { if (!cache_at_zero_ &&
(ret == AVERROR_EOF ||
filtered->best_effort_timestamp > target_ts)) {
seek_ts = qMax(min_seek, seek_ts - second_ts_); seek_ts = qMax(min_seek, seek_ts - second_ts_);
instance_.Seek(seek_ts); instance_.Seek(seek_ts);
if (seek_ts == min_seek) { if (seek_ts == min_seek) {
@@ -901,19 +979,17 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
continue; continue;
} else { } else {
still_seeking = false; still_seeking = false;
} }
} }
if (ret == AVERROR_EOF) { if (ret == AVERROR_EOF) {
// Handle an "expected" EOF by using the last frame of our cache // Handle an "expected" EOF by using the last frame of our cache
cache_at_eof_ = true; cache_at_eof_ = true;
if (cached_frames_.empty()) { if (cached_frames_.empty()) {
qCritical() << "Unexpected codec EOF - unable to retrieve frame"; qCritical()
<< "Unexpected codec EOF - unable to retrieve frame";
} else { } else {
return_frame = cached_frames_.back(); return_frame = cached_frames_.back();
} }
@@ -921,7 +997,6 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
break; break;
} else { } else {
// Cut down to thread count - 1 before we acquire a new frame // Cut down to thread count - 1 before we acquire a new frame
if (cached_frames_.size() > size_t(MaximumQueueSize())) { if (cached_frames_.size() > size_t(MaximumQueueSize())) {
RemoveFirstFrame(); RemoveFirstFrame();
@@ -972,31 +1047,30 @@ void FFmpegDecoder::FreeScaler()
AVFramePtr FFmpegDecoder::GetFrameFromCache(const int64_t &t) const AVFramePtr FFmpegDecoder::GetFrameFromCache(const int64_t &t) const
{ {
if (t < cached_frames_.front()->pts) { if (t < cached_frames_.front()->pts) {
if (cache_at_zero_) { if (cache_at_zero_) {
return cached_frames_.front(); return cached_frames_.front();
} }
} else if (t > cached_frames_.back()->pts) { } else if (t > cached_frames_.back()->pts) {
if (cache_at_eof_) { if (cache_at_eof_) {
return cached_frames_.back(); return cached_frames_.back();
} }
} else { } else {
// We already have this frame in the cache, find it // We already have this frame in the cache, find it
for (auto it=cached_frames_.cbegin(); it!=cached_frames_.cend(); it++) { for (auto it = cached_frames_.cbegin(); it != cached_frames_.cend();
it++) {
AVFramePtr this_frame = *it; AVFramePtr this_frame = *it;
auto next = it; auto next = it;
next++; next++;
if (this_frame->pts == t // Test for an exact match if (this_frame->pts == t // Test for an exact match
|| (next != cached_frames_.cend() && (*next)->pts > t)) { // Or for this frame to be the "closest" ||
(next != cached_frames_.cend() &&
(*next)->pts > t)) { // Or for this frame to be the "closest"
return this_frame; return this_frame;
} }
} }
} }
@@ -1019,11 +1093,11 @@ int FFmpegDecoder::MaximumQueueSize()
return 2; return 2;
} }
FFmpegDecoder::Instance::Instance() : FFmpegDecoder::Instance::Instance()
fmt_ctx_(nullptr), : fmt_ctx_(nullptr)
codec_ctx_(nullptr), , codec_ctx_(nullptr)
avstream_(nullptr), , avstream_(nullptr)
opts_(nullptr) , opts_(nullptr)
{ {
} }
@@ -1034,7 +1108,8 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
// Handle format context error // Handle format context error
if (error_code != 0) { if (error_code != 0) {
qCritical() << "Failed to open input:" << filename << FFmpegError(error_code); qCritical()
<< "Failed to open input:" << filename << FFmpegError(error_code);
return false; return false;
} }
@@ -1055,10 +1130,9 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
// Handle failure to find decoder // Handle failure to find decoder
if (codec == nullptr) { if (codec == nullptr) {
qCritical() << "Failed to find appropriate decoder for this codec:" qCritical()
<< filename << "Failed to find appropriate decoder for this codec:" << filename
<< stream_index << stream_index << avstream_->codecpar->codec_id;
<< avstream_->codecpar->codec_id;
return false; return false;
} }
@@ -1074,7 +1148,8 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
// Handle failure to copy parameters // Handle failure to copy parameters
if (error_code < 0) { if (error_code < 0) {
qCritical() << "Failed to copy parameters from AVStream to AVCodecContext"; qCritical()
<< "Failed to copy parameters from AVStream to AVCodecContext";
return false; return false;
} }
@@ -1125,8 +1200,9 @@ int FFmpegDecoder::Instance::GetFrame(AVPacket *pkt, AVFrame *frame)
// Clear any previous frames // Clear any previous frames
av_frame_unref(frame); av_frame_unref(frame);
while ((ret = avcodec_receive_frame(codec_ctx_, frame)) == AVERROR(EAGAIN) && !eof) { while ((ret = avcodec_receive_frame(codec_ctx_, frame)) ==
AVERROR(EAGAIN) &&
!eof) {
// Find next packet in the correct stream index // Find next packet in the correct stream index
ret = GetPacket(pkt); ret = GetPacket(pkt);
+24 -14
View File
@@ -39,13 +39,13 @@ extern "C" {
#include "codec/decoder.h" #include "codec/decoder.h"
#include "common/ffmpegutils.h" #include "common/ffmpegutils.h"
namespace olive { namespace olive
{
/** /**
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder * @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
*/ */
class FFmpegDecoder : public Decoder class FFmpegDecoder : public Decoder {
{
Q_OBJECT Q_OBJECT
public: public:
// Constructor // Constructor
@@ -56,22 +56,31 @@ public:
virtual QString id() const override; virtual QString id() const override;
virtual bool SupportsVideo() override{return true;} virtual bool SupportsVideo() override
virtual bool SupportsAudio() override{return true;} {
return true;
}
virtual bool SupportsAudio() override
{
return true;
}
virtual FootageDescription Probe(const QString &filename, CancelAtom *cancelled) const override; virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const override;
protected: protected:
virtual bool OpenInternal() override; virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override; virtual TexturePtr
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled) override; RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled) override;
virtual void CloseInternal() override; virtual void CloseInternal() override;
virtual rational GetAudioStartOffset() const override; virtual rational GetAudioStartOffset() const override;
private: private:
class Instance class Instance {
{
public: public:
Instance(); Instance();
@@ -121,7 +130,6 @@ private:
AVCodecContext *codec_ctx_; AVCodecContext *codec_ctx_;
AVStream *avstream_; AVStream *avstream_;
AVDictionary *opts_; AVDictionary *opts_;
}; };
/** /**
@@ -141,7 +149,8 @@ private:
static AVChannelLayout ValidateChannelLayout(AVStream *stream); static AVChannelLayout ValidateChannelLayout(AVStream *stream);
static const char* GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing); static const char *
GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f); static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
@@ -151,7 +160,9 @@ private:
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p); AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
TexturePtr ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVideoParams &p, const AVFramePtr original); TexturePtr ProcessFrameIntoTexture(AVFramePtr f,
const RetrieveVideoParams &p,
const AVFramePtr original);
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled); AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
@@ -179,7 +190,6 @@ private:
bool cache_at_eof_; bool cache_at_eof_;
Instance instance_; Instance instance_;
}; };
} }
+192 -112
View File
@@ -30,21 +30,22 @@ extern "C" {
#include "common/ffmpegutils.h" #include "common/ffmpegutils.h"
namespace olive { namespace olive
{
FFmpegEncoder::FFmpegEncoder(const EncodingParams &params) : FFmpegEncoder::FFmpegEncoder(const EncodingParams &params)
Encoder(params), : Encoder(params)
fmt_ctx_(nullptr), , fmt_ctx_(nullptr)
video_stream_(nullptr), , video_stream_(nullptr)
video_codec_ctx_(nullptr), , video_codec_ctx_(nullptr)
video_scale_ctx_(nullptr), , video_scale_ctx_(nullptr)
video_buffersrc_ctx_(nullptr), , video_buffersrc_ctx_(nullptr)
video_buffersink_ctx_(nullptr), , video_buffersink_ctx_(nullptr)
audio_stream_(nullptr), , audio_stream_(nullptr)
audio_codec_ctx_(nullptr), , audio_codec_ctx_(nullptr)
audio_resample_ctx_(nullptr), , audio_resample_ctx_(nullptr)
audio_frame_(nullptr), , audio_frame_(nullptr)
open_(false) , open_(false)
{ {
} }
@@ -56,12 +57,14 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
if (codec_info) { if (codec_info) {
for (int i = 0; codec_info->pix_fmts[i] != -1; i++) { for (int i = 0; codec_info->pix_fmts[i] != -1; i++) {
if (FFmpegUtils::ConvertJPEGSpaceToRegularSpace(codec_info->pix_fmts[i]) != codec_info->pix_fmts[i]) { if (FFmpegUtils::ConvertJPEGSpaceToRegularSpace(
codec_info->pix_fmts[i]) != codec_info->pix_fmts[i]) {
// This is a deprecated "JPEG" space, skip it // This is a deprecated "JPEG" space, skip it
continue; continue;
} }
const char* pix_fmt_name = av_get_pix_fmt_name(codec_info->pix_fmts[i]); const char *pix_fmt_name =
av_get_pix_fmt_name(codec_info->pix_fmts[i]);
pix_fmts.append(pix_fmt_name); pix_fmts.append(pix_fmt_name);
} }
} }
@@ -69,7 +72,8 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
return pix_fmts; return pix_fmts;
} }
std::vector<SampleFormat> FFmpegEncoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const std::vector<SampleFormat>
FFmpegEncoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
{ {
std::vector<SampleFormat> f; std::vector<SampleFormat> f;
@@ -77,20 +81,15 @@ std::vector<SampleFormat> FFmpegEncoder::GetSampleFormatsForCodec(ExportCodec::C
// FFmpeg lists these as separate codecs so we need custom functionality here // FFmpeg lists these as separate codecs so we need custom functionality here
// We list signed 16 first because ExportDialog will always use the first element by default // We list signed 16 first because ExportDialog will always use the first element by default
// (because first element is the "default" in tFFmpeg) // (because first element is the "default" in tFFmpeg)
f = { f = { SampleFormat::S16, SampleFormat::U8, SampleFormat::S32,
SampleFormat::S16, SampleFormat::S64, SampleFormat::F32, SampleFormat::F64 };
SampleFormat::U8,
SampleFormat::S32,
SampleFormat::S64,
SampleFormat::F32,
SampleFormat::F64
};
} else { } else {
const AVCodec *codec_info = GetEncoder(c, SampleFormat::INVALID); const AVCodec *codec_info = GetEncoder(c, SampleFormat::INVALID);
if (codec_info && codec_info->sample_fmts) { if (codec_info && codec_info->sample_fmts) {
for (int i = 0; codec_info->sample_fmts[i] != -1; i++) { for (int i = 0; codec_info->sample_fmts[i] != -1; i++) {
SampleFormat this_format = FFmpegUtils::GetNativeSampleFormat(static_cast<AVSampleFormat>(codec_info->sample_fmts[i])); SampleFormat this_format = FFmpegUtils::GetNativeSampleFormat(
static_cast<AVSampleFormat>(codec_info->sample_fmts[i]));
if (this_format != SampleFormat::INVALID) { if (this_format != SampleFormat::INVALID) {
f.push_back(this_format); f.push_back(this_format);
} }
@@ -114,7 +113,8 @@ bool FFmpegEncoder::Open()
const char *filename_c_str = filename_bytes.constData(); const char *filename_c_str = filename_bytes.constData();
// Create output format context // Create output format context
error_code = avformat_alloc_output_context2(&fmt_ctx_, nullptr, nullptr, filename_c_str); error_code = avformat_alloc_output_context2(&fmt_ctx_, nullptr, nullptr,
filename_c_str);
// Check error code // Check error code
if (error_code < 0) { if (error_code < 0) {
@@ -124,7 +124,8 @@ bool FFmpegEncoder::Open()
// Initialize a video stream if it's enabled // Initialize a video stream if it's enabled
if (params().video_enabled()) { if (params().video_enabled()) {
if (!InitializeStream(AVMEDIA_TYPE_VIDEO, &video_stream_, &video_codec_ctx_, params().video_codec())) { if (!InitializeStream(AVMEDIA_TYPE_VIDEO, &video_stream_,
&video_codec_ctx_, params().video_codec())) {
return false; return false;
} }
@@ -132,17 +133,20 @@ bool FFmpegEncoder::Open()
PixelFormat native_pixel_fmt = params().video_params().format(); PixelFormat native_pixel_fmt = params().video_params().format();
// This is the format we will need to convert the frame to for swscale to understand it // This is the format we will need to convert the frame to for swscale to understand it
video_conversion_fmt_ = FFmpegUtils::GetCompatiblePixelFormat(native_pixel_fmt); video_conversion_fmt_ =
FFmpegUtils::GetCompatiblePixelFormat(native_pixel_fmt);
// This is the equivalent pixel format above as an AVPixelFormat that swscale can understand // This is the equivalent pixel format above as an AVPixelFormat that swscale can understand
AVPixelFormat src_alpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(video_conversion_fmt_, AVPixelFormat src_alpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(
VideoParams::kRGBAChannelCount); video_conversion_fmt_, VideoParams::kRGBAChannelCount);
AVPixelFormat src_noalpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(video_conversion_fmt_, AVPixelFormat src_noalpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(
VideoParams::kRGBChannelCount); video_conversion_fmt_, VideoParams::kRGBChannelCount);
if (src_alpha_pix_fmt == AV_PIX_FMT_NONE || src_noalpha_pix_fmt == AV_PIX_FMT_NONE) { if (src_alpha_pix_fmt == AV_PIX_FMT_NONE ||
SetError(tr("Failed to find suitable pixel format for this buffer")); src_noalpha_pix_fmt == AV_PIX_FMT_NONE) {
SetError(
tr("Failed to find suitable pixel format for this buffer"));
return false; return false;
} }
@@ -157,17 +161,22 @@ bool FFmpegEncoder::Open()
static const int FILTER_ARG_SZ = 1024; static const int FILTER_ARG_SZ = 1024;
char filter_args[FILTER_ARG_SZ]; char filter_args[FILTER_ARG_SZ];
snprintf(filter_args, FILTER_ARG_SZ, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", snprintf(
filter_args, FILTER_ARG_SZ,
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
params().video_params().effective_width(), params().video_params().effective_width(),
params().video_params().effective_height(), params().video_params().effective_height(), src_alpha_pix_fmt,
src_alpha_pix_fmt,
params().video_params().time_base().numerator(), params().video_params().time_base().numerator(),
params().video_params().time_base().denominator(), params().video_params().time_base().denominator(),
params().video_params().pixel_aspect_ratio().numerator(), params().video_params().pixel_aspect_ratio().numerator(),
params().video_params().pixel_aspect_ratio().denominator()); params().video_params().pixel_aspect_ratio().denominator());
avfilter_graph_create_filter(&video_buffersrc_ctx_, avfilter_get_by_name("buffer"), "in", filter_args, nullptr, video_scale_ctx_); avfilter_graph_create_filter(&video_buffersrc_ctx_,
avfilter_graph_create_filter(&video_buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, video_scale_ctx_); avfilter_get_by_name("buffer"), "in",
filter_args, nullptr, video_scale_ctx_);
avfilter_graph_create_filter(&video_buffersink_ctx_,
avfilter_get_by_name("buffersink"), "out",
nullptr, nullptr, video_scale_ctx_);
AVFilterContext *last_filter = video_buffersrc_ctx_; AVFilterContext *last_filter = video_buffersrc_ctx_;
@@ -176,9 +185,15 @@ bool FFmpegEncoder::Open()
AVFilterContext *range_filter; AVFilterContext *range_filter;
snprintf(filter_args, FILTER_ARG_SZ, "in_range=full:out_range=%s", snprintf(filter_args, FILTER_ARG_SZ, "in_range=full:out_range=%s",
params().video_params().color_range() == VideoParams::kColorRangeFull ? "full" : "limited"); params().video_params().color_range() ==
VideoParams::kColorRangeFull ?
"full" :
"limited");
avfilter_graph_create_filter(&range_filter, avfilter_get_by_name("scale"), "range", filter_args, nullptr, video_scale_ctx_); avfilter_graph_create_filter(&range_filter,
avfilter_get_by_name("scale"), "range",
filter_args, nullptr,
video_scale_ctx_);
avfilter_link(last_filter, 0, range_filter, 0); avfilter_link(last_filter, 0, range_filter, 0);
last_filter = range_filter; last_filter = range_filter;
@@ -188,9 +203,13 @@ bool FFmpegEncoder::Open()
// Transform pixel format // Transform pixel format
AVFilterContext *format_filter; AVFilterContext *format_filter;
snprintf(filter_args, FILTER_ARG_SZ, "pix_fmts=%u", encoder_pix_fmt); snprintf(filter_args, FILTER_ARG_SZ, "pix_fmts=%u",
encoder_pix_fmt);
avfilter_graph_create_filter(&format_filter, avfilter_get_by_name("format"), "format", filter_args, nullptr, video_scale_ctx_); avfilter_graph_create_filter(&format_filter,
avfilter_get_by_name("format"),
"format", filter_args, nullptr,
video_scale_ctx_);
avfilter_link(last_filter, 0, format_filter, 0); avfilter_link(last_filter, 0, format_filter, 0);
last_filter = format_filter; last_filter = format_filter;
@@ -206,14 +225,17 @@ bool FFmpegEncoder::Open()
// Initialize an audio stream if it's enabled // Initialize an audio stream if it's enabled
if (params().audio_enabled()) { if (params().audio_enabled()) {
if (!InitializeStream(AVMEDIA_TYPE_AUDIO, &audio_stream_, &audio_codec_ctx_, params().audio_codec())) { if (!InitializeStream(AVMEDIA_TYPE_AUDIO, &audio_stream_,
&audio_codec_ctx_, params().audio_codec())) {
return false; return false;
} }
} }
// Initialize a subtitle stream if it's enabled // Initialize a subtitle stream if it's enabled
if (params().subtitles_enabled()) { if (params().subtitles_enabled()) {
if (!InitializeStream(AVMEDIA_TYPE_SUBTITLE, &subtitle_stream_, &subtitle_codec_ctx_, params().subtitles_codec())) { if (!InitializeStream(AVMEDIA_TYPE_SUBTITLE, &subtitle_stream_,
&subtitle_codec_ctx_,
params().subtitles_codec())) {
return false; return false;
} }
} }
@@ -249,7 +271,8 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
AVFramePtr input_frame = CreateAVFramePtr(av_frame_alloc()); AVFramePtr input_frame = CreateAVFramePtr(av_frame_alloc());
input_frame->width = frame->width(); input_frame->width = frame->width();
input_frame->height = frame->height(); input_frame->height = frame->height();
input_frame->format = FFmpegUtils::GetFFmpegPixelFormat(frame->format(), frame->channel_count()); input_frame->format = FFmpegUtils::GetFFmpegPixelFormat(
frame->format(), frame->channel_count());
input_frame->data[0] = reinterpret_cast<uint8_t *>(frame->data()); input_frame->data[0] = reinterpret_cast<uint8_t *>(frame->data());
input_frame->linesize[0] = frame->linesize_bytes(); input_frame->linesize[0] = frame->linesize_bytes();
@@ -259,7 +282,8 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
input_frame->color_range = video_codec_ctx_->color_range; input_frame->color_range = video_codec_ctx_->color_range;
int r; int r;
r = av_buffersrc_add_frame_flags(video_buffersrc_ctx_, input_frame.get(), AV_BUFFERSRC_FLAG_KEEP_REF); r = av_buffersrc_add_frame_flags(video_buffersrc_ctx_, input_frame.get(),
AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) { if (r < 0) {
FFmpegError(tr("Failed to add frame to filter graph"), r); FFmpegError(tr("Failed to add frame to filter graph"), r);
return false; return false;
@@ -272,7 +296,8 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
return false; return false;
} }
encoded_frame->pts = qRound64(time.toDouble() / av_q2d(video_codec_ctx_->time_base)); encoded_frame->pts =
qRound64(time.toDouble() / av_q2d(video_codec_ctx_->time_base));
return WriteAVFrame(encoded_frame.get(), video_codec_ctx_, video_stream_); return WriteAVFrame(encoded_frame.get(), video_codec_ctx_, video_stream_);
} }
@@ -295,8 +320,11 @@ bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
size_t input_sample_count = std::min(end - start, max_frame); size_t input_sample_count = std::min(end - start, max_frame);
int input_linesize; int input_linesize;
int r = av_samples_alloc_array_and_samples(&input_data, &input_linesize, audio.audio_params().channel_count(), int r = av_samples_alloc_array_and_samples(
input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio.audio_params().format()), 0); &input_data, &input_linesize, audio.audio_params().channel_count(),
input_sample_count,
FFmpegUtils::GetFFmpegSampleFormat(audio.audio_params().format()),
0);
if (r < 0) { if (r < 0) {
FFmpegError(tr("Failed to allocate sample array"), r); FFmpegError(tr("Failed to allocate sample array"), r);
@@ -304,13 +332,18 @@ bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
} else { } else {
int bpsc = audio.audio_params().bytes_per_sample_per_channel(); int bpsc = audio.audio_params().bytes_per_sample_per_channel();
for (int i = 0; i < audio.audio_params().channel_count(); i++) { for (int i = 0; i < audio.audio_params().channel_count(); i++) {
memcpy(input_data[i], audio.data(i) + start, input_sample_count * bpsc); memcpy(input_data[i], audio.data(i) + start,
input_sample_count * bpsc);
} }
start += input_sample_count; start += input_sample_count;
} }
result = WriteAudioData(audio.audio_params().is_valid() ? audio.audio_params() : params().audio_params(), const_cast<const uint8_t**>(input_data), input_sample_count); result = WriteAudioData(audio.audio_params().is_valid() ?
audio.audio_params() :
params().audio_params(),
const_cast<const uint8_t **>(input_data),
input_sample_count);
if (input_data) { if (input_data) {
av_freep(&input_data[0]); av_freep(&input_data[0]);
@@ -321,7 +354,9 @@ bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
return result; return result;
} }
bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_t **input_data, int input_sample_count) bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params,
const uint8_t **input_data,
int input_sample_count)
{ {
if (!InitializeResampleContext(audio_params)) { if (!InitializeResampleContext(audio_params)) {
qCritical() << "Failed to initialize resample context"; qCritical() << "Failed to initialize resample context";
@@ -331,32 +366,46 @@ bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_
bool result = true; bool result = true;
// Create output buffer // Create output buffer
int output_sample_count = input_sample_count ? swr_get_out_samples(audio_resample_ctx_, input_sample_count) : 102400; int output_sample_count =
input_sample_count ?
swr_get_out_samples(audio_resample_ctx_, input_sample_count) :
102400;
uint8_t **output_data = nullptr; uint8_t **output_data = nullptr;
int output_linesize; int output_linesize;
av_samples_alloc_array_and_samples(&output_data, &output_linesize, audio_stream_->codecpar->ch_layout.nb_channels, av_samples_alloc_array_and_samples(
output_sample_count, static_cast<AVSampleFormat>(audio_stream_->codecpar->format), 0); &output_data, &output_linesize,
audio_stream_->codecpar->ch_layout.nb_channels, output_sample_count,
static_cast<AVSampleFormat>(audio_stream_->codecpar->format), 0);
// Perform conversion // Perform conversion
int converted = swr_convert(audio_resample_ctx_, output_data, output_sample_count, const_cast<const uint8_t**>(input_data), input_sample_count); int converted = swr_convert(audio_resample_ctx_, output_data,
output_sample_count,
const_cast<const uint8_t **>(input_data),
input_sample_count);
if (converted > 0) { if (converted > 0) {
// Split sample buffer into frames // Split sample buffer into frames
for (int i = 0; i < converted;) { for (int i = 0; i < converted;) {
int frame_remaining_samples = audio_max_samples_ - audio_frame_offset_; int frame_remaining_samples =
audio_max_samples_ - audio_frame_offset_;
int converted_remaining_samples = converted - i; int converted_remaining_samples = converted - i;
int copy_length = qMin(frame_remaining_samples, converted_remaining_samples); int copy_length =
qMin(frame_remaining_samples, converted_remaining_samples);
av_samples_copy(audio_frame_->data, output_data, audio_frame_offset_, i, av_samples_copy(audio_frame_->data, output_data,
copy_length, audio_frame_offset_, i, copy_length,
audio_frame_->ch_layout.nb_channels, static_cast<AVSampleFormat>(audio_frame_->format)); audio_frame_->ch_layout.nb_channels,
static_cast<AVSampleFormat>(audio_frame_->format));
audio_frame_offset_ += copy_length; audio_frame_offset_ += copy_length;
i += copy_length; i += copy_length;
if (audio_frame_offset_ == audio_max_samples_ || (i == converted && !input_data)) { if (audio_frame_offset_ == audio_max_samples_ ||
(i == converted && !input_data)) {
// Got all the samples we needed, write the frame // Got all the samples we needed, write the frame
audio_frame_->pts = av_rescale_q(audio_write_count_, {1, audio_codec_ctx_->sample_rate}, audio_codec_ctx_->time_base); audio_frame_->pts = av_rescale_q(
audio_write_count_, { 1, audio_codec_ctx_->sample_rate },
audio_codec_ctx_->time_base);
WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_); WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_);
audio_write_count_ += audio_frame_offset_; audio_write_count_ += audio_frame_offset_;
@@ -370,7 +419,9 @@ bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_
if (!input_data && audio_frame_offset_ > 0) { if (!input_data && audio_frame_offset_ > 0) {
audio_frame_->nb_samples = audio_frame_offset_; audio_frame_->nb_samples = audio_frame_offset_;
audio_frame_->pts = av_rescale_q(audio_write_count_, {1, audio_codec_ctx_->sample_rate}, audio_codec_ctx_->time_base); audio_frame_->pts = av_rescale_q(audio_write_count_,
{ 1, audio_codec_ctx_->sample_rate },
audio_codec_ctx_->time_base);
WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_); WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_);
} }
@@ -392,12 +443,11 @@ QString GetAssTime(const rational &time)
int64_t mm = (total_centiseconds / 6000) % 60; int64_t mm = (total_centiseconds / 6000) % 60;
int64_t hh = total_centiseconds / 360000; int64_t hh = total_centiseconds / 360000;
return QStringLiteral("%1:%2:%3.%4").arg( return QStringLiteral("%1:%2:%3.%4")
QString::number(hh), .arg(QString::number(hh),
QStringLiteral("%1").arg(mm, 2, 10, QLatin1Char('0')), QStringLiteral("%1").arg(mm, 2, 10, QLatin1Char('0')),
QStringLiteral("%1").arg(ss, 2, 10, QLatin1Char('0')), QStringLiteral("%1").arg(ss, 2, 10, QLatin1Char('0')),
QStringLiteral("%1").arg(cs, 2, 10, QLatin1Char('0')) QStringLiteral("%1").arg(cs, 2, 10, QLatin1Char('0')));
);
} }
bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block) bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
@@ -409,10 +459,14 @@ bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
pkt->stream_index = subtitle_stream_->index; pkt->stream_index = subtitle_stream_->index;
pkt->data = (uint8_t *)utf8_sub.data(); pkt->data = (uint8_t *)utf8_sub.data();
pkt->size = utf8_sub.size(); pkt->size = utf8_sub.size();
pkt->pts = Timecode::time_to_timestamp(sub_block->in(), subtitle_codec_ctx_->time_base, Timecode::kFloor); pkt->pts = Timecode::time_to_timestamp(
pkt->duration = av_rescale_q(qRound64(sub_block->length().toDouble() * 1000), {1, 1000}, subtitle_codec_ctx_->time_base); sub_block->in(), subtitle_codec_ctx_->time_base, Timecode::kFloor);
pkt->duration =
av_rescale_q(qRound64(sub_block->length().toDouble() * 1000),
{ 1, 1000 }, subtitle_codec_ctx_->time_base);
pkt->dts = pkt->pts; pkt->dts = pkt->pts;
av_packet_rescale_ts(pkt, subtitle_codec_ctx_->time_base, subtitle_stream_->time_base); av_packet_rescale_ts(pkt, subtitle_codec_ctx_->time_base,
subtitle_stream_->time_base);
int err = av_interleaved_write_frame(fmt_ctx_, pkt); int err = av_interleaved_write_frame(fmt_ctx_, pkt);
bool ret = true; bool ret = true;
@@ -541,12 +595,14 @@ void FFmpegEncoder::FFmpegError(const QString& context, int error_code)
char err[1024]; char err[1024];
av_strerror(error_code, err, 1024); av_strerror(error_code, err, 1024);
QString formatted_err = tr("%1: %2 %3").arg(context, err, QString::number(error_code)); QString formatted_err =
tr("%1: %2 %3").arg(context, err, QString::number(error_code));
qDebug() << formatted_err; qDebug() << formatted_err;
SetError(formatted_err); SetError(formatted_err);
} }
bool FFmpegEncoder::WriteAVFrame(AVFrame *frame, AVCodecContext* codec_ctx, AVStream* stream) bool FFmpegEncoder::WriteAVFrame(AVFrame *frame, AVCodecContext *codec_ctx,
AVStream *stream)
{ {
// Send raw frame to the encoder // Send raw frame to the encoder
int error_code = avcodec_send_frame(codec_ctx, frame); int error_code = avcodec_send_frame(codec_ctx, frame);
@@ -567,7 +623,8 @@ bool FFmpegEncoder::WriteAVFrame(AVFrame *frame, AVCodecContext* codec_ctx, AVSt
if (error_code == AVERROR(EAGAIN)) { if (error_code == AVERROR(EAGAIN)) {
break; break;
} else if (error_code < 0) { } else if (error_code < 0) {
FFmpegError(tr("Failed to receive packet from decoder"), error_code); FFmpegError(tr("Failed to receive packet from decoder"),
error_code);
goto fail; goto fail;
} }
@@ -595,22 +652,29 @@ fail:
return succeeded; return succeeded;
} }
bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AVCodecContext** codec_ctx_ptr, const ExportCodec::Codec& codec) bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream **stream_ptr,
AVCodecContext **codec_ctx_ptr,
const ExportCodec::Codec &codec)
{ {
if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) { if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO &&
SetError(tr("Cannot initialize a stream that is not a video, audio, or subtitle type")); type != AVMEDIA_TYPE_SUBTITLE) {
SetError(tr(
"Cannot initialize a stream that is not a video, audio, or subtitle type"));
return false; return false;
} }
// Find encoder // Find encoder
const AVCodec* encoder = GetEncoder(codec, params().audio_params().format()); const AVCodec *encoder =
GetEncoder(codec, params().audio_params().format());
if (!encoder) { if (!encoder) {
SetError(tr("Failed to find codec for 0x%1").arg(codec, 16)); SetError(tr("Failed to find codec for 0x%1").arg(codec, 16));
return false; return false;
} }
if (encoder->type != type) { if (encoder->type != type) {
SetError(tr("Retrieved unexpected codec type %1 for codec %2").arg(QString::number(encoder->type), QString::number(codec))); SetError(
tr("Retrieved unexpected codec type %1 for codec %2")
.arg(QString::number(encoder->type), QString::number(codec)));
return false; return false;
} }
@@ -625,35 +689,47 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
if (type == AVMEDIA_TYPE_VIDEO) { if (type == AVMEDIA_TYPE_VIDEO) {
codec_ctx->width = params().video_params().width(); codec_ctx->width = params().video_params().width();
codec_ctx->height = params().video_params().height(); codec_ctx->height = params().video_params().height();
codec_ctx->sample_aspect_ratio = params().video_params().pixel_aspect_ratio().toAVRational(); codec_ctx->sample_aspect_ratio =
codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational(); params().video_params().pixel_aspect_ratio().toAVRational();
codec_ctx->framerate = params().video_params().frame_rate().toAVRational(); codec_ctx->time_base =
params().video_params().frame_rate_as_time_base().toAVRational();
codec_ctx->framerate =
params().video_params().frame_rate().toAVRational();
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8()); codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
codec_ctx->color_range = params().video_params().color_range() == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; codec_ctx->color_range = params().video_params().color_range() ==
VideoParams::kColorRangeFull ?
AVCOL_RANGE_JPEG :
AVCOL_RANGE_MPEG;
if (params().video_params().interlacing() != VideoParams::kInterlaceNone) { if (params().video_params().interlacing() !=
VideoParams::kInterlaceNone) {
// FIXME: I actually don't know what these flags do, the documentation helpfully doesn't // FIXME: I actually don't know what these flags do, the documentation helpfully doesn't
// explain them at all. I hope using both of them is the right thing to do. // explain them at all. I hope using both of them is the right thing to do.
codec_ctx->flags |= AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME; codec_ctx->flags |= AV_CODEC_FLAG_INTERLACED_DCT |
AV_CODEC_FLAG_INTERLACED_ME;
if (params().video_params().interlacing() ==
if (params().video_params().interlacing() == VideoParams::kInterlacedTopFirst) { VideoParams::kInterlacedTopFirst) {
codec_ctx->field_order = AV_FIELD_TT; codec_ctx->field_order = AV_FIELD_TT;
} else { } else {
codec_ctx->field_order = AV_FIELD_BB; codec_ctx->field_order = AV_FIELD_BB;
if (codec == ExportCodec::kCodecH264 || codec == ExportCodec::kCodecH264rgb) { if (codec == ExportCodec::kCodecH264 ||
codec == ExportCodec::kCodecH264rgb) {
// For some reason, FFmpeg doesn't set libx264's bff flag so we have to do it ourselves // For some reason, FFmpeg doesn't set libx264's bff flag so we have to do it ourselves
av_opt_set(codec_ctx->priv_data, "x264opts", "bff=1", AV_OPT_SEARCH_CHILDREN); av_opt_set(codec_ctx->priv_data, "x264opts", "bff=1",
AV_OPT_SEARCH_CHILDREN);
} }
} }
} }
// Set custom options // Set custom options
{ {
for (auto i=params().video_opts().begin();i!=params().video_opts().end();i++) { for (auto i = params().video_opts().begin();
i != params().video_opts().end(); i++) {
if (!i.key().startsWith(QStringLiteral("ove_"))) { if (!i.key().startsWith(QStringLiteral("ove_"))) {
av_opt_set(codec_ctx->priv_data, i.key().toUtf8(), i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN); av_opt_set(codec_ctx->priv_data, i.key().toUtf8(),
i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN);
} }
} }
@@ -670,12 +746,14 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
} }
if (params().video_buffer_size() > 0) { if (params().video_buffer_size() > 0) {
codec_ctx->rc_buffer_size = static_cast<int>(params().video_buffer_size()); codec_ctx->rc_buffer_size =
static_cast<int>(params().video_buffer_size());
} }
// nclc tags. See https://ffmpeg.org/doxygen/4.0/pixfmt_8h.html#ad384ee5a840bafd73daef08e6d9cafe7 // nclc tags. See https://ffmpeg.org/doxygen/4.0/pixfmt_8h.html#ad384ee5a840bafd73daef08e6d9cafe7
// ffprobe -v error -show_format -show_streams "C:\Users\Tom\Documents\srgb correct tags.mov" // ffprobe -v error -show_format -show_streams "C:\Users\Tom\Documents\srgb correct tags.mov"
if (params().color_transform().output().contains(QStringLiteral("sRGB"), Qt::CaseInsensitive)) { if (params().color_transform().output().contains(
QStringLiteral("sRGB"), Qt::CaseInsensitive)) {
codec_ctx->color_primaries = AVCOL_PRI_BT709; codec_ctx->color_primaries = AVCOL_PRI_BT709;
codec_ctx->color_trc = AVCOL_TRC_IEC61966_2_1; codec_ctx->color_trc = AVCOL_TRC_IEC61966_2_1;
codec_ctx->colorspace = AVCOL_SPC_BT709; codec_ctx->colorspace = AVCOL_SPC_BT709;
@@ -687,11 +765,11 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
} }
} else if (type == AVMEDIA_TYPE_AUDIO) { } else if (type == AVMEDIA_TYPE_AUDIO) {
// Assume audio stream // Assume audio stream
codec_ctx->sample_rate = params().audio_params().sample_rate(); codec_ctx->sample_rate = params().audio_params().sample_rate();
codec_ctx->ch_layout = params().audio_params().channel_layout(); codec_ctx->ch_layout = params().audio_params().channel_layout();
codec_ctx->sample_fmt = FFmpegUtils::GetFFmpegSampleFormat(params().audio_params().format()); codec_ctx->sample_fmt = FFmpegUtils::GetFFmpegSampleFormat(
params().audio_params().format());
codec_ctx->time_base = { 1, codec_ctx->sample_rate }; codec_ctx->time_base = { 1, codec_ctx->sample_rate };
if (params().audio_bit_rate() > 0) { if (params().audio_bit_rate() > 0) {
@@ -699,14 +777,13 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
} }
} else if (type == AVMEDIA_TYPE_SUBTITLE) { } else if (type == AVMEDIA_TYPE_SUBTITLE) {
codec_ctx->time_base = av_get_time_base_q(); codec_ctx->time_base = av_get_time_base_q();
QByteArray ass_header = SubtitleParams::GenerateASSHeader().toUtf8(); QByteArray ass_header = SubtitleParams::GenerateASSHeader().toUtf8();
codec_ctx->subtitle_header = new uint8_t[ass_header.size()]; codec_ctx->subtitle_header = new uint8_t[ass_header.size()];
memcpy(codec_ctx->subtitle_header, ass_header.constData(), ass_header.size()); memcpy(codec_ctx->subtitle_header, ass_header.constData(),
ass_header.size());
codec_ctx->subtitle_header_size = ass_header.size(); codec_ctx->subtitle_header_size = ass_header.size();
} }
if (!SetupCodecContext(stream, codec_ctx, encoder)) { if (!SetupCodecContext(stream, codec_ctx, encoder)) {
@@ -716,7 +793,9 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
return true; return true;
} }
bool FFmpegEncoder::InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx, const AVCodec* codec) bool FFmpegEncoder::InitializeCodecContext(AVStream **stream,
AVCodecContext **codec_ctx,
const AVCodec *codec)
{ {
*stream = avformat_new_stream(fmt_ctx_, nullptr); *stream = avformat_new_stream(fmt_ctx_, nullptr);
if (!(*stream)) { if (!(*stream)) {
@@ -734,7 +813,9 @@ bool FFmpegEncoder::InitializeCodecContext(AVStream **stream, AVCodecContext **c
return true; return true;
} }
bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ctx, const AVCodec* codec) bool FFmpegEncoder::SetupCodecContext(AVStream *stream,
AVCodecContext *codec_ctx,
const AVCodec *codec)
{ {
int error_code; int error_code;
@@ -762,7 +843,8 @@ bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ct
// Copy context settings to codecpar object // Copy context settings to codecpar object
error_code = avcodec_parameters_from_context(stream->codecpar, codec_ctx); error_code = avcodec_parameters_from_context(stream->codecpar, codec_ctx);
if (error_code < 0) { if (error_code < 0) {
FFmpegError(tr("Failed to copy codec parameters to stream"), error_code); FFmpegError(tr("Failed to copy codec parameters to stream"),
error_code);
return false; return false;
} }
@@ -828,15 +910,11 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio)
} }
AVChannelLayout layout = audio.channel_layout(); AVChannelLayout layout = audio.channel_layout();
// Create resample context // Create resample context
swr_alloc_set_opts2(&audio_resample_ctx_, swr_alloc_set_opts2(&audio_resample_ctx_, &audio_codec_ctx_->ch_layout,
&audio_codec_ctx_->ch_layout,
audio_codec_ctx_->sample_fmt, audio_codec_ctx_->sample_fmt,
audio_codec_ctx_->sample_rate, audio_codec_ctx_->sample_rate, &layout,
&layout,
FFmpegUtils::GetFFmpegSampleFormat(audio.format()), FFmpegUtils::GetFFmpegSampleFormat(audio.format()),
audio.sample_rate(), audio.sample_rate(), 0, nullptr);
0,
nullptr);
if (!audio_resample_ctx_) { if (!audio_resample_ctx_) {
return false; return false;
} }
@@ -852,7 +930,8 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio)
// If not, use another frame size // If not, use another frame size
if (params().video_enabled()) { if (params().video_enabled()) {
// If we're encoding video, use enough samples to cover roughly one frame of video // If we're encoding video, use enough samples to cover roughly one frame of video
audio_max_samples_ = params().audio_params().time_to_samples(params().video_params().frame_rate_as_time_base()); audio_max_samples_ = params().audio_params().time_to_samples(
params().video_params().frame_rate_as_time_base());
} else { } else {
// If no video, just use an arbitrary number // If no video, just use an arbitrary number
audio_max_samples_ = 256; audio_max_samples_ = 256;
@@ -880,7 +959,8 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio)
return true; return true;
} }
const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c, SampleFormat aformat) const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c,
SampleFormat aformat)
{ {
switch (c) { switch (c) {
case ExportCodec::kCodecH264: case ExportCodec::kCodecH264:
+22 -13
View File
@@ -31,25 +31,29 @@ extern "C" {
#include "codec/encoder.h" #include "codec/encoder.h"
namespace olive { namespace olive
class FFmpegEncoder : public Encoder
{ {
class FFmpegEncoder : public Encoder {
Q_OBJECT Q_OBJECT
public: public:
FFmpegEncoder(const EncodingParams &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;
@@ -71,18 +75,24 @@ private:
*/ */
void FFmpegError(const QString &context, int error_code); void FFmpegError(const QString &context, int error_code);
bool WriteAVFrame(AVFrame* frame, AVCodecContext *codec_ctx, AVStream *stream); bool WriteAVFrame(AVFrame *frame, AVCodecContext *codec_ctx,
AVStream *stream);
bool InitializeStream(enum AVMediaType type, AVStream** stream, AVCodecContext** codec_ctx, const ExportCodec::Codec &codec); bool InitializeStream(enum AVMediaType type, AVStream **stream,
bool InitializeCodecContext(AVStream** stream, AVCodecContext** codec_ctx, const AVCodec* codec); AVCodecContext **codec_ctx,
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx, const AVCodec *codec); const ExportCodec::Codec &codec);
bool InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx,
const AVCodec *codec);
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx,
const AVCodec *codec);
void FlushEncoders(); void FlushEncoders();
void FlushCodecCtx(AVCodecContext *codec_ctx, AVStream *stream); void FlushCodecCtx(AVCodecContext *codec_ctx, AVStream *stream);
bool InitializeResampleContext(const AudioParams &audio); bool InitializeResampleContext(const AudioParams &audio);
static const AVCodec *GetEncoder(ExportCodec::Codec c, SampleFormat aformat); static const AVCodec *GetEncoder(ExportCodec::Codec c,
SampleFormat aformat);
AVFormatContext *fmt_ctx_; AVFormatContext *fmt_ctx_;
@@ -105,7 +115,6 @@ private:
AVCodecContext *subtitle_codec_ctx_; AVCodecContext *subtitle_codec_ctx_;
bool open_; bool open_;
}; };
} }
+27 -19
View File
@@ -28,12 +28,13 @@
#include "common/oiioutils.h" #include "common/oiioutils.h"
#include "render/framemanager.h" #include "render/framemanager.h"
namespace olive { namespace olive
{
Frame::Frame() : Frame::Frame()
data_(nullptr), : data_(nullptr)
data_size_(0), , data_size_(0)
timestamp_(0) , timestamp_(0)
{ {
} }
@@ -56,14 +57,16 @@ void Frame::set_video_params(const VideoParams &params)
{ {
params_ = params; params_ = params;
linesize_ = generate_linesize_bytes(width(), params_.format(), params_.channel_count()); linesize_ = generate_linesize_bytes(width(), params_.format(),
params_.channel_count());
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel(); linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
} }
FramePtr Frame::Interlace(FramePtr top, FramePtr bottom) FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
{ {
if (top->video_params() != bottom->video_params()) { if (top->video_params() != bottom->video_params()) {
qCritical() << "Tried to interlace two frames that had incompatible parameters"; qCritical()
<< "Tried to interlace two frames that had incompatible parameters";
return nullptr; return nullptr;
} }
@@ -77,17 +80,18 @@ FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
FramePtr which = (i % 2 == 0) ? top : bottom; FramePtr which = (i % 2 == 0) ? top : bottom;
memcpy(interlaced->data() + i * linesize, memcpy(interlaced->data() + i * linesize,
which->const_data() + i*linesize, which->const_data() + i * linesize, linesize);
linesize);
} }
return interlaced; return interlaced;
} }
int Frame::generate_linesize_bytes(int width, PixelFormat format, int channel_count) int Frame::generate_linesize_bytes(int width, PixelFormat format,
int channel_count)
{ {
// Align to 32 bytes (not sure if this is necessary?) // Align to 32 bytes (not sure if this is necessary?)
return VideoParams::GetBytesPerPixel(format, channel_count) * ((width + 31) & ~31); return VideoParams::GetBytesPerPixel(format, channel_count) *
((width + 31) & ~31);
} }
Color Frame::get_pixel(int x, int y) const Color Frame::get_pixel(int x, int y) const
@@ -96,9 +100,11 @@ Color Frame::get_pixel(int x, int y) const
return Color(); return Color();
} }
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel(); int byte_offset =
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
return Color(reinterpret_cast<const char*>(data_ + byte_offset), video_params().format(), video_params().channel_count()); return Color(reinterpret_cast<const char *>(data_ + byte_offset),
video_params().format(), video_params().channel_count());
} }
bool Frame::contains_pixel(int x, int y) const bool Frame::contains_pixel(int x, int y) const
@@ -112,9 +118,11 @@ void Frame::set_pixel(int x, int y, const Color &c)
return; return;
} }
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel(); int byte_offset =
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
c.toData(reinterpret_cast<char*>(data_ + byte_offset), video_params().format(), video_params().channel_count()); c.toData(reinterpret_cast<char *>(data_ + byte_offset),
video_params().format(), video_params().channel_count());
} }
bool Frame::allocate() bool Frame::allocate()
@@ -159,14 +167,14 @@ FramePtr Frame::convert(PixelFormat format) const
converted->allocate(); converted->allocate();
// Do the conversion through OIIO for convenience // Do the conversion through OIIO for convenience
OIIO::ImageBuf src(OIIO::ImageSpec(width(), height(), OIIO::ImageBuf src(
channel_count(), OIIO::ImageSpec(width(), height(), channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format()))); OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
OIIOUtils::FrameToBuffer(this, &src); OIIOUtils::FrameToBuffer(this, &src);
OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(), OIIO::ImageBuf dst(OIIO::ImageSpec(
channel_count(), converted->width(), converted->height(), channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(format))); OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
if (dst.copy_pixels(src)) { if (dst.copy_pixels(src)) {
+5 -5
View File
@@ -28,7 +28,8 @@
#include "common/define.h" #include "common/define.h"
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
{
class Frame; class Frame;
using FramePtr = std::shared_ptr<Frame>; using FramePtr = std::shared_ptr<Frame>;
@@ -36,8 +37,7 @@ using FramePtr = std::shared_ptr<Frame>;
/** /**
* @brief Video frame data or audio sample data from a Decoder * @brief Video frame data or audio sample data from a Decoder
*/ */
class Frame class Frame {
{
public: public:
Frame(); Frame();
@@ -52,7 +52,8 @@ public:
static FramePtr Interlace(FramePtr top, FramePtr bottom); static FramePtr Interlace(FramePtr top, FramePtr bottom);
static int generate_linesize_bytes(int width, PixelFormat format, int channel_count); static int generate_linesize_bytes(int width, PixelFormat format,
int channel_count);
int linesize_pixels() const int linesize_pixels() const
{ {
@@ -164,7 +165,6 @@ private:
int linesize_; int linesize_;
int linesize_pixels_; int linesize_pixels_;
}; };
} }
+37 -20
View File
@@ -31,12 +31,13 @@
#include "core.h" #include "core.h"
#include "render/renderer.h" #include "render/renderer.h"
namespace olive { namespace olive
{
QStringList OIIODecoder::supported_formats_; QStringList OIIODecoder::supported_formats_;
OIIODecoder::OIIODecoder() : OIIODecoder::OIIODecoder()
image_(nullptr) : image_(nullptr)
{ {
} }
@@ -45,7 +46,8 @@ QString OIIODecoder::id() const
return QStringLiteral("oiio"); return QStringLiteral("oiio");
} }
FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cancelled) const FootageDescription OIIODecoder::Probe(const QString &filename,
CancelAtom *cancelled) const
{ {
Q_UNUSED(cancelled) Q_UNUSED(cancelled)
@@ -85,8 +87,10 @@ FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cance
// This is a multilayer image and this image might have an offset // This is a multilayer image and this image might have an offset
OIIO::ImageSpec root_spec = in->spec(0); OIIO::ImageSpec root_spec = in->spec(0);
float norm_x = spec.x + float(spec.width)*0.5f - float(root_spec.width)*0.5f; float norm_x = spec.x + float(spec.width) * 0.5f -
float norm_y = spec.y + float(spec.height)*0.5f - float(root_spec.height)*0.5f; float(root_spec.width) * 0.5f;
float norm_y = spec.y + float(spec.height) * 0.5f -
float(root_spec.height) * 0.5f;
video_params.set_x(norm_x); video_params.set_x(norm_x);
video_params.set_y(norm_y); video_params.set_y(norm_y);
@@ -124,8 +128,7 @@ TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec()); VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
vp.set_divider(p.divider); vp.set_divider(p.divider);
if (!buffer_.is_allocated() if (!buffer_.is_allocated() || last_params_.divider != p.divider) {
|| last_params_.divider != p.divider) {
last_params_ = p; last_params_ = p;
buffer_.destroy(); buffer_.destroy();
@@ -137,7 +140,9 @@ TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data()); image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
} else { } else {
OIIO::ImageBuf buf(image_->spec()); OIIO::ImageBuf buf(image_->spec());
image_->read_image(0,0,0,-1,image_->spec().format, buf.localpixels(), buf.pixel_stride(), buf.scanline_stride(), buf.z_stride()); image_->read_image(0, 0, 0, -1, image_->spec().format,
buf.localpixels(), buf.pixel_stride(),
buf.scanline_stride(), buf.z_stride());
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here) // Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
int px_sz = vp.GetBytesPerPixel(); int px_sz = vp.GetBytesPerPixel();
@@ -146,15 +151,18 @@ TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
for (int dst_x = 0; dst_x < buffer_.width(); dst_x++) { for (int dst_x = 0; dst_x < buffer_.width(); dst_x++) {
int src_x = dst_x * buf.spec().width / buffer_.width(); int src_x = dst_x * buf.spec().width / buffer_.width();
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y + px_sz * dst_x, memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y +
static_cast<uint8_t*>(buf.localpixels()) + buf.scanline_stride() * src_y + px_sz * src_x, px_sz * dst_x,
static_cast<uint8_t *>(buf.localpixels()) +
buf.scanline_stride() * src_y + px_sz * src_x,
px_sz); px_sz);
} }
} }
} }
} }
return p.renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels()); return p.renderer->CreateTexture(vp, buffer_.data(),
buffer_.linesize_pixels());
} }
void OIIODecoder::CloseInternal() void OIIODecoder::CloseInternal()
@@ -170,7 +178,9 @@ bool OIIODecoder::FileTypeIsSupported(const QString& fn)
// Check if we've created the supported formats list, create it if not // Check if we've created the supported formats list, create it if not
if (supported_formats_.isEmpty()) { if (supported_formats_.isEmpty()) {
QStringList extension_list = QString::fromStdString(OIIO::get_string_attribute("extension_list")).split(';'); QStringList extension_list =
QString::fromStdString(OIIO::get_string_attribute("extension_list"))
.split(';');
// The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions // The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions
foreach (const QString &ext, extension_list) { foreach (const QString &ext, extension_list) {
@@ -180,7 +190,8 @@ bool OIIODecoder::FileTypeIsSupported(const QString& fn)
} }
} }
if (!supported_formats_.contains(QFileInfo(fn).suffix(), Qt::CaseInsensitive)) { if (!supported_formats_.contains(QFileInfo(fn).suffix(),
Qt::CaseInsensitive)) {
return false; return false;
} }
@@ -203,17 +214,20 @@ bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
const OIIO::ImageSpec &spec = image_->spec(); const OIIO::ImageSpec &spec = image_->spec();
// We use RGBA frames because that tends to be the native format of GPUs // We use RGBA frames because that tends to be the native format of GPUs
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)); pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
if (pix_fmt_ == PixelFormat::INVALID) { if (pix_fmt_ == PixelFormat::INVALID) {
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format"; qWarning()
<< "Failed to convert OIIO::ImageDesc to native pixel format";
return false; return false;
} }
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_); oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) { if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
qCritical() << "Failed to determine appropriate OIIO basetype from native format"; qCritical()
<< "Failed to determine appropriate OIIO basetype from native format";
return false; return false;
} }
@@ -230,15 +244,18 @@ void OIIODecoder::CloseImageHandle()
buffer_.destroy(); buffer_.destroy();
} }
VideoParams OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec) VideoParams
OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
{ {
VideoParams video_params; VideoParams video_params;
video_params.set_width(spec.width); video_params.set_width(spec.width);
video_params.set_height(spec.height); video_params.set_height(spec.height);
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype))); video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
video_params.set_channel_count(spec.nchannels); video_params.set_channel_count(spec.nchannels);
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec)); video_params.set_pixel_aspect_ratio(
OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
video_params.set_video_type(VideoParams::kVideoTypeStill); video_params.set_video_type(VideoParams::kVideoTypeStill);
return video_params; return video_params;
+11 -7
View File
@@ -26,10 +26,10 @@
#include "codec/decoder.h" #include "codec/decoder.h"
namespace olive { namespace olive
class OIIODecoder : public Decoder
{ {
class OIIODecoder : public Decoder {
Q_OBJECT Q_OBJECT
public: public:
OIIODecoder(); OIIODecoder();
@@ -38,13 +38,18 @@ public:
virtual QString id() const override; virtual QString id() const override;
virtual bool SupportsVideo() override{return true;} virtual bool SupportsVideo() override
{
return true;
}
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const override; virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const override;
protected: protected:
virtual bool OpenInternal() override; virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override; virtual TexturePtr
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual void CloseInternal() override; virtual void CloseInternal() override;
private: private:
@@ -65,7 +70,6 @@ private:
RetrieveVideoParams last_params_; RetrieveVideoParams last_params_;
static QStringList supported_formats_; static QStringList supported_formats_;
}; };
} }
+8 -6
View File
@@ -22,12 +22,12 @@
#include "common/oiioutils.h" #include "common/oiioutils.h"
namespace olive { namespace olive
OIIOEncoder::OIIOEncoder(const EncodingParams &params) :
Encoder(params)
{ {
OIIOEncoder::OIIOEncoder(const EncodingParams &params)
: Encoder(params)
{
} }
bool OIIOEncoder::Open() bool OIIOEncoder::Open()
@@ -45,13 +45,15 @@ bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
} }
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format()); OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
OIIO::ImageSpec spec(frame->width(), frame->height(), frame->channel_count(), type); OIIO::ImageSpec spec(frame->width(), frame->height(),
frame->channel_count(), type);
if (!output->open(filename, spec)) { if (!output->open(filename, spec)) {
return false; return false;
} }
if (!output->write_image(type, frame->data(), OIIO::AutoStride, frame->linesize_bytes())) { if (!output->write_image(type, frame->data(), OIIO::AutoStride,
frame->linesize_bytes())) {
return false; return false;
} }
+5 -5
View File
@@ -23,10 +23,10 @@
#include "codec/encoder.h" #include "codec/encoder.h"
namespace olive { namespace olive
class OIIOEncoder : public Encoder
{ {
class OIIOEncoder : public Encoder {
Q_OBJECT Q_OBJECT
public: public:
OIIOEncoder(const EncodingParams &params); OIIOEncoder(const EncodingParams &params);
@@ -34,12 +34,12 @@ public:
public slots: public slots:
virtual bool Open() override; virtual bool Open() override;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override; virtual bool WriteFrame(olive::FramePtr frame,
olive::core::rational time) override;
virtual bool WriteAudio(const SampleBuffer &audio) override; virtual bool WriteAudio(const SampleBuffer &audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override; virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override; virtual void Close() override;
}; };
} }
+10 -7
View File
@@ -20,12 +20,12 @@
#include "planarfiledevice.h" #include "planarfiledevice.h"
namespace olive { namespace olive
PlanarFileDevice::PlanarFileDevice(QObject *parent) :
QObject(parent)
{ {
PlanarFileDevice::PlanarFileDevice(QObject *parent)
: QObject(parent)
{
} }
PlanarFileDevice::~PlanarFileDevice() PlanarFileDevice::~PlanarFileDevice()
@@ -33,7 +33,8 @@ PlanarFileDevice::~PlanarFileDevice()
close(); close();
} }
bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMode mode) bool PlanarFileDevice::open(const QVector<QString> &filenames,
QIODevice::OpenMode mode)
{ {
if (isOpen()) { if (isOpen()) {
// Already open // Already open
@@ -54,7 +55,8 @@ bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMo
return true; return true;
} }
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offset) qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel,
qint64 offset)
{ {
qint64 ret = -1; qint64 ret = -1;
@@ -68,7 +70,8 @@ qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offs
return ret; return ret;
} }
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel, qint64 offset) qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel,
qint64 offset)
{ {
qint64 ret = -1; qint64 ret = -1;
+5 -5
View File
@@ -25,12 +25,12 @@
#include <QFile> #include <QFile>
#include <QObject> #include <QObject>
namespace olive { namespace olive
{
using namespace core; using namespace core;
class PlanarFileDevice : public QObject class PlanarFileDevice : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
PlanarFileDevice(QObject *parent = nullptr); PlanarFileDevice(QObject *parent = nullptr);
@@ -46,7 +46,8 @@ public:
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0); qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 write(const char **data, qint64 bytes_per_channel, qint64 offset = 0); qint64 write(const char **data, qint64 bytes_per_channel,
qint64 offset = 0);
qint64 size() const; qint64 size() const;
@@ -56,7 +57,6 @@ public:
private: private:
QVector<QFile *> files_; QVector<QFile *> files_;
}; };
} }
+3 -6
View File
@@ -23,15 +23,12 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
class AutoScroll { class AutoScroll {
public: public:
enum Method { enum Method { kNone, kPage, kSmooth };
kNone,
kPage,
kSmooth
};
}; };
} }
+5 -3
View File
@@ -24,7 +24,8 @@
#include "common/define.h" #include "common/define.h"
#include "render/cancelatom.h" #include "render/cancelatom.h"
namespace olive { namespace olive
{
class CancelableObject { class CancelableObject {
public: public:
@@ -49,11 +50,12 @@ public:
} }
protected: protected:
virtual void CancelEvent(){} virtual void CancelEvent()
{
}
private: private:
CancelAtom cancel_; CancelAtom cancel_;
}; };
} }
+15 -7
View File
@@ -34,16 +34,23 @@ CommandLineParser::~CommandLineParser()
} }
} }
const CommandLineParser::Option *CommandLineParser::AddOption(const QStringList &strings, const QString &description, bool takes_arg, const QString &arg_placeholder, bool hidden) const CommandLineParser::Option *
CommandLineParser::AddOption(const QStringList &strings,
const QString &description, bool takes_arg,
const QString &arg_placeholder, bool hidden)
{ {
Option *o = new Option(); Option *o = new Option();
options_.append({strings, description, o, takes_arg, arg_placeholder, hidden}); options_.append(
{ strings, description, o, takes_arg, arg_placeholder, hidden });
return o; return o;
} }
const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArgument(const QString &name, const QString &description, bool required) const CommandLineParser::PositionalArgument *
CommandLineParser::AddPositionalArgument(const QString &name,
const QString &description,
bool required)
{ {
PositionalArgument *a = new PositionalArgument(); PositionalArgument *a = new PositionalArgument();
@@ -103,8 +110,7 @@ found_flag:
void CommandLineParser::PrintHelp(const char *filename) void CommandLineParser::PrintHelp(const char *filename)
{ {
printf("%s %s\n", printf("%s %s\n", QCoreApplication::applicationName().toUtf8().constData(),
QCoreApplication::applicationName().toUtf8().constData(),
QCoreApplication::applicationVersion().toUtf8().constData()); QCoreApplication::applicationVersion().toUtf8().constData());
printf("Copyright (C) 2018-2022 Olive Team\n"); printf("Copyright (C) 2018-2022 Olive Team\n");
@@ -138,7 +144,8 @@ void CommandLineParser::PrintHelp(const char* filename)
basename = filename; basename = filename;
} }
printf("Usage: %s [options] %s\n\n", basename, positional_args.toUtf8().constData()); printf("Usage: %s [options] %s\n\n", basename,
positional_args.toUtf8().constData());
foreach (const KnownOption &o, options_) { foreach (const KnownOption &o, options_) {
if (o.hidden) { if (o.hidden) {
continue; continue;
@@ -160,7 +167,8 @@ void CommandLineParser::PrintHelp(const char* filename)
if (o.arg_placeholder.isEmpty()) { if (o.arg_placeholder.isEmpty()) {
printf(" %s\n", all_args.toUtf8().constData()); printf(" %s\n", all_args.toUtf8().constData());
} else { } else {
printf(" %s <%s>\n", all_args.toUtf8().constData(), o.arg_placeholder.toUtf8().constData()); printf(" %s <%s>\n", all_args.toUtf8().constData(),
o.arg_placeholder.toUtf8().constData());
} }
printf(" %s\n\n", o.description.toUtf8().constData()); printf(" %s\n\n", o.description.toUtf8().constData());
+9 -9
View File
@@ -36,15 +36,13 @@
* the user as a command line argument. Therefore we needed a custom implementation that could * the user as a command line argument. Therefore we needed a custom implementation that could
* parse arguments without the need for a Q(Core)Application to be present already. * parse arguments without the need for a Q(Core)Application to be present already.
*/ */
class CommandLineParser class CommandLineParser {
{
public: public:
~CommandLineParser(); ~CommandLineParser();
DISABLE_COPY_MOVE(CommandLineParser) DISABLE_COPY_MOVE(CommandLineParser)
class PositionalArgument class PositionalArgument {
{
public: public:
PositionalArgument() = default; PositionalArgument() = default;
@@ -60,7 +58,6 @@ public:
private: private:
QString setting_; QString setting_;
}; };
class Option : public PositionalArgument { class Option : public PositionalArgument {
@@ -82,14 +79,18 @@ public:
private: private:
bool is_set_; bool is_set_;
}; };
CommandLineParser() = default; CommandLineParser() = default;
const Option* AddOption(const QStringList& strings, const QString& description, bool takes_arg = false, const QString& arg_placeholder = QString(), bool hidden = false); const Option *AddOption(const QStringList &strings,
const QString &description, bool takes_arg = false,
const QString &arg_placeholder = QString(),
bool hidden = false);
const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false); const PositionalArgument *AddPositionalArgument(const QString &name,
const QString &description,
bool required = false);
void Process(const QVector<QString> &argv); void Process(const QVector<QString> &argv);
@@ -115,7 +116,6 @@ private:
QVector<KnownOption> options_; QVector<KnownOption> options_;
QVector<KnownPositionalArgument> positional_args_; QVector<KnownPositionalArgument> positional_args_;
}; };
#endif // COMMANDLINEPARSER_H #endif // COMMANDLINEPARSER_H
+22 -11
View File
@@ -40,12 +40,16 @@ crashpad::CrashpadClient *client;
bool InitializeCrashpad() bool InitializeCrashpad()
{ {
QString report_path = QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("reports")); QString report_path = QDir(olive::FileFunctions::GetTempFilePath())
.filePath(QStringLiteral("reports"));
QString handler_fn = olive::FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("crashpad_handler")); QString handler_fn =
olive::FileFunctions::GetFormattedExecutableForPlatform(
QStringLiteral("crashpad_handler"));
// Generate absolute path // Generate absolute path
QString handler_abs_path = QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn); QString handler_abs_path =
QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
bool status = false; bool status = false;
@@ -54,7 +58,9 @@ bool InitializeCrashpad()
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path)); base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
base::FilePath metrics_dir(QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("metrics")))); base::FilePath metrics_dir(
QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath())
.filePath(QStringLiteral("metrics"))));
// Metadata that will be posted to the server with the crash report map // Metadata that will be posted to the server with the crash report map
std::map<std::string, std::string> annotations; std::map<std::string, std::string> annotations;
@@ -65,24 +71,29 @@ bool InitializeCrashpad()
arguments.push_back("--no-upload-gzip"); arguments.push_back("--no-upload-gzip");
// Initialize Crashpad database // Initialize Crashpad database
std::unique_ptr<crashpad::CrashReportDatabase> database = crashpad::CrashReportDatabase::Initialize(reports_dir); std::unique_ptr<crashpad::CrashReportDatabase> database =
if (database == NULL) return false; crashpad::CrashReportDatabase::Initialize(reports_dir);
if (database == NULL)
return false;
// Disable automated crash uploads // Disable automated crash uploads
crashpad::Settings *settings = database->GetSettings(); crashpad::Settings *settings = database->GetSettings();
if (settings == NULL) return false; if (settings == NULL)
return false;
settings->SetUploadsEnabled(false); settings->SetUploadsEnabled(false);
// Start crash handler // Start crash handler
client = new crashpad::CrashpadClient(); client = new crashpad::CrashpadClient();
status = client->StartHandler(handler, reports_dir, metrics_dir, status = client->StartHandler(
"https://olivevideoeditor.org/crashpad/report.php", handler, reports_dir, metrics_dir,
annotations, arguments, true, true); "https://olivevideoeditor.org/crashpad/report.php", annotations,
arguments, true, true);
} }
// Override Crashpad exception filter with our own // Override Crashpad exception filter with our own
if (!status) { if (!status) {
qWarning() << "Failed to start Crashpad, automatic crash reporting will be disabled"; qWarning()
<< "Failed to start Crashpad, automatic crash reporting will be disabled";
} }
return status; return status;
+4 -2
View File
@@ -20,9 +20,11 @@
#include "debug.h" #include "debug.h"
namespace olive { namespace olive
{
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) void DebugHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg)
{ {
QByteArray localMsg = msg.toLocal8Bit(); QByteArray localMsg = msg.toLocal8Bit();
+4 -2
View File
@@ -25,9 +25,11 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); void DebugHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg);
} }
+3 -4
View File
@@ -26,10 +26,10 @@
//#define ALLOW_RETURNING_INFINITY //#define ALLOW_RETURNING_INFINITY
namespace olive { namespace olive
class Decibel
{ {
class Decibel {
public: public:
// In basically all circumstances, this should calculate to 0.0 linear // In basically all circumstances, this should calculate to 0.0 linear
static constexpr double MINIMUM = -200.0; static constexpr double MINIMUM = -200.0;
@@ -96,7 +96,6 @@ public:
private: private:
static constexpr double LOG100 = 4.60517018599; static constexpr double LOG100 = 4.60517018599;
}; };
} }
+8 -4
View File
@@ -21,7 +21,8 @@
#ifndef OLIVECOMMONDEFINE_H #ifndef OLIVECOMMONDEFINE_H
#define OLIVECOMMONDEFINE_H #define OLIVECOMMONDEFINE_H
namespace olive { namespace olive
{
/// The minimum size an icon in ProjectExplorer can be /// The minimum size an icon in ProjectExplorer can be
const int kProjectIconSizeMinimum = 16; const int kProjectIconSizeMinimum = 16;
@@ -39,9 +40,12 @@ const int kBytesInGigabyte = 1073741824;
#define MACRO_NAME_AS_STR(s) #s #define MACRO_NAME_AS_STR(s) #s
#define MACRO_VAL_AS_STR(s) MACRO_NAME_AS_STR(s) #define MACRO_VAL_AS_STR(s) MACRO_NAME_AS_STR(s)
#define OLIVE_NS_CONST_ARG(x, y) QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y) #define OLIVE_NS_CONST_ARG(x, y) \
#define OLIVE_NS_ARG(x, y) QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y) QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_RETURN_ARG(x, y) QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y) #define OLIVE_NS_ARG(x, y) \
QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_RETURN_ARG(x, y) \
QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
/** /**
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we * Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
+2 -1
View File
@@ -23,7 +23,8 @@
#include <stdint.h> #include <stdint.h>
namespace olive { namespace olive
{
inline int64_t GetDigitCount(int64_t input) inline int64_t GetDigitCount(int64_t input)
{ {
+18 -11
View File
@@ -20,9 +20,12 @@
#include "common/ffmpegutils.h" #include "common/ffmpegutils.h"
namespace olive { namespace olive
{
AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt, PixelFormat maximum) AVPixelFormat
FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
PixelFormat maximum)
{ {
AVPixelFormat possible_pix_fmts[3]; AVPixelFormat possible_pix_fmts[3];
@@ -35,9 +38,7 @@ AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt
possible_pix_fmts[2] = AV_PIX_FMT_NONE; possible_pix_fmts[2] = AV_PIX_FMT_NONE;
} }
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, pix_fmt, 1,
pix_fmt,
1,
nullptr); nullptr);
} }
@@ -136,11 +137,16 @@ int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f) AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
{ {
switch (f) { switch (f) {
case AV_PIX_FMT_YUVJ420P: return AV_PIX_FMT_YUV420P; case AV_PIX_FMT_YUVJ420P:
case AV_PIX_FMT_YUVJ422P: return AV_PIX_FMT_YUV422P; return AV_PIX_FMT_YUV420P;
case AV_PIX_FMT_YUVJ444P: return AV_PIX_FMT_YUV444P; case AV_PIX_FMT_YUVJ422P:
case AV_PIX_FMT_YUVJ440P: return AV_PIX_FMT_YUV440P; return AV_PIX_FMT_YUV422P;
case AV_PIX_FMT_YUVJ411P: return AV_PIX_FMT_YUV411P; case AV_PIX_FMT_YUVJ444P:
return AV_PIX_FMT_YUV444P;
case AV_PIX_FMT_YUVJ440P:
return AV_PIX_FMT_YUV440P;
case AV_PIX_FMT_YUVJ411P:
return AV_PIX_FMT_YUV411P;
default: default:
break; break;
} }
@@ -148,7 +154,8 @@ AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
return f; return f;
} }
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt, int channel_layout) AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
int channel_layout)
{ {
if (channel_layout == VideoParams::kRGBChannelCount) { if (channel_layout == VideoParams::kRGBChannelCount) {
switch (pix_fmt) { switch (pix_fmt) {
+7 -3
View File
@@ -31,7 +31,8 @@ extern "C" {
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
{
using namespace core; using namespace core;
@@ -40,7 +41,9 @@ public:
/** /**
* @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss * @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss
*/ */
static AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt, PixelFormat maximum = PixelFormat::INVALID); static AVPixelFormat
GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
PixelFormat maximum = PixelFormat::INVALID);
/** /**
* @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss * @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss
@@ -50,7 +53,8 @@ public:
/** /**
* @brief Returns an FFmpeg pixel format for a given native pixel format * @brief Returns an FFmpeg pixel format for a given native pixel format
*/ */
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat& pix_fmt, int channel_layout); static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
int channel_layout);
/** /**
* @brief Returns a native sample format type for a given AVSampleFormat * @brief Returns a native sample format type for a given AVSampleFormat
+36 -19
View File
@@ -29,7 +29,8 @@
#include "config/config.h" #include "config/config.h"
namespace olive { namespace olive
{
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename) QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
{ {
@@ -43,7 +44,8 @@ QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
hash.addData(info.absoluteFilePath().toUtf8()); hash.addData(info.absoluteFilePath().toUtf8());
hash.addData(QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8()); hash.addData(
QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
QByteArray result = hash.result(); QByteArray result = hash.result();
@@ -55,7 +57,8 @@ QString FileFunctions::GetConfigurationLocation()
if (IsPortable()) { if (IsPortable()) {
return GetApplicationPath(); return GetApplicationPath();
} else { } else {
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString s =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir(s).mkpath("."); QDir(s).mkpath(".");
return s; return s;
} }
@@ -73,7 +76,9 @@ QString FileFunctions::GetApplicationPath()
QString FileFunctions::GetTempFilePath() QString FileFunctions::GetTempFilePath()
{ {
QString temp_path = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)) QString temp_path =
QDir(
QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
.filePath(QCoreApplication::organizationName())) .filePath(QCoreApplication::organizationName()))
.filePath(QCoreApplication::applicationName()); .filePath(QCoreApplication::applicationName());
@@ -83,20 +88,23 @@ QString FileFunctions::GetTempFilePath()
return temp_path; return temp_path;
} }
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest) bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString &source,
const QString &dest)
{ {
QFileInfoList info_list = QDir(source).entryInfoList(); QFileInfoList info_list = QDir(source).entryInfoList();
foreach (const QFileInfo &info, info_list) { foreach (const QFileInfo &info, info_list) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually // QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) { if (info.fileName() == QStringLiteral(".") ||
info.fileName() == QStringLiteral("..")) {
continue; continue;
} }
QString dest_equivalent = QDir(dest).filePath(info.fileName()); QString dest_equivalent = QDir(dest).filePath(info.fileName());
if (info.isDir()) { if (info.isDir()) {
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(), dest_equivalent)) { if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(),
dest_equivalent)) {
return false; return false;
} }
} else if (QFileInfo::exists(dest_equivalent)) { } else if (QFileInfo::exists(dest_equivalent)) {
@@ -107,12 +115,14 @@ bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString& source, co
return true; return true;
} }
void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bool overwrite) void FileFunctions::CopyDirectory(const QString &source, const QString &dest,
bool overwrite)
{ {
QDir d(source); QDir d(source);
if (!d.exists()) { if (!d.exists()) {
qCritical() << "Failed to copy directory, source" << source << "didn't exist"; qCritical()
<< "Failed to copy directory, source" << source << "didn't exist";
return; return;
} }
@@ -127,7 +137,8 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
foreach (const QFileInfo &info, l) { foreach (const QFileInfo &info, l) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually // QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) { if (info.fileName() == QStringLiteral(".") ||
info.fileName() == QStringLiteral("..")) {
continue; continue;
} }
@@ -140,8 +151,10 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
// Copy file // Copy file
if (overwrite && QFile::exists(dest_file_path)) { if (overwrite && QFile::exists(dest_file_path)) {
QFile file(dest_file_path); QFile file(dest_file_path);
file.setPermissions(file.permissions() | QFileDevice::WriteOwner | QFileDevice::WriteUser | file.setPermissions(
QFileDevice::WriteGroup | QFileDevice::WriteOther); file.permissions() | QFileDevice::WriteOwner |
QFileDevice::WriteUser | QFileDevice::WriteGroup |
QFileDevice::WriteOther);
file.remove(); file.remove();
} }
@@ -150,13 +163,15 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
} }
} }
bool FileFunctions::DirectoryIsValid(const QDir &d, bool try_to_create_if_not_exists) bool FileFunctions::DirectoryIsValid(const QDir &d,
bool try_to_create_if_not_exists)
{ {
// Return whether the directory exists, or whether it could be created if it doesn't // Return whether the directory exists, or whether it could be created if it doesn't
return d.exists() || d.mkpath(QStringLiteral(".")); return d.exists() || d.mkpath(QStringLiteral("."));
} }
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension) QString FileFunctions::EnsureFilenameExtension(QString fn,
const QString &extension)
{ {
// No-op if either input is empty // No-op if either input is empty
if (!fn.isEmpty() && !extension.isEmpty()) { if (!fn.isEmpty() && !extension.isEmpty()) {
@@ -201,16 +216,16 @@ QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
QString temp_abs_path; QString temp_abs_path;
do { do {
temp_abs_path = original_info.dir().filePath( temp_abs_path = original_info.dir().filePath(
QStringLiteral("%1.tmp%2%3").arg(basename, QStringLiteral("%1.tmp%2%3")
QString::number(counter), .arg(basename, QString::number(counter), complete_suffix));
complete_suffix));
counter++; counter++;
} while (QFileInfo::exists(temp_abs_path)); } while (QFileInfo::exists(temp_abs_path));
return temp_abs_path; return temp_abs_path;
} }
bool FileFunctions::RenameFileAllowOverwrite(const QString &from, const QString &to) bool FileFunctions::RenameFileAllowOverwrite(const QString &from,
const QString &to)
{ {
if (QFileInfo::exists(to) && !QFile::remove(to)) { if (QFileInfo::exists(to) && !QFile::remove(to)) {
qCritical() << "Couldn't remove existing file" << to << "for overwrite"; qCritical() << "Couldn't remove existing file" << to << "for overwrite";
@@ -228,7 +243,9 @@ bool FileFunctions::RenameFileAllowOverwrite(const QString &from, const QString
QString FileFunctions::GetAutoRecoveryRoot() QString FileFunctions::GetAutoRecoveryRoot()
{ {
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("autorecovery")); return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppLocalDataLocation))
.filePath(QStringLiteral("autorecovery"));
} }
} }
+12 -9
View File
@@ -26,7 +26,8 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
/** /**
* @brief A collection of static file and directory functions * @brief A collection of static file and directory functions
@@ -49,11 +50,14 @@ public:
static QString GetTempFilePath(); static QString GetTempFilePath();
static bool CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest); static bool CanCopyDirectoryWithoutOverwriting(const QString &source,
const QString &dest);
static void CopyDirectory(const QString& source, const QString& dest, bool overwrite = false); static void CopyDirectory(const QString &source, const QString &dest,
bool overwrite = false);
static bool DirectoryIsValid(const QDir& dir, bool try_to_create_if_not_exists = true); static bool DirectoryIsValid(const QDir &dir,
bool try_to_create_if_not_exists = true);
/** /**
* @brief Ensures a given filename has a certain extension * @brief Ensures a given filename has a certain extension
@@ -64,7 +68,8 @@ public:
* @return The filename provided either untouched or with the extension appended to it. * @return The filename provided either untouched or with the extension appended to it.
*/ */
static QString EnsureFilenameExtension(QString fn, const QString& extension); static QString EnsureFilenameExtension(QString fn,
const QString &extension);
static QString ReadFileAsString(const QString &filename); static QString ReadFileAsString(const QString &filename);
@@ -83,7 +88,8 @@ public:
/** /**
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first * @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
*/ */
static bool RenameFileAllowOverwrite(const QString& from, const QString& to); static bool RenameFileAllowOverwrite(const QString &from,
const QString &to);
inline static QString GetFormattedExecutableForPlatform(QString unformatted) inline static QString GetFormattedExecutableForPlatform(QString unformatted)
{ {
@@ -95,11 +101,8 @@ public:
} }
static QString GetAutoRecoveryRoot(); static QString GetAutoRecoveryRoot();
}; };
} }
#endif // FILEFUNCTIONS_H #endif // FILEFUNCTIONS_H
+58 -32
View File
@@ -5,12 +5,11 @@
#include "xmlutils.h" #include "xmlutils.h"
namespace olive { namespace olive
{
const QVector<QString> Html::kBlockTags = { const QVector<QString> Html::kBlockTags = { QStringLiteral("p"),
QStringLiteral("p"), QStringLiteral("div") };
QStringLiteral("div")
};
inline bool StrEquals(const QStringView &a, const QStringView &b) inline bool StrEquals(const QStringView &a, const QStringView &b)
{ {
@@ -71,14 +70,14 @@ void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
reader.readNext(); reader.readNext();
if (reader.tokenType() == QXmlStreamReader::StartElement) { if (reader.tokenType() == QXmlStreamReader::StartElement) {
QString tag = reader.name().toString().toLower(); QString tag = reader.name().toString().toLower();
fmt_stack.append({ tag, ReadCharFormat(reader.attributes()) }); fmt_stack.append({ tag, ReadCharFormat(reader.attributes()) });
current_fmt = MergeHtmlFormats(fmt_stack); current_fmt = MergeHtmlFormats(fmt_stack);
if (kBlockTags.contains(tag)) { if (kBlockTags.contains(tag)) {
QTextBlockFormat block_fmt = ReadBlockFormat(reader.attributes()); QTextBlockFormat block_fmt =
ReadBlockFormat(reader.attributes());
if (inside_block) { if (inside_block) {
c.setBlockFormat(block_fmt); c.setBlockFormat(block_fmt);
c.setBlockCharFormat(current_fmt); c.setBlockCharFormat(current_fmt);
@@ -89,12 +88,10 @@ void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
} }
} else if (reader.tokenType() == QXmlStreamReader::Characters) { } else if (reader.tokenType() == QXmlStreamReader::Characters) {
QString characters = reader.text().toString(); QString characters = reader.text().toString();
c.insertText(characters, current_fmt); c.insertText(characters, current_fmt);
} else if (reader.tokenType() == QXmlStreamReader::EndElement) { } else if (reader.tokenType() == QXmlStreamReader::EndElement) {
QString tag = reader.name().toString().toLower(); QString tag = reader.name().toString().toLower();
for (int i = fmt_stack.size() - 1; i >= 0; i--) { for (int i = fmt_stack.size() - 1; i >= 0; i--) {
@@ -108,7 +105,6 @@ void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
break; break;
} }
} }
} }
} }
@@ -126,11 +122,14 @@ void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
// Write block alignment // Write block alignment
if (!(fmt.alignment() & Qt::AlignLeft)) { if (!(fmt.alignment() & Qt::AlignLeft)) {
if (fmt.alignment() & Qt::AlignRight) { if (fmt.alignment() & Qt::AlignRight) {
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("right")); writer->writeAttribute(QStringLiteral("align"),
QStringLiteral("right"));
} else if (fmt.alignment() & Qt::AlignHCenter) { } else if (fmt.alignment() & Qt::AlignHCenter) {
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("center")); writer->writeAttribute(QStringLiteral("align"),
QStringLiteral("center"));
} else if (fmt.alignment() & Qt::AlignJustify) { } else if (fmt.alignment() & Qt::AlignJustify) {
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("justify")); writer->writeAttribute(QStringLiteral("align"),
QStringLiteral("justify"));
} }
} }
@@ -143,7 +142,8 @@ void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
QString style; QString style;
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) { if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
WriteCSSProperty(&style, QStringLiteral("line-height"), QStringLiteral("%1%").arg(fmt.lineHeight())); WriteCSSProperty(&style, QStringLiteral("line-height"),
QStringLiteral("%1%").arg(fmt.lineHeight()));
} }
WriteCharFormat(&style, block.charFormat()); WriteCharFormat(&style, block.charFormat());
@@ -163,7 +163,8 @@ void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
writer->writeEndElement(); // p writer->writeEndElement(); // p
} }
void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment) void Html::WriteFragment(QXmlStreamWriter *writer,
const QTextFragment &fragment)
{ {
const QTextCharFormat &fmt = fragment.charFormat(); const QTextCharFormat &fmt = fragment.charFormat();
@@ -192,7 +193,8 @@ void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment
writer->writeEndElement(); // span writer->writeEndElement(); // span
} }
void Html::WriteCSSProperty(QString *style, const QString &key, const QStringList &values) void Html::WriteCSSProperty(QString *style, const QString &key,
const QStringList &values)
{ {
QString value; QString value;
foreach (QString v, values) { foreach (QString v, values) {
@@ -210,23 +212,30 @@ void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
{ {
QStringList families = fmt.fontFamilies().toStringList(); QStringList families = fmt.fontFamilies().toStringList();
if (!families.isEmpty()) { if (!families.isEmpty()) {
WriteCSSProperty(style, QStringLiteral("font-family"), families.first()); WriteCSSProperty(style, QStringLiteral("font-family"),
families.first());
} }
if (fmt.hasProperty(QTextFormat::FontPointSize)) { if (fmt.hasProperty(QTextFormat::FontPointSize)) {
WriteCSSProperty(style, QStringLiteral("font-size"), QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize()))); WriteCSSProperty(
style, QStringLiteral("font-size"),
QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
} }
if (fmt.hasProperty(QTextFormat::FontWeight)) { if (fmt.hasProperty(QTextFormat::FontWeight)) {
WriteCSSProperty(style, QStringLiteral("font-weight"), QString::number(fmt.fontWeight() * 8)); WriteCSSProperty(style, QStringLiteral("font-weight"),
QString::number(fmt.fontWeight() * 8));
} }
if (fmt.hasProperty(QTextFormat::FontItalic)) { if (fmt.hasProperty(QTextFormat::FontItalic)) {
WriteCSSProperty(style, QStringLiteral("font-style"), fmt.fontItalic() ? QStringLiteral("italic") : QStringLiteral("normal")); WriteCSSProperty(style, QStringLiteral("font-style"),
fmt.fontItalic() ? QStringLiteral("italic") :
QStringLiteral("normal"));
} }
if (fmt.hasProperty(QTextFormat::FontStyleName)) { if (fmt.hasProperty(QTextFormat::FontStyleName)) {
WriteCSSProperty(style, QStringLiteral("-ove-font-style"), fmt.fontStyleName().toString()); WriteCSSProperty(style, QStringLiteral("-ove-font-style"),
fmt.fontStyleName().toString());
} }
QStringList deco; QStringList deco;
@@ -254,7 +263,11 @@ void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
if (color.alpha() == 255) { if (color.alpha() == 255) {
cs = color.name(); cs = color.name();
} else if (color.alpha()) { } else if (color.alpha()) {
cs = QStringLiteral("rgba(%1, %2, %3, %4)").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()), QString::number(color.alphaF())); cs = QStringLiteral("rgba(%1, %2, %3, %4)")
.arg(QString::number(color.red()),
QString::number(color.green()),
QString::number(color.blue()),
QString::number(color.alphaF()));
} }
WriteCSSProperty(style, QStringLiteral("color"), cs); WriteCSSProperty(style, QStringLiteral("color"), cs);
@@ -262,17 +275,22 @@ void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
if (fmt.fontCapitalization() != QFont::MixedCase) { if (fmt.fontCapitalization() != QFont::MixedCase) {
if (fmt.fontCapitalization() == QFont::SmallCaps) { if (fmt.fontCapitalization() == QFont::SmallCaps) {
WriteCSSProperty(style, QStringLiteral("font-variant"), QStringLiteral("small-caps")); WriteCSSProperty(style, QStringLiteral("font-variant"),
QStringLiteral("small-caps"));
// TODO: Add others // TODO: Add others
} }
} }
if (fmt.fontLetterSpacing() != 0.0) { if (fmt.fontLetterSpacing() != 0.0) {
WriteCSSProperty(style, QStringLiteral("letter-spacing"), QStringLiteral("%1%").arg(QString::number(fmt.fontLetterSpacing()))); WriteCSSProperty(style, QStringLiteral("letter-spacing"),
QStringLiteral("%1%").arg(
QString::number(fmt.fontLetterSpacing())));
} }
if (fmt.fontStretch() != 0) { if (fmt.fontStretch() != 0) {
WriteCSSProperty(style, QStringLiteral("font-stretch"), QStringLiteral("%1%").arg(QString::number(fmt.fontStretch()))); WriteCSSProperty(
style, QStringLiteral("font-stretch"),
QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
} }
} }
@@ -290,25 +308,29 @@ QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
if (it.key() == QStringLiteral("font-family")) { if (it.key() == QStringLiteral("font-family")) {
fmt.setFontFamilies({ first_val }); fmt.setFontFamilies({ first_val });
} else if (it.key() == QStringLiteral("font-size")) { } else if (it.key() == QStringLiteral("font-size")) {
if (first_val.endsWith(QStringLiteral("pt"), Qt::CaseInsensitive)) { if (first_val.endsWith(QStringLiteral("pt"),
Qt::CaseInsensitive)) {
fmt.setFontPointSize(first_val.chopped(2).toDouble()); fmt.setFontPointSize(first_val.chopped(2).toDouble());
} }
} else if (it.key() == QStringLiteral("font-weight")) { } else if (it.key() == QStringLiteral("font-weight")) {
fmt.setFontWeight(first_val.toInt() / 8); fmt.setFontWeight(first_val.toInt() / 8);
} else if (it.key() == QStringLiteral("font-style")) { } else if (it.key() == QStringLiteral("font-style")) {
fmt.setFontItalic(StrEquals(first_val, QStringLiteral("italic"))); fmt.setFontItalic(
StrEquals(first_val, QStringLiteral("italic")));
} else if (it.key() == QStringLiteral("text-decoration")) { } else if (it.key() == QStringLiteral("text-decoration")) {
foreach (const QString &v, it.value()) { foreach (const QString &v, it.value()) {
if (StrEquals(v, QStringLiteral("underline"))) { if (StrEquals(v, QStringLiteral("underline"))) {
fmt.setFontUnderline(true); fmt.setFontUnderline(true);
} else if (StrEquals(v, QStringLiteral("line-through"))) { } else if (StrEquals(v,
QStringLiteral("line-through"))) {
fmt.setFontStrikeOut(true); fmt.setFontStrikeOut(true);
} else if (StrEquals(v, QStringLiteral("overline"))) { } else if (StrEquals(v, QStringLiteral("overline"))) {
fmt.setFontOverline(true); fmt.setFontOverline(true);
} }
} }
} else if (it.key() == QStringLiteral("color")) { } else if (it.key() == QStringLiteral("color")) {
if (first_val.startsWith(QStringLiteral("rgba"), Qt::CaseInsensitive)) { if (first_val.startsWith(QStringLiteral("rgba"),
Qt::CaseInsensitive)) {
QString vals_only = first_val; QString vals_only = first_val;
vals_only.remove(QStringLiteral("rgba")); vals_only.remove(QStringLiteral("rgba"));
vals_only.remove(QStringLiteral("(")); vals_only.remove(QStringLiteral("("));
@@ -331,7 +353,8 @@ QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
} }
} else if (it.key() == QStringLiteral("letter-spacing")) { } else if (it.key() == QStringLiteral("letter-spacing")) {
if (first_val.contains(QChar('%'))) { if (first_val.contains(QChar('%'))) {
fmt.setFontLetterSpacing(first_val.chopped(1).toDouble()); fmt.setFontLetterSpacing(
first_val.chopped(1).toDouble());
} }
} else if (it.key() == QStringLiteral("font-stretch")) { } else if (it.key() == QStringLiteral("font-stretch")) {
if (first_val.contains(QChar('%'))) { if (first_val.contains(QChar('%'))) {
@@ -370,7 +393,9 @@ QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
if (it.key() == QStringLiteral("line-height")) { if (it.key() == QStringLiteral("line-height")) {
const QString &first_val = it.value().constFirst(); const QString &first_val = it.value().constFirst();
if (first_val.contains(QChar('%'))) { if (first_val.contains(QChar('%'))) {
block_fmt.setLineHeight(first_val.chopped(1).toDouble(), QTextBlockFormat::ProportionalHeight); block_fmt.setLineHeight(
first_val.chopped(1).toDouble(),
QTextBlockFormat::ProportionalHeight);
} }
} }
} }
@@ -425,7 +450,8 @@ QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
values.append(current_str); values.append(current_str);
current_str.clear(); current_str.clear();
} }
} else if (in_quote.isNull() && (current_char == QChar('\'') || current_char == QChar('"'))) { } else if (in_quote.isNull() && (current_char == QChar('\'') ||
current_char == QChar('"'))) {
in_quote = current_char; in_quote = current_char;
} else { } else {
current_str.append(current_char); current_str.append(current_char);
+12 -7
View File
@@ -6,7 +6,8 @@
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
namespace olive { namespace olive
{
/** /**
* @brief Functions for converting HTML to QTextDocument and vice versa * @brief Functions for converting HTML to QTextDocument and vice versa
@@ -33,26 +34,30 @@ public:
private: private:
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block); static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
static void WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment); static void WriteFragment(QXmlStreamWriter *writer,
const QTextFragment &fragment);
static void WriteCSSProperty(QString *style, const QString &key, const QStringList &value); static void WriteCSSProperty(QString *style, const QString &key,
static void WriteCSSProperty(QString *style, const QString &key, const QString &value) const QStringList &value);
static void WriteCSSProperty(QString *style, const QString &key,
const QString &value)
{ {
WriteCSSProperty(style, key, QStringList({ value })); WriteCSSProperty(style, key, QStringList({ value }));
} }
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt); static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
static QTextCharFormat ReadCharFormat(const QXmlStreamAttributes &attributes); static QTextCharFormat
ReadCharFormat(const QXmlStreamAttributes &attributes);
static QTextBlockFormat ReadBlockFormat(const QXmlStreamAttributes &attributes); static QTextBlockFormat
ReadBlockFormat(const QXmlStreamAttributes &attributes);
static void AppendStringAutoSpace(QString *s, const QString &append); static void AppendStringAutoSpace(QString *s, const QString &append);
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s); static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
static const QVector<QString> kBlockTags; static const QVector<QString> kBlockTags;
}; };
} }
+2 -1
View File
@@ -2,7 +2,8 @@
#include <QMutex> #include <QMutex>
namespace olive { namespace olive
{
uint64_t job_time_index = 0; uint64_t job_time_index = 0;
QMutex job_time_mutex; QMutex job_time_mutex;
+3 -4
View File
@@ -4,10 +4,10 @@
#include <QDebug> #include <QDebug>
#include <stdint.h> #include <stdint.h>
namespace olive { namespace olive
class JobTime
{ {
class JobTime {
public: public:
JobTime(); JobTime();
@@ -50,7 +50,6 @@ public:
private: private:
uint64_t value_; uint64_t value_;
}; };
} }
+4 -3
View File
@@ -28,12 +28,13 @@ template<typename T>
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value * t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
* in between a and b at that point linearly. * in between a and b at that point linearly.
*/ */
T lerp(T a, T b, double t) { T lerp(T a, T b, double t)
{
return (a * (1.0 - t)) + (b * t); return (a * (1.0 - t)) + (b * t);
} }
template<typename T> template <typename T> T lerp(T a, T b, float t)
T lerp(T a, T b, float t) { {
return (a * (1.0f - t)) + (b * t); return (a * (1.0f - t)) + (b * t);
} }
+17 -13
View File
@@ -31,7 +31,8 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
/** /**
* @brief MemoryPool base class * @brief MemoryPool base class
@@ -46,8 +47,7 @@ namespace olive {
* `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data * `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data
* will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool. * will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool.
*/ */
class MemoryPool : public QObject class MemoryPool : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
/** /**
@@ -63,7 +63,8 @@ public:
clear_timer_ = new QTimer(); clear_timer_ = new QTimer();
clear_timer_->setInterval(kMaxEmptyArenaLife); clear_timer_->setInterval(kMaxEmptyArenaLife);
clear_timer_->moveToThread(qApp->thread()); clear_timer_->moveToThread(qApp->thread());
connect(clear_timer_, &QTimer::timeout, this, &MemoryPool::ClearEmptyArenas, Qt::DirectConnection); connect(clear_timer_, &QTimer::timeout, this,
&MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection); QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
} }
@@ -199,7 +200,6 @@ public:
int64_t timestamp_; int64_t timestamp_;
int64_t accessed_; int64_t accessed_;
}; };
using ElementPtr = std::shared_ptr<Element>; using ElementPtr = std::shared_ptr<Element>;
@@ -245,7 +245,8 @@ public:
// This buffer is available // This buffer is available
available_.replace(i, false); available_.replace(i, false);
ElementPtr e = std::make_shared<Element>(this, ElementPtr e = std::make_shared<Element>(
this,
reinterpret_cast<uint8_t *>(data_ + i * element_sz_)); reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
lent_elements_.push_back(e.get()); lent_elements_.push_back(e.get());
@@ -262,7 +263,8 @@ public:
void Release(Element *e) void Release(Element *e)
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_); quintptr diff = reinterpret_cast<quintptr>(e->data()) -
reinterpret_cast<quintptr>(data_);
int index = diff / element_sz_; int index = diff / element_sz_;
@@ -335,7 +337,6 @@ public:
std::list<Element *> lent_elements_; std::list<Element *> lent_elements_;
qint64 empty_time_; qint64 empty_time_;
}; };
/** /**
@@ -369,13 +370,15 @@ public:
} }
if (element_count_ <= 0) { if (element_count_ <= 0) {
qCritical() << "Failed to create arena, element count was invalid:" << element_count_; qCritical() << "Failed to create arena, element count was invalid:"
<< element_count_;
return nullptr; return nullptr;
} }
Arena *a = new Arena(this); Arena *a = new Arena(this);
if (!a->Allocate(ele_sz, element_count_)) { if (!a->Allocate(ele_sz, element_count_)) {
qCritical() << "Failed to create arena, allocation failed. Out of memory?"; qCritical()
<< "Failed to create arena, allocation failed. Out of memory?";
delete a; delete a;
return nullptr; return nullptr;
} }
@@ -411,12 +414,14 @@ private slots:
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
const qint64 min_time = QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife; const qint64 min_time =
QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife;
for (auto it = arenas_.begin(); it != arenas_.end();) { for (auto it = arenas_.begin(); it != arenas_.end();) {
Arena *arena = (*it); Arena *arena = (*it);
if (arena->GetUsageCount() == 0 && arena->GetTimeArenaWasMadeEmpty() <= min_time) { if (arena->GetUsageCount() == 0 &&
arena->GetTimeArenaWasMadeEmpty() <= min_time) {
qDebug() << "Removing an empty arena"; qDebug() << "Removing an empty arena";
delete arena; delete arena;
it = arenas_.erase(it); it = arenas_.erase(it);
@@ -425,7 +430,6 @@ private slots:
} }
} }
} }
}; };
} }
+2 -1
View File
@@ -20,7 +20,8 @@
#include "ocioutils.h" #include "ocioutils.h"
namespace olive { namespace olive
{
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format) OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
{ {
+3 -3
View File
@@ -26,10 +26,10 @@ namespace OCIO = OCIO_NAMESPACE;
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
class OCIOUtils
{ {
class OCIOUtils {
public: public:
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format); static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
}; };
+8 -12
View File
@@ -22,29 +22,25 @@
#include <QDebug> #include <QDebug>
namespace olive { namespace olive
{
void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf) void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
{ {
buf->set_pixels(OIIO::ROI(), buf->set_pixels(OIIO::ROI(), buf->spec().format, frame->const_data(),
buf->spec().format, OIIO::AutoStride, frame->linesize_bytes());
frame->const_data(),
OIIO::AutoStride,
frame->linesize_bytes());
} }
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame *frame) void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame *frame)
{ {
buf->get_pixels(OIIO::ROI(), buf->get_pixels(OIIO::ROI(), buf->spec().format, frame->data(),
buf->spec().format, OIIO::AutoStride, frame->linesize_bytes());
frame->data(),
OIIO::AutoStride,
frame->linesize_bytes());
} }
rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec) rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
{ {
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1)); return rational::fromDouble(
spec.get_float_attribute("PixelAspectRatio", 1));
} }
PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type) PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type)
+4 -3
View File
@@ -27,11 +27,13 @@
#include "codec/frame.h" #include "codec/frame.h"
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
{
class OIIOUtils { class OIIOUtils {
public: public:
static OIIO::TypeDesc::BASETYPE GetOIIOBaseTypeFromFormat(PixelFormat format) static OIIO::TypeDesc::BASETYPE
GetOIIOBaseTypeFromFormat(PixelFormat format)
{ {
switch (format) { switch (format) {
case PixelFormat::U8: case PixelFormat::U8:
@@ -57,7 +59,6 @@ public:
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type); static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec); static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec);
}; };
} }
+2 -1
View File
@@ -25,7 +25,8 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
uint32_t ceil_to_power_of_2(uint32_t v) uint32_t ceil_to_power_of_2(uint32_t v)
{ {
+25 -14
View File
@@ -22,9 +22,11 @@
#include <QDebug> #include <QDebug>
namespace olive { namespace olive
{
int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString& s) { int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString &s)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
return fm.width(s); return fm.width(s);
#else #else
@@ -47,7 +49,9 @@ QFrame *QtUtils::CreateVerticalLine()
return l; return l;
} }
int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &message, QMessageBox::StandardButtons buttons) int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon,
const QString &title, const QString &message,
QMessageBox::StandardButtons buttons)
{ {
QMessageBox b(parent); QMessageBox b(parent);
b.setIcon(icon); b.setIcon(icon);
@@ -85,7 +89,8 @@ QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
return dt.toString(Qt::TextDate); return dt.toString(Qt::TextDate);
} }
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width) QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm,
int bounding_width)
{ {
QStringList list; QStringList list;
@@ -95,16 +100,17 @@ QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, in
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
QString this_line = lines.at(i); QString this_line = lines.at(i);
while (this_line.size() > 1 && QFontMetricsWidth(fm, this_line) >= bounding_width) { while (this_line.size() > 1 &&
QFontMetricsWidth(fm, this_line) >= bounding_width) {
int old_size = this_line.size(); int old_size = this_line.size();
int hard_break = -1; int hard_break = -1;
for (int j = this_line.size() - 1; j >= 0; j--) { for (int j = this_line.size() - 1; j >= 0; j--) {
const QChar &char_test = this_line.at(j); const QChar &char_test = this_line.at(j);
if (char_test.isSpace() if (char_test.isSpace() || char_test == '-') {
|| char_test == '-') { if (QFontMetricsWidth(fm, this_line.left(j)) <
if (QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) { bounding_width) {
if (!char_test.isSpace()) { if (!char_test.isSpace()) {
j++; j++;
} }
@@ -113,13 +119,16 @@ QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, in
list.append(chopped); list.append(chopped);
while (j < this_line.size() && this_line.at(j).isSpace()) { while (j < this_line.size() &&
this_line.at(j).isSpace()) {
j++; j++;
} }
this_line.remove(0, j); this_line.remove(0, j);
break; break;
} }
} else if (hard_break == -1 && QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) { } else if (hard_break == -1 &&
QFontMetricsWidth(fm, this_line.left(j)) <
bounding_width) {
// In case we can't find a better place to split, split at the earliest time the line // In case we can't find a better place to split, split at the earliest time the line
// goes under the width limit // goes under the width limit
hard_break = j; hard_break = j;
@@ -131,7 +140,8 @@ QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, in
list.append(this_line.left(hard_break)); list.append(this_line.left(hard_break));
this_line.remove(0, hard_break); this_line.remove(0, hard_break);
} else { } else {
qWarning() << "Failed to find anywhere to wrap. Returning full line."; qWarning()
<< "Failed to find anywhere to wrap. Returning full line.";
break; break;
} }
} }
@@ -145,7 +155,8 @@ QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, in
return list; return list;
} }
Qt::KeyboardModifiers QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e) Qt::KeyboardModifiers
QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
{ {
if (e & Qt::ControlModifier & Qt::ShiftModifier) { if (e & Qt::ControlModifier & Qt::ShiftModifier) {
return e; return e;
@@ -195,7 +206,8 @@ QColor QtUtils::toQColor(const core::Color &i)
return c; return c;
} }
namespace core { namespace core
{
uint qHash(const core::rational &r, uint seed) uint qHash(const core::rational &r, uint seed)
{ {
@@ -207,7 +219,6 @@ uint qHash(const core::TimeRange &r, uint seed)
return qHash(r.in(), seed) ^ qHash(r.out(), seed); return qHash(r.in(), seed) ^ qHash(r.out(), seed);
} }
} }
} }
+13 -10
View File
@@ -29,7 +29,8 @@
#include <QFrame> #include <QFrame>
#include <QMessageBox> #include <QMessageBox>
namespace olive { namespace olive
{
class QtUtils { class QtUtils {
public: public:
@@ -46,21 +47,24 @@ public:
static QFrame *CreateVerticalLine(); static QFrame *CreateVerticalLine();
static int MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok); static int MsgBox(QWidget *parent, QMessageBox::Icon icon,
const QString &title, const QString &message,
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
static QDateTime GetCreationDate(const QFileInfo &info); static QDateTime GetCreationDate(const QFileInfo &info);
static QString GetFormattedDateTime(const QDateTime &dt); static QString GetFormattedDateTime(const QDateTime &dt);
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width); static QStringList WordWrapString(const QString &s, const QFontMetrics &fm,
int bounding_width);
static Qt::KeyboardModifiers FlipControlAndShiftModifiers(Qt::KeyboardModifiers e); static Qt::KeyboardModifiers
FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
static void SetComboBoxData(QComboBox *cb, int data); static void SetComboBoxData(QComboBox *cb, int data);
static void SetComboBoxData(QComboBox *cb, const QString &data); static void SetComboBoxData(QComboBox *cb, const QString &data);
template <typename T> template <typename T> static T *GetParentOfType(const QObject *child)
static T *GetParentOfType(const QObject *child)
{ {
QObject *t = child->parent(); QObject *t = child->parent();
@@ -87,15 +91,14 @@ public:
/** /**
* @brief Convert a NodeParam value to a pointer of any kind * @brief Convert a NodeParam value to a pointer of any kind
*/ */
template<class T> template <class T> static T *ValueToPtr(const QVariant &ptr)
static T* ValueToPtr(const QVariant &ptr)
{ {
return reinterpret_cast<T *>(ptr.value<quintptr>()); return reinterpret_cast<T *>(ptr.value<quintptr>());
} }
}; };
namespace core { namespace core
{
uint qHash(const core::rational &r, uint seed = 0); uint qHash(const core::rational &r, uint seed = 0);
uint qHash(const core::TimeRange &r, uint seed = 0); uint qHash(const core::TimeRange &r, uint seed = 0);
+1 -2
View File
@@ -21,8 +21,7 @@
#ifndef RANGE_H #ifndef RANGE_H
#define RANGE_H #define RANGE_H
template <typename T> template <typename T> bool InRange(T a, T b, T range)
bool InRange(T a, T b, T range)
{ {
return (a >= b - range && a <= b + range); return (a >= b - range && a <= b + range);
} }
+21 -19
View File
@@ -24,23 +24,23 @@
#include <QMessageBox> #include <QMessageBox>
#include <QRegularExpression> #include <QRegularExpression>
namespace olive { namespace olive
{
double GetFloatRatioFromUser(QWidget* parent, double GetFloatRatioFromUser(QWidget *parent, const QString &title, bool *ok_in)
const QString& title,
bool* ok_in)
{ {
QString s; QString s;
forever { forever
{
bool ok; bool ok;
s = QInputDialog::getText(parent, s = QInputDialog::getText(
title, parent, title,
QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"), QCoreApplication::translate(
QLineEdit::Normal, "RatioDialog",
s, "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
&ok); QLineEdit::Normal, s, &ok);
if (!ok) { if (!ok) {
// User cancelled dialog, do nothing // User cancelled dialog, do nothing
@@ -50,7 +50,8 @@ double GetFloatRatioFromUser(QWidget* parent,
return qSNaN(); return qSNaN();
} }
QStringList ratio_components = s.split(QRegularExpression(QStringLiteral(":|;|\\/"))); QStringList ratio_components =
s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
if (ratio_components.size() == 1) { if (ratio_components.size() == 1) {
bool float_ok; bool float_ok;
@@ -69,10 +70,7 @@ double GetFloatRatioFromUser(QWidget* parent,
double num = ratio_components.at(0).toDouble(&numer_ok); double num = ratio_components.at(0).toDouble(&numer_ok);
double den = ratio_components.at(1).toDouble(&denom_ok); double den = ratio_components.at(1).toDouble(&denom_ok);
if (numer_ok if (numer_ok && denom_ok && num > 0 && den > 0) {
&& denom_ok
&& num > 0
&& den > 0) {
// Exit loop and set this ratio // Exit loop and set this ratio
if (ok_in) { if (ok_in) {
*ok_in = true; *ok_in = true;
@@ -81,10 +79,14 @@ double GetFloatRatioFromUser(QWidget* parent,
} }
} }
QMessageBox::warning(parent, QMessageBox::warning(
parent,
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"), QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a " QCoreApplication::translate(
"rational fraction with a ':' or a '/' separator.").arg(s), "RatioDialog",
"Failed to parse \"%1\" into an aspect ratio. Please format a "
"rational fraction with a ':' or a '/' separator.")
.arg(s),
QMessageBox::Ok); QMessageBox::Ok);
} }
} }
+3 -3
View File
@@ -23,10 +23,10 @@
#include <QInputDialog> #include <QInputDialog>
namespace olive { namespace olive
{
double GetFloatRatioFromUser(QWidget* parent, double GetFloatRatioFromUser(QWidget *parent, const QString &title,
const QString& title,
bool *ok_in); bool *ok_in);
} }
+1 -4
View File
@@ -4,9 +4,7 @@
#include <QMap> #include <QMap>
#include <QMutex> #include <QMutex>
template <typename K, typename V> template <typename K, typename V> class ThreadSafeMap {
class ThreadSafeMap
{
public: public:
ThreadSafeMap() = default; ThreadSafeMap() = default;
@@ -21,7 +19,6 @@ private:
QMutex mutex_; QMutex mutex_;
QMap<K, V> map_; QMap<K, V> map_;
}; };
#endif // THREADSAFEMAP_H #endif // THREADSAFEMAP_H
+4 -2
View File
@@ -6,9 +6,11 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
inline QString ToHex(quint64 t) { inline QString ToHex(quint64 t)
{
return QStringLiteral("%1").arg(t, 0, 16); return QStringLiteral("%1").arg(t, 0, 16);
} }
+1 -2
View File
@@ -21,8 +21,7 @@
#ifndef UTIL_H #ifndef UTIL_H
#define UTIL_H #define UTIL_H
template <typename T> template <typename T> inline T mid(T a, T b)
inline T mid(T a, T b)
{ {
return (a + b) * 0.5; return (a + b) * 0.5;
} }
+5 -4
View File
@@ -23,15 +23,16 @@
#include "node/block/block.h" #include "node/block/block.h"
#include "node/factory.h" #include "node/factory.h"
namespace olive { namespace olive
{
bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom) bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom)
{ {
QXmlStreamReader::TokenType token; QXmlStreamReader::TokenType token;
while ((token = reader->readNext()) != QXmlStreamReader::Invalid while ((token = reader->readNext()) != QXmlStreamReader::Invalid &&
&& token != QXmlStreamReader::EndDocument token != QXmlStreamReader::EndDocument &&
&& (!cancel_atom || !cancel_atom->IsCancelled())) { (!cancel_atom || !cancel_atom->IsCancelled())) {
if (reader->isEndElement()) { if (reader->isEndElement()) {
return false; return false;
} else if (reader->isStartElement()) { } else if (reader->isStartElement()) {
+4 -2
View File
@@ -27,7 +27,8 @@
#include "render/cancelatom.h" #include "render/cancelatom.h"
#include "undo/undocommand.h" #include "undo/undocommand.h"
namespace olive { namespace olive
{
class Block; class Block;
class Node; class Node;
@@ -46,7 +47,8 @@ class NodeGroup;
* *
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error * See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
*/ */
bool XMLReadNextStartElement(QXmlStreamReader* reader, CancelAtom *cancel_atom = nullptr); bool XMLReadNextStartElement(QXmlStreamReader *reader,
CancelAtom *cancel_atom = nullptr);
} }
+207 -99
View File
@@ -37,7 +37,8 @@
#include "ui/style/style.h" #include "ui/style/style.h"
#include "window/mainwindow/mainwindow.h" #include "window/mainwindow/mainwindow.h"
namespace olive { namespace olive
{
Config Config::current_config_; Config Config::current_config_;
@@ -46,14 +47,16 @@ Config::Config()
SetDefaults(); SetDefaults();
} }
void Config::SetEntryInternal(const QString &key, NodeValue::Type type, const QVariant &data) void Config::SetEntryInternal(const QString &key, NodeValue::Type type,
const QVariant &data)
{ {
config_map_[key] = { type, data }; config_map_[key] = { type, data };
} }
QString Config::GetConfigFilePath() QString Config::GetConfigFilePath()
{ {
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("config.xml")); return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("config.xml"));
} }
Config &Config::Current() Config &Config::Current()
@@ -64,106 +67,190 @@ Config &Config::Current()
void Config::SetDefaults() void Config::SetDefaults()
{ {
config_map_.clear(); config_map_.clear();
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText, StyleManager::kDefaultStyle); SetEntryInternal(QStringLiteral("Style"), NodeValue::kText,
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt, Timecode::kTimecodeDropFrame); StyleManager::kDefaultStyle);
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2))); SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt,
Timecode::kTimecodeDropFrame);
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational,
QVariant::fromValue(rational(2)));
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean, true); true);
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1); SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt, 20); true);
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000); SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt,
1);
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt,
20);
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt,
10000);
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString()); SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false); false);
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeValue::kBoolean, true); false);
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("EditToolSelectsLinks"),
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false); NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"),
NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"),
NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); false);
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false); false);
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false); true);
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage); SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"),
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true); NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"),
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, true); NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk); SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt,
AutoScroll::kPage);
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"),
NodeValue::kInt, ImportTool::kDWSAsk);
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean, true); true);
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true); true);
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false); true);
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("ShowClipWhileDragging"),
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false); NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt, Timeline::kThumbnailInOut); SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt, Timeline::kWaveformsEnabled); Timeline::kThumbnailInOut);
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt,
Timeline::kWaveformsEnabled);
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve")); SetEntryInternal(
SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve")); QStringLiteral("DefaultVideoTransition"), NodeValue::kText,
SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1))); QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(
QStringLiteral("DefaultAudioTransition"), NodeValue::kText,
QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultTransitionLength"),
NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt, 48); SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText, QString()); 48);
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt, QFont::Bold); SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText,
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean, true); QString());
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt,
QFont::Bold);
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000); SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt, ColorCoding::kRed); SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt, ColorCoding::kMaroon); ColorCoding::kRed);
SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt, ColorCoding::kOrange); SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt, ColorCoding::kBrown); ColorCoding::kMaroon);
SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt, ColorCoding::kYellow); SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt, ColorCoding::kOlive); ColorCoding::kOrange);
SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt, ColorCoding::kLime); SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt, ColorCoding::kGreen); ColorCoding::kBrown);
SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt, ColorCoding::kCyan); SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt, ColorCoding::kTeal); ColorCoding::kYellow);
SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt, ColorCoding::kBlue); SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt, ColorCoding::kNavy); ColorCoding::kOlive);
SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt,
ColorCoding::kLime);
SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt,
ColorCoding::kGreen);
SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt,
ColorCoding::kCyan);
SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt,
ColorCoding::kTeal);
SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt,
ColorCoding::kBlue);
SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt,
ColorCoding::kNavy);
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString()); SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText,
QString());
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString()); SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO); 48000);
SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string())); SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"),
NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(
QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText,
QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV); SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM); ExportFormat::kFormatWAV);
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO); ExportCodec::kCodecPCM);
SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string())); SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"),
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320); NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"),
NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(
QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText,
QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt,
320);
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0))); SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational,
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(60))); QVariant::fromValue(rational(0)));
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational,
QVariant::fromValue(rational(60)));
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt, 1920); SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt, 1080); 1920);
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeValue::kRational, QVariant::fromValue(rational(1))); SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeValue::kRational, QVariant::fromValue(rational(1001, 30000))); 1080);
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeValue::kInt, VideoParams::kInterlaceNone); SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"),
SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"), NodeValue::kBoolean, false); NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"),
SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt, QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO))); NodeValue::kRational,
QVariant::fromValue(rational(1001, 30000)));
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"),
NodeValue::kInt, VideoParams::kInterlaceNone);
SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"),
NodeValue::kInt, 48000);
SetEntryInternal(
QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt,
QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
// Online/offline settings // Online/offline settings
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt, PixelFormat::F32); SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt, PixelFormat::F16); PixelFormat::F32);
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt,
PixelFormat::F16);
SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt, ColorCoding::kLime); SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt,
ColorCoding::kLime);
} }
void Config::Load() void Config::Load()
@@ -175,7 +262,8 @@ void Config::Load()
} }
if (!config_file.open(QFile::ReadOnly)) { if (!config_file.open(QFile::ReadOnly)) {
qWarning() << "Failed to load application settings. This session will use defaults."; qWarning()
<< "Failed to load application settings. This session will use defaults.";
return; return;
} }
@@ -196,22 +284,26 @@ void Config::Load()
config_version = value; config_version = value;
if (!value.contains(".")) { if (!value.contains(".")) {
qDebug() << "CONFIG: This is a 0.1.x config file, upconvert"; qDebug()
<< "CONFIG: This is a 0.1.x config file, upconvert";
} }
} else if (key == QStringLiteral("DefaultSequenceFrameRate") && !config_version.contains('.')) { } else if (key == QStringLiteral("DefaultSequenceFrameRate") &&
!config_version.contains('.')) {
// 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest // 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest
// supported rational // supported rational
qDebug() << " CONFIG: Finding closest match to" << value; qDebug() << " CONFIG: Finding closest match to" << value;
double config_fr = value.toDouble(); double config_fr = value.toDouble();
const QVector<rational>& supported_frame_rates = VideoParams::kSupportedFrameRates; const QVector<rational> &supported_frame_rates =
VideoParams::kSupportedFrameRates;
rational match = supported_frame_rates.first(); rational match = supported_frame_rates.first();
double match_diff = qAbs(match.toDouble() - config_fr); double match_diff = qAbs(match.toDouble() - config_fr);
for (int i = 1; i < supported_frame_rates.size(); i++) { for (int i = 1; i < supported_frame_rates.size(); i++) {
double diff = qAbs(supported_frame_rates.at(i).toDouble() - config_fr); double diff = qAbs(
supported_frame_rates.at(i).toDouble() - config_fr);
if (diff < match_diff) { if (diff < match_diff) {
match = supported_frame_rates.at(i); match = supported_frame_rates.at(i);
@@ -219,11 +311,13 @@ void Config::Load()
} }
} }
qDebug() << " CONFIG: Closest match was" << match.toDouble(); qDebug()
<< " CONFIG: Closest match was" << match.toDouble();
current_config_[key] = QVariant::fromValue(match.flipped()); current_config_[key] = QVariant::fromValue(match.flipped());
} else { } else {
current_config_[key] = NodeValue::StringToValue(current_config_.GetConfigEntryType(key), value, false); current_config_[key] = NodeValue::StringToValue(
current_config_.GetConfigEntryType(key), value, false);
} }
} }
@@ -234,10 +328,14 @@ void Config::Load()
} }
if (reader.hasError()) { if (reader.hasError()) {
QMessageBox::critical(Core::instance()->main_window(), QMessageBox::critical(
Core::instance()->main_window(),
QCoreApplication::translate("Config", "Error loading settings"), QCoreApplication::translate("Config", "Error loading settings"),
QCoreApplication::translate("Config", "Failed to load application settings. This session will " QCoreApplication::translate(
"use defaults.\n\n%1").arg(reader.errorString()), "Config",
"Failed to load application settings. This session will "
"use defaults.\n\n%1")
.arg(reader.errorString()),
QMessageBox::Ok); QMessageBox::Ok);
current_config_.SetDefaults(); current_config_.SetDefaults();
} }
@@ -248,14 +346,18 @@ void Config::Load()
void Config::Save() void Config::Save()
{ {
QString real_filename = GetConfigFilePath(); QString real_filename = GetConfigFilePath();
QString temp_filename = FileFunctions::GetSafeTemporaryFilename(real_filename); QString temp_filename =
FileFunctions::GetSafeTemporaryFilename(real_filename);
QFile config_file(temp_filename); QFile config_file(temp_filename);
if (!config_file.open(QFile::WriteOnly)) { if (!config_file.open(QFile::WriteOnly)) {
QMessageBox::critical(Core::instance()->main_window(), QMessageBox::critical(
Core::instance()->main_window(),
QCoreApplication::translate("Config", "Error saving settings"), QCoreApplication::translate("Config", "Error saving settings"),
QCoreApplication::translate("Config", "Failed to save application settings. The application " QCoreApplication::translate(
"Config",
"Failed to save application settings. The application "
"may lack write permissions for this location."), "may lack write permissions for this location."),
QMessageBox::Ok); QMessageBox::Ok);
return; return;
@@ -269,16 +371,19 @@ void Config::Save()
writer.writeStartElement("Configuration"); writer.writeStartElement("Configuration");
// Anything after the hyphen is considered "unimportant" information // Anything after the hyphen is considered "unimportant" information
writer.writeTextElement("Version", QCoreApplication::applicationVersion().split('-').first()); writer.writeTextElement(
"Version", QCoreApplication::applicationVersion().split('-').first());
QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_); QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_);
while (iterator.hasNext()) { while (iterator.hasNext()) {
iterator.next(); iterator.next();
QString value = NodeValue::ValueToString(iterator.value().type, iterator.value().data, false); QString value = NodeValue::ValueToString(iterator.value().type,
iterator.value().data, false);
if (iterator.value().type == NodeValue::kNone) { if (iterator.value().type == NodeValue::kNone) {
qWarning() << "Config key" << iterator.key() << "had null type and was discarded"; qWarning() << "Config key" << iterator.key()
<< "had null type and was discarded";
} else { } else {
writer.writeTextElement(iterator.key(), value); writer.writeTextElement(iterator.key(), value);
} }
@@ -290,8 +395,11 @@ void Config::Save()
config_file.close(); config_file.close();
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename, real_filename)) { if (!FileFunctions::RenameFileAllowOverwrite(temp_filename,
qWarning() << QStringLiteral("Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.") real_filename)) {
qWarning()
<< QStringLiteral(
"Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
.arg(real_filename, temp_filename); .arg(real_filename, temp_filename);
} }
} }
+4 -2
View File
@@ -27,7 +27,8 @@
#include "node/value.h" #include "node/value.h"
namespace olive { namespace olive
{
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)] #define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
#define OLIVE_CONFIG_STR(x) Config::Current()[x] #define OLIVE_CONFIG_STR(x) Config::Current()[x]
@@ -56,7 +57,8 @@ private:
QVariant data; QVariant data;
}; };
void SetEntryInternal(const QString& key, NodeValue::Type type, const QVariant& data); void SetEntryInternal(const QString &key, NodeValue::Type type,
const QVariant &data);
QMap<QString, ConfigEntry> config_map_; QMap<QString, ConfigEntry> config_map_;
+231 -145
View File
@@ -78,20 +78,21 @@
#include "window/mainwindow/mainstatusbar.h" #include "window/mainwindow/mainstatusbar.h"
#include "window/mainwindow/mainwindow.h" #include "window/mainwindow/mainwindow.h"
namespace olive { namespace olive
{
Core *Core::instance_ = nullptr; Core *Core::instance_ = nullptr;
Core::Core(const CoreParams& params) : Core::Core(const CoreParams &params)
main_window_(nullptr), : main_window_(nullptr)
open_project_(nullptr), , open_project_(nullptr)
tool_(Tool::kPointer), , tool_(Tool::kPointer)
addable_object_(Tool::kAddableEmpty), , addable_object_(Tool::kAddableEmpty)
snapping_(true), , snapping_(true)
core_params_(params), , core_params_(params)
magic_(false), , magic_(false)
pixel_sampling_users_(0), , pixel_sampling_users_(0)
shown_cache_full_warning_(false) , shown_cache_full_warning_(false)
{ {
// Store reference to this object, making the assumption that Core will only ever be made in // Store reference to this object, making the assumption that Core will only ever be made in
// main(). This will obviously break if not. // main(). This will obviously break if not.
@@ -169,7 +170,8 @@ void Core::Start()
StartGUI(core_params_.fullscreen()); StartGUI(core_params_.fullscreen());
// If we have a startup // If we have a startup
QMetaObject::invokeMethod(this, "OpenStartupProject", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "OpenStartupProject",
Qt::QueuedConnection);
break; break;
case CoreParams::kHeadlessExport: case CoreParams::kHeadlessExport:
qInfo() << "Headless export is not fully implemented yet"; qInfo() << "Headless export is not fully implemented yet";
@@ -182,12 +184,11 @@ void Core::Start()
// Manual crash triggering // Manual crash triggering
if (core_params_.crash_on_startup()) { if (core_params_.crash_on_startup()) {
const int interval = 5000; const int interval = 5000;
qInfo() << "Manual crash was triggered. Application will crash in" << interval << "ms"; qInfo() << "Manual crash was triggered. Application will crash in"
<< interval << "ms";
QTimer *crash_timer = new QTimer(this); QTimer *crash_timer = new QTimer(this);
crash_timer->setInterval(interval); crash_timer->setInterval(interval);
connect(crash_timer, &QTimer::timeout, this, []{ connect(crash_timer, &QTimer::timeout, this, [] { abort(); });
abort();
});
crash_timer->start(); crash_timer->start();
} }
} }
@@ -238,7 +239,8 @@ UndoStack *Core::undo_stack()
void Core::ImportFiles(const QStringList &urls, Folder *parent) void Core::ImportFiles(const QStringList &urls, Folder *parent)
{ {
if (urls.isEmpty()) { if (urls.isEmpty()) {
QMessageBox::critical(main_window_, tr("Import error"), tr("Nothing to import")); QMessageBox::critical(main_window_, tr("Import error"),
tr("Nothing to import"));
return; return;
} }
@@ -250,9 +252,11 @@ void Core::ImportFiles(const QStringList &urls, Folder* parent)
return; return;
} }
TaskDialog* task_dialog = new TaskDialog(pim, tr("Importing..."), main_window()); TaskDialog *task_dialog =
new TaskDialog(pim, tr("Importing..."), main_window());
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::ImportTaskComplete); connect(task_dialog, &TaskDialog::TaskSucceeded, this,
&Core::ImportTaskComplete);
task_dialog->open(); task_dialog->open();
} }
@@ -333,19 +337,22 @@ void Core::DialogAboutShow()
void Core::DialogImportShow() void Core::DialogImportShow()
{ {
// Open dialog for user to select files // Open dialog for user to select files
QStringList files = QFileDialog::getOpenFileNames(main_window_, QStringList files =
tr("Import footage...")); QFileDialog::getOpenFileNames(main_window_, tr("Import footage..."));
// Check if the user actually selected files to import // Check if the user actually selected files to import
if (!files.isEmpty()) { if (!files.isEmpty()) {
// Locate the most recently focused Project panel (assume that's the panel the user wants to import into) // Locate the most recently focused Project panel (assume that's the panel the user wants to import into)
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>(); ProjectPanel *active_project_panel =
PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
Project *active_project; Project *active_project;
if (active_project_panel == nullptr // Check that we found a Project panel if (active_project_panel ==
|| (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project nullptr // Check that we found a Project panel
QMessageBox::critical(main_window_, tr("Failed to import footage"), tr("Failed to find active Project panel")); || (active_project = active_project_panel->project()) ==
nullptr) { // and that we could find an active Project
QMessageBox::critical(main_window_, tr("Failed to import footage"),
tr("Failed to find active Project panel"));
return; return;
} }
@@ -370,8 +377,8 @@ void Core::DialogProjectPropertiesShow()
ProjectPropertiesDialog ppd(proj, main_window_); ProjectPropertiesDialog ppd(proj, main_window_);
ppd.exec(); ppd.exec();
} else { } else {
QMessageBox::critical(main_window_, QMessageBox::critical(
tr("No Active Project"), main_window_, tr("No Active Project"),
tr("No project is currently open to set the properties for"), tr("No project is currently open to set the properties for"),
QMessageBox::Ok); QMessageBox::Ok);
} }
@@ -385,7 +392,8 @@ void Core::DialogExportShow()
} }
#ifdef USE_OTIO #ifdef USE_OTIO
bool Core::DialogImportOTIOShow(const QList<Sequence*>& sequences) { bool Core::DialogImportOTIOShow(const QList<Sequence *> &sequences)
{
Project *active_project = GetActiveProject(); Project *active_project = GetActiveProject();
OTIOPropertiesDialog opd(sequences, active_project); OTIOPropertiesDialog opd(sequences, active_project);
return opd.exec() == QDialog::Accepted; return opd.exec() == QDialog::Accepted;
@@ -395,12 +403,15 @@ bool Core::DialogImportOTIOShow(const QList<Sequence*>& sequences) {
void Core::CreateNewFolder() void Core::CreateNewFolder()
{ {
// Locate the most recently focused Project panel (assume that's the panel the user wants to import into) // Locate the most recently focused Project panel (assume that's the panel the user wants to import into)
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>(); ProjectPanel *active_project_panel =
PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
Project *active_project; Project *active_project;
if (active_project_panel == nullptr // Check that we found a Project panel if (active_project_panel == nullptr // Check that we found a Project panel
|| (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project || (active_project = active_project_panel->project()) ==
QMessageBox::critical(main_window_, tr("Failed to create new folder"), tr("Failed to find active project")); nullptr) { // and that we could find an active Project
QMessageBox::critical(main_window_, tr("Failed to create new folder"),
tr("Failed to find active project"));
return; return;
} }
@@ -430,7 +441,8 @@ void Core::CreateNewSequence()
Project *active_project = GetActiveProject(); Project *active_project = GetActiveProject();
if (!active_project) { if (!active_project) {
QMessageBox::critical(main_window_, tr("Failed to create new sequence"), tr("Failed to find active project")); QMessageBox::critical(main_window_, tr("Failed to create new sequence"),
tr("Failed to find active project"));
return; return;
} }
@@ -444,25 +456,25 @@ void Core::CreateNewSequence()
sd.SetUndoable(false); sd.SetUndoable(false);
if (sd.exec() == QDialog::Accepted) { if (sd.exec() == QDialog::Accepted) {
// Create an undoable command // Create an undoable command
MultiUndoCommand *command = new MultiUndoCommand(); MultiUndoCommand *command = new MultiUndoCommand();
command->add_child(new NodeAddCommand(active_project, new_sequence)); command->add_child(new NodeAddCommand(active_project, new_sequence));
command->add_child(new FolderAddChild(GetSelectedFolderInActiveProject(), new_sequence)); command->add_child(new FolderAddChild(
command->add_child(new NodeSetPositionCommand(new_sequence, new_sequence, Node::Position())); GetSelectedFolderInActiveProject(), new_sequence));
command->add_child(new NodeSetPositionCommand(
new_sequence, new_sequence, Node::Position()));
command->add_child(new OpenSequenceCommand(new_sequence)); command->add_child(new OpenSequenceCommand(new_sequence));
// Create and connect default nodes to new sequence // Create and connect default nodes to new sequence
new_sequence->add_default_nodes(command); new_sequence->add_default_nodes(command);
Core::instance()->undo_stack()->push(command, tr("Created New Sequence")); Core::instance()->undo_stack()->push(command,
tr("Created New Sequence"));
} else { } else {
// If the dialog was accepted, ownership goes to the AddItemCommand. But if we get here, just delete // If the dialog was accepted, ownership goes to the AddItemCommand. But if we get here, just delete
delete new_sequence; delete new_sequence;
} }
} }
@@ -509,7 +521,8 @@ bool Core::AddOpenProjectFromTask(Task *task, bool add_to_recents)
void Core::SetActiveProject(Project *p) void Core::SetActiveProject(Project *p)
{ {
if (open_project_) { if (open_project_) {
disconnect(open_project_, &Project::ModifiedChanged, this, &Core::ProjectWasModified); disconnect(open_project_, &Project::ModifiedChanged, this,
&Core::ProjectWasModified);
} }
open_project_ = p; open_project_ = p;
@@ -517,7 +530,8 @@ void Core::SetActiveProject(Project *p)
main_window_->SetProject(p); main_window_->SetProject(p);
if (open_project_) { if (open_project_) {
connect(open_project_, &Project::ModifiedChanged, this, &Core::ProjectWasModified); connect(open_project_, &Project::ModifiedChanged, this,
&Core::ProjectWasModified);
} }
} }
@@ -534,7 +548,8 @@ void Core::ImportTaskComplete(Task* task)
for (int i = 0; i < f->GetVideoStreamCount(); i++) { for (int i = 0; i < f->GetVideoStreamCount(); i++) {
const VideoParams &vs = f->GetVideoParams(i); const VideoParams &vs = f->GetVideoParams(i);
if (!(vs.video_type() == VideoParams::kVideoTypeStill && vs.enabled() == (i == 0))) { if (!(vs.video_type() == VideoParams::kVideoTypeStill &&
vs.enabled() == (i == 0))) {
all_stills = false; all_stills = false;
} }
} }
@@ -544,11 +559,15 @@ void Core::ImportTaskComplete(Task* task)
d.setIcon(QMessageBox::Question); d.setIcon(QMessageBox::Question);
d.setWindowTitle(tr("Multi-Layer Image")); d.setWindowTitle(tr("Multi-Layer Image"));
d.setText(tr("The file '%1' has multiple layers. Would you like these layers to be " d.setText(
"separated across multiple tracks or merged into a single image?").arg(f->filename())); tr("The file '%1' has multiple layers. Would you like these layers to be "
"separated across multiple tracks or merged into a single image?")
.arg(f->filename()));
auto multi_btn = d.addButton(tr("Multiple Layers"), QMessageBox::YesRole); auto multi_btn =
auto single_btn = d.addButton(tr("Single Layer"), QMessageBox::NoRole); d.addButton(tr("Multiple Layers"), QMessageBox::YesRole);
auto single_btn =
d.addButton(tr("Single Layer"), QMessageBox::NoRole);
auto cancel_btn = d.addButton(QMessageBox::Cancel); auto cancel_btn = d.addButton(QMessageBox::Cancel);
d.exec(); d.exec();
@@ -571,11 +590,14 @@ void Core::ImportTaskComplete(Task* task)
} }
if (import_task->HasInvalidFiles()) { if (import_task->HasInvalidFiles()) {
ProjectImportErrorDialog d(import_task->GetInvalidFiles(), main_window_); ProjectImportErrorDialog d(import_task->GetInvalidFiles(),
main_window_);
d.exec(); d.exec();
} }
undo_stack_.push(command, tr("Imported %1 File(s)").arg(import_task->GetImportedFootage().size())); undo_stack_.push(
command,
tr("Imported %1 File(s)").arg(import_task->GetImportedFootage().size()));
main_window_->SelectFootage(import_task->GetImportedFootage()); main_window_->SelectFootage(import_task->GetImportedFootage());
} }
@@ -587,7 +609,8 @@ bool Core::ConfirmImageSequence(const QString& filename)
mb.setIcon(QMessageBox::Question); mb.setIcon(QMessageBox::Question);
mb.setWindowTitle(tr("Possible image sequence detected")); mb.setWindowTitle(tr("Possible image sequence detected"));
mb.setText(tr("The file '%1' looks like it might be part of an image " mb.setText(tr("The file '%1' looks like it might be part of an image "
"sequence. Would you like to import it as such?").arg(filename)); "sequence. Would you like to import it as such?")
.arg(filename));
mb.addButton(QMessageBox::Yes); mb.addButton(QMessageBox::Yes);
mb.addButton(QMessageBox::No); mb.addButton(QMessageBox::No);
@@ -605,7 +628,8 @@ bool Core::StartHeadlessExport()
const QString &startup_project = core_params_.startup_project(); const QString &startup_project = core_params_.startup_project();
if (startup_project.isEmpty()) { if (startup_project.isEmpty()) {
qCritical().noquote() << tr("You must specify a project file to export"); qCritical().noquote()
<< tr("You must specify a project file to export");
return false; return false;
} }
@@ -689,14 +713,15 @@ bool Core::StartHeadlessExport()
void Core::OpenStartupProject() void Core::OpenStartupProject()
{ {
const QString &startup_project = core_params_.startup_project(); const QString &startup_project = core_params_.startup_project();
bool startup_project_exists = !startup_project.isEmpty() && QFileInfo::exists(startup_project); bool startup_project_exists = !startup_project.isEmpty() &&
QFileInfo::exists(startup_project);
// Load startup project // Load startup project
if (!startup_project_exists && !startup_project.isEmpty()) { if (!startup_project_exists && !startup_project.isEmpty()) {
QMessageBox::warning(main_window_, QMessageBox::warning(main_window_, tr("Failed to open startup file"),
tr("Failed to open startup file"),
tr("The project \"%1\" doesn't exist. " tr("The project \"%1\" doesn't exist. "
"A new project will be started instead.").arg(startup_project), "A new project will be started instead.")
.arg(startup_project),
QMessageBox::Ok); QMessageBox::Ok);
} }
@@ -712,7 +737,8 @@ void Core::OpenStartupProject()
void Core::AddRecoveryProjectFromTask(Task *task) void Core::AddRecoveryProjectFromTask(Task *task)
{ {
if (AddOpenProjectFromTask(task, false)) { if (AddOpenProjectFromTask(task, false)) {
ProjectLoadBaseTask* load_task = static_cast<ProjectLoadBaseTask*>(task); ProjectLoadBaseTask *load_task =
static_cast<ProjectLoadBaseTask *>(task);
Project *project = load_task->GetLoadedProject(); Project *project = load_task->GetLoadedProject();
@@ -747,9 +773,7 @@ void Core::StartGUI(bool full_screen)
DiskManager::CreateInstance(); DiskManager::CreateInstance();
// Connect the PanelFocusManager to the application's focus change signal // Connect the PanelFocusManager to the application's focus change signal
connect(qApp, connect(qApp, &QApplication::focusChanged, PanelManager::instance(),
&QApplication::focusChanged,
PanelManager::instance(),
&PanelManager::FocusChanged); &PanelManager::FocusChanged);
// Set KDDockWidgets flags // Set KDDockWidgets flags
@@ -774,12 +798,14 @@ void Core::StartGUI(bool full_screen)
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
// Workaround for Qt bug where menus don't appear in full screen mode // Workaround for Qt bug where menus don't appear in full screen mode
// See: https://doc.qt.io/qt-5/windows-issues.html // See: https://doc.qt.io/qt-5/windows-issues.html
QWindowsWindowFunctions::setHasBorderInFullScreen(main_window_->windowHandle(), true); QWindowsWindowFunctions::setHasBorderInFullScreen(
main_window_->windowHandle(), true);
#endif #endif
// Start autorecovery timer using the config value as its interval // Start autorecovery timer using the config value as its interval
SetAutorecoveryInterval(OLIVE_CONFIG("AutorecoveryInterval").toInt()); SetAutorecoveryInterval(OLIVE_CONFIG("AutorecoveryInterval").toInt());
connect(&autorecovery_timer_, &QTimer::timeout, this, &Core::SaveAutorecovery); connect(&autorecovery_timer_, &QTimer::timeout, this,
&Core::SaveAutorecovery);
autorecovery_timer_.start(); autorecovery_timer_.start();
// Load recently opened projects list // Load recently opened projects list
@@ -802,24 +828,28 @@ void Core::SaveProjectInternal(const QString& override_filename)
// Create save manager // Create save manager
Task *psm; Task *psm;
if (open_project_->filename().endsWith(QStringLiteral(".otio"), Qt::CaseInsensitive)) { if (open_project_->filename().endsWith(QStringLiteral(".otio"),
Qt::CaseInsensitive)) {
#ifdef USE_OTIO #ifdef USE_OTIO
psm = new SaveOTIOTask(open_project_); psm = new SaveOTIOTask(open_project_);
#else #else
QMessageBox::critical(main_window_, QMessageBox::critical(
tr("Missing OpenTimelineIO Libraries"), main_window_, tr("Missing OpenTimelineIO Libraries"),
tr("This build was compiled without OpenTimelineIO and therefore " tr("This build was compiled without OpenTimelineIO and therefore "
"cannot open OpenTimelineIO files.")); "cannot open OpenTimelineIO files."));
return; return;
#endif #endif
} else { } else {
bool use_compression = !open_project_->filename().endsWith(QStringLiteral(".ovexml"), Qt::CaseInsensitive); bool use_compression = !open_project_->filename().endsWith(
QStringLiteral(".ovexml"), Qt::CaseInsensitive);
psm = new ProjectSaveTask(open_project_, use_compression); psm = new ProjectSaveTask(open_project_, use_compression);
static_cast<ProjectSaveTask*>(psm)->SetLayout(main_window_->SaveLayout()); static_cast<ProjectSaveTask *>(psm)->SetLayout(
main_window_->SaveLayout());
if (!override_filename.isEmpty()) { if (!override_filename.isEmpty()) {
// Set override filename if provided // Set override filename if provided
static_cast<ProjectSaveTask*>(psm)->SetOverrideFilename(override_filename); static_cast<ProjectSaveTask *>(psm)->SetOverrideFilename(
override_filename);
} }
} }
@@ -844,26 +874,28 @@ void Core::SaveProjectInternal(const QString& override_filename)
ViewerOutput *Core::GetSequenceToExport() ViewerOutput *Core::GetSequenceToExport()
{ {
// First try the most recently focused time based window // First try the most recently focused time based window
TimeBasedPanel* time_panel = PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>(); TimeBasedPanel *time_panel =
PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
// If that fails try defaulting to the first timeline (i.e. if a project has just been loaded). // If that fails try defaulting to the first timeline (i.e. if a project has just been loaded).
if (!time_panel->GetConnectedViewer()) { if (!time_panel->GetConnectedViewer()) {
// Safe to assume there will always be one timeline. // Safe to assume there will always be one timeline.
time_panel = PanelManager::instance()->GetPanelsOfType<TimelinePanel>().first(); time_panel =
PanelManager::instance()->GetPanelsOfType<TimelinePanel>().first();
} }
if (time_panel && time_panel->GetConnectedViewer()) { if (time_panel && time_panel->GetConnectedViewer()) {
if (time_panel->GetConnectedViewer()->GetLength() == 0) { if (time_panel->GetConnectedViewer()->GetLength() == 0) {
QMessageBox::critical(main_window_, QMessageBox::critical(
tr("Error"), main_window_, tr("Error"),
tr("This Sequence is empty. There is nothing to export."), tr("This Sequence is empty. There is nothing to export."),
QMessageBox::Ok); QMessageBox::Ok);
} else { } else {
return time_panel->GetConnectedViewer(); return time_panel->GetConnectedViewer();
} }
} else { } else {
QMessageBox::critical(main_window_, QMessageBox::critical(
tr("Error"), main_window_, tr("Error"),
tr("No valid sequence detected.\n\nMake sure a sequence is loaded and it has a connected Viewer node."), tr("No valid sequence detected.\n\nMake sure a sequence is loaded and it has a connected Viewer node."),
QMessageBox::Ok); QMessageBox::Ok);
} }
@@ -873,7 +905,9 @@ ViewerOutput *Core::GetSequenceToExport()
QString Core::GetAutoRecoveryIndexFilename() QString Core::GetAutoRecoveryIndexFilename()
{ {
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("unrecovered")); return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppLocalDataLocation))
.filePath(QStringLiteral("unrecovered"));
} }
void Core::SaveUnrecoveredList() void Core::SaveUnrecoveredList()
@@ -908,20 +942,27 @@ void Core::SaveUnrecoveredList()
bool Core::RevertProjectInternal(bool by_opening_existing) bool Core::RevertProjectInternal(bool by_opening_existing)
{ {
if (open_project_->filename().isEmpty()) { if (open_project_->filename().isEmpty()) {
QMessageBox::critical(main_window_, tr("Revert"), QMessageBox::critical(
main_window_, tr("Revert"),
tr("This project has not yet been saved, therefore there is no last saved state to revert to.")); tr("This project has not yet been saved, therefore there is no last saved state to revert to."));
} else { } else {
QString msg; QString msg;
if (by_opening_existing) { if (by_opening_existing) {
msg = tr("The project \"%1\" is already open. By re-opening it, the project will revert to " msg =
"its last saved state. Any unsaved changes will be lost. Do you wish to continue?").arg(open_project_->filename()); tr("The project \"%1\" is already open. By re-opening it, the project will revert to "
"its last saved state. Any unsaved changes will be lost. Do you wish to continue?")
.arg(open_project_->filename());
} else { } else {
msg = tr("This will revert the project \"%1\" back to its last saved state. " msg =
"All unsaved changes will be lost. Do you wish to continue?").arg(open_project_->name()); tr("This will revert the project \"%1\" back to its last saved state. "
"All unsaved changes will be lost. Do you wish to continue?")
.arg(open_project_->name());
} }
if (QMessageBox::question(main_window_, tr("Revert"), msg, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { if (QMessageBox::question(main_window_, tr("Revert"), msg,
QMessageBox::Ok | QMessageBox::Cancel) ==
QMessageBox::Ok) {
// Copy filename because CloseProject is going to delete `p` // Copy filename because CloseProject is going to delete `p`
QString filename = open_project_->filename(); QString filename = open_project_->filename();
@@ -954,16 +995,22 @@ void Core::SaveAutorecovery()
{ {
if (OLIVE_CONFIG("AutorecoveryEnabled").toBool()) { if (OLIVE_CONFIG("AutorecoveryEnabled").toBool()) {
if (open_project_ && !open_project_->has_autorecovery_been_saved()) { if (open_project_ && !open_project_->has_autorecovery_been_saved()) {
QDir project_autorecovery_dir(QDir(FileFunctions::GetAutoRecoveryRoot()).filePath(open_project_->GetUuid().toString())); QDir project_autorecovery_dir(
QDir(FileFunctions::GetAutoRecoveryRoot())
.filePath(open_project_->GetUuid().toString()));
if (FileFunctions::DirectoryIsValid(project_autorecovery_dir)) { if (FileFunctions::DirectoryIsValid(project_autorecovery_dir)) {
QString this_autorecovery_path = project_autorecovery_dir.filePath(QStringLiteral("%1.ove").arg(QString::number(QDateTime::currentSecsSinceEpoch()))); QString this_autorecovery_path =
project_autorecovery_dir.filePath(
QStringLiteral("%1.ove").arg(QString::number(
QDateTime::currentSecsSinceEpoch())));
SaveProjectInternal(this_autorecovery_path); SaveProjectInternal(this_autorecovery_path);
open_project_->set_autorecovery_saved(true); open_project_->set_autorecovery_saved(true);
// Keep track of projects that where the "newest" save is the recovery project // Keep track of projects that where the "newest" save is the recovery project
if (!autorecovered_projects_.contains(open_project_->GetUuid())) { if (!autorecovered_projects_.contains(
open_project_->GetUuid())) {
autorecovered_projects_.append(open_project_->GetUuid()); autorecovered_projects_.append(open_project_->GetUuid());
} }
@@ -971,27 +1018,34 @@ void Core::SaveAutorecovery()
// Write human-readable real name so it's not just a UUID // Write human-readable real name so it's not just a UUID
{ {
QFile realname_file(project_autorecovery_dir.filePath(QStringLiteral("realname.txt"))); QFile realname_file(project_autorecovery_dir.filePath(
QStringLiteral("realname.txt")));
realname_file.open(QFile::WriteOnly); realname_file.open(QFile::WriteOnly);
realname_file.write(open_project_->pretty_filename().toUtf8()); realname_file.write(
open_project_->pretty_filename().toUtf8());
realname_file.close(); realname_file.close();
} }
int64_t max_recoveries_per_file = OLIVE_CONFIG("AutorecoveryMaximum").toLongLong(); int64_t max_recoveries_per_file =
OLIVE_CONFIG("AutorecoveryMaximum").toLongLong();
// Since we write an extra file, increment total allowed files by 1 // Since we write an extra file, increment total allowed files by 1
max_recoveries_per_file++; max_recoveries_per_file++;
// Delete old entries // Delete old entries
QStringList recovery_files = project_autorecovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name); QStringList recovery_files = project_autorecovery_dir.entryList(
QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
while (recovery_files.size() > max_recoveries_per_file) { while (recovery_files.size() > max_recoveries_per_file) {
bool deleted = false; bool deleted = false;
for (int i = 0; i < recovery_files.size(); i++) { for (int i = 0; i < recovery_files.size(); i++) {
const QString &f = recovery_files.at(i); const QString &f = recovery_files.at(i);
if (f.endsWith(QStringLiteral(".ove"), Qt::CaseInsensitive)) { if (f.endsWith(QStringLiteral(".ove"),
QString delete_full_path = project_autorecovery_dir.filePath(f); Qt::CaseInsensitive)) {
qDebug() << "Deleted old recovery:" << delete_full_path; QString delete_full_path =
project_autorecovery_dir.filePath(f);
qDebug()
<< "Deleted old recovery:" << delete_full_path;
QFile::remove(delete_full_path); QFile::remove(delete_full_path);
recovery_files.removeAt(i); recovery_files.removeAt(i);
deleted = true; deleted = true;
@@ -1006,7 +1060,8 @@ void Core::SaveAutorecovery()
} }
} }
} else { } else {
QMessageBox::critical(main_window_, tr("Auto-Recovery Error"), QMessageBox::critical(
main_window_, tr("Auto-Recovery Error"),
tr("Failed to save auto-recovery to \"%1\". " tr("Failed to save auto-recovery to \"%1\". "
"Olive may not have permission to this directory.") "Olive may not have permission to this directory.")
.arg(project_autorecovery_dir.absolutePath())); .arg(project_autorecovery_dir.absolutePath()));
@@ -1039,7 +1094,8 @@ Project* Core::GetActiveProject() const
Folder *Core::GetSelectedFolderInActiveProject() const Folder *Core::GetSelectedFolderInActiveProject() const
{ {
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>(); ProjectPanel *active_project_panel =
PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
if (active_project_panel) { if (active_project_panel) {
return active_project_panel->GetSelectedFolder(); return active_project_panel->GetSelectedFolder();
@@ -1050,7 +1106,8 @@ Folder *Core::GetSelectedFolderInActiveProject() const
Timecode::Display Core::GetTimecodeDisplay() const Timecode::Display Core::GetTimecodeDisplay() const
{ {
return static_cast<Timecode::Display>(OLIVE_CONFIG("TimecodeDisplay").toInt()); return static_cast<Timecode::Display>(
OLIVE_CONFIG("TimecodeDisplay").toInt());
} }
void Core::SetTimecodeDisplay(Timecode::Display d) void Core::SetTimecodeDisplay(Timecode::Display d)
@@ -1099,7 +1156,8 @@ QString Core::GetProjectFilter(bool include_any_filter)
for (auto it = FILTERS.cbegin(); it != FILTERS.cend(); it++) { for (auto it = FILTERS.cbegin(); it != FILTERS.cend(); it++) {
combined.append(QStringLiteral("*.%1").arg(it->second)); combined.append(QStringLiteral("*.%1").arg(it->second));
} }
filters.append(QStringLiteral("%1 (%2)").arg(tr("All Supported Projects"), combined.join(' '))); filters.append(QStringLiteral("%1 (%2)").arg(
tr("All Supported Projects"), combined.join(' ')));
} }
for (auto it = FILTERS.cbegin(); it != FILTERS.cend(); it++) { for (auto it = FILTERS.cbegin(); it != FILTERS.cend(); it++) {
@@ -1111,17 +1169,20 @@ QString Core::GetProjectFilter(bool include_any_filter)
QString Core::GetRecentProjectsFilePath() QString Core::GetRecentProjectsFilePath()
{ {
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("recent")); return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("recent"));
} }
void Core::SetStartupLocale() void Core::SetStartupLocale()
{ {
// Set language // Set language
if (!core_params_.startup_language().isEmpty()) { if (!core_params_.startup_language().isEmpty()) {
if (translator_->load(core_params_.startup_language()) && QApplication::installTranslator(translator_)) { if (translator_->load(core_params_.startup_language()) &&
QApplication::installTranslator(translator_)) {
return; return;
} else { } else {
qWarning() << "Failed to load translation file. Falling back to defaults."; qWarning()
<< "Failed to load translation file. Falling back to defaults.";
} }
} }
@@ -1133,7 +1194,8 @@ void Core::SetStartupLocale()
} }
if (!SetLanguage(use_locale)) { if (!SetLanguage(use_locale)) {
qWarning() << "Trying to use locale" << use_locale << "but couldn't find a translation for it"; qWarning() << "Trying to use locale" << use_locale
<< "but couldn't find a translation for it";
} }
} }
@@ -1168,12 +1230,15 @@ void Core::OpenNodeInViewer(ViewerOutput *viewer)
main_window_->OpenNodeInViewer(viewer); main_window_->OpenNodeInViewer(viewer);
} }
void Core::OpenExportDialogForViewer(ViewerOutput *viewer, bool start_still_image) void Core::OpenExportDialogForViewer(ViewerOutput *viewer,
bool start_still_image)
{ {
ExportDialog* ed = new ExportDialog(viewer, start_still_image, main_window_); ExportDialog *ed =
new ExportDialog(viewer, start_still_image, main_window_);
connect(ed, &ExportDialog::finished, ed, &ExportDialog::deleteLater); connect(ed, &ExportDialog::finished, ed, &ExportDialog::deleteLater);
ed->open(); ed->open();
connect(ed, &ExportDialog::RequestImportFile, this, &Core::ImportSingleFile); connect(ed, &ExportDialog::RequestImportFile, this,
&Core::ImportSingleFile);
} }
void Core::CheckForAutoRecoveries() void Core::CheckForAutoRecoveries()
@@ -1182,9 +1247,11 @@ void Core::CheckForAutoRecoveries()
if (autorecovery_index.exists()) { if (autorecovery_index.exists()) {
// Uh-oh, we have auto-recoveries to prompt // Uh-oh, we have auto-recoveries to prompt
if (autorecovery_index.open(QFile::ReadOnly)) { if (autorecovery_index.open(QFile::ReadOnly)) {
QStringList recovery_filenames = QString::fromUtf8(autorecovery_index.readAll()).split('\n'); QStringList recovery_filenames =
QString::fromUtf8(autorecovery_index.readAll()).split('\n');
AutoRecoveryDialog ard(tr("The following projects had unsaved changes when Olive " AutoRecoveryDialog ard(
tr("The following projects had unsaved changes when Olive "
"forcefully quit. Would you like to load them?"), "forcefully quit. Would you like to load them?"),
recovery_filenames, true, main_window_); recovery_filenames, true, main_window_);
ard.exec(); ard.exec();
@@ -1194,10 +1261,12 @@ void Core::CheckForAutoRecoveries()
// Delete recovery index since we don't need it anymore // Delete recovery index since we don't need it anymore
QFile::remove(GetAutoRecoveryIndexFilename()); QFile::remove(GetAutoRecoveryIndexFilename());
} else { } else {
QMessageBox::critical(main_window_, tr("Auto-Recovery Error"), QMessageBox::critical(
main_window_, tr("Auto-Recovery Error"),
tr("Found auto-recoveries but failed to load the auto-recovery index. " tr("Found auto-recoveries but failed to load the auto-recovery index. "
"Auto-recover projects will have to be opened manually.\n\n" "Auto-recover projects will have to be opened manually.\n\n"
"Your recoverable projects are still available at: %1").arg(FileFunctions::GetAutoRecoveryRoot())); "Your recoverable projects are still available at: %1")
.arg(FileFunctions::GetAutoRecoveryRoot()));
} }
} }
} }
@@ -1205,8 +1274,10 @@ void Core::CheckForAutoRecoveries()
void Core::BrowseAutoRecoveries() void Core::BrowseAutoRecoveries()
{ {
// List all auto-recovery entries // List all auto-recovery entries
AutoRecoveryDialog ard(tr("The following project versions have been auto-saved:"), AutoRecoveryDialog ard(
QDir(FileFunctions::GetAutoRecoveryRoot()).entryList(QDir::Dirs | QDir::NoDotAndDotDot), tr("The following project versions have been auto-saved:"),
QDir(FileFunctions::GetAutoRecoveryRoot())
.entryList(QDir::Dirs | QDir::NoDotAndDotDot),
false, main_window_); false, main_window_);
ard.exec(); ard.exec();
} }
@@ -1235,7 +1306,8 @@ void Core::WarnCacheFull()
if (!shown_cache_full_warning_ && main_window_) { if (!shown_cache_full_warning_ && main_window_) {
shown_cache_full_warning_ = true; shown_cache_full_warning_ = true;
QMessageBox::warning(main_window_, tr("Disk Cache Full"), QMessageBox::warning(
main_window_, tr("Disk Cache Full"),
tr("The disk cache is currently full and Olive is having to delete old " tr("The disk cache is currently full and Olive is having to delete old "
"frames to keep it within the limits set in the Disk preferences. This " "frames to keep it within the limits set in the Disk preferences. This "
"will result in SIGNIFICANTLY reduced cache performance.\n\n" "will result in SIGNIFICANTLY reduced cache performance.\n\n"
@@ -1259,7 +1331,8 @@ bool Core::SaveProjectAs()
// Somewhat hacky method of extracting the extension from the name filter // Somewhat hacky method of extracting the extension from the name filter
const QString &name_filter = fd.selectedNameFilter(); const QString &name_filter = fd.selectedNameFilter();
int ext_index = name_filter.indexOf(QStringLiteral("(*.")) + 3; int ext_index = name_filter.indexOf(QStringLiteral("(*.")) + 3;
QString extension = name_filter.mid(ext_index, name_filter.size() - ext_index - 1); QString extension =
name_filter.mid(ext_index, name_filter.size() - ext_index - 1);
fn = FileFunctions::EnsureFilenameExtension(fn, extension); fn = FileFunctions::EnsureFilenameExtension(fn, extension);
@@ -1328,8 +1401,8 @@ void Core::OpenProjectInternal(const QString &filename, bool recovery_project)
#ifdef USE_OTIO #ifdef USE_OTIO
load_task = new LoadOTIOTask(filename); load_task = new LoadOTIOTask(filename);
#else #else
QMessageBox::critical(main_window_, QMessageBox::critical(
tr("Missing OpenTimelineIO Libraries"), main_window_, tr("Missing OpenTimelineIO Libraries"),
tr("This build was compiled without OpenTimelineIO and therefore " tr("This build was compiled without OpenTimelineIO and therefore "
"cannot open OpenTimelineIO files.")); "cannot open OpenTimelineIO files."));
return; return;
@@ -1339,12 +1412,15 @@ void Core::OpenProjectInternal(const QString &filename, bool recovery_project)
load_task = new ProjectLoadTask(filename); load_task = new ProjectLoadTask(filename);
} }
TaskDialog* task_dialog = new TaskDialog(load_task, tr("Load Project"), main_window()); TaskDialog *task_dialog =
new TaskDialog(load_task, tr("Load Project"), main_window());
if (recovery_project) { if (recovery_project) {
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddRecoveryProjectFromTask); connect(task_dialog, &TaskDialog::TaskSucceeded, this,
&Core::AddRecoveryProjectFromTask);
} else { } else {
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddOpenProjectFromTaskAndAddToRecents); connect(task_dialog, &TaskDialog::TaskSucceeded, this,
&Core::AddOpenProjectFromTaskAndAddToRecents);
} }
task_dialog->open(); task_dialog->open();
@@ -1366,7 +1442,8 @@ int Core::CountFilesInFileList(const QFileInfoList &filenames)
if (f.fileName() == "." || f.fileName() == "..") { if (f.fileName() == "." || f.fileName() == "..") {
continue; continue;
} else if (f.isDir()) { } else if (f.isDir()) {
QFileInfoList info_list = QDir(f.absoluteFilePath()).entryInfoList(); QFileInfoList info_list =
QDir(f.absoluteFilePath()).entryInfoList();
file_count += CountFilesInFileList(info_list); file_count += CountFilesInFileList(info_list);
} else { } else {
@@ -1395,12 +1472,9 @@ bool Core::LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent)
} }
} }
QString s = QInputDialog::getText(main_window_, QString s = QInputDialog::getText(main_window_, tr("Label Node"),
tr("Label Node"), tr("Set node label"), QLineEdit::Normal,
tr("Set node label"), start_label, &ok);
QLineEdit::Normal,
start_label,
&ok);
if (ok) { if (ok) {
NodeRenameCommand *rename_command = new NodeRenameCommand(); NodeRenameCommand *rename_command = new NodeRenameCommand();
@@ -1412,7 +1486,8 @@ bool Core::LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent)
if (parent) { if (parent) {
parent->add_child(rename_command); parent->add_child(rename_command);
} else { } else {
undo_stack_.push(rename_command, tr("Renamed %1 Node(s)").arg(nodes.size())); undo_stack_.push(rename_command,
tr("Renamed %1 Node(s)").arg(nodes.size()));
} }
return true; return true;
@@ -1421,7 +1496,8 @@ bool Core::LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent)
return false; return false;
} }
Sequence *Core::CreateNewSequenceForProject(const QString &format, Project* project) Sequence *Core::CreateNewSequenceForProject(const QString &format,
Project *project)
{ {
Sequence *new_sequence = new Sequence(); Sequence *new_sequence = new Sequence();
@@ -1443,9 +1519,11 @@ void Core::OpenProjectFromRecentList(int index)
if (QFileInfo::exists(open_fn)) { if (QFileInfo::exists(open_fn)) {
OpenProjectInternal(open_fn); OpenProjectInternal(open_fn);
} else if (QMessageBox::information(main_window(), } else if (
tr("Cannot open recent project"), QMessageBox::information(
tr("The project \"%1\" doesn't exist. Would you like to remove this file from the recent list?").arg(open_fn), main_window(), tr("Cannot open recent project"),
tr("The project \"%1\" doesn't exist. Would you like to remove this file from the recent list?")
.arg(open_fn),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
recent_projects_.removeAt(index); recent_projects_.removeAt(index);
@@ -1464,10 +1542,12 @@ bool Core::CloseProject(bool auto_open_new, bool ignore_modified)
mb.setWindowModality(Qt::WindowModal); mb.setWindowModality(Qt::WindowModal);
mb.setIcon(QMessageBox::Question); mb.setIcon(QMessageBox::Question);
mb.setWindowTitle(tr("Unsaved Changes")); mb.setWindowTitle(tr("Unsaved Changes"));
mb.setText(tr("The project '%1' has unsaved changes. Would you like to save them?") mb.setText(
tr("The project '%1' has unsaved changes. Would you like to save them?")
.arg(open_project_->name())); .arg(open_project_->name()));
QPushButton* yes_btn = mb.addButton(tr("Save"), QMessageBox::YesRole); QPushButton *yes_btn =
mb.addButton(tr("Save"), QMessageBox::YesRole);
mb.addButton(tr("Don't Save"), QMessageBox::NoRole); mb.addButton(tr("Don't Save"), QMessageBox::NoRole);
@@ -1504,13 +1584,15 @@ bool Core::CloseProject(bool auto_open_new, bool ignore_modified)
void Core::CacheActiveSequence(bool in_out_only) void Core::CacheActiveSequence(bool in_out_only)
{ {
TimeBasedPanel* p = PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>(); TimeBasedPanel *p =
PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
if (p && p->GetConnectedViewer()) { if (p && p->GetConnectedViewer()) {
// Hacky but works for now // Hacky but works for now
// Find Viewer attached to this TimeBasedPanel // Find Viewer attached to this TimeBasedPanel
QList<ViewerPanel*> all_viewers = PanelManager::instance()->GetPanelsOfType<ViewerPanel>(); QList<ViewerPanel *> all_viewers =
PanelManager::instance()->GetPanelsOfType<ViewerPanel>();
ViewerPanel *found_panel = nullptr; ViewerPanel *found_panel = nullptr;
@@ -1528,8 +1610,8 @@ void Core::CacheActiveSequence(bool in_out_only)
found_panel->CacheEntireSequence(); found_panel->CacheEntireSequence();
} }
} else { } else {
QMessageBox::critical(main_window_, QMessageBox::critical(
tr("Failed to cache sequence"), main_window_, tr("Failed to cache sequence"),
tr("No active viewer found with this sequence."), tr("No active viewer found with this sequence."),
QMessageBox::Ok); QMessageBox::Ok);
} }
@@ -1558,7 +1640,8 @@ QString StripWindowsDriveLetter(QString s)
return s; return s;
} }
bool Core::ValidateFootageInLoadedProject(Project* project, const QString& project_saved_url) bool Core::ValidateFootageInLoadedProject(Project *project,
const QString &project_saved_url)
{ {
QVector<Footage *> footage_we_couldnt_validate; QVector<Footage *> footage_we_couldnt_validate;
@@ -1567,7 +1650,8 @@ bool Core::ValidateFootageInLoadedProject(Project* project, const QString& proje
QString footage_fn = StripWindowsDriveLetter(footage->filename()); QString footage_fn = StripWindowsDriveLetter(footage->filename());
QString project_fn = StripWindowsDriveLetter(project_saved_url); QString project_fn = StripWindowsDriveLetter(project_saved_url);
if (!QFileInfo::exists(footage_fn) && !project_saved_url.isEmpty()) { if (!QFileInfo::exists(footage_fn) &&
!project_saved_url.isEmpty()) {
// If the footage doesn't exist, it might have moved with the project // If the footage doesn't exist, it might have moved with the project
const QString &project_current_url = project->filename(); const QString &project_current_url = project->filename();
@@ -1576,12 +1660,15 @@ bool Core::ValidateFootageInLoadedProject(Project* project, const QString& proje
QDir saved_dir(QFileInfo(project_fn).dir()); QDir saved_dir(QFileInfo(project_fn).dir());
QDir true_dir(QFileInfo(project_current_url).dir()); QDir true_dir(QFileInfo(project_current_url).dir());
QString relative_filename = saved_dir.relativeFilePath(footage_fn); QString relative_filename =
QString transformed_abs_filename = true_dir.filePath(relative_filename); saved_dir.relativeFilePath(footage_fn);
QString transformed_abs_filename =
true_dir.filePath(relative_filename);
if (QFileInfo::exists(transformed_abs_filename)) { if (QFileInfo::exists(transformed_abs_filename)) {
// Use this file instead // Use this file instead
qInfo() << "Resolved" << footage_fn << "relatively to" << transformed_abs_filename; qInfo() << "Resolved" << footage_fn << "relatively to"
<< transformed_abs_filename;
footage->set_filename(transformed_abs_filename); footage->set_filename(transformed_abs_filename);
} }
} }
@@ -1611,7 +1698,8 @@ bool Core::SetLanguage(const QString &locale)
QApplication::removeTranslator(translator_); QApplication::removeTranslator(translator_);
QString resource_path = QStringLiteral(":/ts/%1").arg(locale); QString resource_path = QStringLiteral(":/ts/%1").arg(locale);
if (translator_->load(resource_path) && QApplication::installTranslator(translator_)) { if (translator_->load(resource_path) &&
QApplication::installTranslator(translator_)) {
return true; return true;
} }
@@ -1620,20 +1708,18 @@ bool Core::SetLanguage(const QString &locale)
void Core::OpenProject() void Core::OpenProject()
{ {
QString file = QFileDialog::getOpenFileName(main_window_, QString file = QFileDialog::getOpenFileName(
tr("Open Project"), main_window_, tr("Open Project"), QString(), GetProjectFilter(true));
QString(),
GetProjectFilter(true));
if (!file.isEmpty()) { if (!file.isEmpty()) {
OpenProjectInternal(file); OpenProjectInternal(file);
} }
} }
Core::CoreParams::CoreParams() : Core::CoreParams::CoreParams()
mode_(kRunNormal), : mode_(kRunNormal)
run_fullscreen_(false), , run_fullscreen_(false)
crash_(false) , crash_(false)
{ {
} }
+19 -18
View File
@@ -35,7 +35,8 @@
#include "undo/undostack.h" #include "undo/undostack.h"
#include "widget/projectexplorer/projectviewmodel.h" #include "widget/projectexplorer/projectviewmodel.h"
namespace olive { namespace olive
{
class MainWindow; class MainWindow;
@@ -48,20 +49,14 @@ class MainWindow;
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder, * The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
* opening the import dialog, etc.) * opening the import dialog, etc.)
*/ */
class Core : public QObject class Core : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
class CoreParams class CoreParams {
{
public: public:
CoreParams(); CoreParams();
enum RunMode { enum RunMode { kRunNormal, kHeadlessExport, kHeadlessPreCache };
kRunNormal,
kHeadlessExport,
kHeadlessPreCache
};
bool fullscreen() const bool fullscreen() const
{ {
@@ -123,7 +118,6 @@ public:
bool run_fullscreen_; bool run_fullscreen_;
bool crash_; bool crash_;
}; };
/** /**
@@ -248,12 +242,14 @@ public:
/** /**
* @brief Show a dialog to the user to rename a set of nodes * @brief Show a dialog to the user to rename a set of nodes
*/ */
bool LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent = nullptr); bool LabelNodes(const QVector<Node *> &nodes,
MultiUndoCommand *parent = nullptr);
/** /**
* @brief Create a new sequence named appropriately for the active project * @brief Create a new sequence named appropriately for the active project
*/ */
static Sequence* CreateNewSequenceForProject(const QString &format, Project *project); static Sequence *CreateNewSequenceForProject(const QString &format,
Project *project);
static Sequence *CreateNewSequenceForProject(Project *project) static Sequence *CreateNewSequenceForProject(Project *project)
{ {
return CreateNewSequenceForProject(tr("Sequence %1"), project); return CreateNewSequenceForProject(tr("Sequence %1"), project);
@@ -277,7 +273,8 @@ public:
/** /**
* @brief Check each footage object for whether it still exists or has changed * @brief Check each footage object for whether it still exists or has changed
*/ */
bool ValidateFootageInLoadedProject(Project* project, const QString &project_saved_url); bool ValidateFootageInLoadedProject(Project *project,
const QString &project_saved_url);
/** /**
* @brief Changes the current language * @brief Changes the current language
@@ -297,9 +294,13 @@ public:
void OpenNodeInViewer(ViewerOutput *viewer); void OpenNodeInViewer(ViewerOutput *viewer);
void OpenExportDialogForViewer(ViewerOutput *viewer, bool start_still_image); void OpenExportDialogForViewer(ViewerOutput *viewer,
bool start_still_image);
bool IsMagicEnabled() const { return magic_; } bool IsMagicEnabled() const
{
return magic_;
}
public slots: public slots:
/** /**
@@ -610,10 +611,10 @@ private slots:
/** /**
* @brief Internal project open * @brief Internal project open
*/ */
void OpenProjectInternal(const QString& filename, bool recovery_project = false); void OpenProjectInternal(const QString &filename,
bool recovery_project = false);
void ImportSingleFile(const QString &f); void ImportSingleFile(const QString &f);
}; };
} }
+66 -31
View File
@@ -40,7 +40,8 @@
#include "common/filefunctions.h" #include "common/filefunctions.h"
#include "version.h" #include "version.h"
namespace olive { namespace olive
{
CrashHandlerDialog::CrashHandlerDialog(const QString &report_path) CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
{ {
@@ -52,7 +53,8 @@ CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please help us fix it by " layout->addWidget(new QLabel(
tr("We're sorry, Olive has crashed. Please help us fix it by "
"sending an error report."))); "sending an error report.")));
QSplitter *splitter = new QSplitter(Qt::Vertical); QSplitter *splitter = new QSplitter(Qt::Vertical);
@@ -60,7 +62,8 @@ CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
layout->addWidget(splitter); layout->addWidget(splitter);
summary_edit_ = new QTextEdit(); summary_edit_ = new QTextEdit();
summary_edit_->setPlaceholderText(tr("Describe what you were doing in as much detail as " summary_edit_->setPlaceholderText(
tr("Describe what you were doing in as much detail as "
"possible. If you can, provide steps to reproduce this crash.")); "possible. If you can, provide steps to reproduce this crash."));
splitter->addWidget(summary_edit_); splitter->addWidget(summary_edit_);
@@ -83,11 +86,13 @@ CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
btn_layout->addStretch(); btn_layout->addStretch();
send_report_btn_ = new QPushButton(tr("Send Error Report")); send_report_btn_ = new QPushButton(tr("Send Error Report"));
connect(send_report_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::SendErrorReport); connect(send_report_btn_, &QPushButton::clicked, this,
&CrashHandlerDialog::SendErrorReport);
btn_layout->addWidget(send_report_btn_); btn_layout->addWidget(send_report_btn_);
dont_send_btn_ = new QPushButton(tr("Don't Send")); dont_send_btn_ = new QPushButton(tr("Don't Send"));
connect(dont_send_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::reject); connect(dont_send_btn_, &QPushButton::clicked, this,
&CrashHandlerDialog::reject);
btn_layout->addWidget(dont_send_btn_); btn_layout->addWidget(dont_send_btn_);
layout->addLayout(btn_layout); layout->addLayout(btn_layout);
@@ -117,7 +122,8 @@ QString CrashHandlerDialog::GetSymbolPath()
symbols_path = app_path.filePath(QStringLiteral("symbols")); symbols_path = app_path.filePath(QStringLiteral("symbols"));
#elif BUILDFLAG(IS_LINUX) #elif BUILDFLAG(IS_LINUX)
app_path.cdUp(); app_path.cdUp();
symbols_path = app_path.filePath(QStringLiteral("share/olive-editor/symbols")); symbols_path =
app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
#elif BUILDFLAG(IS_APPLE) #elif BUILDFLAG(IS_APPLE)
app_path.cdUp(); app_path.cdUp();
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols")); symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
@@ -132,13 +138,18 @@ void CrashHandlerDialog::GenerateReport()
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &CrashHandlerDialog::ReadProcessFinished); this, &CrashHandlerDialog::ReadProcessFinished);
connect(p, &QProcess::readyReadStandardOutput, this, &CrashHandlerDialog::ReadProcessHasData); connect(p, &QProcess::readyReadStandardOutput, this,
&CrashHandlerDialog::ReadProcessHasData);
QString stackwalk_filename = FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("minidump_stackwalk")); QString stackwalk_filename =
FileFunctions::GetFormattedExecutableForPlatform(
QStringLiteral("minidump_stackwalk"));
QString stackwalk_bin = QDir(qApp->applicationDirPath()).filePath(stackwalk_filename); QString stackwalk_bin =
QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
p->start(stackwalk_bin, { report_filename_, GetSymbolPath() }); p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin)); crash_report_->setText(
QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
} }
void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply) void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
@@ -153,7 +164,9 @@ void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply)
b.setIcon(QMessageBox::Critical); b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Upload Failed")); b.setWindowTitle(tr("Upload Failed"));
b.setText(tr("Failed to send error report (%1). Please try again later.").arg(QString::number(reply->error()))); b.setText(
tr("Failed to send error report (%1). Please try again later.")
.arg(QString::number(reply->error())));
b.addButton(QMessageBox::Ok); b.addButton(QMessageBox::Ok);
b.exec(); b.exec();
@@ -161,7 +174,8 @@ void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply)
} }
} }
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &se) void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply,
const QList<QSslError> &se)
{ {
QStringList errors; QStringList errors;
for (const QSslError &err : se) { for (const QSslError &err : se) {
@@ -172,7 +186,8 @@ void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply, const QList<QSslE
b.setIcon(QMessageBox::Critical); b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("SSL Error")); b.setWindowTitle(tr("SSL Error"));
b.setText(tr("Encountered the following SSL errors:\n\n%1").arg(errors.join('\n'))); b.setText(tr("Encountered the following SSL errors:\n\n%1")
.arg(errors.join('\n')));
b.addButton(QMessageBox::Ok); b.addButton(QMessageBox::Ok);
b.exec(); b.exec();
} }
@@ -190,7 +205,8 @@ void CrashHandlerDialog::AttemptToFindReport()
void CrashHandlerDialog::ReadProcessHasData() void CrashHandlerDialog::ReadProcessHasData()
{ {
report_data_.append(static_cast<QProcess*>(sender())->readAllStandardOutput()); report_data_.append(
static_cast<QProcess *>(sender())->readAllStandardOutput());
} }
void CrashHandlerDialog::ReadProcessFinished() void CrashHandlerDialog::ReadProcessFinished()
@@ -206,48 +222,62 @@ void CrashHandlerDialog::SendErrorReport()
QMessageBox b(this); QMessageBox b(this);
b.setIcon(QMessageBox::Question); b.setIcon(QMessageBox::Question);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setText(tr("You must write a description to submit this crash report.")); b.setText(
tr("You must write a description to submit this crash report."));
b.addButton(QMessageBox::Ok); b.addButton(QMessageBox::Ok);
b.exec(); b.exec();
return; return;
} }
QNetworkAccessManager *manager = new QNetworkAccessManager(); QNetworkAccessManager *manager = new QNetworkAccessManager();
connect(manager, &QNetworkAccessManager::finished, this, &CrashHandlerDialog::ReplyFinished); connect(manager, &QNetworkAccessManager::finished, this,
connect(manager, &QNetworkAccessManager::sslErrors, this, &CrashHandlerDialog::HandleSslErrors); &CrashHandlerDialog::ReplyFinished);
connect(manager, &QNetworkAccessManager::sslErrors, this,
&CrashHandlerDialog::HandleSslErrors);
QNetworkRequest request; QNetworkRequest request;
request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
request.setUrl(QStringLiteral("https://olivevideoeditor.org/crashpad/report.php")); request.setUrl(
QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
// Create HTTP form // Create HTTP form
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpMultiPart *multipart =
new QHttpMultiPart(QHttpMultiPart::FormDataType);
// Create description section // Create description section
QHttpPart desc_part; QHttpPart desc_part;
desc_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8")); desc_part.setHeader(QNetworkRequest::ContentTypeHeader,
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"description\"")); QStringLiteral("text/plain; charset=UTF-8"));
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"description\""));
desc_part.setBody(summary_edit_->toPlainText().toUtf8()); desc_part.setBody(summary_edit_->toPlainText().toUtf8());
multipart->append(desc_part); multipart->append(desc_part);
// Create report section // Create report section
QHttpPart report_part; QHttpPart report_part;
report_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8")); report_part.setHeader(QNetworkRequest::ContentTypeHeader,
report_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"report\"")); QStringLiteral("text/plain; charset=UTF-8"));
report_part.setHeader(QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"report\""));
report_part.setBody(report_data_); report_part.setBody(report_data_);
multipart->append(report_part); multipart->append(report_part);
// Create commit section // Create commit section
QHttpPart commit_part; QHttpPart commit_part;
commit_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8")); commit_part.setHeader(QNetworkRequest::ContentTypeHeader,
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"commit\"")); QStringLiteral("text/plain; charset=UTF-8"));
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"commit\""));
commit_part.setBody(kAppVersionLong.toUtf8()); commit_part.setBody(kAppVersionLong.toUtf8());
multipart->append(commit_part); multipart->append(commit_part);
// Create dump section // Create dump section
QHttpPart dump_part; QHttpPart dump_part;
dump_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream")); dump_part.setHeader(QNetworkRequest::ContentTypeHeader,
dump_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"") QStringLiteral("application/octet-stream"));
dump_part.setHeader(
QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
.arg(QFileInfo(report_filename_).fileName())); .arg(QFileInfo(report_filename_).fileName()));
QFile *dump_file = new QFile(report_filename_); QFile *dump_file = new QFile(report_filename_);
dump_file->open(QFile::ReadOnly); dump_file->open(QFile::ReadOnly);
@@ -268,7 +298,8 @@ void CrashHandlerDialog::SendErrorReport()
#endif #endif
symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name)); symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name));
QStringList folders_in_symbol_path = symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); QStringList folders_in_symbol_path =
symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
if (folders_in_symbol_path.size() > 0) { if (folders_in_symbol_path.size() > 0) {
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first())); symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
@@ -294,8 +325,11 @@ void CrashHandlerDialog::SendErrorReport()
#endif #endif
QString symbol_full_path = symbol_dir.filePath(symbol_filename); QString symbol_full_path = symbol_dir.filePath(symbol_filename);
QHttpPart sym_part; QHttpPart sym_part;
sym_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream")); sym_part.setHeader(QNetworkRequest::ContentTypeHeader,
sym_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"") QStringLiteral("application/octet-stream"));
sym_part.setHeader(
QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
.arg(symbol_filename)); .arg(symbol_filename));
QFile sym_file(symbol_full_path); QFile sym_file(symbol_full_path);
@@ -329,7 +363,8 @@ void CrashHandlerDialog::closeEvent(QCloseEvent* e)
b.setIcon(QMessageBox::Warning); b.setIcon(QMessageBox::Warning);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Confirm Close")); b.setWindowTitle(tr("Confirm Close"));
b.setText(tr("Crash report is still uploading. Closing now may result in no " b.setText(
tr("Crash report is still uploading. Closing now may result in no "
"report being sent. Are you sure you wish to close?")); "report being sent. Are you sure you wish to close?"));
b.addButton(QMessageBox::Ok); b.addButton(QMessageBox::Ok);
b.addButton(QMessageBox::Cancel); b.addButton(QMessageBox::Cancel);
+3 -4
View File
@@ -30,10 +30,10 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
class CrashHandlerDialog : public QDialog
{ {
class CrashHandlerDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
CrashHandlerDialog(const QString &report_path); CrashHandlerDialog(const QString &report_path);
@@ -74,7 +74,6 @@ private slots:
void ReadProcessFinished(); void ReadProcessFinished();
void SendErrorReport(); void SendErrorReport();
}; };
} }
+30 -17
View File
@@ -30,13 +30,15 @@
#include "patreon.h" #include "patreon.h"
#include "scrollinglabel.h" #include "scrollinglabel.h"
namespace olive { namespace olive
{
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) : AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent)
QDialog(parent) : QDialog(parent)
{ {
if (welcome_dialog) { if (welcome_dialog) {
setWindowTitle(tr("Welcome to %1").arg(QApplication::applicationName())); setWindowTitle(
tr("Welcome to %1").arg(QApplication::applicationName()));
} else { } else {
setWindowTitle(tr("About %1").arg(QApplication::applicationName())); setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
} }
@@ -44,25 +46,29 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
QFontMetrics fm = fontMetrics(); QFontMetrics fm = fontMetrics();
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height()); layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
fm.height());
QHBoxLayout *horiz_layout = new QHBoxLayout(); QHBoxLayout *horiz_layout = new QHBoxLayout();
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height()); horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
fm.height());
horiz_layout->setSpacing(fm.height() * 2); horiz_layout->setSpacing(fm.height() * 2);
QLabel* icon = new QLabel(QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>")); QLabel *icon = new QLabel(
QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
icon->setAlignment(Qt::AlignCenter); icon->setAlignment(Qt::AlignCenter);
horiz_layout->addWidget(icon); horiz_layout->addWidget(icon);
// Construct About text // Construct About text
QLabel* label = QLabel *label = new QLabel(
new QLabel(QStringLiteral("<html><head/><body>" QStringLiteral("<html><head/><body>"
"<p><b>%1</b> %2</p>" // AppName (version identifier) "<p><b>%1</b> %2</p>" // AppName (version identifier)
"<p><a href=\"https://www.olivevideoeditor.org/\">" "<p><a href=\"https://www.olivevideoeditor.org/\">"
"https://www.olivevideoeditor.org/" "https://www.olivevideoeditor.org/"
"</a></p>" "</a></p>"
"<p>%3</p>" // First statement "<p>%3</p>" // First statement
"</body></html>").arg(QApplication::applicationName(), "</body></html>")
.arg(QApplication::applicationName(),
QApplication::applicationVersion(), QApplication::applicationVersion(),
tr("Olive is a free open source non-linear video editor. " tr("Olive is a free open source non-linear video editor. "
"This software is licensed under the GNU GPL Version 3."))); "This software is licensed under the GNU GPL Version 3.")));
@@ -72,7 +78,8 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
label->setWordWrap(true); label->setWordWrap(true);
label->setOpenExternalLinks(true); label->setOpenExternalLinks(true);
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); label->setTextInteractionFlags(Qt::TextSelectableByMouse |
Qt::LinksAccessibleByMouse);
label->setCursor(Qt::IBeamCursor); label->setCursor(Qt::IBeamCursor);
horiz_layout->addWidget(label); horiz_layout->addWidget(label);
@@ -84,16 +91,20 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
QString opening_statement; QString opening_statement;
if (welcome_dialog || patrons.isEmpty()) { if (welcome_dialog || patrons.isEmpty()) {
opening_statement = tr("<b>Olive relies on support from the community to continue its development.</b>"); opening_statement = tr(
"<b>Olive relies on support from the community to continue its development.</b>");
} else { } else {
opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people."); opening_statement = tr(
"Olive wouldn't be possible without the support of gracious donations from the following people.");
} }
QLabel* support_lbl = new QLabel(tr("<html>%1 " QLabel *support_lbl = new QLabel(
tr("<html>%1 "
"If you like this project, please consider making a " "If you like this project, please consider making a "
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or " "<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to " "<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
"support its development.</html>").arg(opening_statement)); "support its development.</html>")
.arg(opening_statement));
support_lbl->setWordWrap(true); support_lbl->setWordWrap(true);
support_lbl->setAlignment(Qt::AlignCenter); support_lbl->setAlignment(Qt::AlignCenter);
support_lbl->setOpenExternalLinks(true); support_lbl->setOpenExternalLinks(true);
@@ -112,13 +123,15 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
btn_layout->setSpacing(0); btn_layout->setSpacing(0);
if (welcome_dialog) { if (welcome_dialog) {
dont_show_again_checkbox_ = new QCheckBox(tr("Don't show this message again")); dont_show_again_checkbox_ =
new QCheckBox(tr("Don't show this message again"));
btn_layout->addWidget(dont_show_again_checkbox_); btn_layout->addWidget(dont_show_again_checkbox_);
} else { } else {
dont_show_again_checkbox_ = nullptr; dont_show_again_checkbox_ = nullptr;
} }
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok, this);
if (!welcome_dialog) { if (!welcome_dialog) {
buttons->setCenterButtons(true); buttons->setCenterButtons(true);
} }
+3 -4
View File
@@ -26,7 +26,8 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
/** /**
* @brief The AboutDialog class * @brief The AboutDialog class
@@ -34,8 +35,7 @@ namespace olive {
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from * The About dialog (accessible through Help > About). Contains license and version information. This can be run from
* anywhere * anywhere
*/ */
class AboutDialog : public QDialog class AboutDialog : public QDialog {
{
Q_OBJECT Q_OBJECT
public: public:
/** /**
@@ -54,7 +54,6 @@ public slots:
private: private:
QCheckBox *dont_show_again_checkbox_; QCheckBox *dont_show_again_checkbox_;
}; };
} }
+11 -8
View File
@@ -24,20 +24,21 @@
#include "common/qtutils.h" #include "common/qtutils.h"
namespace olive { namespace olive
{
const int ScrollingLabel::kMinLineHeight = 10; const int ScrollingLabel::kMinLineHeight = 10;
ScrollingLabel::ScrollingLabel(QWidget *parent) : ScrollingLabel::ScrollingLabel(QWidget *parent)
QWidget(parent), : QWidget(parent)
animate_(0) , animate_(0)
{ {
timer_.setInterval(50); timer_.setInterval(50);
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate); connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate);
} }
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent) : ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent)
ScrollingLabel(parent) : ScrollingLabel(parent)
{ {
SetText(text); SetText(text);
} }
@@ -88,7 +89,8 @@ void ScrollingLabel::paintEvent(QPaintEvent *e)
double mul = double(y) / double(text_height_); double mul = double(y) / double(text_height_);
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul); SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
SetOpacityOfScanLine(map.scanLine(map.height()-1-y), map.width(), 4, mul); SetOpacityOfScanLine(map.scanLine(map.height() - 1 - y),
map.width(), 4, mul);
} }
} }
@@ -96,7 +98,8 @@ void ScrollingLabel::paintEvent(QPaintEvent *e)
wp.drawImage(0, 0, map); wp.drawImage(0, 0, map);
} }
void ScrollingLabel::SetOpacityOfScanLine(uchar *scan_line, int width, int channels, double mul) void ScrollingLabel::SetOpacityOfScanLine(uchar *scan_line, int width,
int channels, double mul)
{ {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
uchar *pixel = &scan_line[x * 4]; uchar *pixel = &scan_line[x * 4];
+5 -5
View File
@@ -24,10 +24,10 @@
#include <QTimer> #include <QTimer>
#include <QWidget> #include <QWidget>
namespace olive { namespace olive
class ScrollingLabel : public QWidget
{ {
class ScrollingLabel : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
ScrollingLabel(QWidget *parent = nullptr); ScrollingLabel(QWidget *parent = nullptr);
@@ -49,7 +49,8 @@ protected:
virtual void paintEvent(QPaintEvent *e) override; virtual void paintEvent(QPaintEvent *e) override;
private: private:
static void SetOpacityOfScanLine(uchar* scan_line, int width, int channels, double mul); static void SetOpacityOfScanLine(uchar *scan_line, int width, int channels,
double mul);
static const int kMinLineHeight; static const int kMinLineHeight;
@@ -63,7 +64,6 @@ private:
private slots: private slots:
void AnimationUpdate(); void AnimationUpdate();
}; };
} }
+40 -36
View File
@@ -25,11 +25,12 @@
#include <QMenuBar> #include <QMenuBar>
#include <QVBoxLayout> #include <QVBoxLayout>
namespace olive { namespace olive
{
ActionSearch::ActionSearch(QWidget *parent) : ActionSearch::ActionSearch(QWidget *parent)
QDialog(parent), : QDialog(parent)
menu_bar_(nullptr) , menu_bar_(nullptr)
{ {
// ActionSearch requires a parent widget // ActionSearch requires a parent widget
Q_ASSERT(parent != nullptr); Q_ASSERT(parent != nullptr);
@@ -58,13 +59,16 @@ ActionSearch::ActionSearch(QWidget *parent) :
entry_field->setPlaceholderText(tr("Search for action...")); entry_field->setPlaceholderText(tr("Search for action..."));
// Connect signals/slots // Connect signals/slots
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &))); connect(entry_field, SIGNAL(textChanged(const QString &)), this,
SLOT(search_update(const QString &)));
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action())); connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field. // moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
// We override it here to select the upper or lower item in the list. // We override it here to select the upper or lower item in the list.
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up())); connect(entry_field, SIGNAL(moveSelectionUp()), this,
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down())); SLOT(move_selection_up()));
connect(entry_field, SIGNAL(moveSelectionDown()), this,
SLOT(move_selection_down()));
layout->addWidget(entry_field); layout->addWidget(entry_field);
// Construct list of actions // Construct list of actions
@@ -89,7 +93,8 @@ void ActionSearch::SetMenuBar(QMenuBar *menu_bar)
menu_bar_ = menu_bar; menu_bar_ = menu_bar;
} }
void ActionSearch::search_update(const QString &s, const QString &p, QMenu *parent) void ActionSearch::search_update(const QString &s, const QString &p,
QMenu *parent)
{ {
// Do nothing if there's no menu bar to work with // Do nothing if there's no menu bar to work with
if (menu_bar_ == nullptr) { if (menu_bar_ == nullptr) {
@@ -102,7 +107,6 @@ void ActionSearch::search_update(const QString &s, const QString &p, QMenu *pare
// submenus). // submenus).
if (parent == nullptr) { if (parent == nullptr) {
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus // If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
// (and their submenus). // (and their submenus).
@@ -125,31 +129,28 @@ void ActionSearch::search_update(const QString &s, const QString &p, QMenu *pare
} }
} else { } else {
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`. // Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by // The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
// adding the current menu's text to the existing hierarchy (passed in `p`). // adding the current menu's text to the existing hierarchy (passed in `p`).
QString menu_text; QString menu_text;
if (!p.isEmpty()) menu_text += p + " > "; if (!p.isEmpty())
menu_text += parent->title().replace("&", ""); // Strip out any &s used in menu action names menu_text += p + " > ";
menu_text += parent->title().replace(
"&", ""); // Strip out any &s used in menu action names
// Loop over the menu's actions // Loop over the menu's actions
QList<QAction *> actions = parent->actions(); QList<QAction *> actions = parent->actions();
for (int i = 0; i < actions.size(); i++) { for (int i = 0; i < actions.size(); i++) {
QAction *a = actions.at(i); QAction *a = actions.at(i);
// Ignore separator actions // Ignore separator actions
if (!a->isSeparator()) { if (!a->isSeparator()) {
if (a->menu() != nullptr) { if (a->menu() != nullptr) {
// If the action is a menu, run this function recursively on it // If the action is a menu, run this function recursively on it
search_update(s, menu_text, a->menu()); search_update(s, menu_text, a->menu());
} else { } else {
// This is a valid non-separator non-menu action, so check it against the currently entered string. // This is a valid non-separator non-menu action, so check it against the currently entered string.
// Strip out all &s from the action's name // Strip out all &s from the action's name
@@ -157,45 +158,43 @@ void ActionSearch::search_update(const QString &s, const QString &p, QMenu *pare
// See if the action's name contains any of the currently entered string // See if the action's name contains any of the currently entered string
if (comp.contains(s, Qt::CaseInsensitive)) { if (comp.contains(s, Qt::CaseInsensitive)) {
// If so, we add it to the list widget. // If so, we add it to the list widget.
QListWidgetItem* item = new QListWidgetItem(QStringLiteral("%1\n(%2)").arg(comp, menu_text), list_widget); QListWidgetItem *item = new QListWidgetItem(
QStringLiteral("%1\n(%2)").arg(comp, menu_text),
list_widget);
// Add a pointer to the original QAction in the item's data // Add a pointer to the original QAction in the item's data
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a)); item->setData(Qt::UserRole + 1,
reinterpret_cast<quintptr>(a));
list_widget->addItem(item); list_widget->addItem(item);
} }
} }
} }
} }
} }
} }
void ActionSearch::perform_action() { void ActionSearch::perform_action()
{
// Loop over all the items in the list and if we find one that's selected, we trigger it. // Loop over all the items in the list and if we find one that's selected, we trigger it.
QList<QListWidgetItem *> selected_items = list_widget->selectedItems(); QList<QListWidgetItem *> selected_items = list_widget->selectedItems();
if (list_widget->count() > 0 && selected_items.size() > 0) { if (list_widget->count() > 0 && selected_items.size() > 0) {
QListWidgetItem *item = selected_items.at(0); QListWidgetItem *item = selected_items.at(0);
// Get QAction pointer from item's data // Get QAction pointer from item's data
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>()); QAction *a = reinterpret_cast<QAction *>(
item->data(Qt::UserRole + 1).value<quintptr>());
a->trigger(); a->trigger();
} }
// Close this popup // Close this popup
accept(); accept();
} }
void ActionSearch::move_selection_up() { void ActionSearch::move_selection_up()
{
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start // Here we loop over all the items to find the currently selected one, and then select the one above it. We start
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very // iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
// bottom item). // bottom item).
@@ -210,8 +209,8 @@ void ActionSearch::move_selection_up() {
} }
} }
void ActionSearch::move_selection_down() { void ActionSearch::move_selection_down()
{
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it // Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very // one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
// bottom item). // bottom item).
@@ -226,7 +225,10 @@ void ActionSearch::move_selection_down() {
} }
} }
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {} ActionSearchEntry::ActionSearchEntry(QWidget *parent)
: QLineEdit(parent)
{
}
bool ActionSearchEntry::event(QEvent *e) bool ActionSearchEntry::event(QEvent *e)
{ {
@@ -259,13 +261,15 @@ bool ActionSearchEntry::event(QEvent *e)
return QLineEdit::event(e); return QLineEdit::event(e);
} }
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {} ActionSearchList::ActionSearchList(QWidget *parent)
: QListWidget(parent)
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) { {
}
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *)
{
// Indiscriminately emit a signal on any double click // Indiscriminately emit a signal on any double click
emit dbl_click(); emit dbl_click();
} }
} }
+8 -4
View File
@@ -29,7 +29,8 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
class ActionSearchList; class ActionSearchList;
@@ -39,8 +40,7 @@ class ActionSearchList;
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing * A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid. * rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
*/ */
class ActionSearch : public QDialog class ActionSearch : public QDialog {
{
Q_OBJECT Q_OBJECT
public: public:
/** /**
@@ -82,7 +82,8 @@ private slots:
* The current menu to loop over. In most cases, this should be left as nullptr when called externally. * The current menu to loop over. In most cases, this should be left as nullptr when called externally.
* search_update() will fill this automatically as it needs while calling itself recursively. * search_update() will fill this automatically as it needs while calling itself recursively.
*/ */
void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr); void search_update(const QString &s, const QString &p = nullptr,
QMenu *parent = nullptr);
/** /**
* @brief Perform the currently selected action * @brief Perform the currently selected action
@@ -108,6 +109,7 @@ private slots:
* selection is already at the bottom of the list, this is a no-op. * selection is already at the bottom of the list, this is a no-op.
*/ */
void move_selection_down(); void move_selection_down();
private: private:
/** /**
* @brief Main widget that shows the list of commands * @brief Main widget that shows the list of commands
@@ -136,6 +138,7 @@ public:
* Usually ActionSearch. * Usually ActionSearch.
*/ */
ActionSearchList(QWidget *parent); ActionSearchList(QWidget *parent);
protected: protected:
/** /**
* @brief Override of QListWidget's double click event that emits a signal. * @brief Override of QListWidget's double click event that emits a signal.
@@ -164,6 +167,7 @@ public:
* Usually ActionSearch. * Usually ActionSearch.
*/ */
ActionSearchEntry(QWidget *parent); ActionSearchEntry(QWidget *parent);
protected: protected:
/** /**
* @brief Override of QLineEdit's key press event that listens for up/down key presses. * @brief Override of QLineEdit's key press event that listens for up/down key presses.
+34 -15
View File
@@ -29,12 +29,15 @@
#include "core.h" #include "core.h"
namespace olive { namespace olive
{
#define super QDialog #define super QDialog
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message, const QStringList &recoveries, bool autocheck_latest, QWidget* parent) : AutoRecoveryDialog::AutoRecoveryDialog(const QString &message,
QDialog(parent) const QStringList &recoveries,
bool autocheck_latest, QWidget *parent)
: QDialog(parent)
{ {
Init(message); Init(message);
@@ -65,14 +68,18 @@ void AutoRecoveryDialog::Init(const QString& header_text)
tree_widget_->setHeaderHidden(true); tree_widget_->setHeaderHidden(true);
layout->addWidget(tree_widget_); layout->addWidget(tree_widget_);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load")); buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
connect(buttons, &QDialogButtonBox::accepted, this, &AutoRecoveryDialog::accept); connect(buttons, &QDialogButtonBox::accepted, this,
connect(buttons, &QDialogButtonBox::rejected, this, &AutoRecoveryDialog::reject); &AutoRecoveryDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this,
&AutoRecoveryDialog::reject);
layout->addWidget(buttons); layout->addWidget(buttons);
} }
void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autocheck_latest) void AutoRecoveryDialog::PopulateTree(const QStringList &recoveries,
bool autocheck_latest)
{ {
// Each entry in `recoveries` is a directory with 1+ recovery projects in it // Each entry in `recoveries` is a directory with 1+ recovery projects in it
QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot()); QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot());
@@ -83,7 +90,8 @@ void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autoch
QString pretty_name; QString pretty_name;
{ {
// Retrieve pretty name // Retrieve pretty name
QFile pretty_name_file(recovery_dir.filePath(QStringLiteral("realname.txt"))); QFile pretty_name_file(
recovery_dir.filePath(QStringLiteral("realname.txt")));
if (pretty_name_file.open(QFile::ReadOnly)) { if (pretty_name_file.open(QFile::ReadOnly)) {
// Read pretty name that we should have written in the autorecovery process // Read pretty name that we should have written in the autorecovery process
pretty_name = QString::fromUtf8(pretty_name_file.readAll()); pretty_name = QString::fromUtf8(pretty_name_file.readAll());
@@ -101,30 +109,41 @@ void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autoch
{ {
// Populate with recoveries // Populate with recoveries
QStringList entries = recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); QStringList entries =
recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot,
QDir::Name | QDir::Reversed);
for (int i = 0; i < entries.size(); i++) { for (int i = 0; i < entries.size(); i++) {
const QString &entry = entries.at(i); const QString &entry = entries.at(i);
if (entry.endsWith(QStringLiteral(".ove"), Qt::CaseInsensitive)) { if (entry.endsWith(QStringLiteral(".ove"),
QTreeWidgetItem* entry_item = new QTreeWidgetItem(top_level); Qt::CaseInsensitive)) {
QTreeWidgetItem *entry_item =
new QTreeWidgetItem(top_level);
bool ok; bool ok;
qint64 recovery_time = entry.left(entry.indexOf('.')).toLongLong(&ok); qint64 recovery_time =
entry.left(entry.indexOf('.')).toLongLong(&ok);
QString entry_name; QString entry_name;
if (ok) { if (ok) {
// Set as time/date of recovery // Set as time/date of recovery
entry_name = QDateTime::fromSecsSinceEpoch(recovery_time).toString(); entry_name =
QDateTime::fromSecsSinceEpoch(recovery_time)
.toString();
} else { } else {
// Fallback if we couldn't discern a date from this // Fallback if we couldn't discern a date from this
entry_name = entry; entry_name = entry;
} }
entry_item->setText(0, entry_name); entry_item->setText(0, entry_name);
entry_item->setData(0, kFilenameRole, recovery_dir.filePath(entry)); entry_item->setData(0, kFilenameRole,
recovery_dir.filePath(entry));
// Allow to be checked, auto-checking the first entry // Allow to be checked, auto-checking the first entry
entry_item->setCheckState(0, (autocheck_latest && top_level->childCount() == 1) ? Qt::Checked : Qt::Unchecked); entry_item->setCheckState(
0, (autocheck_latest && top_level->childCount() == 1) ?
Qt::Checked :
Qt::Unchecked);
checkable_items_.append(entry_item); checkable_items_.append(entry_item);
} }
+6 -8
View File
@@ -26,13 +26,14 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
class AutoRecoveryDialog : public QDialog
{ {
class AutoRecoveryDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
AutoRecoveryDialog(const QString& message, const QStringList& recoveries, bool autocheck_latest, QWidget* parent); AutoRecoveryDialog(const QString &message, const QStringList &recoveries,
bool autocheck_latest, QWidget *parent);
public slots: public slots:
virtual void accept() override; virtual void accept() override;
@@ -46,10 +47,7 @@ private:
QVector<QTreeWidgetItem *> checkable_items_; QVector<QTreeWidgetItem *> checkable_items_;
enum DataRole { enum DataRole { kFilenameRole = Qt::UserRole };
kFilenameRole = Qt::UserRole
};
}; };
} }
+52 -33
View File
@@ -26,11 +26,13 @@
#include "common/qtutils.h" #include "common/qtutils.h"
namespace olive { namespace olive
{
ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start, QWidget *parent) : ColorDialog::ColorDialog(ColorManager *color_manager, const ManagedColor &start,
QDialog(parent), QWidget *parent)
color_manager_(color_manager) : QDialog(parent)
, color_manager_(color_manager)
{ {
setWindowTitle(tr("Select Color")); setWindowTitle(tr("Select Color"));
@@ -52,7 +54,8 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
wheel_layout->addWidget(color_wheel_); wheel_layout->addWidget(color_wheel_);
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical); hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
hsv_value_gradient_->setFixedWidth(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH"))); hsv_value_gradient_->setFixedWidth(
QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
wheel_layout->addWidget(hsv_value_gradient_); wheel_layout->addWidget(hsv_value_gradient_);
QHBoxLayout *swatch_layout = new QHBoxLayout(); QHBoxLayout *swatch_layout = new QHBoxLayout();
@@ -82,29 +85,44 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
// Split window 50/50 // Split window 50/50
splitter->setSizes({ INT_MAX, INT_MAX }); splitter->setSizes({ INT_MAX, INT_MAX });
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor); connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor); color_values_widget_, &ColorValuesWidget::SetColor);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor); connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor); hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor); connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_,
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor); &ColorSwatchChooser::SetCurrentColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor); connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor); color_values_widget_, &ColorValuesWidget::SetColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor); connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor); color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_, &ColorWheelWidget::SetSelectedColor); connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_, &ColorValuesWidget::SetColor); swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_,
&ColorSwatchChooser::SetCurrentColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_,
&ColorGradientWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_,
&ColorWheelWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_,
&ColorValuesWidget::SetColor);
connect(color_wheel_, &ColorWheelWidget::DiameterChanged, hsv_value_gradient_, &ColorGradientWidget::setFixedHeight); connect(color_wheel_, &ColorWheelWidget::DiameterChanged,
hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
layout->addWidget(buttons); layout->addWidget(buttons);
SetColor(start); SetColor(start);
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this, &ColorDialog::ColorSpaceChanged); connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this,
&ColorDialog::ColorSpaceChanged);
ColorSpaceChanged(chooser_->input(), chooser_->output()); ColorSpaceChanged(chooser_->input(), chooser_->output());
// Set default size ratio to 2:1 // Set default size ratio to 2:1
@@ -119,18 +137,15 @@ void ColorDialog::SetColor(const ManagedColor &start)
Color managed_start; Color managed_start;
if (start.color_input().isEmpty()) { if (start.color_input().isEmpty()) {
managed_start = start; managed_start = start;
} else { } else {
// Convert reference color to the input space // Convert reference color to the input space
ColorProcessorPtr linear_to_input = ColorProcessor::Create(color_manager_, ColorProcessorPtr linear_to_input = ColorProcessor::Create(
color_manager_->GetReferenceColorSpace(), color_manager_, color_manager_->GetReferenceColorSpace(),
start.color_input()); start.color_input());
managed_start = linear_to_input->ConvertColor(start); managed_start = linear_to_input->ConvertColor(start);
} }
color_wheel_->SetSelectedColor(managed_start); color_wheel_->SetSelectedColor(managed_start);
@@ -164,15 +179,17 @@ ColorTransform ColorDialog::GetColorSpaceOutput() const
return chooser_->output(); return chooser_->output();
} }
void ColorDialog::ColorSpaceChanged(const QString &input, const ColorTransform &output) void ColorDialog::ColorSpaceChanged(const QString &input,
const ColorTransform &output)
{ {
input_to_ref_processor_ = ColorProcessor::Create(color_manager_, input, color_manager_->GetReferenceColorSpace()); input_to_ref_processor_ = ColorProcessor::Create(
color_manager_, input, color_manager_->GetReferenceColorSpace());
ColorProcessorPtr ref_to_display = ColorProcessor::Create(color_manager_, ColorProcessorPtr ref_to_display = ColorProcessor::Create(
color_manager_->GetReferenceColorSpace(), color_manager_, color_manager_->GetReferenceColorSpace(), output);
output);
ColorProcessorPtr ref_to_input = ColorProcessor::Create(color_manager_, color_manager_->GetReferenceColorSpace(), input); ColorProcessorPtr ref_to_input = ColorProcessor::Create(
color_manager_, color_manager_->GetReferenceColorSpace(), input);
// FIXME: For some reason, using OCIO::TRANSFORM_DIR_INVERSE (wrapped by ColorProcessor::kInverse) causes OCIO to // FIXME: For some reason, using OCIO::TRANSFORM_DIR_INVERSE (wrapped by ColorProcessor::kInverse) causes OCIO to
// crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget) // crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget)
@@ -185,8 +202,10 @@ void ColorDialog::ColorSpaceChanged(const QString &input, const ColorTransform &
ColorProcessor::kInverse);*/ ColorProcessor::kInverse);*/
color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display); color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_, ref_to_display); hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_,
color_values_widget_->SetColorProcessor(input_to_ref_processor_, ref_to_display, nullptr, ref_to_input); ref_to_display);
color_values_widget_->SetColorProcessor(
input_to_ref_processor_, ref_to_display, nullptr, ref_to_input);
} }
} }
+6 -5
View File
@@ -31,10 +31,10 @@
#include "widget/colorwheel/colorvalueswidget.h" #include "widget/colorwheel/colorvalueswidget.h"
#include "widget/colorwheel/colorwheelwidget.h" #include "widget/colorwheel/colorwheelwidget.h"
namespace olive { namespace olive
class ColorDialog : public QDialog
{ {
class ColorDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
/** /**
@@ -56,7 +56,9 @@ public:
* *
* QWidget parent. * QWidget parent.
*/ */
ColorDialog(ColorManager* color_manager, const ManagedColor &start = Color(1.0f, 1.0f, 1.0f), QWidget* parent = nullptr); ColorDialog(ColorManager *color_manager,
const ManagedColor &start = Color(1.0f, 1.0f, 1.0f),
QWidget *parent = nullptr);
/** /**
* @brief Retrieves the color selected by the user * @brief Retrieves the color selected by the user
@@ -89,7 +91,6 @@ private:
private slots: private slots:
void ColorSpaceChanged(const QString &input, const ColorTransform &output); void ColorSpaceChanged(const QString &input, const ColorTransform &output);
}; };
} }
+12 -10
View File
@@ -26,10 +26,11 @@
#include "core.h" #include "core.h"
namespace olive { namespace olive
{
ConfigDialogBase::ConfigDialogBase(QWidget* parent) : ConfigDialogBase::ConfigDialogBase(QWidget *parent)
QDialog(parent) : QDialog(parent)
{ {
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
@@ -46,17 +47,18 @@ ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
QDialogButtonBox *button_box = new QDialogButtonBox(this); QDialogButtonBox *button_box = new QDialogButtonBox(this);
button_box->setOrientation(Qt::Horizontal); button_box->setOrientation(Qt::Horizontal);
button_box->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); button_box->setStandardButtons(QDialogButtonBox::Cancel |
QDialogButtonBox::Ok);
layout->addWidget(button_box); layout->addWidget(button_box);
connect(button_box, &QDialogButtonBox::accepted, this, &ConfigDialogBase::accept); connect(button_box, &QDialogButtonBox::accepted, this,
connect(button_box, &QDialogButtonBox::rejected, this, &ConfigDialogBase::reject); &ConfigDialogBase::accept);
connect(button_box, &QDialogButtonBox::rejected, this,
&ConfigDialogBase::reject);
connect(list_widget_, connect(list_widget_, &QListWidget::currentRowChanged,
&QListWidget::currentRowChanged, preference_pane_stack_, &QStackedWidget::setCurrentIndex);
preference_pane_stack_,
&QStackedWidget::setCurrentIndex);
} }
void ConfigDialogBase::accept() void ConfigDialogBase::accept()
+6 -5
View File
@@ -27,10 +27,10 @@
#include "configdialogbasetab.h" #include "configdialogbasetab.h"
namespace olive { namespace olive
class ConfigDialogBase : public QDialog
{ {
class ConfigDialogBase : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
ConfigDialogBase(QWidget *parent = nullptr); ConfigDialogBase(QWidget *parent = nullptr);
@@ -44,7 +44,9 @@ private slots:
protected: protected:
void AddTab(ConfigDialogBaseTab *tab, const QString &title); void AddTab(ConfigDialogBaseTab *tab, const QString &title);
virtual void AcceptEvent(){} virtual void AcceptEvent()
{
}
private: private:
QListWidget *list_widget_; QListWidget *list_widget_;
@@ -52,7 +54,6 @@ private:
QStackedWidget *preference_pane_stack_; QStackedWidget *preference_pane_stack_;
QList<ConfigDialogBaseTab *> tabs_; QList<ConfigDialogBaseTab *> tabs_;
}; };
} }
@@ -20,7 +20,8 @@
#include "configdialogbasetab.h" #include "configdialogbasetab.h"
namespace olive { namespace olive
{
bool ConfigDialogBaseTab::Validate() bool ConfigDialogBaseTab::Validate()
{ {
+3 -3
View File
@@ -26,10 +26,10 @@
#include "config/config.h" #include "config/config.h"
#include "undo/undocommand.h" #include "undo/undocommand.h"
namespace olive { namespace olive
class ConfigDialogBaseTab : public QWidget
{ {
class ConfigDialogBaseTab : public QWidget {
public: public:
ConfigDialogBaseTab() = default; ConfigDialogBaseTab() = default;
+36 -21
View File
@@ -28,17 +28,19 @@
#include "config/config.h" #include "config/config.h"
#include "core.h" #include "core.h"
namespace olive { namespace olive
{
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget* parent) : DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
QDialog(parent), : QDialog(parent)
folder_(folder) , folder_(folder)
{ {
QGridLayout *layout = new QGridLayout(this); QGridLayout *layout = new QGridLayout(this);
int row = 0; int row = 0;
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())), row, 0, 1, 2); layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())),
row, 0, 1, 2);
setWindowTitle(tr("Disk Cache Settings")); setWindowTitle(tr("Disk Cache Settings"));
row++; row++;
@@ -48,32 +50,40 @@ DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget* parent) :
maximum_cache_slider_ = new FloatSlider(); maximum_cache_slider_ = new FloatSlider();
maximum_cache_slider_->SetFormat(tr("%1 GB")); maximum_cache_slider_->SetFormat(tr("%1 GB"));
maximum_cache_slider_->SetMinimum(1.0); maximum_cache_slider_->SetMinimum(1.0);
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) / static_cast<double>(kBytesInGigabyte)); maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) /
static_cast<double>(kBytesInGigabyte));
layout->addWidget(maximum_cache_slider_, row, 1); layout->addWidget(maximum_cache_slider_, row, 1);
row++; row++;
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache")); clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
connect(clear_cache_btn_, &QPushButton::clicked, this, static_cast<void(DiskCacheDialog::*)()>(&DiskCacheDialog::ClearDiskCache)); connect(clear_cache_btn_, &QPushButton::clicked, this,
static_cast<void (DiskCacheDialog::*)()>(
&DiskCacheDialog::ClearDiskCache));
layout->addWidget(clear_cache_btn_, row, 1); layout->addWidget(clear_cache_btn_, row, 1);
row++; row++;
clear_disk_cache_ = new QCheckBox(tr("Automatically clear disk cache on close")); clear_disk_cache_ =
new QCheckBox(tr("Automatically clear disk cache on close"));
clear_disk_cache_->setChecked(folder->GetClearOnClose()); clear_disk_cache_->setChecked(folder->GetClearOnClose());
layout->addWidget(clear_disk_cache_, row, 1); layout->addWidget(clear_disk_cache_, row, 1);
row++; row++;
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox *buttons =
connect(buttons, &QDialogButtonBox::accepted, this, &DiskCacheDialog::accept); new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::rejected, this, &DiskCacheDialog::reject); connect(buttons, &QDialogButtonBox::accepted, this,
&DiskCacheDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this,
&DiskCacheDialog::reject);
layout->addWidget(buttons, row, 0, 1, 2); layout->addWidget(buttons, row, 0, 1, 2);
} }
void DiskCacheDialog::accept() void DiskCacheDialog::accept()
{ {
qint64 new_disk_cache_limit = qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte); qint64 new_disk_cache_limit =
qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
if (new_disk_cache_limit != folder_->GetLimit()) { if (new_disk_cache_limit != folder_->GetLimit()) {
folder_->SetLimit(new_disk_cache_limit); folder_->SetLimit(new_disk_cache_limit);
} }
@@ -90,22 +100,27 @@ void DiskCacheDialog::ClearDiskCache()
ClearDiskCache(folder_->GetPath(), this, clear_cache_btn_); ClearDiskCache(folder_->GetPath(), this, clear_cache_btn_);
} }
void DiskCacheDialog::ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn) void DiskCacheDialog::ClearDiskCache(const QString &path, QWidget *parent,
QPushButton *clear_btn)
{ {
if (QMessageBox::question(parent, if (QMessageBox::question(
tr("Clear Disk Cache"), parent, tr("Clear Disk Cache"),
tr("Are you sure you want to clear the disk cache in '%1'?").arg(path), tr("Are you sure you want to clear the disk cache in '%1'?")
.arg(path),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (clear_btn) clear_btn->setEnabled(false); if (clear_btn)
clear_btn->setEnabled(false);
if (DiskManager::instance()->ClearDiskCache(path)) { if (DiskManager::instance()->ClearDiskCache(path)) {
if (clear_btn) clear_btn->setText(tr("Disk Cache Cleared")); if (clear_btn)
clear_btn->setText(tr("Disk Cache Cleared"));
} else { } else {
QMessageBox::information(parent, QMessageBox::information(
tr("Clear Disk Cache"), parent, tr("Clear Disk Cache"),
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."), tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
QMessageBox::Ok); QMessageBox::Ok);
if (clear_btn) clear_btn->setText(tr("Disk Cache Partially Cleared")); if (clear_btn)
clear_btn->setText(tr("Disk Cache Partially Cleared"));
} }
} }
} }
+5 -5
View File
@@ -28,15 +28,16 @@
#include "render/diskmanager.h" #include "render/diskmanager.h"
#include "widget/slider/floatslider.h" #include "widget/slider/floatslider.h"
namespace olive { namespace olive
class DiskCacheDialog : public QDialog
{ {
class DiskCacheDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr); DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
static void ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn = nullptr); static void ClearDiskCache(const QString &path, QWidget *parent,
QPushButton *clear_btn = nullptr);
public slots: public slots:
virtual void accept() override; virtual void accept() override;
@@ -52,7 +53,6 @@ private:
private slots: private slots:
void ClearDiskCache(); void ClearDiskCache();
}; };
} }
+27 -21
View File
@@ -28,15 +28,16 @@
#include "common/qtutils.h" #include "common/qtutils.h"
#include "widget/slider/integerslider.h" #include "widget/slider/integerslider.h"
namespace olive { namespace olive
{
AV1Section::AV1Section(QWidget *parent) : AV1Section::AV1Section(QWidget *parent)
AV1Section(AV1CRFSection::kDefaultAV1CRF, parent) : AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
{ {
} }
AV1Section::AV1Section(int default_crf, QWidget *parent) : AV1Section::AV1Section(int default_crf, QWidget *parent)
CodecSection(parent) : CodecSection(parent)
{ {
QGridLayout *layout = new QGridLayout(this); QGridLayout *layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@@ -45,7 +46,8 @@ AV1Section::AV1Section(int default_crf, QWidget *parent) :
layout->addWidget(new QLabel(tr("Preset:")), row, 0); layout->addWidget(new QLabel(tr("Preset:")), row, 0);
preset_combobox_ = new QComboBox(); preset_combobox_ = new QComboBox();
preset_combobox_->setToolTip(tr("This parameter governs the efficiency/encode-time trade-off.\n" preset_combobox_->setToolTip(tr(
"This parameter governs the efficiency/encode-time trade-off.\n"
"Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n" "Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n"
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value.")); "Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
@@ -61,7 +63,8 @@ AV1Section::AV1Section(int default_crf, QWidget *parent) :
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0); layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
QComboBox *compression_box = new QComboBox(); QComboBox *compression_box = new QComboBox();
compression_box->setToolTip(tr("This parameter governs the quality/size trade-off.\n" compression_box->setToolTip(tr(
"This parameter governs the quality/size trade-off.\n"
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n" "Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
"Lower CRF values retain more detail at the cost of larger file sizes.\n" "Lower CRF values retain more detail at the cost of larger file sizes.\n"
"The possible range of CRF in SVT-AV1 is 1-63.")); "The possible range of CRF in SVT-AV1 is 1-63."));
@@ -79,29 +82,29 @@ AV1Section::AV1Section(int default_crf, QWidget *parent) :
crf_section_ = new AV1CRFSection(default_crf); crf_section_ = new AV1CRFSection(default_crf);
compression_method_stack_->addWidget(crf_section_); compression_method_stack_->addWidget(crf_section_);
connect(compression_box, connect(
compression_box,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
compression_method_stack_, compression_method_stack_, &QStackedWidget::setCurrentIndex);
&QStackedWidget::setCurrentIndex);
} }
void AV1Section::AddOpts(EncodingParams *params) void AV1Section::AddOpts(EncodingParams *params)
{ {
CompressionMethod method = static_cast<CompressionMethod>(
CompressionMethod method = static_cast<CompressionMethod>(compression_method_stack_->currentIndex()); compression_method_stack_->currentIndex());
if (method == kConstantRateFactor) { if (method == kConstantRateFactor) {
// Set Quantizer value // Set Quantizer value
params->set_video_option(QStringLiteral("qp"), QString::number(crf_section_->GetValue())); params->set_video_option(QStringLiteral("qp"),
QString::number(crf_section_->GetValue()));
} }
params->set_video_option(QStringLiteral("preset"), QString::number(preset_combobox_->currentIndex())); params->set_video_option(QStringLiteral("preset"),
QString::number(preset_combobox_->currentIndex()));
} }
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) : AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
QWidget(parent) : QWidget(parent)
{ {
QHBoxLayout *layout = new QHBoxLayout(this); QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@@ -113,15 +116,18 @@ AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) :
layout->addWidget(crf_slider_); layout->addWidget(crf_slider_);
IntegerSlider *crf_input = new IntegerSlider(); IntegerSlider *crf_input = new IntegerSlider();
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH"))); crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(
crf_input->fontMetrics(), QStringLiteral("HHHH")));
crf_input->SetMinimum(kMinimumCRF); crf_input->SetMinimum(kMinimumCRF);
crf_input->SetMaximum(kMaximumCRF); crf_input->SetMaximum(kMaximumCRF);
crf_input->SetValue(default_crf); crf_input->SetValue(default_crf);
crf_input->SetDefaultValue(default_crf); crf_input->SetDefaultValue(default_crf);
layout->addWidget(crf_input); layout->addWidget(crf_input);
connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue); connect(crf_slider_, &QSlider::valueChanged, crf_input,
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_, &QSlider::setValue); &IntegerSlider::SetValue);
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_,
&QSlider::setValue);
} }
int AV1CRFSection::GetValue() const int AV1CRFSection::GetValue() const
+4 -6
View File
@@ -28,10 +28,10 @@
#include "codecsection.h" #include "codecsection.h"
#include "widget/slider/floatslider.h" #include "widget/slider/floatslider.h"
namespace olive { namespace olive
class AV1CRFSection : public QWidget
{ {
class AV1CRFSection : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
AV1CRFSection(int default_crf, QWidget *parent = nullptr); AV1CRFSection(int default_crf, QWidget *parent = nullptr);
@@ -45,11 +45,9 @@ private:
static const int kMaximumCRF = 63; static const int kMaximumCRF = 63;
QSlider *crf_slider_; QSlider *crf_slider_;
}; };
class AV1Section : public CodecSection class AV1Section : public CodecSection {
{
Q_OBJECT Q_OBJECT
public: public:
enum CompressionMethod { enum CompressionMethod {
+9 -5
View File
@@ -23,10 +23,11 @@
#include <QGridLayout> #include <QGridLayout>
#include <QLabel> #include <QLabel>
namespace olive { namespace olive
{
CineformSection::CineformSection(QWidget *parent) : CineformSection::CineformSection(QWidget *parent)
CodecSection(parent) : CodecSection(parent)
{ {
QGridLayout *layout = new QGridLayout(this); QGridLayout *layout = new QGridLayout(this);
@@ -79,12 +80,15 @@ CineformSection::CineformSection(QWidget *parent) :
void CineformSection::AddOpts(EncodingParams *params) void CineformSection::AddOpts(EncodingParams *params)
{ {
params->set_video_option(QStringLiteral("quality"), QString::number(quality_combobox_->currentIndex())); params->set_video_option(
QStringLiteral("quality"),
QString::number(quality_combobox_->currentIndex()));
} }
void CineformSection::SetOpts(const EncodingParams *p) void CineformSection::SetOpts(const EncodingParams *p)
{ {
quality_combobox_->setCurrentIndex(p->video_option(QStringLiteral("quality")).toInt()); quality_combobox_->setCurrentIndex(
p->video_option(QStringLiteral("quality")).toInt());
} }
} }
+3 -4
View File
@@ -25,10 +25,10 @@
#include "codecsection.h" #include "codecsection.h"
namespace olive { namespace olive
class CineformSection : public CodecSection
{ {
class CineformSection : public CodecSection {
Q_OBJECT Q_OBJECT
public: public:
CineformSection(QWidget *parent = nullptr); CineformSection(QWidget *parent = nullptr);
@@ -39,7 +39,6 @@ public:
private: private:
QComboBox *quality_combobox_; QComboBox *quality_combobox_;
}; };
} }
+4 -3
View File
@@ -20,10 +20,11 @@
#include "codecsection.h" #include "codecsection.h"
namespace olive { namespace olive
{
CodecSection::CodecSection(QWidget *parent) : CodecSection::CodecSection(QWidget *parent)
QWidget(parent) : QWidget(parent)
{ {
} }
+11 -6
View File
@@ -25,18 +25,23 @@
#include "codec/encoder.h" #include "codec/encoder.h"
namespace olive { namespace olive
class CodecSection : public QWidget
{ {
class CodecSection : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
CodecSection(QWidget *parent = nullptr); CodecSection(QWidget *parent = nullptr);
virtual void AddOpts(EncodingParams* params){Q_UNUSED(params)} virtual void AddOpts(EncodingParams *params)
{
virtual void SetOpts(const EncodingParams *p){Q_UNUSED(p)} Q_UNUSED(params)
}
virtual void SetOpts(const EncodingParams *p)
{
Q_UNUSED(p)
}
}; };
} }
+6 -3
View File
@@ -20,7 +20,8 @@
#include "codecstack.h" #include "codecstack.h"
namespace olive { namespace olive
{
#define super QStackedWidget #define super QStackedWidget
@@ -41,9 +42,11 @@ void CodecStack::OnChange(int index)
{ {
for (int i = 0; i < count(); i++) { for (int i = 0; i < count(); i++) {
if (i == index) { if (i == index) {
widget(i)->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); widget(i)->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
} else { } else {
widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); widget(i)->setSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Ignored);
} }
widget(i)->adjustSize(); widget(i)->adjustSize();
} }

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