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'
+61 -32
View File
@@ -28,9 +28,10 @@
#include "config/config.h" #include "config/config.h"
namespace olive { namespace olive
{
AudioManager* AudioManager::instance_ = nullptr; AudioManager *AudioManager::instance_ = nullptr;
void AudioManager::CreateInstance() void AudioManager::CreateInstance()
{ {
@@ -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()
+13 -10
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,18 +42,18 @@ namespace olive {
* Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to * Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to
* the rest of the system. * the rest of the system.
*/ */
class AudioManager : public QObject class AudioManager : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
static void CreateInstance(); static void CreateInstance();
static void DestroyInstance(); static void DestroyInstance();
static AudioManager* instance(); static AudioManager *instance();
void SetOutputNotifyInterval(int n); void SetOutputNotifyInterval(int n);
bool PushToOutput(const AudioParams &params, const QByteArray& samples, QString *error = nullptr); bool PushToOutput(const AudioParams &params, const QByteArray &samples,
QString *error = nullptr);
void ClearBufferedOutput(); void ClearBufferedOutput();
@@ -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();
@@ -97,7 +101,7 @@ private:
void CloseOutputStream(); void CloseOutputStream();
static AudioManager* instance_; static AudioManager *instance_;
PaDeviceIndex output_device_; PaDeviceIndex output_device_;
PaStream *output_stream_; PaStream *output_stream_;
@@ -108,7 +112,6 @@ private:
PaStream *input_stream_; PaStream *input_stream_;
FFmpegEncoder *input_encoder_; FFmpegEncoder *input_encoder_;
}; };
} }
+55 -36
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();
@@ -95,17 +98,17 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
// Set speed_log to the remainder // Set speed_log to the remainder
speed_log -= whole; speed_log -= whole;
for (int i=0;i<=whole;i++) { for (int i = 0; i <= whole; i++) {
double filter_tempo = (i == whole) ? std::pow(base, speed_log) : base; double filter_tempo = (i == whole) ? std::pow(base, speed_log) :
/* base;
/*
if (qFuzzyCompare(filter_tempo, 1.0)) { if (qFuzzyCompare(filter_tempo, 1.0)) {
// This filter would do nothing // This filter would do nothing
continue; continue;
}*/ }*/
previous_filter = CreateTempoFilter(filter_graph_, previous_filter =
previous_filter, CreateTempoFilter(filter_graph_, previous_filter, filter_tempo);
filter_tempo);
if (!previous_filter) { if (!previous_filter) {
qCritical() << "Failed to create audio tempo filter"; qCritical() << "Failed to create audio tempo filter";
@@ -116,21 +119,26 @@ 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";
@@ -226,12 +237,13 @@ int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffe
if (in && nb_in_samples) { if (in && nb_in_samples) {
// Set frame parameters // Set frame parameters
in_frame_->nb_samples = nb_in_samples; in_frame_->nb_samples = nb_in_samples;
for (int i=0; i<from_.channel_count(); i++) { for (int i = 0; i < from_.channel_count(); i++) {
in_frame_->data[i] = reinterpret_cast<uint8_t*>(in[i]); in_frame_->data[i] = reinterpret_cast<uint8_t *>(in[i]);
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples); in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
} }
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_, AV_BUFFERSRC_FLAG_KEEP_REF); r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_,
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,22 +296,27 @@ 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];
snprintf(speed_param, 20, "%f", tempo); snprintf(speed_param, 20, "%f", tempo);
AVFilterContext* tempo_ctx = nullptr; AVFilterContext *tempo_ctx = nullptr;
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"), "atempo", speed_param, nullptr, graph) >= 0 if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"),
&& avfilter_link(link, 0, tempo_ctx, 0) == 0) { "atempo", speed_param, nullptr,
graph) >= 0 &&
avfilter_link(link, 0, tempo_ctx, 0) == 0) {
return tempo_ctx; return tempo_ctx;
} }
+23 -12
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,28 +44,40 @@ 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_;
AVFilterContext* buffersrc_ctx_; AVFilterContext *buffersrc_ctx_;
AVFilterContext* buffersink_ctx_; AVFilterContext *buffersink_ctx_;
AudioParams from_; AudioParams from_;
AVSampleFormat from_fmt_; AVSampleFormat from_fmt_;
@@ -76,7 +88,6 @@ private:
AVFrame *in_frame_; AVFrame *in_frame_;
AVFrame *out_frame_; AVFrame *out_frame_;
}; };
} }
+138 -82
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) {
@@ -50,24 +57,28 @@ void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples
double chunk_size = double(sample_rate) / double(target_rate); double chunk_size = double(sample_rate) / double(target_rate);
for (size_t i=0; i<samples_length; i+=channels_) { for (size_t i = 0; i < samples_length; i += channels_) {
size_t src_start = qRound((double(i) * chunk_size)) / channels_; size_t src_start = qRound((double(i) * chunk_size)) / channels_;
size_t src_end = qMin(size_t(qRound64((double(i + channels_) * chunk_size))) / channels_, samples.sample_count()); size_t src_end = qMin(
size_t(qRound64((double(i + channels_) * chunk_size))) / channels_,
samples.sample_count());
Sample summary = SumSamples(samples, Sample summary = SumSamples(samples, src_start, src_end - src_start);
src_start,
src_end - src_start);
memcpy(&data.data()[i + start_index], memcpy(&data.data()[i + start_index], summary.data(),
summary.data(),
summary.size() * sizeof(SamplePerChannel)); summary.size() * sizeof(SamplePerChannel));
} }
} }
void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::Sample &input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational &start, double output_rate, AudioVisualWaveform::Sample &output_data) void AudioVisualWaveform::OverwriteSamplesFromMipmap(
const AudioVisualWaveform::Sample &input, double input_sample_rate,
size_t &input_start, size_t &input_length, const rational &start,
double output_rate, AudioVisualWaveform::Sample &output_data)
{ {
size_t start_index = time_to_samples(start, output_rate); size_t start_index = time_to_samples(start, output_rate);
size_t samples_length = time_to_samples(static_cast<double>(input_length / channels_) / input_sample_rate, output_rate); size_t samples_length = time_to_samples(
static_cast<double>(input_length / channels_) / input_sample_rate,
output_rate);
size_t end_index = start_index + samples_length; size_t end_index = start_index + samples_length;
if (output_data.size() < end_index) { if (output_data.size() < end_index) {
@@ -77,11 +88,12 @@ void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::
// We guarantee mipmaps are powers of two so integer division should be perfectly accurate here // We guarantee mipmaps are powers of two so integer division should be perfectly accurate here
size_t chunk_size = input_sample_rate / output_rate; size_t chunk_size = input_sample_rate / output_rate;
for (size_t i=0; i<samples_length; i+=channels_) { for (size_t i = 0; i < samples_length; i += channels_) {
Sample summary = ReSumSamples(&input.data()[input_start + (i*chunk_size)], chunk_size * channels_, channels_); Sample summary =
ReSumSamples(&input.data()[input_start + (i * chunk_size)],
chunk_size * channels_, channels_);
memcpy(&output_data.data()[i + start_index], memcpy(&output_data.data()[i + start_index], summary.data(),
summary.data(),
summary.size() * sizeof(SamplePerChannel)); summary.size() * sizeof(SamplePerChannel));
} }
@@ -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,29 +138,34 @@ 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);
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first; rational rate = it->first;
Sample& our_arr = it->second; Sample &our_arr = it->second;
const Sample& their_arr = sums.mipmapped_data_.at(rate); const Sample &their_arr = sums.mipmapped_data_.at(rate);
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
// Get our destination sample // Get our destination sample
size_t our_start_index = time_to_samples(dest - virtual_start_, rate_dbl); size_t our_start_index =
time_to_samples(dest - virtual_start_, rate_dbl);
// Get our source sample // Get our source sample
size_t their_start_index = time_to_samples(offset, rate_dbl); size_t their_start_index = time_to_samples(offset, rate_dbl);
@@ -166,27 +188,32 @@ 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);
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first; rational rate = it->first;
Sample& our_arr = it->second; Sample &our_arr = it->second;
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
// Get our destination sample // Get our destination sample
size_t our_start_index = time_to_samples(start - virtual_start_, rate_dbl); size_t our_start_index =
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);
@@ -213,10 +242,10 @@ void AudioVisualWaveform::TrimIn(rational length)
length = -length; length = -length;
} }
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first; rational rate = it->first;
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
Sample& data = it->second; Sample &data = it->second;
size_t chop_length = time_to_samples(length, rate_dbl); size_t chop_length = time_to_samples(length, rate_dbl);
if (chop_length == 0) { if (chop_length == 0) {
@@ -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;
@@ -257,10 +287,10 @@ void AudioVisualWaveform::Resize(const rational &length)
return; return;
} }
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first; rational rate = it->first;
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
Sample& data = it->second; Sample &data = it->second;
size_t chop_length = time_to_samples(length, rate_dbl); size_t chop_length = time_to_samples(length, rate_dbl);
@@ -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
@@ -312,7 +346,7 @@ void ExpandMinMaxChannel(const float *a, size_t length, float &min_val, float &m
// loop over 'a' and compare current elements with min and max 4 by 4. // loop over 'a' and compare current elements with min and max 4 by 4.
// we need to make sure we don't read out of boundaries should 'a' length be not mod. 4 // we need to make sure we don't read out of boundaries should 'a' length be not mod. 4
for(size_t i = 4; i < length-4; i+=4) { for (size_t i = 4; i < length - 4; i += 4) {
__m128 cur = _mm_loadu_ps(a + i); __m128 cur = _mm_loadu_ps(a + i);
max = _mm_max_ps(max, cur); max = _mm_max_ps(max, cur);
min = _mm_min_ps(min, cur); min = _mm_min_ps(min, cur);
@@ -337,20 +371,25 @@ void ExpandMinMaxChannel(const float *a, size_t length, float &min_val, float &m
// I bet you don't find annotated low level code very often. // I bet you don't find annotated low level code very often.
#else #else
// Standard unoptimized function // Standard unoptimized function
for (size_t i=0; i<length; i++) { for (size_t i = 0; i < length; i++) {
min_val = std::min(min_val, a[i]); min_val = std::min(min_val, a[i]);
max_val = std::max(max_val, a[i]); max_val = std::max(max_val, a[i]);
} }
#endif #endif
} }
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index, size_t length) AudioVisualWaveform::Sample
AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index,
size_t length)
{ {
int channels = samples.audio_params().channel_count(); int channels = samples.audio_params().channel_count();
AudioVisualWaveform::Sample summed_samples(channels); AudioVisualWaveform::Sample summed_samples(channels);
for (int channel=0; channel<samples.audio_params().channel_count(); channel++) { for (int channel = 0; channel < samples.audio_params().channel_count();
ExpandMinMaxChannel(samples.data(channel) + start_index, length, summed_samples[channel].min, summed_samples[channel].max); channel++) {
ExpandMinMaxChannel(samples.data(channel) + start_index, length,
summed_samples[channel].min,
summed_samples[channel].max);
} }
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip // for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
@@ -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;
@@ -399,7 +439,7 @@ void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample& sample, in
int channel_height = height / sample.size(); int channel_height = height / sample.size();
int channel_half_height = channel_height / 2; int channel_half_height = channel_height / 2;
for (size_t i=0;i<sample.size();i++) { for (size_t i = 0; i < sample.size(); i++) {
float max = qMin(sample.at(i).max, 1.0f); float max = qMin(sample.at(i).max, 1.0f);
float min = qMax(sample.at(i).min, -1.0f); float min = qMax(sample.at(i).min, -1.0f);
@@ -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;
@@ -435,9 +480,10 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
rational rate = using_mipmap->first; rational rate = using_mipmap->first;
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
const Sample& arr = using_mipmap->second; const Sample &arr = using_mipmap->second;
size_t start_sample_index = samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl); size_t start_sample_index =
samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl);
if (start_sample_index >= arr.size()) { if (start_sample_index >= arr.size()) {
return; return;
@@ -449,7 +495,7 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
Sample summary; Sample summary;
size_t summary_index = -1; size_t summary_index = -1;
const QRect& viewport = painter->viewport(); const QRect &viewport = painter->viewport();
QPoint top_left = painter->transform().map(viewport.topLeft()); QPoint top_left = painter->transform().map(viewport.topLeft());
size_t start = qMax(rect.x(), -top_left.x()); size_t start = qMax(rect.x(), -top_left.x());
@@ -457,19 +503,25 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool(); bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
for (size_t i=start;i<end;i++) { for (size_t i = start; i < end; i++) {
sample_index = next_sample_index; sample_index = next_sample_index;
if (sample_index == arr.size()) { if (sample_index == arr.size()) {
break; break;
} }
next_sample_index = std::min(arr.size(), next_sample_index = std::min(
size_t(start_sample_index + std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) / scale) * samples.channel_count())); arr.size(),
size_t(start_sample_index +
std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) /
scale) *
samples.channel_count()));
if (summary_index != sample_index) { if (summary_index != sample_index) {
summary = AudioVisualWaveform::ReSumSamples(&arr.at(sample_index), summary = AudioVisualWaveform::ReSumSamples(
qMax(size_t(samples.channel_count()), next_sample_index - sample_index), &arr.at(sample_index),
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;
} }
+35 -16
View File
@@ -25,7 +25,8 @@
#include <QPainter> #include <QPainter>
#include <QVector> #include <QVector>
namespace olive { namespace olive
{
using namespace core; using namespace core;
@@ -56,7 +57,7 @@ public:
channels_ = channels; channels_ = channels;
} }
const rational& length() const const rational &length() const
{ {
return length_; return length_;
} }
@@ -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();
}; };
+12 -10
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_;
} }
@@ -59,7 +61,7 @@ void CLIProgressDialog::Update()
std::cout << sized_title.toUtf8().constData(); std::cout << sized_title.toUtf8().constData();
// Pad out the rest of the title area if necessary // Pad out the rest of the title area if necessary
for (int i=sized_title.size(); i<title_columns; i++) { for (int i = sized_title.size(); i < title_columns; i++) {
std::cout << " "; std::cout << " ";
} }
@@ -72,12 +74,12 @@ void CLIProgressDialog::Update()
int bar_prog = qRound(progress_ * progress_bar_columns); int bar_prog = qRound(progress_ * progress_bar_columns);
// Draw filled in bar // Draw filled in bar
for (int i=0;i<bar_prog;i++) { for (int i = 0; i < bar_prog; i++) {
std::cout << "="; std::cout << "=";
} }
// Draw empty space // Draw empty space
for (int i=bar_prog;i<progress_bar_columns;i++) { for (int i = bar_prog; i < progress_bar_columns; i++) {
std::cout << " "; std::cout << " ";
} }
+4 -5
View File
@@ -26,12 +26,12 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
class CLIProgressDialog : public QObject
{ {
class CLIProgressDialog : public QObject {
public: public:
CLIProgressDialog(const QString &title, QObject* parent = nullptr); CLIProgressDialog(const QString &title, QObject *parent = nullptr);
public slots: public slots:
void SetProgress(double p); void SetProgress(double p);
@@ -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);
} }
+5 -6
View File
@@ -24,19 +24,18 @@
#include "cli/cliprogress/cliprogressdialog.h" #include "cli/cliprogress/cliprogressdialog.h"
#include "task/task.h" #include "task/task.h"
namespace olive { namespace olive
class CLITaskDialog : public CLIProgressDialog
{ {
class CLITaskDialog : public CLIProgressDialog {
Q_OBJECT Q_OBJECT
public: public:
CLITaskDialog(Task *task, QObject* parent = nullptr); CLITaskDialog(Task *task, QObject *parent = nullptr);
bool Run(); bool Run();
private: private:
Task* task_; Task *task_;
}; };
} }
+31 -17
View File
@@ -4,19 +4,23 @@
#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 };
} }
ConformTask *conforming_task = nullptr; ConformTask *conforming_task = nullptr;
@@ -35,34 +39,44 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i
// We conform to a different filename until it's done to make it clear even across sessions // We conform to a different filename until it's done to make it clear even across sessions
// whether this conform is ready or not // whether this conform is ready or not
QVector<QString> working_filenames = filenames; QVector<QString> working_filenames = filenames;
for (int i=0; i<working_filenames.size(); i++) { for (int i = 0; i < working_filenames.size(); i++) {
working_filenames[i].append(QStringLiteral(".working")); working_filenames[i].append(QStringLiteral(".working"));
} }
conforming_task = new ConformTask(decoder_id, stream, params, working_filenames); conforming_task =
connect(conforming_task, &ConformTask::Finished, this, &ConformManager::ConformTaskFinished); new ConformTask(decoder_id, stream, params, working_filenames);
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) {
do { do {
conform_done_condition_.wait(&mutex_); conform_done_condition_.wait(&mutex_);
} while (!AllConformsExist(filenames)); } while (!AllConformsExist(filenames));
return {kConformExists, filenames, nullptr}; return { kConformExists, filenames, nullptr };
} }
return {kConformGenerating, QVector<QString>(), conforming_task}; return { kConformGenerating, QVector<QString>(), conforming_task };
} }
QVector<QString> ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params) QVector<QString>
ConformManager::GetConformedFilename(const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params)
{ {
QVector<QString> filenames(params.channel_count()); QVector<QString> filenames(params.channel_count());
for (int i=0; i<filenames.size(); i++) { for (int i = 0; i < filenames.size(); i++) {
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.%6.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()), QString index_fn =
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()),
@@ -93,7 +107,7 @@ void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
ConformData data; ConformData data;
// Remove conform data from list // Remove conform data from list
for (int i=0; i<conforming_.size(); i++) { for (int i = 0; i < conforming_.size(); i++) {
const ConformData &c = conforming_.at(i); const ConformData &c = conforming_.at(i);
if (c.task == task) { if (c.task == task) {
data = c; data = c;
@@ -104,7 +118,7 @@ void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
if (succeeded) { if (succeeded) {
// Move file to standard conform name, making it clear this conform is ready for use // Move file to standard conform name, making it clear this conform is ready for use
for (int i=0; i<data.finished_filename.size(); i++) { for (int i = 0; i < data.finished_filename.size(); i++) {
const QString &finished = data.finished_filename.at(i); const QString &finished = data.finished_filename.at(i);
const QString &working = data.working_filename.at(i); const QString &working = data.working_filename.at(i);
@@ -117,7 +131,7 @@ void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
emit ConformReady(); emit ConformReady();
} else { } else {
// Failed, just delete the working filename if exists // Failed, just delete the working filename if exists
for (int i=0; i<data.working_filename.size(); i++) { for (int i = 0; i < data.working_filename.size(); i++) {
QFile::remove(data.working_filename.at(i)); QFile::remove(data.working_filename.at(i));
} }
} }
+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
+56 -26
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)
@@ -242,7 +258,7 @@ int Decoder::GetImageSequenceDigitCount(const QString &filename)
// See if basename contains a number at the end // See if basename contains a number at the end
int digit_count = 0; int digit_count = 0;
for (int i=basename.size()-1;i>=0;i--) { for (int i = basename.size() - 1; i >= 0; i--) {
if (basename.at(i).isDigit()) { if (basename.at(i).isDigit()) {
digit_count++; digit_count++;
} else { } else {
@@ -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);
} }
} }
+57 -40
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)
{ {
} }
@@ -112,12 +117,12 @@ public:
*this = CodecStream(); *this = CodecStream();
} }
bool operator==(const CodecStream& rhs) const bool operator==(const CodecStream &rhs) const
{ {
return filename_ == rhs.filename_ && stream_ == rhs.stream_; return filename_ == rhs.filename_ && stream_ == rhs.stream_;
} }
const QString& filename() const const QString &filename() const
{ {
return filename_; return filename_;
} }
@@ -138,7 +143,6 @@ public:
int stream_; int stream_;
Block *block_; Block *block_;
}; };
/** /**
@@ -150,12 +154,11 @@ public:
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't * already open and the stream == the stream provided. Returns FALSE if the stream couldn't
* be opened OR if already open and the stream is NOT the same. * be opened OR if already open and the stream is NOT the same.
*/ */
bool Open(const CodecStream& stream); bool Open(const CodecStream &stream);
static const rational kAnyTimecode; static const rational kAnyTimecode;
struct RetrieveVideoParams struct RetrieveVideoParams {
{
Renderer *renderer = nullptr; Renderer *renderer = nullptr;
rational time; rational time;
int divider = 1; int divider = 1;
@@ -175,7 +178,7 @@ public:
* *
* This function is thread safe and can only run while the decoder is open. \see Open() * This function is thread safe and can only run while the decoder is open. \see Open()
*/ */
TexturePtr RetrieveVideo(const RetrieveVideoParams& p); TexturePtr RetrieveVideo(const RetrieveVideoParams &p);
enum RetrieveAudioStatus { enum RetrieveAudioStatus {
kInvalid = -1, kInvalid = -1,
@@ -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
@@ -231,13 +240,14 @@ public:
* *
* A Decoder instance or nullptr if a Decoder with this ID does not exist * A Decoder instance or nullptr if a Decoder with this ID does not exist
*/ */
static DecoderPtr CreateFromID(const QString& id); static DecoderPtr CreateFromID(const QString &id);
static QString TransformImageSequenceFileName(const QString& filename, const int64_t& number); static QString TransformImageSequenceFileName(const QString &filename,
const int64_t &number);
static int GetImageSequenceDigitCount(const QString& filename); static int GetImageSequenceDigitCount(const QString &filename);
static int64_t GetImageSequenceIndex(const QString& filename); static int64_t GetImageSequenceIndex(const QString &filename);
static QVector<DecoderPtr> ReceiveListOfAllDecoders(); static QVector<DecoderPtr> ReceiveListOfAllDecoders();
@@ -270,9 +280,11 @@ protected:
* Sub-classes must override this function IF they support video. Function is already mutexed * Sub-classes must override this function IF they support video. Function is already mutexed
* so sub-classes don't need to worry about thread safety. * so sub-classes don't need to worry about thread safety.
*/ */
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p); virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams &p);
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled); virtual bool ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled);
void SignalProcessingProgress(int64_t ts, int64_t duration); void SignalProcessingProgress(int64_t ts, int64_t duration);
@@ -281,12 +293,15 @@ protected:
* *
* This function is NOT thread safe and should therefore only be called by thread safe functions. * This function is NOT thread safe and should therefore only be called by thread safe functions.
*/ */
const CodecStream& stream() const const CodecStream &stream() const
{ {
return stream_; return stream_;
} }
virtual rational GetAudioStartOffset() const { return 0; } virtual rational GetAudioStartOffset() const
{
return 0;
}
signals: signals:
/** /**
@@ -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);
+164 -88
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);
@@ -62,7 +67,7 @@ int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
{ {
int start = filename.indexOf(kImageSequenceContainsDigits); int start = filename.indexOf(kImageSequenceContainsDigits);
int digit_count = 0; int digit_count = 0;
for (int i=start+1; i<filename.size(); i++) { for (int i = start + 1; i < filename.size(); i++) {
if (filename.at(i) == '#') { if (filename.at(i) == '#') {
digit_count++; digit_count++;
} else { } else {
@@ -72,7 +77,7 @@ int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
return digit_count; return digit_count;
} }
bool Encoder::FilenameContainsDigitPlaceholder(const QString& filename) bool Encoder::FilenameContainsDigitPlaceholder(const QString &filename)
{ {
return filename.contains(kImageSequenceContainsDigits); return filename.contains(kImageSequenceContainsDigits);
} }
@@ -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
@@ -283,7 +331,7 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
writer->writeEndDocument(); writer->writeEndDocument();
} }
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params) Encoder *Encoder::CreateFromID(Type id, const EncodingParams &params)
{ {
switch (id) { switch (id) {
case kEncoderTypeNone: case kEncoderTypeNone:
@@ -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();
} }
+195 -73
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,78 +54,208 @@ 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);
void Save(QIODevice *device) const; void Save(QIODevice *device) const;
void Save(QXmlStreamWriter* writer) const; void Save(QXmlStreamWriter *writer) const;
bool has_custom_range() const { return has_custom_range_; } bool has_custom_range() const
const TimeRange& custom_range() const { return custom_range_; } {
void set_custom_range(const TimeRange& custom_range) return has_custom_range_;
}
const TimeRange &custom_range() const
{
return 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,28 +317,30 @@ 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;
virtual PixelFormat GetDesiredPixelFormat() const virtual PixelFormat GetDesiredPixelFormat() const
{ {
return PixelFormat::INVALID; return PixelFormat::INVALID;
} }
const QString& GetError() const const QString &GetError() const
{ {
return error_; return error_;
} }
QString GetFilenameForFrame(const rational& frame); QString GetFilenameForFrame(const rational &frame);
static int GetImageSequencePlaceholderDigitCount(const QString& filename); static int GetImageSequencePlaceholderDigitCount(const QString &filename);
static bool FilenameContainsDigitPlaceholder(const QString &filename); static bool FilenameContainsDigitPlaceholder(const QString &filename);
static QString FilenameRemoveDigitPlaceholder(QString filename); static QString FilenameRemoveDigitPlaceholder(QString filename);
@@ -229,14 +351,15 @@ 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;
virtual void Close() = 0; virtual void Close() = 0;
protected: protected:
void SetError(const QString& err) void SetError(const QString &err)
{ {
error_ = err; error_ = err;
} }
@@ -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);
}; };
} }
+38 -25
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)
{ {
@@ -109,21 +110,25 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
{ {
switch (f) { switch (f) {
case kFormatDNxHD: case kFormatDNxHD:
return {ExportCodec::kCodecDNxHD}; return { ExportCodec::kCodecDNxHD };
case kFormatMatroska: case kFormatMatroska:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9}; return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
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:
return {ExportCodec::kCodecPNG}; return { ExportCodec::kCodecPNG };
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:
case kFormatWAV: case kFormatWAV:
case kFormatMPEG4Audio: case kFormatMPEG4Audio:
@@ -143,28 +148,36 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
switch (f) { switch (f) {
// Video/audio formats // Video/audio formats
case kFormatDNxHD: case kFormatDNxHD:
return {ExportCodec::kCodecPCM}; return { ExportCodec::kCodecPCM };
case kFormatMatroska: case kFormatMatroska:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC}; return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
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:
return {ExportCodec::kCodecPCM}; return { ExportCodec::kCodecPCM };
case kFormatAIFF: case kFormatAIFF:
return {ExportCodec::kCodecPCM}; return { ExportCodec::kCodecPCM };
case kFormatMP3: case kFormatMP3:
return {ExportCodec::kCodecMP3}; return { ExportCodec::kCodecMP3 };
case kFormatFLAC: case kFormatFLAC:
return {ExportCodec::kCodecFLAC}; return { ExportCodec::kCodecFLAC };
case kFormatOgg: case kFormatOgg:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM}; return { ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis,
ExportCodec::kCodecPCM };
// Video only formats // Video only formats
case kFormatOpenEXR: case kFormatOpenEXR:
@@ -173,7 +186,6 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
case kFormatSRT: case kFormatSRT:
case kFormatCount: case kFormatCount:
break; break;
} }
return {}; return {};
@@ -199,15 +211,16 @@ QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
break; break;
case kFormatMatroska: case kFormatMatroska:
case kFormatSRT: case kFormatSRT:
return {ExportCodec::kCodecSRT}; return { ExportCodec::kCodecSRT };
} }
return {}; return {};
} }
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, ExportCodec::Codec c) QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f,
ExportCodec::Codec c)
{ {
Encoder* e = Encoder::CreateFromFormat(f, EncodingParams()); Encoder *e = Encoder::CreateFromFormat(f, EncodingParams());
QStringList list; QStringList list;
if (e) { if (e) {
@@ -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);
}; };
} }
+271 -195
View File
@@ -45,23 +45,24 @@ 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)
{ {
} }
bool FFmpegDecoder::OpenInternal() bool FFmpegDecoder::OpenInternal()
{ {
if (instance_.Open(stream().filename().toUtf8(), stream().stream())) { if (instance_.Open(stream().filename().toUtf8(), stream().stream())) {
AVStream* s = instance_.avstream(); AVStream *s = instance_.avstream();
// Store one second in the source's timebase // Store one second in the source's timebase
second_ts_ = qRound64(av_q2d(av_inv_q(s->time_base))); second_ts_ = qRound64(av_q2d(av_inv_q(s->time_base)));
@@ -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:
@@ -151,7 +155,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
case AV_PIX_FMT_YUV422P10LE: case AV_PIX_FMT_YUV422P10LE:
case AV_PIX_FMT_YUV420P12LE: case AV_PIX_FMT_YUV420P12LE:
case AV_PIX_FMT_YUV422P12LE: case AV_PIX_FMT_YUV422P12LE:
plane_params.set_width(plane_params.width()/2); plane_params.set_width(plane_params.width() / 2);
break; break;
} }
@@ -159,25 +163,41 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUV420P10LE: case AV_PIX_FMT_YUV420P10LE:
case AV_PIX_FMT_YUV420P12LE: case AV_PIX_FMT_YUV420P12LE:
plane_params.set_height(plane_params.height()/2); plane_params.set_height(plane_params.height() / 2);
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());
@@ -302,51 +339,52 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
QByteArray filename_c = filename.toUtf8(); QByteArray filename_c = filename.toUtf8();
// Open file in a format context // Open file in a format context
AVFormatContext* fmt_ctx = nullptr; AVFormatContext *fmt_ctx = nullptr;
error_code = avformat_open_input(&fmt_ctx, filename_c, nullptr, nullptr); error_code = avformat_open_input(&fmt_ctx, filename_c, nullptr, nullptr);
// 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
AVPacket* pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
AVFrame* frame = av_frame_alloc(); AVFrame *frame = av_frame_alloc();
{ {
Instance instance; Instance instance;
@@ -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,28 +493,34 @@ 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);
AVPacket* pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
AVFrame* frame = av_frame_alloc(); AVFrame *frame = av_frame_alloc();
int64_t new_dur; int64_t new_dur;
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,29 +540,36 @@ 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;
AVPacket* pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
{ {
Instance instance; Instance instance;
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,28 +620,27 @@ 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);
AVPacket* pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
AVFrame* frame = av_frame_alloc(); AVFrame *frame = av_frame_alloc();
int ret; int ret;
bool success = false; bool success = false;
@@ -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(
reinterpret_cast<uint8_t**>(data.to_raw_ptrs().data()), resampler,
nb_samples, reinterpret_cast<uint8_t **>(data.to_raw_ptrs().data()),
const_cast<const uint8_t**>(frame->data), nb_samples, 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;
} }
@@ -690,17 +764,18 @@ int FFmpegDecoder::GetNativeChannelCount(AVPixelFormat pix_fmt)
} }
} }
AVChannelLayout FFmpegDecoder::ValidateChannelLayout(AVStream* stream) AVChannelLayout FFmpegDecoder::ValidateChannelLayout(AVStream *stream)
{ {
if (av_channel_layout_check(&stream->codecpar->ch_layout)) { if (av_channel_layout_check(&stream->codecpar->ch_layout)) {
return stream->codecpar->ch_layout; return stream->codecpar->ch_layout;
} }
AVChannelLayout layout; AVChannelLayout layout;
av_channel_layout_default(&layout,stream->codecpar->ch_layout.nb_channels); av_channel_layout_default(&layout, stream->codecpar->ch_layout.nb_channels);
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;
} }
@@ -1051,14 +1126,13 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
avstream_ = fmt_ctx_->streams[stream_index]; avstream_ = fmt_ctx_->streams[stream_index];
// Find decoder // Find decoder
const AVCodec* codec = avcodec_find_decoder(avstream_->codecpar->codec_id); const AVCodec *codec = avcodec_find_decoder(avstream_->codecpar->codec_id);
// 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);
@@ -1157,7 +1233,7 @@ int FFmpegDecoder::Instance::GetFrame(AVPacket *pkt, AVFrame *frame)
const char *FFmpegDecoder::Instance::GetSubtitleHeader() const const char *FFmpegDecoder::Instance::GetSubtitleHeader() const
{ {
return reinterpret_cast<const char*>(codec_ctx_->subtitle_header); return reinterpret_cast<const char *>(codec_ctx_->subtitle_header);
} }
int FFmpegDecoder::Instance::GetSubtitle(AVPacket *pkt, AVSubtitle *sub) int FFmpegDecoder::Instance::GetSubtitle(AVPacket *pkt, AVSubtitle *sub)
+33 -23
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();
@@ -80,7 +89,7 @@ private:
Close(); Close();
} }
bool Open(const char* filename, int stream_index); bool Open(const char *filename, int stream_index);
bool IsOpen() const bool IsOpen() const
{ {
@@ -96,32 +105,31 @@ private:
* *
* An FFmpeg error code, or >= 0 on success * An FFmpeg error code, or >= 0 on success
*/ */
int GetFrame(AVPacket* pkt, AVFrame* frame); int GetFrame(AVPacket *pkt, AVFrame *frame);
const char *GetSubtitleHeader() const; const char *GetSubtitleHeader() const;
int GetSubtitle(AVPacket* pkt, AVSubtitle* sub); int GetSubtitle(AVPacket *pkt, AVSubtitle *sub);
int GetPacket(AVPacket *pkt); int GetPacket(AVPacket *pkt);
void Seek(int64_t timestamp); void Seek(int64_t timestamp);
AVFormatContext* fmt_ctx() const AVFormatContext *fmt_ctx() const
{ {
return fmt_ctx_; return fmt_ctx_;
} }
AVStream* avstream() const AVStream *avstream() const
{ {
return avstream_; return avstream_;
} }
private: private:
AVFormatContext* fmt_ctx_; AVFormatContext *fmt_ctx_;
AVCodecContext* codec_ctx_; AVCodecContext *codec_ctx_;
AVStream* avstream_; AVStream *avstream_;
AVDictionary* opts_; AVDictionary *opts_;
}; };
/** /**
@@ -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_;
}; };
} }
+215 -135
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)
{ {
} }
@@ -52,16 +53,18 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
{ {
QStringList pix_fmts; QStringList pix_fmts;
const AVCodec* codec_info = GetEncoder(c, SampleFormat::INVALID); const AVCodec *codec_info = GetEncoder(c, SampleFormat::INVALID);
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);
} }
@@ -111,10 +110,11 @@ bool FFmpegEncoder::Open()
// Convert QString to C string that FFmpeg expects // Convert QString to C string that FFmpeg expects
QByteArray filename_bytes = params().filename().toUtf8(); QByteArray filename_bytes = params().filename().toUtf8();
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,28 +161,39 @@ 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_;
{ {
// Set color range // Set color range
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;
@@ -186,11 +201,15 @@ bool FFmpegEncoder::Open()
if (src_alpha_pix_fmt != encoder_pix_fmt) { if (src_alpha_pix_fmt != encoder_pix_fmt) {
// 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,8 +271,9 @@ 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(
input_frame->data[0] = reinterpret_cast<uint8_t*>(frame->data()); frame->format(), frame->channel_count());
input_frame->data[0] = reinterpret_cast<uint8_t *>(frame->data());
input_frame->linesize[0] = frame->linesize_bytes(); input_frame->linesize[0] = frame->linesize_bytes();
input_frame->color_primaries = video_codec_ctx_->color_primaries; input_frame->color_primaries = video_codec_ctx_->color_primaries;
@@ -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_);
} }
@@ -291,26 +316,34 @@ bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
while (result && start < end) { while (result && start < end) {
// Create input buffer // Create input buffer
uint8_t** input_data = nullptr; uint8_t **input_data = nullptr;
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);
return false; return false;
} 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 =
uint8_t** output_data = nullptr; input_sample_count ?
swr_get_out_samples(audio_resample_ctx_, input_sample_count) :
102400;
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)
@@ -407,12 +457,16 @@ bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
AVPacket *pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
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;
@@ -536,17 +590,19 @@ void FFmpegEncoder::Close()
} }
} }
void FFmpegEncoder::FFmpegError(const QString& context, int error_code) 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);
@@ -557,7 +613,7 @@ bool FFmpegEncoder::WriteAVFrame(AVFrame *frame, AVCodecContext* codec_ctx, AVSt
bool succeeded = false; bool succeeded = false;
AVPacket* pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
// Retrieve packets from encoder // Retrieve packets from encoder
while (error_code >= 0) { while (error_code >= 0) {
@@ -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;
} }
@@ -619,41 +683,53 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
} }
// Set codec parameters // Set codec parameters
AVCodecContext* codec_ctx = *codec_ctx_ptr; AVCodecContext *codec_ctx = *codec_ctx_ptr;
AVStream* stream = *stream_ptr; AVStream *stream = *stream_ptr;
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,26 +765,25 @@ 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(
codec_ctx->time_base = {1, codec_ctx->sample_rate}; params().audio_params().format());
codec_ctx->time_base = { 1, codec_ctx->sample_rate };
if (params().audio_bit_rate() > 0) { if (params().audio_bit_rate() > 0) {
codec_ctx->bit_rate = params().audio_bit_rate(); codec_ctx->bit_rate = params().audio_bit_rate();
} }
} 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;
@@ -742,7 +823,7 @@ bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ct
codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
} }
AVDictionary* codec_opts = nullptr; AVDictionary *codec_opts = nullptr;
// Set thread count // Set thread count
if (params().video_threads() == 0) { if (params().video_threads() == 0) {
@@ -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;
} }
@@ -795,10 +877,10 @@ void FFmpegEncoder::FlushEncoders()
} }
} }
void FFmpegEncoder::FlushCodecCtx(AVCodecContext *codec_ctx, AVStream* stream) void FFmpegEncoder::FlushCodecCtx(AVCodecContext *codec_ctx, AVStream *stream)
{ {
avcodec_send_frame(codec_ctx, nullptr); avcodec_send_frame(codec_ctx, nullptr);
AVPacket* pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
int error_code; int error_code;
do { do {
@@ -826,17 +908,13 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio)
if (audio_resample_ctx_) { if (audio_resample_ctx_) {
return true; return true;
} }
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:
@@ -899,7 +979,7 @@ const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c, SampleFormat afor
return avcodec_find_encoder(AV_CODEC_ID_VP9); return avcodec_find_encoder(AV_CODEC_ID_VP9);
case ExportCodec::kCodecAV1: { case ExportCodec::kCodecAV1: {
const AVCodec *encoder = avcodec_find_encoder_by_name("libsvtav1"); const AVCodec *encoder = avcodec_find_encoder_by_name("libsvtav1");
if(!encoder) if (!encoder)
encoder = avcodec_find_encoder(AV_CODEC_ID_AV1); encoder = avcodec_find_encoder(AV_CODEC_ID_AV1);
return encoder; return encoder;
} }
+32 -23
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,41 +75,46 @@ 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_;
AVStream* video_stream_; AVStream *video_stream_;
AVCodecContext* video_codec_ctx_; AVCodecContext *video_codec_ctx_;
AVFilterGraph *video_scale_ctx_; AVFilterGraph *video_scale_ctx_;
AVFilterContext *video_buffersrc_ctx_; AVFilterContext *video_buffersrc_ctx_;
AVFilterContext *video_buffersink_ctx_; AVFilterContext *video_buffersink_ctx_;
PixelFormat video_conversion_fmt_; PixelFormat video_conversion_fmt_;
AVStream* audio_stream_; AVStream *audio_stream_;
AVCodecContext* audio_codec_ctx_; AVCodecContext *audio_codec_ctx_;
SwrContext* audio_resample_ctx_; SwrContext *audio_resample_ctx_;
AVFrame* audio_frame_; AVFrame *audio_frame_;
int audio_max_samples_; int audio_max_samples_;
int audio_frame_offset_; int audio_frame_offset_;
int audio_write_count_; int audio_write_count_;
AVStream* subtitle_stream_; AVStream *subtitle_stream_;
AVCodecContext* subtitle_codec_ctx_; AVCodecContext *subtitle_codec_ctx_;
bool open_; bool open_;
}; };
} }
+30 -22
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;
} }
@@ -73,21 +76,22 @@ FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
int linesize = interlaced->linesize_bytes(); int linesize = interlaced->linesize_bytes();
for (int i=0; i<interlaced->height(); i++) { for (int i = 0; i < interlaced->height(); i++) {
FramePtr which = (i%2 == 0) ? top : bottom; FramePtr which = (i % 2 == 0) ? top : bottom;
memcpy(interlaced->data() + i*linesize, memcpy(interlaced->data() + i * linesize,
which->const_data() + i*linesize, which->const_data() + i * linesize, linesize);
linesize);
} }
return interlaced; return interlaced;
} }
int Frame::generate_linesize_bytes(int width, PixelFormat format, int channel_count) int Frame::generate_linesize_bytes(int width, PixelFormat format,
int channel_count)
{ {
// Align to 32 bytes (not sure if this is necessary?) // Align to 32 bytes (not sure if this is necessary?)
return VideoParams::GetBytesPerPixel(format, channel_count) * ((width + 31) & ~31); return VideoParams::GetBytesPerPixel(format, channel_count) *
((width + 31) & ~31);
} }
Color Frame::get_pixel(int x, int y) const Color Frame::get_pixel(int x, int y) const
@@ -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)) {
+13 -13
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();
@@ -47,12 +47,13 @@ public:
static FramePtr Create(); static FramePtr Create();
const VideoParams& video_params() const; const VideoParams &video_params() const;
void set_video_params(const VideoParams& params); void set_video_params(const VideoParams &params);
static FramePtr Interlace(FramePtr top, FramePtr bottom); static FramePtr Interlace(FramePtr top, FramePtr bottom);
static int generate_linesize_bytes(int width, PixelFormat format, int channel_count); static int generate_linesize_bytes(int width, PixelFormat format,
int channel_count);
int linesize_pixels() const int linesize_pixels() const
{ {
@@ -86,19 +87,19 @@ public:
Color get_pixel(int x, int y) const; Color get_pixel(int x, int y) const;
bool contains_pixel(int x, int y) const; bool contains_pixel(int x, int y) const;
void set_pixel(int x, int y, const Color& c); void set_pixel(int x, int y, const Color &c);
/** /**
* @brief Get frame's timestamp. * @brief Get frame's timestamp.
* *
* This timestamp is always a rational that will equate to the time in seconds. * This timestamp is always a rational that will equate to the time in seconds.
*/ */
const rational& timestamp() const const rational &timestamp() const
{ {
return timestamp_; return timestamp_;
} }
void set_timestamp(const rational& timestamp) void set_timestamp(const rational &timestamp)
{ {
timestamp_ = timestamp; timestamp_ = timestamp;
} }
@@ -106,7 +107,7 @@ public:
/** /**
* @brief Get the data buffer of this frame * @brief Get the data buffer of this frame
*/ */
char* data() char *data()
{ {
return data_; return data_;
} }
@@ -114,7 +115,7 @@ public:
/** /**
* @brief Get the const data buffer of this frame * @brief Get the const data buffer of this frame
*/ */
const char* const_data() const const char *const_data() const
{ {
return data_; return data_;
} }
@@ -156,7 +157,7 @@ public:
private: private:
VideoParams params_; VideoParams params_;
char* data_; char *data_;
int data_size_; int data_size_;
rational timestamp_; rational timestamp_;
@@ -164,7 +165,6 @@ private:
int linesize_; int linesize_;
int linesize_pixels_; int linesize_pixels_;
}; };
} }
+43 -26
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)
@@ -74,7 +76,7 @@ FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cance
bool stream_enabled = true; bool stream_enabled = true;
int i; int i;
for (i=0; in->seek_subimage(i, 0); i++) { for (i = 0; in->seek_subimage(i, 0); i++) {
OIIO::ImageSpec spec = in->spec(); OIIO::ImageSpec spec = in->spec();
VideoParams video_params = GetVideoParamsFromImageSpec(spec); VideoParams video_params = GetVideoParamsFromImageSpec(spec);
@@ -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,24 +140,29 @@ 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();
for (int dst_y=0; dst_y<buffer_.height(); dst_y++) { for (int dst_y = 0; dst_y < buffer_.height(); dst_y++) {
int src_y = dst_y * buf.spec().height / buffer_.height(); int src_y = dst_y * buf.spec().height / buffer_.height();
for (int dst_x=0; dst_x<buffer_.width(); dst_x++) { for (int dst_x = 0; dst_x < buffer_.width(); dst_x++) {
int src_x = dst_x * buf.spec().width / buffer_.width(); int src_x = dst_x * buf.spec().width / buffer_.width();
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y + px_sz * dst_x, memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y +
static_cast<uint8_t*>(buf.localpixels()) + buf.scanline_stride() * src_y + px_sz * src_x, px_sz * dst_x,
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()
@@ -162,7 +170,7 @@ void OIIODecoder::CloseInternal()
CloseImageHandle(); CloseImageHandle();
} }
bool OIIODecoder::FileTypeIsSupported(const QString& fn) bool OIIODecoder::FileTypeIsSupported(const QString &fn)
{ {
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG) // We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's // will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
@@ -170,17 +178,20 @@ 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) {
QStringList format_and_ext = ext.split(':'); QStringList format_and_ext = ext.split(':');
supported_formats_.append(format_and_ext.at(1).split(',')); supported_formats_.append(format_and_ext.at(1).split(','));
} }
} }
if (!supported_formats_.contains(QFileInfo(fn).suffix(), Qt::CaseInsensitive)) { if (!supported_formats_.contains(QFileInfo(fn).suffix(),
Qt::CaseInsensitive)) {
return false; return false;
} }
@@ -200,20 +211,23 @@ bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
} }
// Check if we can work with this pixel format // Check if we can work with this pixel format
const OIIO::ImageSpec& spec = image_->spec(); const OIIO::ImageSpec &spec = image_->spec();
// We use RGBA frames because that tends to be the native format of GPUs // We use RGBA frames because that tends to be the native format of GPUs
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)); pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
if (pix_fmt_ == PixelFormat::INVALID) { if (pix_fmt_ == PixelFormat::INVALID) {
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format"; qWarning()
<< "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;
+13 -9
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,21 +38,26 @@ 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:
std::unique_ptr<OIIO::ImageInput> image_; std::unique_ptr<OIIO::ImageInput> image_;
static bool FileTypeIsSupported(const QString& fn); static bool FileTypeIsSupported(const QString &fn);
bool OpenImageHandler(const QString& fn, int subimage); bool OpenImageHandler(const QString &fn, int subimage);
void CloseImageHandle(); void CloseImageHandle();
@@ -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;
}; };
} }
+15 -12
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
@@ -43,7 +44,7 @@ bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMo
files_.resize(filenames.size()); files_.resize(filenames.size());
files_.fill(nullptr); files_.fill(nullptr);
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
files_[i] = new QFile(filenames.at(i)); files_[i] = new QFile(filenames.at(i));
if (!files_[i]->open(mode)) { if (!files_[i]->open(mode)) {
close(); close();
@@ -54,12 +55,13 @@ 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;
if (isOpen()) { if (isOpen()) {
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
// Kind of clunky but should be largely fine // Kind of clunky but should be largely fine
ret = files_[i]->read(data[i] + offset, bytes_per_channel); ret = files_[i]->read(data[i] + offset, bytes_per_channel);
} }
@@ -68,12 +70,13 @@ 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;
if (isOpen()) { if (isOpen()) {
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
// Kind of clunky but should be largely fine // Kind of clunky but should be largely fine
ret = files_[i]->write(data[i] + offset, bytes_per_channel); ret = files_[i]->write(data[i] + offset, bytes_per_channel);
} }
@@ -95,7 +98,7 @@ bool PlanarFileDevice::seek(qint64 pos)
{ {
bool ret = true; bool ret = true;
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
ret = files_[i]->seek(pos) & ret; ret = files_[i]->seek(pos) & ret;
} }
@@ -104,7 +107,7 @@ bool PlanarFileDevice::seek(qint64 pos)
void PlanarFileDevice::close() void PlanarFileDevice::close()
{ {
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
QFile *f = files_.at(i); QFile *f = files_.at(i);
if (f) { if (f) {
if (f->isOpen()) { if (f->isOpen()) {
+6 -6
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;
@@ -55,8 +56,7 @@ public:
void close(); void close();
private: private:
QVector<QFile*> files_; QVector<QFile *> files_;
}; };
} }
+3 -6
View File
@@ -23,15 +23,12 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
class AutoScroll { class AutoScroll {
public: public:
enum Method { enum Method { kNone, kPage, kSmooth };
kNone,
kPage,
kSmooth
};
}; };
} }
+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_;
}; };
} }
+32 -24
View File
@@ -25,29 +25,36 @@
CommandLineParser::~CommandLineParser() CommandLineParser::~CommandLineParser()
{ {
foreach (const KnownOption& o, options_) { foreach (const KnownOption &o, options_) {
delete o.option; delete o.option;
} }
foreach (const KnownPositionalArgument& a, positional_args_) { foreach (const KnownPositionalArgument &a, positional_args_) {
delete a.option; delete a.option;
} }
} }
const CommandLineParser::Option *CommandLineParser::AddOption(const QStringList &strings, const QString &description, bool takes_arg, const QString &arg_placeholder, bool hidden) const CommandLineParser::Option *
CommandLineParser::AddOption(const QStringList &strings,
const QString &description, bool takes_arg,
const QString &arg_placeholder, bool hidden)
{ {
Option* o = new Option(); Option *o = new Option();
options_.append({strings, description, o, takes_arg, arg_placeholder, hidden}); options_.append(
{ strings, description, o, takes_arg, arg_placeholder, hidden });
return o; return o;
} }
const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArgument(const QString &name, const QString &description, bool required) const CommandLineParser::PositionalArgument *
CommandLineParser::AddPositionalArgument(const QString &name,
const QString &description,
bool required)
{ {
PositionalArgument* a = new PositionalArgument(); PositionalArgument *a = new PositionalArgument();
positional_args_.append({name, description, a, required}); positional_args_.append({ name, description, a, required });
return a; return a;
} }
@@ -56,7 +63,7 @@ void CommandLineParser::Process(const QVector<QString> &argv)
{ {
int positional_index = 0; int positional_index = 0;
for (int i=1; i<argv.size(); i++) { for (int i = 1; i < argv.size(); i++) {
if (argv[i][0] == '-') { if (argv[i][0] == '-') {
// Must be an option // Must be an option
@@ -65,16 +72,16 @@ void CommandLineParser::Process(const QVector<QString> &argv)
bool matched_known = false; bool matched_known = false;
for (int j=0; j<options_.size(); j++) { for (int j = 0; j < options_.size(); j++) {
KnownOption& o = options_[j]; KnownOption &o = options_[j];
foreach (const QString& s, o.args) { foreach (const QString &s, o.args) {
if (!s.compare(arg_basename, Qt::CaseInsensitive)) { if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
// Flag discovered! // Flag discovered!
o.option->Set(); o.option->Set();
if (o.takes_arg && i+1 < argv.size()) { if (o.takes_arg && i + 1 < argv.size()) {
o.option->SetSetting(argv[i+1]); o.option->SetSetting(argv[i + 1]);
i++; i++;
} }
@@ -101,16 +108,15 @@ 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");
QString positional_args; QString positional_args;
for (int i=0; i<positional_args_.size(); i++) { for (int i = 0; i < positional_args_.size(); i++) {
if (i > 0) { if (i > 0) {
positional_args.append(' '); positional_args.append(' ');
} }
@@ -120,7 +126,7 @@ void CommandLineParser::PrintHelp(const char* filename)
positional_args.append(']'); positional_args.append(']');
} }
const char* basename; const char *basename;
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
basename = strrchr(filename, '\\'); basename = strrchr(filename, '\\');
if (!basename) { if (!basename) {
@@ -138,20 +144,21 @@ 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,
foreach (const KnownOption& o, options_) { positional_args.toUtf8().constData());
foreach (const KnownOption &o, options_) {
if (o.hidden) { if (o.hidden) {
continue; continue;
} }
QString all_args; QString all_args;
for (int i=0; i<o.args.size(); i++) { for (int i = 0; i < o.args.size(); i++) {
if (i > 0) { if (i > 0) {
all_args.append(QStringLiteral(", ")); all_args.append(QStringLiteral(", "));
} }
const QString& this_arg = o.args.at(i); const QString &this_arg = o.args.at(i);
all_args.append('-'); all_args.append('-');
all_args.append(this_arg); all_args.append(this_arg);
@@ -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());
+14 -14
View File
@@ -36,31 +36,28 @@
* the user as a command line argument. Therefore we needed a custom implementation that could * the user as a command line argument. Therefore we needed a custom implementation that could
* parse arguments without the need for a Q(Core)Application to be present already. * parse arguments without the need for a Q(Core)Application to be present already.
*/ */
class CommandLineParser class CommandLineParser {
{
public: public:
~CommandLineParser(); ~CommandLineParser();
DISABLE_COPY_MOVE(CommandLineParser) DISABLE_COPY_MOVE(CommandLineParser)
class PositionalArgument class PositionalArgument {
{
public: public:
PositionalArgument() = default; PositionalArgument() = default;
const QString& GetSetting() const const QString &GetSetting() const
{ {
return setting_; return setting_;
} }
void SetSetting(const QString& s) void SetSetting(const QString &s)
{ {
setting_ = s; setting_ = s;
} }
private: private:
QString setting_; QString setting_;
}; };
class Option : public PositionalArgument { class Option : public PositionalArgument {
@@ -82,24 +79,28 @@ 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);
void PrintHelp(const char* filename); void PrintHelp(const char *filename);
private: private:
struct KnownOption { struct KnownOption {
QStringList args; QStringList args;
QString description; QString description;
Option* option; Option *option;
bool takes_arg; bool takes_arg;
QString arg_placeholder; QString arg_placeholder;
bool hidden; bool hidden;
@@ -108,14 +109,13 @@ private:
struct KnownPositionalArgument { struct KnownPositionalArgument {
QString name; QString name;
QString description; QString description;
PositionalArgument* option; PositionalArgument *option;
bool required; bool required;
}; };
QVector<KnownOption> options_; QVector<KnownOption> options_;
QVector<KnownPositionalArgument> positional_args_; QVector<KnownPositionalArgument> positional_args_;
}; };
#endif // COMMANDLINEPARSER_H #endif // COMMANDLINEPARSER_H
+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;
+5 -3
View File
@@ -20,13 +20,15 @@
#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();
const char* msg_type = "UNKNOWN"; const char *msg_type = "UNKNOWN";
switch (type) { switch (type) {
case QtDebugMsg: case QtDebugMsg:
msg_type = "DEBUG"; msg_type = "DEBUG";
+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;
}; };
} }
+9 -5
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
@@ -49,7 +53,7 @@ const int kBytesInGigabyte = 1073741824;
*/ */
#define DISABLE_COPY(Class) \ #define DISABLE_COPY(Class) \
Class(const Class &) = delete;\ Class(const Class &) = delete; \
Class &operator=(const Class &) = delete; Class &operator=(const Class &) = delete;
#define DISABLE_MOVE(Class) \ #define DISABLE_MOVE(Class) \
+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)
{ {
+19 -12
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);
} }
@@ -56,7 +57,7 @@ SampleFormat FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
return SampleFormat::F32; return SampleFormat::F32;
case AV_SAMPLE_FMT_DBL: case AV_SAMPLE_FMT_DBL:
return SampleFormat::F64; return SampleFormat::F64;
case AV_SAMPLE_FMT_U8P : case AV_SAMPLE_FMT_U8P:
return SampleFormat::U8P; return SampleFormat::U8P;
case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S16P:
return SampleFormat::S16P; return SampleFormat::S16P;
@@ -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) {
+10 -6
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,22 +41,25 @@ public:
/** /**
* @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss * @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss
*/ */
static AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt, PixelFormat maximum = PixelFormat::INVALID); static AVPixelFormat
GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
PixelFormat maximum = PixelFormat::INVALID);
/** /**
* @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss * @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss
*/ */
static PixelFormat GetCompatiblePixelFormat(const PixelFormat& pix_fmt); static PixelFormat GetCompatiblePixelFormat(const PixelFormat &pix_fmt);
/** /**
* @brief Returns an FFmpeg pixel format for a given native pixel format * @brief Returns an FFmpeg pixel format for a given native pixel format
*/ */
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat& pix_fmt, int channel_layout); static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
int channel_layout);
/** /**
* @brief Returns a native sample format type for a given AVSampleFormat * @brief Returns a native sample format type for a given AVSampleFormat
*/ */
static SampleFormat GetNativeSampleFormat(const AVSampleFormat& smp_fmt); static SampleFormat GetNativeSampleFormat(const AVSampleFormat &smp_fmt);
/** /**
* @brief Returns an FFmpeg sample format type for a given native type * @brief Returns an FFmpeg sample format type for a given native type
@@ -83,7 +87,7 @@ public:
using AVFramePtr = std::shared_ptr<AVFrame>; using AVFramePtr = std::shared_ptr<AVFrame>;
inline AVFramePtr CreateAVFramePtr(AVFrame *f) inline AVFramePtr CreateAVFramePtr(AVFrame *f)
{ {
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); }); return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
} }
inline AVFramePtr CreateAVFramePtr() inline AVFramePtr CreateAVFramePtr()
{ {
+38 -21
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;
} }
@@ -125,9 +135,10 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
QFileInfoList l = d.entryInfoList(); QFileInfoList l = d.entryInfoList();
foreach (const QFileInfo& info, l) { foreach (const QFileInfo &info, l) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually // QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) { if (info.fileName() == QStringLiteral(".") ||
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"));
} }
} }
+15 -12
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
@@ -41,7 +42,7 @@ public:
*/ */
static bool IsPortable(); static bool IsPortable();
static QString GetUniqueFileIdentifier(const QString& filename); static QString GetUniqueFileIdentifier(const QString &filename);
static QString GetConfigurationLocation(); static QString GetConfigurationLocation();
@@ -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,9 +68,10 @@ public:
* @return The filename provided either untouched or with the extension appended to it. * @return The filename provided either untouched or with the extension appended to it.
*/ */
static QString EnsureFilenameExtension(QString fn, const QString& extension); static QString EnsureFilenameExtension(QString fn,
const QString &extension);
static QString ReadFileAsString(const QString& filename); static QString ReadFileAsString(const QString &filename);
/** /**
* @brief Returns a temporary filename that can be used while writing rather than the original * @brief Returns a temporary filename that can be used while writing rather than the original
@@ -78,12 +83,13 @@ public:
* This function returns a slight variant of the filename provided that's guaranteed to not exist * This function returns a slight variant of the filename provided that's guaranteed to not exist
* and therefore won't overwrite anything important. * and therefore won't overwrite anything important.
*/ */
static QString GetSafeTemporaryFilename(const QString& original); static QString GetSafeTemporaryFilename(const QString &original);
/** /**
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first * @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
*/ */
static bool RenameFileAllowOverwrite(const QString& from, const QString& to); static bool RenameFileAllowOverwrite(const QString &from,
const QString &to);
inline static QString GetFormattedExecutableForPlatform(QString unformatted) inline static QString GetFormattedExecutableForPlatform(QString unformatted)
{ {
@@ -95,11 +101,8 @@ public:
} }
static QString GetAutoRecoveryRoot(); static QString GetAutoRecoveryRoot();
}; };
} }
#endif // FILEFUNCTIONS_H #endif // FILEFUNCTIONS_H
+69 -43
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)
{ {
@@ -24,7 +23,7 @@ QString Html::DocToHtml(const QTextDocument *doc)
//writer.setAutoFormatting(true); //writer.setAutoFormatting(true);
for (auto it=doc->begin(); it!=doc->end(); it=it.next()) { for (auto it = doc->begin(); it != doc->end(); it = it.next()) {
WriteBlock(&writer, it); WriteBlock(&writer, it);
} }
@@ -40,7 +39,7 @@ QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack)
{ {
QTextCharFormat f; QTextCharFormat f;
for (int i=0; i<stack.size(); i++) { for (int i = 0; i < stack.size(); i++) {
f.merge(stack.at(i).format); f.merge(stack.at(i).format);
} }
@@ -63,7 +62,7 @@ void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
QTextCharFormat default_fmt; QTextCharFormat default_fmt;
default_fmt.setFontWeight(QFont::Normal); default_fmt.setFontWeight(QFont::Normal);
fmt_stack.append({QStringLiteral("html"), default_fmt}); fmt_stack.append({ QStringLiteral("html"), default_fmt });
QTextCharFormat current_fmt; QTextCharFormat current_fmt;
@@ -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,15 +88,13 @@ 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--) {
if (fmt_stack.at(i).tag == tag) { if (fmt_stack.at(i).tag == tag) {
fmt_stack.removeAt(i); fmt_stack.removeAt(i);
current_fmt = MergeHtmlFormats(fmt_stack); current_fmt = MergeHtmlFormats(fmt_stack);
@@ -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());
@@ -155,7 +155,7 @@ void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
auto it = block.begin(); auto it = block.begin();
if (it != block.end()) { if (it != block.end()) {
for (; it!=block.end(); it++) { for (; it != block.end(); it++) {
WriteFragment(writer, it.fragment()); WriteFragment(writer, it.fragment());
} }
} }
@@ -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())));
} }
} }
@@ -284,31 +302,35 @@ QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
if (StrEquals(attr.name(), QStringLiteral("style"))) { if (StrEquals(attr.name(), QStringLiteral("style"))) {
auto css = GetCSSFromStyle(attr.value().toString()); auto css = GetCSSFromStyle(attr.value().toString());
for (auto it=css.begin(); it!=css.end(); it++) { for (auto it = css.begin(); it != css.end(); it++) {
const QString &first_val = it.value().first(); const QString &first_val = it.value().first();
if (it.key() == QStringLiteral("font-family")) { if (it.key() == QStringLiteral("font-family")) {
fmt.setFontFamilies({first_val}); fmt.setFontFamilies({ first_val });
} else if (it.key() == QStringLiteral("font-size")) { } else if (it.key() == QStringLiteral("font-size")) {
if (first_val.endsWith(QStringLiteral("pt"), Qt::CaseInsensitive)) { if (first_val.endsWith(QStringLiteral("pt"),
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('%'))) {
@@ -366,11 +389,13 @@ QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
} else if (StrEquals(attr.name(), QStringLiteral("style"))) { } else if (StrEquals(attr.name(), QStringLiteral("style"))) {
auto css = GetCSSFromStyle(attr.value().toString()); auto css = GetCSSFromStyle(attr.value().toString());
for (auto it=css.begin(); it!=css.end(); it++) { for (auto it = css.begin(); it != css.end(); it++) {
if (it.key() == QStringLiteral("line-height")) { if (it.key() == QStringLiteral("line-height")) {
const QString &first_val = it.value().constFirst(); const QString &first_val = it.value().constFirst();
if (first_val.contains(QChar('%'))) { if (first_val.contains(QChar('%'))) {
block_fmt.setLineHeight(first_val.chopped(1).toDouble(), QTextBlockFormat::ProportionalHeight); block_fmt.setLineHeight(
first_val.chopped(1).toDouble(),
QTextBlockFormat::ProportionalHeight);
} }
} }
} }
@@ -409,7 +434,7 @@ QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
const QString &val = kv.at(1); const QString &val = kv.at(1);
QChar in_quote(0); QChar in_quote(0);
QString current_str; QString current_str;
for (int i=0; i<val.size(); i++) { for (int i = 0; i < val.size(); i++) {
const QChar &current_char = val.at(i); const QChar &current_char = val.at(i);
if (!in_quote.isNull()) { if (!in_quote.isNull()) {
@@ -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);
+13 -8
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;
}; };
} }
+3 -2
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;
@@ -24,7 +25,7 @@ void JobTime::Acquire()
} }
QDebug operator<<(QDebug debug, const olive::JobTime& r) QDebug operator<<(QDebug debug, const olive::JobTime &r)
{ {
return debug.space() << r.value(); return debug.space() << r.value();
} }
+4 -5
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,12 +50,11 @@ public:
private: private:
uint64_t value_; uint64_t value_;
}; };
} }
QDebug operator<<(QDebug debug, const olive::JobTime& r); QDebug operator<<(QDebug debug, const olive::JobTime &r);
Q_DECLARE_METATYPE(olive::JobTime) Q_DECLARE_METATYPE(olive::JobTime)
+5 -4
View File
@@ -21,19 +21,20 @@
#ifndef LERP_H #ifndef LERP_H
#define LERP_H #define LERP_H
template<typename T> template <typename T>
/** /**
* @brief Linearly interpolate a value between a and b using t * @brief Linearly interpolate a value between a and b using t
* *
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value * t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
* in between a and b at that point linearly. * in between a and b at that point linearly.
*/ */
T lerp(T a, T b, double t) { T lerp(T a, T b, double t)
{
return (a * (1.0 - t)) + (b * t); return (a * (1.0 - t)) + (b * t);
} }
template<typename T> template <typename T> T lerp(T a, T b, float t)
T lerp(T a, T b, float t) { {
return (a * (1.0f - t)) + (b * t); return (a * (1.0f - t)) + (b * t);
} }
+39 -35
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);
} }
@@ -125,7 +126,7 @@ public:
* *
* There is no need to use this outside of the memory pool's internal functions. * There is no need to use this outside of the memory pool's internal functions.
*/ */
Element(Arena* parent, uint8_t* data) Element(Arena *parent, uint8_t *data)
{ {
parent_ = parent; parent_ = parent;
data_ = data; data_ = data;
@@ -147,17 +148,17 @@ public:
/** /**
* @brief Access data represented in the pool * @brief Access data represented in the pool
*/ */
inline uint8_t* data() const inline uint8_t *data() const
{ {
return data_; return data_;
} }
inline const int64_t& timestamp() const inline const int64_t &timestamp() const
{ {
return timestamp_; return timestamp_;
} }
inline void set_timestamp(const int64_t& timestamp) inline void set_timestamp(const int64_t &timestamp)
{ {
timestamp_ = timestamp; timestamp_ = timestamp;
} }
@@ -178,7 +179,7 @@ public:
* Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of * Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of
* time, it can probably be freed back into the pool). This requires all usages to call `access()`. * time, it can probably be freed back into the pool). This requires all usages to call `access()`.
*/ */
inline const int64_t& last_accessed() const inline const int64_t &last_accessed() const
{ {
return accessed_; return accessed_;
} }
@@ -192,14 +193,13 @@ public:
} }
private: private:
Arena* parent_; Arena *parent_;
uint8_t* data_; uint8_t *data_;
int64_t timestamp_; int64_t timestamp_;
int64_t accessed_; int64_t accessed_;
}; };
using ElementPtr = std::shared_ptr<Element>; using ElementPtr = std::shared_ptr<Element>;
@@ -213,7 +213,7 @@ public:
*/ */
class Arena { class Arena {
public: public:
Arena(MemoryPool* parent) Arena(MemoryPool *parent)
{ {
parent_ = parent; parent_ = parent;
data_ = nullptr; data_ = nullptr;
@@ -223,12 +223,12 @@ public:
~Arena() ~Arena()
{ {
std::list<Element*> copy = lent_elements_; std::list<Element *> copy = lent_elements_;
foreach (Element* e, copy) { foreach (Element *e, copy) {
e->release(); e->release();
} }
delete [] data_; delete[] data_;
} }
DISABLE_COPY_MOVE(Arena) DISABLE_COPY_MOVE(Arena)
@@ -240,13 +240,14 @@ public:
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
for (int i=0;i<available_.size();i++) { for (int i = 0; i < available_.size(); i++) {
if (available_.at(i)) { if (available_.at(i)) {
// This buffer is available // This buffer is available
available_.replace(i, false); available_.replace(i, false);
ElementPtr e = std::make_shared<Element>(this, ElementPtr e = std::make_shared<Element>(
reinterpret_cast<uint8_t*>(data_ + i * element_sz_)); this,
reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
lent_elements_.push_back(e.get()); lent_elements_.push_back(e.get());
return e; return e;
@@ -259,10 +260,11 @@ public:
/** /**
* @brief Releases an element back into the pool for use elsewhere * @brief Releases an element back into the pool for use elsewhere
*/ */
void Release(Element* e) void Release(Element *e)
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_); quintptr diff = reinterpret_cast<quintptr>(e->data()) -
reinterpret_cast<quintptr>(data_);
int index = diff / element_sz_; int index = diff / element_sz_;
@@ -320,9 +322,9 @@ public:
} }
private: private:
MemoryPool* parent_; MemoryPool *parent_;
uint8_t* data_; uint8_t *data_;
size_t allocated_sz_; size_t allocated_sz_;
@@ -332,10 +334,9 @@ public:
size_t element_sz_; size_t element_sz_;
std::list<Element*> lent_elements_; std::list<Element *> lent_elements_;
qint64 empty_time_; qint64 empty_time_;
}; };
/** /**
@@ -346,7 +347,7 @@ public:
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
// Attempt to get an element from an arena // Attempt to get an element from an arena
foreach (Arena* a, arenas_) { foreach (Arena *a, arenas_) {
ElementPtr e = a->Get(); ElementPtr e = a->Get();
if (e) { if (e) {
@@ -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;
} }
@@ -398,7 +401,7 @@ protected:
private: private:
int element_count_; int element_count_;
std::list<Arena*> arenas_; std::list<Arena *> arenas_;
QMutex lock_; QMutex lock_;
@@ -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);
}; };
+11 -15
View File
@@ -22,29 +22,25 @@
#include <QDebug> #include <QDebug>
namespace olive { namespace olive
void OIIOUtils::FrameToBuffer(const Frame* frame, OIIO::ImageBuf *buf)
{ {
buf->set_pixels(OIIO::ROI(),
buf->spec().format, void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
frame->const_data(), {
OIIO::AutoStride, buf->set_pixels(OIIO::ROI(), buf->spec().format, frame->const_data(),
frame->linesize_bytes()); OIIO::AutoStride, frame->linesize_bytes());
} }
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame* frame) void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame *frame)
{ {
buf->get_pixels(OIIO::ROI(), buf->get_pixels(OIIO::ROI(), buf->spec().format, frame->data(),
buf->spec().format, OIIO::AutoStride, frame->linesize_bytes());
frame->data(),
OIIO::AutoStride,
frame->linesize_bytes());
} }
rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec) rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
{ {
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1)); return rational::fromDouble(
spec.get_float_attribute("PixelAspectRatio", 1));
} }
PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type) PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type)
+7 -6
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:
@@ -50,14 +52,13 @@ public:
return OIIO::TypeDesc::UNKNOWN; return OIIO::TypeDesc::UNKNOWN;
} }
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf* buf); static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf);
static void BufferToFrame(OIIO::ImageBuf* buf, Frame* frame); static void BufferToFrame(OIIO::ImageBuf *buf, Frame *frame);
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type); static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec); static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec);
}; };
} }
+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)
{ {
+30 -19
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
@@ -34,7 +36,7 @@ int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString& s) {
QFrame *QtUtils::CreateHorizontalLine() QFrame *QtUtils::CreateHorizontalLine()
{ {
QFrame* horizontal_line = new QFrame(); QFrame *horizontal_line = new QFrame();
horizontal_line->setFrameShape(QFrame::HLine); horizontal_line->setFrameShape(QFrame::HLine);
horizontal_line->setFrameShadow(QFrame::Sunken); horizontal_line->setFrameShadow(QFrame::Sunken);
return horizontal_line; return horizontal_line;
@@ -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,26 +89,28 @@ QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
return dt.toString(Qt::TextDate); return dt.toString(Qt::TextDate);
} }
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width) QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm,
int bounding_width)
{ {
QStringList list; QStringList list;
QStringList lines = s.split('\n'); QStringList lines = s.split('\n');
// Iterate every line // Iterate every line
for (int i=0; i<lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
QString this_line = lines.at(i); QString this_line = lines.at(i);
while (this_line.size() > 1 && QFontMetricsWidth(fm, this_line) >= bounding_width) { while (this_line.size() > 1 &&
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;
@@ -164,7 +175,7 @@ Qt::KeyboardModifiers QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifier
void QtUtils::SetComboBoxData(QComboBox *cb, int data) void QtUtils::SetComboBoxData(QComboBox *cb, int data)
{ {
for (int i=0; i<cb->count(); i++) { for (int i = 0; i < cb->count(); i++) {
if (cb->itemData(i).toInt() == data) { if (cb->itemData(i).toInt() == data) {
cb->setCurrentIndex(i); cb->setCurrentIndex(i);
break; break;
@@ -174,7 +185,7 @@ void QtUtils::SetComboBoxData(QComboBox *cb, int data)
void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data) void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data)
{ {
for (int i=0; i<cb->count(); i++) { for (int i = 0; i < cb->count(); i++) {
if (cb->itemData(i).toString() == data) { if (cb->itemData(i).toString() == data) {
cb->setCurrentIndex(i); cb->setCurrentIndex(i);
break; break;
@@ -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);
} }
} }
} }
+21 -18
View File
@@ -29,7 +29,8 @@
#include <QFrame> #include <QFrame>
#include <QMessageBox> #include <QMessageBox>
namespace olive { namespace olive
{
class QtUtils { class QtUtils {
public: public:
@@ -40,32 +41,35 @@ public:
* latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for * latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for
* earlier. * earlier.
*/ */
static int QFontMetricsWidth(QFontMetrics fm, const QString& s); static int QFontMetricsWidth(QFontMetrics fm, const QString &s);
static QFrame* CreateHorizontalLine(); static QFrame *CreateHorizontalLine();
static QFrame* CreateVerticalLine(); static QFrame *CreateVerticalLine();
static int MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok); static int MsgBox(QWidget *parent, QMessageBox::Icon icon,
const QString &title, const QString &message,
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
static QDateTime GetCreationDate(const QFileInfo &info); static QDateTime GetCreationDate(const QFileInfo &info);
static QString GetFormattedDateTime(const QDateTime &dt); static QString GetFormattedDateTime(const QDateTime &dt);
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width); static QStringList WordWrapString(const QString &s, const QFontMetrics &fm,
int bounding_width);
static Qt::KeyboardModifiers FlipControlAndShiftModifiers(Qt::KeyboardModifiers e); static Qt::KeyboardModifiers
FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
static void SetComboBoxData(QComboBox *cb, int data); static void SetComboBoxData(QComboBox *cb, int data);
static void SetComboBoxData(QComboBox *cb, const QString &data); static void SetComboBoxData(QComboBox *cb, const QString &data);
template <typename T> template <typename T> static T *GetParentOfType(const QObject *child)
static T *GetParentOfType(const QObject *child)
{ {
QObject *t = child->parent(); QObject *t = child->parent();
while (t) { while (t) {
if (T *p = dynamic_cast<T*>(t)) { if (T *p = dynamic_cast<T *>(t)) {
return p; return p;
} }
t = t->parent(); t = t->parent();
@@ -79,7 +83,7 @@ public:
/** /**
* @brief Convert a pointer to a value that can be sent between NodeParams * @brief Convert a pointer to a value that can be sent between NodeParams
*/ */
static QVariant PtrToValue(void* ptr) static QVariant PtrToValue(void *ptr)
{ {
return reinterpret_cast<quintptr>(ptr); return reinterpret_cast<quintptr>(ptr);
} }
@@ -87,18 +91,17 @@ 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);
} }
} }
+4 -4
View File
@@ -23,11 +23,11 @@
#include <QInputDialog> #include <QInputDialog>
namespace olive { namespace olive
{
double GetFloatRatioFromUser(QWidget* parent, double GetFloatRatioFromUser(QWidget *parent, const QString &title,
const QString& title, bool *ok_in);
bool* ok_in);
} }
+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()) {
+5 -3
View File
@@ -27,7 +27,8 @@
#include "render/cancelatom.h" #include "render/cancelatom.h"
#include "undo/undocommand.h" #include "undo/undocommand.h"
namespace olive { namespace olive
{
class Block; class Block;
class Node; class Node;
@@ -35,7 +36,7 @@ class NodeInput;
class NodeGroup; class NodeGroup;
#define XMLAttributeLoop(reader, item) \ #define XMLAttributeLoop(reader, item) \
foreach (const QXmlStreamAttribute& item, reader->attributes()) foreach (const QXmlStreamAttribute &item, reader->attributes())
/** /**
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document * @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
@@ -46,7 +47,8 @@ class NodeGroup;
* *
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error * See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
*/ */
bool XMLReadNextStartElement(QXmlStreamReader* reader, CancelAtom *cancel_atom = nullptr); bool XMLReadNextStartElement(QXmlStreamReader *reader,
CancelAtom *cancel_atom = nullptr);
} }
+209 -101
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);
} }
} }
+8 -6
View File
@@ -27,14 +27,15 @@
#include "node/value.h" #include "node/value.h"
namespace olive { namespace olive
{
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)] #define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
#define OLIVE_CONFIG_STR(x) Config::Current()[x] #define OLIVE_CONFIG_STR(x) Config::Current()[x]
class Config { class Config {
public: public:
static Config& Current(); static Config &Current();
void SetDefaults(); void SetDefaults();
@@ -42,11 +43,11 @@ public:
static void Save(); static void Save();
QVariant operator[](const QString&) const; QVariant operator[](const QString &) const;
QVariant& operator[](const QString&); QVariant &operator[](const QString &);
NodeValue::Type GetConfigEntryType(const QString& key) const; NodeValue::Type GetConfigEntryType(const QString &key) const;
private: private:
Config(); Config();
@@ -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_;
+286 -200
View File
File diff suppressed because it is too large Load Diff
+55 -54
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
{ {
@@ -88,17 +83,17 @@ public:
return startup_project_; return startup_project_;
} }
void set_startup_project(const QString& p) void set_startup_project(const QString &p)
{ {
startup_project_ = p; startup_project_ = p;
} }
const QString& startup_language() const const QString &startup_language() const
{ {
return startup_language_; return startup_language_;
} }
void set_startup_language(const QString& s) void set_startup_language(const QString &s)
{ {
startup_language_ = s; startup_language_ = s;
} }
@@ -123,7 +118,6 @@ public:
bool run_fullscreen_; bool run_fullscreen_;
bool crash_; bool crash_;
}; };
/** /**
@@ -131,16 +125,16 @@ public:
* *
* Currently empty * Currently empty
*/ */
Core(const CoreParams& params); Core(const CoreParams &params);
/** /**
* @brief Core object accessible from anywhere in the code * @brief Core object accessible from anywhere in the code
* *
* Use this to access Core functions. * Use this to access Core functions.
*/ */
static Core* instance(); static Core *instance();
const CoreParams& core_params() const const CoreParams &core_params() const
{ {
return core_params_; return core_params_;
} }
@@ -166,12 +160,12 @@ public:
* *
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode. * Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
*/ */
MainWindow* main_window(); MainWindow *main_window();
/** /**
* @brief Retrieve UndoStack object * @brief Retrieve UndoStack object
*/ */
UndoStack* undo_stack(); UndoStack *undo_stack();
/** /**
* @brief Import a list of files * @brief Import a list of files
@@ -181,32 +175,32 @@ public:
* *
* @param urls * @param urls
*/ */
void ImportFiles(const QStringList& urls, Folder *parent); void ImportFiles(const QStringList &urls, Folder *parent);
/** /**
* @brief Get the currently active tool * @brief Get the currently active tool
*/ */
const Tool::Item& tool() const; const Tool::Item &tool() const;
/** /**
* @brief Get the currently selected object that the add tool should make (if the add tool is active) * @brief Get the currently selected object that the add tool should make (if the add tool is active)
*/ */
const Tool::AddableObject& GetSelectedAddableObject() const; const Tool::AddableObject &GetSelectedAddableObject() const;
/** /**
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active) * @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
*/ */
const QString& GetSelectedTransition() const; const QString &GetSelectedTransition() const;
/** /**
* @brief Get current snapping value * @brief Get current snapping value
*/ */
const bool& snapping() const; const bool &snapping() const;
/** /**
* @brief Returns a list of the most recently opened/saved projects * @brief Returns a list of the most recently opened/saved projects
*/ */
const QStringList& GetRecentProjects() const; const QStringList &GetRecentProjects() const;
/** /**
* @brief Get the currently active project * @brief Get the currently active project
@@ -218,8 +212,8 @@ public:
* *
* The active Project file, or nullptr if the heuristic couldn't find one. * The active Project file, or nullptr if the heuristic couldn't find one.
*/ */
Project* GetActiveProject() const; Project *GetActiveProject() const;
Folder* GetSelectedFolderInActiveProject() const; Folder *GetSelectedFolderInActiveProject() const;
/** /**
* @brief Gets current timecode display mode * @brief Gets current timecode display mode
@@ -236,7 +230,7 @@ public:
*/ */
void SetAutorecoveryInterval(int minutes); void SetAutorecoveryInterval(int minutes);
static void CopyStringToClipboard(const QString& s); static void CopyStringToClipboard(const QString &s);
static QString PasteStringFromClipboard(); static QString PasteStringFromClipboard();
@@ -248,13 +242,15 @@ 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,
static Sequence* CreateNewSequenceForProject(Project *project) Project *project);
static Sequence *CreateNewSequenceForProject(Project *project)
{ {
return CreateNewSequenceForProject(tr("Sequence %1"), project); return CreateNewSequenceForProject(tr("Sequence %1"), project);
} }
@@ -277,29 +273,34 @@ 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
*/ */
bool SetLanguage(const QString& locale); bool SetLanguage(const QString &locale);
/** /**
* @brief Show message in main window's status bar * @brief Show message in main window's status bar
* *
* Shorthand for Core::instance()->main_window()->statusBar()->showMessage(); * Shorthand for Core::instance()->main_window()->statusBar()->showMessage();
*/ */
void ShowStatusBarMessage(const QString& s, int timeout = 0); void ShowStatusBarMessage(const QString &s, int timeout = 0);
void ClearStatusBarMessage(); void ClearStatusBarMessage();
void OpenRecoveryProject(const QString& filename); void OpenRecoveryProject(const QString &filename);
void OpenNodeInViewer(ViewerOutput* viewer); void OpenNodeInViewer(ViewerOutput *viewer);
void OpenExportDialogForViewer(ViewerOutput *viewer, bool start_still_image); void OpenExportDialogForViewer(ViewerOutput *viewer,
bool start_still_image);
bool IsMagicEnabled() const { return magic_; } bool IsMagicEnabled() const
{
return magic_;
}
public slots: public slots:
/** /**
@@ -324,12 +325,12 @@ public slots:
* *
* @param tool * @param tool
*/ */
void SetTool(const Tool::Item& tool); void SetTool(const Tool::Item &tool);
/** /**
* @brief Set the current snapping setting * @brief Set the current snapping setting
*/ */
void SetSnapping(const bool& b); void SetSnapping(const bool &b);
/** /**
* @brief Show an About dialog * @brief Show an About dialog
@@ -360,7 +361,7 @@ public slots:
* @brief Show OTIO import dialog * @brief Show OTIO import dialog
*/ */
#ifdef USE_OTIO #ifdef USE_OTIO
bool DialogImportOTIOShow(const QList<Sequence*>& sequences); bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
#endif #endif
/** /**
@@ -376,12 +377,12 @@ public slots:
/** /**
* @brief Set the currently selected object that the add tool should make * @brief Set the currently selected object that the add tool should make
*/ */
void SetSelectedAddableObject(const Tool::AddableObject& obj); void SetSelectedAddableObject(const Tool::AddableObject &obj);
/** /**
* @brief Set the currently selected object that the add tool should make * @brief Set the currently selected object that the add tool should make
*/ */
void SetSelectedTransitionObject(const QString& obj); void SetSelectedTransitionObject(const QString &obj);
/** /**
* @brief Clears the list of recently opened/saved projects * @brief Clears the list of recently opened/saved projects
@@ -410,7 +411,7 @@ signals:
/** /**
* @brief Signal emitted when the tool is changed from somewhere * @brief Signal emitted when the tool is changed from somewhere
*/ */
void ToolChanged(const Tool::Item& tool); void ToolChanged(const Tool::Item &tool);
/** /**
* @brief Signal emitted when addable object changes through SetSelectedAddableObject * @brief Signal emitted when addable object changes through SetSelectedAddableObject
@@ -420,7 +421,7 @@ signals:
/** /**
* @brief Signal emitted when the snapping setting is changed * @brief Signal emitted when the snapping setting is changed
*/ */
void SnappingChanged(const bool& b); void SnappingChanged(const bool &b);
/** /**
* @brief Signal emitted when the default timecode display mode changed * @brief Signal emitted when the default timecode display mode changed
@@ -502,16 +503,16 @@ private:
/** /**
* @brief Adds a project to the "open projects" list * @brief Adds a project to the "open projects" list
*/ */
void AddOpenProject(olive::Project* p, bool add_to_recents = false); void AddOpenProject(olive::Project *p, bool add_to_recents = false);
bool AddOpenProjectFromTask(Task* task, bool add_to_recents); bool AddOpenProjectFromTask(Task *task, bool add_to_recents);
void SetActiveProject(Project *p); void SetActiveProject(Project *p);
/** /**
* @brief Internal main window object * @brief Internal main window object
*/ */
MainWindow* main_window_; MainWindow *main_window_;
/** /**
* @brief List of currently open projects * @brief List of currently open projects
@@ -561,12 +562,12 @@ private:
/** /**
* @brief Static singleton core instance * @brief Static singleton core instance
*/ */
static Core* instance_; static Core *instance_;
/** /**
* @brief Internal translator * @brief Internal translator
*/ */
QTranslator* translator_; QTranslator *translator_;
/** /**
* @brief List of projects that are unsaved but have autorecovery projects * @brief List of projects that are unsaved but have autorecovery projects
@@ -590,7 +591,7 @@ private slots:
void ProjectSaveSucceeded(Task *task); void ProjectSaveSucceeded(Task *task);
bool AddOpenProjectFromTaskAndAddToRecents(Task* task) bool AddOpenProjectFromTaskAndAddToRecents(Task *task)
{ {
return AddOpenProjectFromTask(task, true); return AddOpenProjectFromTask(task, true);
} }
@@ -605,15 +606,15 @@ private slots:
void OpenStartupProject(); void OpenStartupProject();
void AddRecoveryProjectFromTask(Task* task); void AddRecoveryProjectFromTask(Task *task);
/** /**
* @brief Internal project open * @brief Internal project open
*/ */
void OpenProjectInternal(const QString& filename, bool recovery_project = false); void OpenProjectInternal(const QString &filename,
bool recovery_project = false);
void ImportSingleFile(const QString &f); void ImportSingleFile(const QString &f);
}; };
} }
+78 -43
View File
@@ -40,9 +40,10 @@
#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)
{ {
setWindowTitle(tr("Olive")); setWindowTitle(tr("Olive"));
setWindowFlags(Qt::WindowStaysOnTopHint); setWindowFlags(Qt::WindowStaysOnTopHint);
@@ -50,23 +51,25 @@ CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
report_filename_ = report_path; report_filename_ = report_path;
waiting_for_upload_ = false; waiting_for_upload_ = false;
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);
splitter->setChildrenCollapsible(false); splitter->setChildrenCollapsible(false);
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_);
QWidget* crash_widget = new QWidget(); QWidget *crash_widget = new QWidget();
QVBoxLayout* crash_widget_layout = new QVBoxLayout(crash_widget); QVBoxLayout *crash_widget_layout = new QVBoxLayout(crash_widget);
crash_widget_layout->setMargin(0); crash_widget_layout->setMargin(0);
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:"))); crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
@@ -78,16 +81,18 @@ CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
splitter->addWidget(crash_widget); splitter->addWidget(crash_widget);
QHBoxLayout* btn_layout = new QHBoxLayout(); QHBoxLayout *btn_layout = new QHBoxLayout();
btn_layout->setMargin(0); btn_layout->setMargin(0);
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"));
@@ -128,20 +134,25 @@ QString CrashHandlerDialog::GetSymbolPath()
void CrashHandlerDialog::GenerateReport() void CrashHandlerDialog::GenerateReport()
{ {
QProcess* p = new QProcess(); QProcess *p = new QProcess();
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &CrashHandlerDialog::ReadProcessFinished); this, &CrashHandlerDialog::ReadProcessFinished);
connect(p, &QProcess::readyReadStandardOutput, this, &CrashHandlerDialog::ReadProcessHasData); connect(p, &QProcess::readyReadStandardOutput, this,
&CrashHandlerDialog::ReadProcessHasData);
QString stackwalk_filename = FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("minidump_stackwalk")); QString stackwalk_filename =
FileFunctions::GetFormattedExecutableForPlatform(
QStringLiteral("minidump_stackwalk"));
QString stackwalk_bin = QDir(qApp->applicationDirPath()).filePath(stackwalk_filename); QString stackwalk_bin =
p->start(stackwalk_bin, {report_filename_, GetSymbolPath()}); QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin)); p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
crash_report_->setText(
QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
} }
void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply) void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
{ {
waiting_for_upload_ = false; waiting_for_upload_ = false;
@@ -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,50 +222,64 @@ 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);
dump_part.setBodyDevice(dump_file); dump_part.setBodyDevice(dump_file);
dump_file->setParent(multipart); // Delete file with multipart dump_file->setParent(multipart); // Delete file with multipart
@@ -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);
@@ -323,13 +357,14 @@ void CrashHandlerDialog::SendErrorReport()
waiting_for_upload_ = true; waiting_for_upload_ = true;
} }
void CrashHandlerDialog::closeEvent(QCloseEvent* e) void CrashHandlerDialog::closeEvent(QCloseEvent *e)
{ {
QMessageBox b(this); QMessageBox b(this);
b.setIcon(QMessageBox::Warning); b.setIcon(QMessageBox::Warning);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Confirm Close")); b.setWindowTitle(tr("Confirm Close"));
b.setText(tr("Crash report is still uploading. Closing now may result in no " b.setText(
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);
+9 -10
View File
@@ -30,13 +30,13 @@
#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);
private: private:
void SetGUIObjectsEnabled(bool e); void SetGUIObjectsEnabled(bool e);
@@ -45,13 +45,13 @@ private:
static QString GetSymbolPath(); static QString GetSymbolPath();
QTextEdit* summary_edit_; QTextEdit *summary_edit_;
QTextEdit* crash_report_; QTextEdit *crash_report_;
QPushButton* send_report_btn_; QPushButton *send_report_btn_;
QPushButton* dont_send_btn_; QPushButton *dont_send_btn_;
QString report_filename_; QString report_filename_;
@@ -60,7 +60,7 @@ private:
bool waiting_for_upload_; bool waiting_for_upload_;
protected: protected:
virtual void closeEvent(QCloseEvent* e) override; virtual void closeEvent(QCloseEvent *e) override;
private slots: private slots:
void ReplyFinished(QNetworkReply *reply); void ReplyFinished(QNetworkReply *reply);
@@ -74,7 +74,6 @@ private slots:
void ReadProcessFinished(); void ReadProcessFinished();
void SendErrorReport(); void SendErrorReport();
}; };
} }
+33 -20
View File
@@ -30,39 +30,45 @@
#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()));
} }
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(),
horiz_layout->setSpacing(fm.height()*2); fm.height());
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,23 +91,27 @@ 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);
layout->addWidget(support_lbl); layout->addWidget(support_lbl);
if (!patrons.isEmpty()) { if (!patrons.isEmpty()) {
ScrollingLabel* scroll = new ScrollingLabel(patrons); ScrollingLabel *scroll = new ScrollingLabel(patrons);
scroll->StartAnimating(); scroll->StartAnimating();
layout->addWidget(scroll); layout->addWidget(scroll);
} }
@@ -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_;
}; };
} }
+21 -18
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);
} }
@@ -50,7 +51,7 @@ void ScrollingLabel::SetText(const QStringList &text)
text_height_ = fm.height(); text_height_ = fm.height();
int width = 0; int width = 0;
foreach (const QString& s, text_) { foreach (const QString &s, text_) {
width = qMax(width, QtUtils::QFontMetricsWidth(fm, s)); width = qMax(width, QtUtils::QFontMetricsWidth(fm, s));
} }
@@ -69,8 +70,8 @@ void ScrollingLabel::paintEvent(QPaintEvent *e)
QFontMetrics fm = p.fontMetrics(); QFontMetrics fm = p.fontMetrics();
int half_width = width(); int half_width = width();
for (int i=0; i<text_.size(); i++) { for (int i = 0; i < text_.size(); i++) {
int text_y = fm.ascent() + height() - animate_ + (fm.height()*i); int text_y = fm.ascent() + height() - animate_ + (fm.height() * i);
int text_bottom = text_y + fm.descent(); int text_bottom = text_y + fm.descent();
int text_top = text_y - fm.ascent(); int text_top = text_y - fm.ascent();
@@ -78,17 +79,18 @@ void ScrollingLabel::paintEvent(QPaintEvent *e)
continue; continue;
} }
const QString& s = text_.at(i); const QString &s = text_.at(i);
int width = QtUtils::QFontMetricsWidth(fm, s); int width = QtUtils::QFontMetricsWidth(fm, s);
p.drawText(half_width/2 - width/2, text_y, s); p.drawText(half_width / 2 - width / 2, text_y, s);
} }
for (int y=0; y<text_height_; y++) { for (int y = 0; y < text_height_; y++) {
double mul = double(y)/double(text_height_); double mul = double(y) / double(text_height_);
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul); SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
SetOpacityOfScanLine(map.scanLine(map.height()-1-y), map.width(), 4, mul); SetOpacityOfScanLine(map.scanLine(map.height() - 1 - y),
map.width(), 4, mul);
} }
} }
@@ -96,12 +98,13 @@ 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];
for (int c=0; c<channels; c++) { for (int c = 0; c < channels; c++) {
pixel[c] *= mul; pixel[c] *= mul;
} }
} }
+9 -9
View File
@@ -24,16 +24,16 @@
#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);
ScrollingLabel(const QStringList& text, QWidget* parent = nullptr); ScrollingLabel(const QStringList &text, QWidget *parent = nullptr);
void SetText(const QStringList& text); void SetText(const QStringList &text);
void StartAnimating() void StartAnimating()
{ {
@@ -46,10 +46,11 @@ public:
} }
protected: protected:
virtual void paintEvent(QPaintEvent* e) override; virtual void paintEvent(QPaintEvent *e) override;
private: private:
static void SetOpacityOfScanLine(uchar* scan_line, int width, int channels, double mul); static void SetOpacityOfScanLine(uchar *scan_line, int width, int channels,
double mul);
static const int kMinLineHeight; static const int kMinLineHeight;
@@ -63,7 +64,6 @@ private:
private slots: private slots:
void AnimationUpdate(); void AnimationUpdate();
}; };
} }
+62 -58
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);
@@ -39,32 +40,35 @@ ActionSearch::ActionSearch(QWidget *parent) :
setStyleSheet("#ASDiag{border: 2px solid #808080;}"); setStyleSheet("#ASDiag{border: 2px solid #808080;}");
// Size proportionally to the parent (usually MainWindow). // Size proportionally to the parent (usually MainWindow).
resize(parent->width()/3, parent->height()/3); resize(parent->width() / 3, parent->height() / 3);
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it. // Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
setWindowFlags(Qt::Popup); setWindowFlags(Qt::Popup);
QVBoxLayout* layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
// Construct the main entry text field. // Construct the main entry text field.
ActionSearchEntry* entry_field = new ActionSearchEntry(this); ActionSearchEntry *entry_field = new ActionSearchEntry(this);
// Set the main entry field font size to 1.2x its standard font size. // Set the main entry field font size to 1.2x its standard font size.
QFont entry_field_font = entry_field->font(); QFont entry_field_font = entry_field->font();
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2)); entry_field_font.setPointSize(qRound(entry_field_font.pointSize() * 1.2));
entry_field->setFont(entry_field_font); entry_field->setFont(entry_field_font);
// Set placeholder text for the main entry field // Set placeholder text for the main entry field
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
@@ -72,7 +76,7 @@ ActionSearch::ActionSearch(QWidget *parent) :
// Set list's font to 1.2x its standard font size // Set list's font to 1.2x its standard font size
QFont list_widget_font = list_widget->font(); QFont list_widget_font = list_widget->font();
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2)); list_widget_font.setPointSize(qRound(list_widget_font.pointSize() * 1.2));
list_widget->setFont(list_widget_font); list_widget->setFont(list_widget_font);
layout->addWidget(list_widget); layout->addWidget(list_widget);
@@ -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,18 +107,17 @@ 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).
// We'll clear all the current items in the list since if we're here, we're just starting. // We'll clear all the current items in the list since if we're here, we're just starting.
list_widget->clear(); list_widget->clear();
QList<QAction*> menus = menu_bar_->actions(); QList<QAction *> menus = menu_bar_->actions();
// Loop through all menus from the menubar and run this function on each one. // Loop through all menus from the menubar and run this function on each one.
for (int i=0;i<menus.size();i++) { for (int i = 0; i < menus.size(); i++) {
QMenu* menu = menus.at(i)->menu(); QMenu *menu = menus.at(i)->menu();
search_update(s, p, menu); search_update(s, p, menu);
} }
@@ -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,82 +158,83 @@ 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).
int lim = list_widget->count(); int lim = list_widget->count();
for (int i=1;i<lim;i++) { for (int i = 1; i < lim; i++) {
if (list_widget->item(i)->isSelected()) { if (list_widget->item(i)->isSelected()) {
list_widget->item(i-1)->setSelected(true); list_widget->item(i - 1)->setSelected(true);
list_widget->scrollToItem(list_widget->item(i-1)); list_widget->scrollToItem(list_widget->item(i - 1));
break; break;
} }
} }
} }
void ActionSearch::move_selection_down() { void ActionSearch::move_selection_down()
{
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it // 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).
int lim = list_widget->count()-1; int lim = list_widget->count() - 1;
for (int i=0;i<lim;i++) { for (int i = 0; i < lim; i++) {
if (list_widget->item(i)->isSelected()) { if (list_widget->item(i)->isSelected()) {
list_widget->item(i+1)->setSelected(true); list_widget->item(i + 1)->setSelected(true);
list_widget->scrollToItem(list_widget->item(i+1)); list_widget->scrollToItem(list_widget->item(i + 1));
break; break;
} }
} }
} }
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {} ActionSearchEntry::ActionSearchEntry(QWidget *parent)
: QLineEdit(parent)
{
}
bool ActionSearchEntry::event(QEvent *e) bool ActionSearchEntry::event(QEvent *e)
{ {
switch (e->type()) { switch (e->type()) {
case QEvent::ShortcutOverride: case QEvent::ShortcutOverride:
switch (static_cast<QKeyEvent*>(e)->key()) { switch (static_cast<QKeyEvent *>(e)->key()) {
case Qt::Key_Up: case Qt::Key_Up:
case Qt::Key_Down: case Qt::Key_Down:
e->accept(); e->accept();
@@ -241,7 +243,7 @@ bool ActionSearchEntry::event(QEvent *e)
break; break;
case QEvent::KeyPress: case QEvent::KeyPress:
// Listen for up/down, otherwise pass the key event to the base class. // Listen for up/down, otherwise pass the key event to the base class.
switch (static_cast<QKeyEvent*>(e)->key()) { switch (static_cast<QKeyEvent *>(e)->key()) {
case Qt::Key_Up: case Qt::Key_Up:
e->accept(); e->accept();
emit moveSelectionUp(); emit moveSelectionUp();
@@ -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();
} }
} }
+14 -10
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:
/** /**
@@ -52,12 +52,12 @@ public:
* *
* QWidget parent. Usually MainWindow. * QWidget parent. Usually MainWindow.
*/ */
ActionSearch(QWidget* parent); ActionSearch(QWidget *parent);
/** /**
* @brief Set the menu bar to use in this action search * @brief Set the menu bar to use in this action search
*/ */
void SetMenuBar(QMenuBar* menu_bar); void SetMenuBar(QMenuBar *menu_bar);
private slots: private slots:
/** /**
* @brief Update the list of actions according to a search query * @brief Update the list of actions according to a search query
@@ -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,16 +109,17 @@ 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
*/ */
ActionSearchList* list_widget; ActionSearchList *list_widget;
/** /**
* @brief Attached menu bar object * @brief Attached menu bar object
*/ */
QMenuBar* menu_bar_; QMenuBar *menu_bar_;
}; };
/** /**
@@ -135,7 +137,8 @@ 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.
@@ -163,7 +166,8 @@ 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.
+41 -22
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);
@@ -43,7 +46,7 @@ AutoRecoveryDialog::AutoRecoveryDialog(const QString &message, const QStringList
void AutoRecoveryDialog::accept() void AutoRecoveryDialog::accept()
{ {
foreach (QTreeWidgetItem* checkable, checkable_items_) { foreach (QTreeWidgetItem *checkable, checkable_items_) {
if (checkable->checkState(0) == Qt::Checked) { if (checkable->checkState(0) == Qt::Checked) {
QString filename = checkable->data(0, kFilenameRole).toString(); QString filename = checkable->data(0, kFilenameRole).toString();
Core::instance()->OpenRecoveryProject(filename); Core::instance()->OpenRecoveryProject(filename);
@@ -53,9 +56,9 @@ void AutoRecoveryDialog::accept()
super::accept(); super::accept();
} }
void AutoRecoveryDialog::Init(const QString& header_text) void AutoRecoveryDialog::Init(const QString &header_text)
{ {
QVBoxLayout* layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
setWindowTitle(tr("Auto-Recovery")); setWindowTitle(tr("Auto-Recovery"));
@@ -65,25 +68,30 @@ 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());
foreach (const QString& recovery_folder, recoveries) { foreach (const QString &recovery_folder, recoveries) {
QDir recovery_dir(autorecovery_root.filePath(recovery_folder)); QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
QString pretty_name; QString pretty_name;
{ {
// Retrieve pretty name // Retrieve pretty name
QFile pretty_name_file(recovery_dir.filePath(QStringLiteral("realname.txt"))); QFile pretty_name_file(
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());
@@ -96,35 +104,46 @@ void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autoch
} }
} }
QTreeWidgetItem* top_level = new QTreeWidgetItem(tree_widget_); QTreeWidgetItem *top_level = new QTreeWidgetItem(tree_widget_);
top_level->setText(0, pretty_name); top_level->setText(0, pretty_name);
{ {
// Populate with recoveries // Populate with recoveries
QStringList entries = recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); QStringList entries =
for (int i=0; i<entries.size(); i++) { recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot,
const QString& entry = entries.at(i); QDir::Name | QDir::Reversed);
for (int i = 0; i < entries.size(); i++) {
const QString &entry = entries.at(i);
if (entry.endsWith(QStringLiteral(".ove"), Qt::CaseInsensitive)) { if (entry.endsWith(QStringLiteral(".ove"),
QTreeWidgetItem* entry_item = new QTreeWidgetItem(top_level); Qt::CaseInsensitive)) {
QTreeWidgetItem *entry_item =
new QTreeWidgetItem(top_level);
bool ok; bool ok;
qint64 recovery_time = entry.left(entry.indexOf('.')).toLongLong(&ok); qint64 recovery_time =
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);
} }
+8 -10
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;
@@ -42,14 +43,11 @@ private:
void PopulateTree(const QStringList &recoveries, bool autocheck); void PopulateTree(const QStringList &recoveries, bool autocheck);
QTreeWidget* tree_widget_; QTreeWidget *tree_widget_;
QVector<QTreeWidgetItem*> checkable_items_; QVector<QTreeWidgetItem *> checkable_items_;
enum DataRole {
kFilenameRole = Qt::UserRole
};
enum DataRole { kFilenameRole = Qt::UserRole };
}; };
} }
+59 -40
View File
@@ -26,33 +26,36 @@
#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"));
QVBoxLayout* layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
QSplitter* splitter = new QSplitter(Qt::Horizontal); QSplitter *splitter = new QSplitter(Qt::Horizontal);
splitter->setChildrenCollapsible(false); splitter->setChildrenCollapsible(false);
layout->addWidget(splitter); layout->addWidget(splitter);
QWidget* graphics_area = new QWidget(); QWidget *graphics_area = new QWidget();
splitter->addWidget(graphics_area); splitter->addWidget(graphics_area);
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area); QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
QHBoxLayout* wheel_layout = new QHBoxLayout(); QHBoxLayout *wheel_layout = new QHBoxLayout();
graphics_layout->addLayout(wheel_layout); graphics_layout->addLayout(wheel_layout);
color_wheel_ = new ColorWheelWidget(); color_wheel_ = new ColorWheelWidget();
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();
@@ -66,8 +69,8 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
swatch_layout->addStretch(); swatch_layout->addStretch();
QWidget* value_area = new QWidget(); QWidget *value_area = new QWidget();
QVBoxLayout* value_layout = new QVBoxLayout(value_area); QVBoxLayout *value_layout = new QVBoxLayout(value_area);
value_layout->setSpacing(0); value_layout->setSpacing(0);
splitter->addWidget(value_area); splitter->addWidget(value_area);
@@ -80,31 +83,46 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
value_layout->addWidget(chooser_); value_layout->addWidget(chooser_);
// 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);
} }
} }
+12 -11
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
@@ -73,23 +75,22 @@ public slots:
void SetColor(const ManagedColor &c); void SetColor(const ManagedColor &c);
private: private:
ColorManager* color_manager_; ColorManager *color_manager_;
ColorWheelWidget* color_wheel_; ColorWheelWidget *color_wheel_;
ColorValuesWidget* color_values_widget_; ColorValuesWidget *color_values_widget_;
ColorGradientWidget* hsv_value_gradient_; ColorGradientWidget *hsv_value_gradient_;
ColorProcessorPtr input_to_ref_processor_; ColorProcessorPtr input_to_ref_processor_;
ColorSpaceChooser* chooser_; ColorSpaceChooser *chooser_;
ColorSwatchChooser *swatch_; ColorSwatchChooser *swatch_;
private slots: private slots:
void ColorSpaceChanged(const QString& input, const ColorTransform &output); void ColorSpaceChanged(const QString &input, const ColorTransform &output);
}; };
} }
+19 -17
View File
@@ -26,14 +26,15 @@
#include "core.h" #include "core.h"
namespace olive { namespace olive
ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
QDialog(parent)
{ {
QVBoxLayout* layout = new QVBoxLayout(this);
QSplitter* splitter = new QSplitter(); ConfigDialogBase::ConfigDialogBase(QWidget *parent)
: QDialog(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
QSplitter *splitter = new QSplitter();
splitter->setChildrenCollapsible(false); splitter->setChildrenCollapsible(false);
layout->addWidget(splitter); layout->addWidget(splitter);
@@ -44,32 +45,33 @@ ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
splitter->addWidget(list_widget_); splitter->addWidget(list_widget_);
splitter->addWidget(preference_pane_stack_); splitter->addWidget(preference_pane_stack_);
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()
{ {
foreach (ConfigDialogBaseTab* tab, tabs_) { foreach (ConfigDialogBaseTab *tab, tabs_) {
if (!tab->Validate()) { if (!tab->Validate()) {
return; return;
} }
} }
MultiUndoCommand* command = new MultiUndoCommand(); MultiUndoCommand *command = new MultiUndoCommand();
foreach (ConfigDialogBaseTab* tab, tabs_) { foreach (ConfigDialogBaseTab *tab, tabs_) {
tab->Accept(command); tab->Accept(command);
} }
+11 -10
View File
@@ -27,13 +27,13 @@
#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);
private slots: private slots:
/** /**
@@ -42,17 +42,18 @@ private slots:
virtual void accept() override; virtual void accept() override;
protected: protected:
void AddTab(ConfigDialogBaseTab* tab, const QString& title); void AddTab(ConfigDialogBaseTab *tab, const QString &title);
virtual void AcceptEvent(){} virtual void AcceptEvent()
{
}
private: private:
QListWidget* list_widget_; QListWidget *list_widget_;
QStackedWidget* preference_pane_stack_; QStackedWidget *preference_pane_stack_;
QList<ConfigDialogBaseTab*> tabs_;
QList<ConfigDialogBaseTab *> tabs_;
}; };
} }
@@ -20,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;
+38 -23
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) :
QDialog(parent),
folder_(folder)
{ {
QGridLayout* layout = new QGridLayout(this);
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
: QDialog(parent)
, folder_(folder)
{
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"));
} }
} }
} }
+10 -10
View File
@@ -28,31 +28,31 @@
#include "render/diskmanager.h" #include "render/diskmanager.h"
#include "widget/slider/floatslider.h" #include "widget/slider/floatslider.h"
namespace olive { namespace olive
class DiskCacheDialog : public QDialog
{ {
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;
private: private:
DiskCacheFolder* folder_; DiskCacheFolder *folder_;
FloatSlider* maximum_cache_slider_; FloatSlider *maximum_cache_slider_;
QCheckBox* clear_disk_cache_; QCheckBox *clear_disk_cache_;
QPushButton* clear_cache_btn_; QPushButton *clear_cache_btn_;
private slots: private slots:
void ClearDiskCache(); void ClearDiskCache();
}; };
} }
+31 -25
View File
@@ -28,24 +28,26 @@
#include "common/qtutils.h" #include "common/qtutils.h"
#include "widget/slider/integerslider.h" #include "widget/slider/integerslider.h"
namespace olive { namespace olive
{
AV1Section::AV1Section(QWidget *parent) : AV1Section::AV1Section(QWidget *parent)
AV1Section(AV1CRFSection::kDefaultAV1CRF, parent) : AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
{ {
} }
AV1Section::AV1Section(int default_crf, QWidget *parent) : AV1Section::AV1Section(int default_crf, QWidget *parent)
CodecSection(parent) : CodecSection(parent)
{ {
QGridLayout* layout = new QGridLayout(this); QGridLayout *layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
int row = 0; int row = 0;
layout->addWidget(new QLabel(tr("Preset:")), row, 0); layout->addWidget(new QLabel(tr("Preset:")), row, 0);
preset_combobox_ = new QComboBox(); preset_combobox_ = new QComboBox();
preset_combobox_->setToolTip(tr("This parameter governs the efficiency/encode-time trade-off.\n" preset_combobox_->setToolTip(tr(
"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."));
@@ -60,8 +62,9 @@ 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,31 +82,31 @@ 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);
crf_slider_ = new QSlider(Qt::Horizontal); crf_slider_ = new QSlider(Qt::Horizontal);
@@ -112,16 +115,19 @@ AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) :
crf_slider_->setValue(default_crf); crf_slider_->setValue(default_crf);
layout->addWidget(crf_slider_); layout->addWidget(crf_slider_);
IntegerSlider* crf_input = new IntegerSlider(); IntegerSlider *crf_input = new IntegerSlider();
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH"))); crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(
crf_input->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
+11 -13
View File
@@ -28,13 +28,13 @@
#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);
int GetValue() const; int GetValue() const;
@@ -44,27 +44,25 @@ private:
static const int kMinimumCRF = 0; static const int kMinimumCRF = 0;
static const int kMaximumCRF = 63; static const int kMaximumCRF = 63;
QSlider* crf_slider_; QSlider *crf_slider_;
}; };
class AV1Section : public CodecSection class AV1Section : public CodecSection {
{
Q_OBJECT Q_OBJECT
public: public:
enum CompressionMethod { enum CompressionMethod {
kConstantRateFactor, kConstantRateFactor,
}; };
AV1Section(QWidget* parent = nullptr); AV1Section(QWidget *parent = nullptr);
AV1Section(int default_crf, QWidget* parent); AV1Section(int default_crf, QWidget *parent);
virtual void AddOpts(EncodingParams* params) override; virtual void AddOpts(EncodingParams *params) override;
private: private:
QStackedWidget* compression_method_stack_; QStackedWidget *compression_method_stack_;
AV1CRFSection* crf_section_; AV1CRFSection *crf_section_;
QComboBox *preset_combobox_; QComboBox *preset_combobox_;
}; };
+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());
} }
} }
+4 -5
View File
@@ -25,21 +25,20 @@
#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);
virtual void AddOpts(EncodingParams* params) override; virtual void AddOpts(EncodingParams *params) override;
virtual void SetOpts(const EncodingParams *p) override; virtual void SetOpts(const EncodingParams *p) override;
private: private:
QComboBox *quality_combobox_; QComboBox *quality_combobox_;
}; };
} }
+4 -3
View File
@@ -20,10 +20,11 @@
#include "codecsection.h" #include "codecsection.h"
namespace olive { namespace olive
{
CodecSection::CodecSection(QWidget *parent) : CodecSection::CodecSection(QWidget *parent)
QWidget(parent) : QWidget(parent)
{ {
} }
+12 -7
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)
}
}; };
} }
+8 -5
View File
@@ -20,12 +20,13 @@
#include "codecstack.h" #include "codecstack.h"
namespace olive { namespace olive
{
#define super QStackedWidget #define super QStackedWidget
CodecStack::CodecStack(QWidget *parent) CodecStack::CodecStack(QWidget *parent)
: super{parent} : super{ parent }
{ {
connect(this, &CodecStack::currentChanged, this, &CodecStack::OnChange); connect(this, &CodecStack::currentChanged, this, &CodecStack::OnChange);
} }
@@ -39,11 +40,13 @@ void CodecStack::addWidget(QWidget *widget)
void CodecStack::OnChange(int index) 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