Merge branch 'master' into pr/1875
This commit is contained in:
+19
-18
@@ -73,11 +73,10 @@ int InputCallback(const void *input, void *output, unsigned long frameCount, con
|
||||
{
|
||||
FFmpegEncoder *f = static_cast<FFmpegEncoder*>(userData);
|
||||
|
||||
SampleBufferPtr s = SampleBuffer::Create();
|
||||
s->set_sample_count(frameCount);
|
||||
s->set_audio_params(f->params().audio_params());
|
||||
AudioParams our_params = f->params().audio_params();
|
||||
our_params.set_format(AudioParams::GetPackedEquivalent(f->params().audio_params().format()));
|
||||
|
||||
f->WriteAudioData(f->params().audio_params(), false, reinterpret_cast<const uint8_t**>(&input), frameCount);
|
||||
f->WriteAudioData(our_params, reinterpret_cast<const uint8_t**>(&input), frameCount);
|
||||
|
||||
return paContinue;
|
||||
}
|
||||
@@ -115,16 +114,22 @@ void AudioManager::ClearBufferedOutput()
|
||||
PaSampleFormat AudioManager::GetPortAudioSampleFormat(AudioParams::Format fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AudioParams::kFormatUnsigned8:
|
||||
case AudioParams::kFormatUnsigned8Packed:
|
||||
case AudioParams::kFormatUnsigned8Planar:
|
||||
return paUInt8;
|
||||
case AudioParams::kFormatSigned16:
|
||||
case AudioParams::kFormatSigned16Packed:
|
||||
case AudioParams::kFormatSigned16Planar:
|
||||
return paInt16;
|
||||
case AudioParams::kFormatSigned32:
|
||||
case AudioParams::kFormatSigned32Packed:
|
||||
case AudioParams::kFormatSigned32Planar:
|
||||
return paInt32;
|
||||
case AudioParams::kFormatFloat32:
|
||||
case AudioParams::kFormatFloat32Packed:
|
||||
case AudioParams::kFormatFloat32Planar:
|
||||
return paFloat32;
|
||||
case AudioParams::kFormatSigned64:
|
||||
case AudioParams::kFormatFloat64:
|
||||
case AudioParams::kFormatSigned64Packed:
|
||||
case AudioParams::kFormatSigned64Planar:
|
||||
case AudioParams::kFormatFloat64Packed:
|
||||
case AudioParams::kFormatFloat64Planar:
|
||||
case AudioParams::kFormatInvalid:
|
||||
case AudioParams::kFormatCount:
|
||||
break;
|
||||
@@ -184,25 +189,21 @@ void AudioManager::HardReset()
|
||||
Pa_Initialize();
|
||||
}
|
||||
|
||||
bool AudioManager::StartRecording(const QString &filename, const AudioParams ¶ms)
|
||||
bool AudioManager::StartRecording(const EncodingParams ¶ms)
|
||||
{
|
||||
if (input_device_ == paNoDevice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EncodingParams encode_param;
|
||||
encode_param.EnableAudio(params, ExportCodec::kCodecMP3);
|
||||
encode_param.SetFilename(filename);
|
||||
|
||||
input_encoder_ = new FFmpegEncoder(encode_param);
|
||||
input_encoder_ = new FFmpegEncoder(params);
|
||||
if (!input_encoder_->Open()) {
|
||||
qCritical() << "Failed to open encoder for recording";
|
||||
return false;
|
||||
}
|
||||
|
||||
PaStreamParameters p = GetPortAudioParams(params, input_device_);
|
||||
PaStreamParameters p = GetPortAudioParams(params.audio_params(), input_device_);
|
||||
|
||||
if (Pa_OpenStream(&input_stream_, &p, nullptr, params.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_) == paNoError) {
|
||||
if (Pa_OpenStream(&input_stream_, &p, nullptr, params.audio_params().sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_) == paNoError) {
|
||||
if (Pa_StartStream(input_stream_) == paNoError) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
|
||||
void HardReset();
|
||||
|
||||
bool StartRecording(const QString &filename, const AudioParams ¶ms);
|
||||
bool StartRecording(const EncodingParams ¶ms);
|
||||
|
||||
void StopRecording();
|
||||
|
||||
|
||||
@@ -42,10 +42,10 @@ bool PackedProcessor::Open(const AudioParams ¶ms)
|
||||
|
||||
swr_ctx_ = swr_alloc_set_opts(nullptr,
|
||||
params.channel_layout(),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format(), false),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(AudioParams::GetPackedEquivalent(params.format())),
|
||||
params.sample_rate(),
|
||||
params.channel_layout(),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format(), true),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
|
||||
params.sample_rate(),
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
@@ -42,10 +42,10 @@ bool PlanarProcessor::Open(const AudioParams ¶ms)
|
||||
|
||||
swr_ctx_ = swr_alloc_set_opts(nullptr,
|
||||
params.channel_layout(),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format(), true),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(AudioParams::GetPlanarEquivalent(params.format())),
|
||||
params.sample_rate(),
|
||||
params.channel_layout(),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format(), false),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
|
||||
params.sample_rate(),
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
+13
-12
@@ -109,7 +109,7 @@ FramePtr Decoder::RetrieveVideo(const rational &timecode, const RetrieveVideoPar
|
||||
return RetrieveVideoInternal(timecode, divider, cancelled);
|
||||
}
|
||||
|
||||
Decoder::RetrieveAudioData Decoder::RetrieveAudio(const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode)
|
||||
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBufferPtr dest, const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
@@ -117,24 +117,27 @@ Decoder::RetrieveAudioData Decoder::RetrieveAudio(const TimeRange &range, const
|
||||
|
||||
if (!stream_.IsValid()) {
|
||||
qCritical() << "Can't retrieve audio on a closed decoder";
|
||||
return {kInvalid, nullptr, nullptr};
|
||||
return kInvalid;
|
||||
}
|
||||
|
||||
if (!SupportsAudio()) {
|
||||
qCritical() << "Decoder doesn't support audio";
|
||||
return {kInvalid, nullptr, nullptr};
|
||||
return kInvalid;
|
||||
}
|
||||
|
||||
// Get conform state from ConformManager
|
||||
ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
|
||||
if (conform.state == ConformManager::kConformGenerating) {
|
||||
return {kWaitingForConform, nullptr, conform.task};
|
||||
// If we need the task, it's available in `conform.task`
|
||||
return kWaitingForConform;
|
||||
}
|
||||
|
||||
// See if we got the conform
|
||||
SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filenames, range, loop_mode, params);
|
||||
|
||||
return {kOK, out_buffer, nullptr};
|
||||
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode, params)) {
|
||||
return kOK;
|
||||
} else {
|
||||
return kUnknownError;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 Decoder::GetLastAccessedTime()
|
||||
@@ -269,12 +272,10 @@ bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const Audi
|
||||
return false;
|
||||
}
|
||||
|
||||
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
|
||||
bool Decoder::RetrieveAudioFromConform(SampleBufferPtr sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
|
||||
{
|
||||
PlanarFileDevice input;
|
||||
if (input.open(conform_filenames, QFile::ReadOnly)) {
|
||||
SampleBufferPtr sample_buffer = SampleBuffer::CreateAllocated(input_params, range.length());
|
||||
|
||||
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
|
||||
qint64 write_index = 0;
|
||||
|
||||
@@ -313,10 +314,10 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QVector<QString> &confor
|
||||
|
||||
input.close();
|
||||
|
||||
return sample_buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Decoder::UpdateLastAccessed()
|
||||
|
||||
+4
-9
@@ -189,13 +189,8 @@ public:
|
||||
enum RetrieveAudioStatus {
|
||||
kInvalid = -1,
|
||||
kOK,
|
||||
kWaitingForConform
|
||||
};
|
||||
|
||||
struct RetrieveAudioData {
|
||||
RetrieveAudioStatus status;
|
||||
SampleBufferPtr samples;
|
||||
Task *task;
|
||||
kWaitingForConform,
|
||||
kUnknownError
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -206,7 +201,7 @@ public:
|
||||
*
|
||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||
*/
|
||||
RetrieveAudioData RetrieveAudio(const TimeRange& range, const AudioParams& params, const QString &cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode);
|
||||
RetrieveAudioStatus RetrieveAudio(SampleBufferPtr dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode);
|
||||
|
||||
/**
|
||||
* @brief Determine the last time this decoder instance was used in any way
|
||||
@@ -312,7 +307,7 @@ signals:
|
||||
private:
|
||||
void UpdateLastAccessed();
|
||||
|
||||
SampleBufferPtr RetrieveAudioFromConform(const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams ¶ms);
|
||||
bool RetrieveAudioFromConform(SampleBufferPtr sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams ¶ms);
|
||||
|
||||
CodecStream stream_;
|
||||
|
||||
|
||||
@@ -320,7 +320,8 @@ Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
|
||||
case ExportFormat::kFormatDNxHD:
|
||||
case ExportFormat::kFormatMatroska:
|
||||
case ExportFormat::kFormatQuickTime:
|
||||
case ExportFormat::kFormatMPEG4:
|
||||
case ExportFormat::kFormatMPEG4Video:
|
||||
case ExportFormat::kFormatMPEG4Audio:
|
||||
case ExportFormat::kFormatWAV:
|
||||
case ExportFormat::kFormatAIFF:
|
||||
case ExportFormat::kFormatMP3:
|
||||
@@ -350,4 +351,9 @@ QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
std::vector<AudioParams::Format> Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
|
||||
{
|
||||
return std::vector<AudioParams::Format>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ public:
|
||||
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms);
|
||||
|
||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
|
||||
virtual std::vector<AudioParams::Format> GetSampleFormatsForCodec(ExportCodec::Codec c) const;
|
||||
|
||||
const EncodingParams& params() const;
|
||||
|
||||
|
||||
@@ -103,4 +103,33 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExportCodec::IsCodecLossless(Codec c)
|
||||
{
|
||||
switch (c) {
|
||||
case kCodecPCM:
|
||||
case kCodecFLAC:
|
||||
return true;
|
||||
case kCodecDNxHD:
|
||||
case kCodecH264:
|
||||
case kCodecH264rgb:
|
||||
case kCodecH265:
|
||||
case kCodecProRes:
|
||||
case kCodecCineform:
|
||||
case kCodecMP2:
|
||||
case kCodecMP3:
|
||||
case kCodecAAC:
|
||||
case kCodecVorbis:
|
||||
case kCodecOpus:
|
||||
case kCodecVP9:
|
||||
case kCodecSRT:
|
||||
case kCodecOpenEXR:
|
||||
case kCodecPNG:
|
||||
case kCodecTIFF:
|
||||
case kCodecCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ class ExportCodec : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Only append to this list (never insert) because indexes are used in serialized files
|
||||
enum Codec {
|
||||
// Video codecs
|
||||
kCodecDNxHD,
|
||||
kCodecH264,
|
||||
kCodecH264rgb,
|
||||
@@ -45,8 +45,6 @@ public:
|
||||
kCodecCineform,
|
||||
kCodecTIFF,
|
||||
kCodecVP9,
|
||||
|
||||
// Audio codecs
|
||||
kCodecMP2,
|
||||
kCodecMP3,
|
||||
kCodecAAC,
|
||||
@@ -54,8 +52,6 @@ public:
|
||||
kCodecOpus,
|
||||
kCodecVorbis,
|
||||
kCodecFLAC,
|
||||
|
||||
// Subtitle codecs
|
||||
kCodecSRT,
|
||||
|
||||
kCodecCount
|
||||
@@ -65,6 +61,8 @@ public:
|
||||
|
||||
static bool IsCodecAStillImage(Codec c);
|
||||
|
||||
static bool IsCodecLossless(Codec c);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -31,8 +31,10 @@ QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
||||
return tr("DNxHD");
|
||||
case kFormatMatroska:
|
||||
return tr("Matroska Video");
|
||||
case kFormatMPEG4:
|
||||
case kFormatMPEG4Video:
|
||||
return tr("MPEG-4 Video");
|
||||
case kFormatMPEG4Audio:
|
||||
return tr("MPEG-4 Audio");
|
||||
case kFormatOpenEXR:
|
||||
return tr("OpenEXR");
|
||||
case kFormatPNG:
|
||||
@@ -70,8 +72,10 @@ QString ExportFormat::GetExtension(ExportFormat::Format f)
|
||||
return QStringLiteral("mxf");
|
||||
case kFormatMatroska:
|
||||
return QStringLiteral("mkv");
|
||||
case kFormatMPEG4:
|
||||
case kFormatMPEG4Video:
|
||||
return QStringLiteral("mp4");
|
||||
case kFormatMPEG4Audio:
|
||||
return QStringLiteral("m4a");
|
||||
case kFormatOpenEXR:
|
||||
return QStringLiteral("exr");
|
||||
case kFormatPNG:
|
||||
@@ -108,7 +112,7 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
|
||||
return {ExportCodec::kCodecDNxHD};
|
||||
case kFormatMatroska:
|
||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9};
|
||||
case kFormatMPEG4:
|
||||
case kFormatMPEG4Video:
|
||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265};
|
||||
case kFormatOpenEXR:
|
||||
return {ExportCodec::kCodecOpenEXR};
|
||||
@@ -122,6 +126,7 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
|
||||
return {ExportCodec::kCodecVP9};
|
||||
case kFormatOgg:
|
||||
case kFormatWAV:
|
||||
case kFormatMPEG4Audio:
|
||||
case kFormatAIFF:
|
||||
case kFormatMP3:
|
||||
case kFormatFLAC:
|
||||
@@ -141,7 +146,8 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
||||
return {ExportCodec::kCodecPCM};
|
||||
case kFormatMatroska:
|
||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC};
|
||||
case kFormatMPEG4:
|
||||
case kFormatMPEG4Video:
|
||||
case kFormatMPEG4Audio:
|
||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3};
|
||||
case kFormatQuickTime:
|
||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
||||
@@ -177,7 +183,8 @@ QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
|
||||
{
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
case kFormatMPEG4:
|
||||
case kFormatMPEG4Video:
|
||||
case kFormatMPEG4Audio:
|
||||
case kFormatOpenEXR:
|
||||
case kFormatQuickTime:
|
||||
case kFormatPNG:
|
||||
@@ -211,4 +218,18 @@ QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, Export
|
||||
return list;
|
||||
}
|
||||
|
||||
std::vector<AudioParams::Format> ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
|
||||
{
|
||||
std::vector<AudioParams::Format> f;
|
||||
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
|
||||
|
||||
if (e) {
|
||||
f = e->GetSampleFormatsForCodec(c);
|
||||
delete e;
|
||||
}
|
||||
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "common/define.h"
|
||||
#include "exportcodec.h"
|
||||
#include "render/audioparams.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -33,10 +34,11 @@ class ExportFormat : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Only append to this list (never insert) because indexes are used in serialized files
|
||||
enum Format {
|
||||
kFormatDNxHD,
|
||||
kFormatMatroska,
|
||||
kFormatMPEG4,
|
||||
kFormatMPEG4Video,
|
||||
kFormatOpenEXR,
|
||||
kFormatQuickTime,
|
||||
kFormatPNG,
|
||||
@@ -48,6 +50,7 @@ public:
|
||||
kFormatOgg,
|
||||
kFormatWebM,
|
||||
kFormatSRT,
|
||||
kFormatMPEG4Audio,
|
||||
|
||||
kFormatCount
|
||||
};
|
||||
@@ -59,6 +62,7 @@ public:
|
||||
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
|
||||
|
||||
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||
static std::vector<AudioParams::Format> GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -455,7 +455,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
||||
// Create resampling context
|
||||
SwrContext* resampler = swr_alloc_set_opts(nullptr,
|
||||
params.channel_layout(),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format(), true),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
|
||||
params.sample_rate(),
|
||||
channel_layout,
|
||||
static_cast<AVSampleFormat>(instance_.avstream()->codecpar->format),
|
||||
|
||||
@@ -50,7 +50,7 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||
{
|
||||
QStringList pix_fmts;
|
||||
|
||||
const AVCodec* codec_info = GetEncoder(c);
|
||||
const AVCodec* codec_info = GetEncoder(c, AudioParams::kFormatInvalid);
|
||||
|
||||
if (codec_info) {
|
||||
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
|
||||
@@ -62,6 +62,38 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||
return pix_fmts;
|
||||
}
|
||||
|
||||
std::vector<AudioParams::Format> FFmpegEncoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
|
||||
{
|
||||
std::vector<AudioParams::Format> f;
|
||||
|
||||
if (c == ExportCodec::kCodecPCM) {
|
||||
// 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
|
||||
// (beacuse first element is the "default" in FFmpeg)
|
||||
f = {
|
||||
AudioParams::kFormatSigned16Packed,
|
||||
AudioParams::kFormatUnsigned8Packed,
|
||||
AudioParams::kFormatSigned32Packed,
|
||||
AudioParams::kFormatSigned64Packed,
|
||||
AudioParams::kFormatFloat32Packed,
|
||||
AudioParams::kFormatFloat64Packed
|
||||
};
|
||||
} else {
|
||||
const AVCodec* codec_info = GetEncoder(c, AudioParams::kFormatInvalid);
|
||||
|
||||
if (codec_info && codec_info->sample_fmts) {
|
||||
for (int i=0; codec_info->sample_fmts[i]!=-1; i++) {
|
||||
AudioParams::Format this_format = FFmpegUtils::GetNativeSampleFormat(static_cast<AVSampleFormat>(codec_info->sample_fmts[i]));
|
||||
if (this_format != AudioParams::kFormatInvalid) {
|
||||
f.push_back(this_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
bool FFmpegEncoder::Open()
|
||||
{
|
||||
if (open_) {
|
||||
@@ -246,14 +278,14 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio)
|
||||
int input_linesize;
|
||||
|
||||
av_samples_alloc_array_and_samples(&input_data, &input_linesize, audio->audio_params().channel_count(),
|
||||
input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio->audio_params().format(), true), 0);
|
||||
input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio->audio_params().format()), 0);
|
||||
|
||||
for (int i=0; i<audio->audio_params().channel_count(); i++) {
|
||||
memcpy(input_data[i], audio->data(i), input_sample_count * audio->audio_params().bytes_per_sample_per_channel());
|
||||
}
|
||||
}
|
||||
|
||||
result = WriteAudioData(audio->audio_params(), true, const_cast<const uint8_t**>(input_data), input_sample_count);
|
||||
result = WriteAudioData(audio->audio_params(), const_cast<const uint8_t**>(input_data), input_sample_count);
|
||||
|
||||
if (input_data) {
|
||||
av_freep(&input_data[0]);
|
||||
@@ -263,9 +295,9 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, bool planar, 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, planar)) {
|
||||
if (!InitializeResampleContext(audio_params)) {
|
||||
qCritical() << "Failed to initialize resample context";
|
||||
return false;
|
||||
}
|
||||
@@ -579,7 +611,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
||||
}
|
||||
|
||||
// Find encoder
|
||||
const AVCodec* encoder = GetEncoder(codec);
|
||||
const AVCodec* encoder = GetEncoder(codec, params().audio_params().format());
|
||||
if (!encoder) {
|
||||
SetError(tr("Failed to find codec for 0x%1").arg(codec, 16));
|
||||
return false;
|
||||
@@ -653,7 +685,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
||||
codec_ctx->sample_rate = params().audio_params().sample_rate();
|
||||
codec_ctx->channel_layout = params().audio_params().channel_layout();
|
||||
codec_ctx->channels = av_get_channel_layout_nb_channels(codec_ctx->channel_layout);
|
||||
codec_ctx->sample_fmt = encoder->sample_fmts[0];
|
||||
codec_ctx->sample_fmt = FFmpegUtils::GetFFmpegSampleFormat(params().audio_params().format());
|
||||
codec_ctx->time_base = {1, codec_ctx->sample_rate};
|
||||
|
||||
if (params().audio_bit_rate() > 0) {
|
||||
@@ -783,7 +815,7 @@ void FFmpegEncoder::FlushCodecCtx(AVCodecContext *codec_ctx, AVStream* stream)
|
||||
av_packet_free(&pkt);
|
||||
}
|
||||
|
||||
bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio, bool planar)
|
||||
bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio)
|
||||
{
|
||||
if (audio_resample_ctx_) {
|
||||
return true;
|
||||
@@ -795,7 +827,7 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio, bool pla
|
||||
audio_codec_ctx_->sample_fmt,
|
||||
audio_codec_ctx_->sample_rate,
|
||||
static_cast<int64_t>(audio.channel_layout()),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(audio.format(), planar),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(audio.format()),
|
||||
audio.sample_rate(),
|
||||
0,
|
||||
nullptr);
|
||||
@@ -842,7 +874,7 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio, bool pla
|
||||
return true;
|
||||
}
|
||||
|
||||
const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c)
|
||||
const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c, AudioParams::Format aformat)
|
||||
{
|
||||
switch (c) {
|
||||
case ExportCodec::kCodecH264:
|
||||
@@ -872,7 +904,30 @@ const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c)
|
||||
case ExportCodec::kCodecAAC:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_AAC);
|
||||
case ExportCodec::kCodecPCM:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_PCM_S16LE);
|
||||
switch (aformat) {
|
||||
case AudioParams::kFormatInvalid:
|
||||
case AudioParams::kFormatCount:
|
||||
case AudioParams::kFormatUnsigned8Planar:
|
||||
case AudioParams::kFormatSigned16Planar:
|
||||
case AudioParams::kFormatSigned32Planar:
|
||||
case AudioParams::kFormatSigned64Planar:
|
||||
case AudioParams::kFormatFloat32Planar:
|
||||
case AudioParams::kFormatFloat64Planar:
|
||||
break;
|
||||
case AudioParams::kFormatUnsigned8Packed:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_PCM_U8);
|
||||
case AudioParams::kFormatSigned16Packed:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_PCM_S16LE);
|
||||
case AudioParams::kFormatSigned32Packed:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_PCM_S32LE);
|
||||
case AudioParams::kFormatSigned64Packed:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_PCM_S64LE);
|
||||
case AudioParams::kFormatFloat32Packed:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_PCM_F32LE);
|
||||
case AudioParams::kFormatFloat64Packed:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_PCM_F64LE);
|
||||
}
|
||||
break;
|
||||
case ExportCodec::kCodecFLAC:
|
||||
return avcodec_find_encoder(AV_CODEC_ID_FLAC);
|
||||
case ExportCodec::kCodecOpus:
|
||||
|
||||
@@ -41,13 +41,15 @@ public:
|
||||
|
||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
|
||||
|
||||
virtual std::vector<AudioParams::Format> GetSampleFormatsForCodec(ExportCodec::Codec c) const override;
|
||||
|
||||
virtual bool Open() override;
|
||||
|
||||
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
|
||||
|
||||
virtual bool WriteAudio(olive::SampleBufferPtr audio) override;
|
||||
|
||||
bool WriteAudioData(const AudioParams &audio_params, bool planar, 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;
|
||||
|
||||
@@ -78,9 +80,9 @@ private:
|
||||
void FlushEncoders();
|
||||
void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream);
|
||||
|
||||
bool InitializeResampleContext(const AudioParams &audio, bool planar);
|
||||
bool InitializeResampleContext(const AudioParams &audio);
|
||||
|
||||
static const AVCodec *GetEncoder(ExportCodec::Codec c);
|
||||
static const AVCodec *GetEncoder(ExportCodec::Codec c, AudioParams::Format aformat);
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ public:
|
||||
|
||||
const int &sample_count() const;
|
||||
void set_sample_count(const int &sample_count);
|
||||
void set_sample_count(const rational &length)
|
||||
{
|
||||
set_sample_count(audio_params_.time_to_samples(length));
|
||||
}
|
||||
|
||||
float* data(int channel)
|
||||
{
|
||||
|
||||
+37
-19
@@ -42,23 +42,29 @@ AudioParams::Format FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return AudioParams::kFormatUnsigned8;
|
||||
return AudioParams::kFormatUnsigned8Packed;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return AudioParams::kFormatSigned16;
|
||||
return AudioParams::kFormatSigned16Packed;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return AudioParams::kFormatSigned32;
|
||||
return AudioParams::kFormatSigned32Packed;
|
||||
case AV_SAMPLE_FMT_S64:
|
||||
return AudioParams::kFormatSigned64;
|
||||
return AudioParams::kFormatSigned64Packed;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return AudioParams::kFormatFloat32;
|
||||
return AudioParams::kFormatFloat32Packed;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
return AudioParams::kFormatFloat64;
|
||||
return AudioParams::kFormatFloat64Packed;
|
||||
case AV_SAMPLE_FMT_U8P :
|
||||
return AudioParams::kFormatUnsigned8Planar;
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
return AudioParams::kFormatSigned16Planar;
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
return AudioParams::kFormatSigned32Planar;
|
||||
case AV_SAMPLE_FMT_S64P:
|
||||
return AudioParams::kFormatSigned64Planar;
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
return AudioParams::kFormatFloat32Planar;
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
return AudioParams::kFormatFloat64Planar;
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
case AV_SAMPLE_FMT_NB:
|
||||
break;
|
||||
@@ -67,21 +73,33 @@ AudioParams::Format FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp
|
||||
return AudioParams::kFormatInvalid;
|
||||
}
|
||||
|
||||
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt, bool planar)
|
||||
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case AudioParams::kFormatUnsigned8:
|
||||
return planar ? AV_SAMPLE_FMT_U8P : AV_SAMPLE_FMT_U8;
|
||||
case AudioParams::kFormatSigned16:
|
||||
return planar ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16;
|
||||
case AudioParams::kFormatSigned32:
|
||||
return planar ? AV_SAMPLE_FMT_S32P : AV_SAMPLE_FMT_S32;
|
||||
case AudioParams::kFormatSigned64:
|
||||
return planar ? AV_SAMPLE_FMT_S64P : AV_SAMPLE_FMT_S64;
|
||||
case AudioParams::kFormatFloat32:
|
||||
return planar ? AV_SAMPLE_FMT_FLTP : AV_SAMPLE_FMT_FLT;
|
||||
case AudioParams::kFormatFloat64:
|
||||
return planar ? AV_SAMPLE_FMT_DBLP : AV_SAMPLE_FMT_DBL;
|
||||
case AudioParams::kFormatUnsigned8Packed:
|
||||
return AV_SAMPLE_FMT_U8;
|
||||
case AudioParams::kFormatSigned16Packed:
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
case AudioParams::kFormatSigned32Packed:
|
||||
return AV_SAMPLE_FMT_S32;
|
||||
case AudioParams::kFormatSigned64Packed:
|
||||
return AV_SAMPLE_FMT_S64;
|
||||
case AudioParams::kFormatFloat32Packed:
|
||||
return AV_SAMPLE_FMT_FLT;
|
||||
case AudioParams::kFormatFloat64Packed:
|
||||
return AV_SAMPLE_FMT_DBL;
|
||||
case AudioParams::kFormatUnsigned8Planar:
|
||||
return AV_SAMPLE_FMT_U8P;
|
||||
case AudioParams::kFormatSigned16Planar:
|
||||
return AV_SAMPLE_FMT_S16P;
|
||||
case AudioParams::kFormatSigned32Planar:
|
||||
return AV_SAMPLE_FMT_S32P;
|
||||
case AudioParams::kFormatSigned64Planar:
|
||||
return AV_SAMPLE_FMT_S64P;
|
||||
case AudioParams::kFormatFloat32Planar:
|
||||
return AV_SAMPLE_FMT_FLTP;
|
||||
case AudioParams::kFormatFloat64Planar:
|
||||
return AV_SAMPLE_FMT_DBLP;
|
||||
case AudioParams::kFormatInvalid:
|
||||
case AudioParams::kFormatCount:
|
||||
break;
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
/**
|
||||
* @brief Returns an FFmpeg sample format type for a given native type
|
||||
*/
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt, bool planar = false);
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -287,7 +287,11 @@ int64_t Timecode::time_to_timestamp(const rational &time, const rational &timeba
|
||||
|
||||
int64_t Timecode::time_to_timestamp(const double &time, const rational &timebase, Rounding floor)
|
||||
{
|
||||
double d = time * timebase.flipped().toDouble();
|
||||
const double d = time * timebase.flipped().toDouble();
|
||||
|
||||
if (std::isnan(d)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (floor) {
|
||||
case kRound:
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "codec/exportformat.h"
|
||||
#include "common/autoscroll.h"
|
||||
#include "common/filefunctions.h"
|
||||
#include "common/xmlutils.h"
|
||||
@@ -120,6 +121,13 @@ void Config::SetDefaults()
|
||||
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString());
|
||||
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
|
||||
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kInt, AudioParams::kFormatSigned16Packed);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320);
|
||||
|
||||
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0)));
|
||||
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(60)));
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
|
||||
|
||||
class Config {
|
||||
public:
|
||||
static Config& Current();
|
||||
|
||||
@@ -194,8 +194,8 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
||||
SetDefaultFilename();
|
||||
|
||||
// Set defaults
|
||||
previously_selected_format_ = ExportFormat::kFormatMPEG4;
|
||||
format_combobox_->SetFormat(ExportFormat::kFormatMPEG4);
|
||||
previously_selected_format_ = ExportFormat::kFormatMPEG4Video;
|
||||
format_combobox_->SetFormat(ExportFormat::kFormatMPEG4Video);
|
||||
connect(format_combobox_, &ExportFormatComboBox::FormatChanged, this, &ExportDialog::FormatChanged);
|
||||
FormatChanged(format_combobox_->GetFormat());
|
||||
|
||||
@@ -211,6 +211,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
||||
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(Config::Current()[QStringLiteral("OnlinePixelFormat")].toInt()));
|
||||
video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
|
||||
audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate());
|
||||
audio_tab_->sample_format_combobox()->SetAttemptToRestoreFormat(false);
|
||||
audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout());
|
||||
|
||||
video_aspect_ratio_ = static_cast<double>(vp.width()) / static_cast<double>(vp.height());
|
||||
@@ -513,9 +514,9 @@ ExportParams ExportDialog::GenerateParams() const
|
||||
video_tab_->interlaced_combobox()->GetInterlaceMode(),
|
||||
1);
|
||||
|
||||
AudioParams audio_render_params(audio_tab_->sample_rate_combobox()->currentData().toInt(),
|
||||
AudioParams audio_render_params(audio_tab_->sample_rate_combobox()->GetSampleRate(),
|
||||
audio_tab_->channel_layout_combobox()->GetChannelLayout(),
|
||||
AudioParams::kInternalFormat);
|
||||
audio_tab_->sample_format_combobox()->GetSampleFormat());
|
||||
|
||||
ExportParams params;
|
||||
params.set_encoder(Encoder::GetTypeFromFormat(format_combobox_->GetFormat()));
|
||||
@@ -553,7 +554,7 @@ ExportParams ExportDialog::GenerateParams() const
|
||||
}
|
||||
|
||||
if (audio_enabled_->isChecked()) {
|
||||
ExportCodec::Codec audio_codec = static_cast<ExportCodec::Codec>(audio_tab_->codec_combobox()->currentData().toInt());
|
||||
ExportCodec::Codec audio_codec = audio_tab_->GetCodec();
|
||||
params.EnableAudio(audio_render_params, audio_codec);
|
||||
|
||||
params.set_audio_bit_rate(audio_tab_->bit_rate_slider()->GetValue() * 1000);
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const int ExportAudioTab::kDefaultBitRate = 320;
|
||||
|
||||
ExportAudioTab::ExportAudioTab(QWidget* parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
@@ -40,6 +42,8 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) :
|
||||
layout->addWidget(new QLabel(tr("Codec:")), row, 0);
|
||||
|
||||
codec_combobox_ = new QComboBox();
|
||||
connect(codec_combobox_, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ExportAudioTab::UpdateSampleFormats);
|
||||
connect(codec_combobox_, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ExportAudioTab::UpdateBitRateEnabled);
|
||||
layout->addWidget(codec_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
@@ -59,7 +63,9 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) :
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Format:")), row, 0);
|
||||
layout->addWidget(new QComboBox(), row, 1);
|
||||
|
||||
sample_format_combobox_ = new SampleFormatComboBox();
|
||||
layout->addWidget(sample_format_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
@@ -68,7 +74,7 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) :
|
||||
bit_rate_slider_ = new IntegerSlider();
|
||||
bit_rate_slider_->SetMinimum(32);
|
||||
bit_rate_slider_->SetMaximum(320);
|
||||
bit_rate_slider_->SetValue(256);
|
||||
bit_rate_slider_->SetValue(kDefaultBitRate);
|
||||
bit_rate_slider_->SetFormat(tr("%1 kbps"));
|
||||
layout->addWidget(bit_rate_slider_, row, 1);
|
||||
|
||||
@@ -79,11 +85,36 @@ int ExportAudioTab::SetFormat(ExportFormat::Format format)
|
||||
{
|
||||
QList<ExportCodec::Codec> acodecs = ExportFormat::GetAudioCodecs(format);
|
||||
setEnabled(!acodecs.isEmpty());
|
||||
codec_combobox()->clear();
|
||||
codec_combobox_->blockSignals(true);
|
||||
codec_combobox_->clear();
|
||||
foreach (ExportCodec::Codec acodec, acodecs) {
|
||||
codec_combobox()->addItem(ExportCodec::GetCodecName(acodec), acodec);
|
||||
codec_combobox_->addItem(ExportCodec::GetCodecName(acodec), acodec);
|
||||
}
|
||||
codec_combobox_->blockSignals(false);
|
||||
fmt_ = format;
|
||||
|
||||
UpdateSampleFormats();
|
||||
UpdateBitRateEnabled();
|
||||
|
||||
return acodecs.size();
|
||||
}
|
||||
|
||||
void ExportAudioTab::UpdateSampleFormats()
|
||||
{
|
||||
auto fmts = ExportFormat::GetSampleFormatsForCodec(fmt_, GetCodec());
|
||||
sample_format_combobox_->SetAvailableFormats(fmts);
|
||||
}
|
||||
|
||||
void ExportAudioTab::UpdateBitRateEnabled()
|
||||
{
|
||||
bool uses_bitrate = !ExportCodec::IsCodecLossless(GetCodec());
|
||||
bit_rate_slider_->setEnabled(uses_bitrate);
|
||||
|
||||
if (!uses_bitrate) {
|
||||
bit_rate_slider_->SetTristate();
|
||||
} else {
|
||||
bit_rate_slider_->SetValue(kDefaultBitRate );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,9 +37,19 @@ class ExportAudioTab : public QWidget
|
||||
public:
|
||||
ExportAudioTab(QWidget* parent = nullptr);
|
||||
|
||||
QComboBox* codec_combobox() const
|
||||
ExportCodec::Codec GetCodec() const
|
||||
{
|
||||
return codec_combobox_;
|
||||
return static_cast<ExportCodec::Codec>(codec_combobox_->currentData().toInt());
|
||||
}
|
||||
|
||||
void SetCodec(ExportCodec::Codec c)
|
||||
{
|
||||
for (int i=0; i<codec_combobox_->count(); i++) {
|
||||
if (codec_combobox_->itemData(i) == c) {
|
||||
codec_combobox_->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SampleRateComboBox* sample_rate_combobox() const
|
||||
@@ -47,6 +57,11 @@ public:
|
||||
return sample_rate_combobox_;
|
||||
}
|
||||
|
||||
SampleFormatComboBox* sample_format_combobox() const
|
||||
{
|
||||
return sample_format_combobox_;
|
||||
}
|
||||
|
||||
ChannelLayoutComboBox* channel_layout_combobox() const
|
||||
{
|
||||
return channel_layout_combobox_;
|
||||
@@ -61,11 +76,20 @@ public slots:
|
||||
int SetFormat(ExportFormat::Format format);
|
||||
|
||||
private:
|
||||
ExportFormat::Format fmt_;
|
||||
QComboBox* codec_combobox_;
|
||||
SampleRateComboBox* sample_rate_combobox_;
|
||||
ChannelLayoutComboBox* channel_layout_combobox_;
|
||||
SampleFormatComboBox *sample_format_combobox_;
|
||||
IntegerSlider* bit_rate_slider_;
|
||||
|
||||
static const int kDefaultBitRate;
|
||||
|
||||
private slots:
|
||||
void UpdateSampleFormats();
|
||||
|
||||
void UpdateBitRateEnabled();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -33,12 +33,16 @@ ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent) :
|
||||
case kShowAllFormats:
|
||||
break;
|
||||
case kShowAudioOnly:
|
||||
if (!ExportFormat::GetVideoCodecs(f).isEmpty()) {
|
||||
if (!ExportFormat::GetVideoCodecs(f).isEmpty()
|
||||
|| !ExportFormat::GetSubtitleCodecs(f).isEmpty()
|
||||
|| ExportFormat::GetAudioCodecs(f).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case kShowVideoOnly:
|
||||
if (!ExportFormat::GetAudioCodecs(f).isEmpty()) {
|
||||
if (ExportFormat::GetVideoCodecs(f).isEmpty()
|
||||
|| !ExportFormat::GetSubtitleCodecs(f).isEmpty()
|
||||
|| !ExportFormat::GetAudioCodecs(f).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -117,6 +117,8 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(const std::vector<TimelineMarker
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &MarkerPropertiesDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &MarkerPropertiesDialog::reject);
|
||||
layout->addWidget(buttons, row, 0, 1, 2);
|
||||
|
||||
setWindowTitle(tr("Edit Markers"));
|
||||
}
|
||||
|
||||
void MarkerPropertiesDialog::accept()
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
|
||||
#include "audio/audiomanager.h"
|
||||
#include "config/config.h"
|
||||
#include "dialog/export/exportaudiotab.h"
|
||||
#include "dialog/export/exportformatcombobox.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -99,14 +97,21 @@ PreferencesAudioTab::PreferencesAudioTab()
|
||||
|
||||
fmt_layout->addWidget(new QLabel(tr("Format:")));
|
||||
|
||||
ExportFormatComboBox *fmt_combo = new ExportFormatComboBox(ExportFormatComboBox::kShowAudioOnly);
|
||||
fmt_combo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
fmt_layout->addWidget(fmt_combo);
|
||||
record_format_combo_ = new ExportFormatComboBox(ExportFormatComboBox::kShowAudioOnly);
|
||||
record_format_combo_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
record_format_combo_->SetFormat(static_cast<ExportFormat::Format>(OLIVE_CONFIG("AudioRecordingFormat").toInt()));
|
||||
fmt_layout->addWidget(record_format_combo_);
|
||||
|
||||
ExportAudioTab *audio_recording_options = new ExportAudioTab();
|
||||
recording_layout->addWidget(audio_recording_options);
|
||||
record_options_ = new ExportAudioTab();
|
||||
record_options_->SetFormat(record_format_combo_->GetFormat());
|
||||
record_options_->SetCodec(static_cast<ExportCodec::Codec>(OLIVE_CONFIG("AudioRecordingCodec").toInt()));
|
||||
record_options_->sample_rate_combobox()->SetSampleRate(OLIVE_CONFIG("AudioRecordingSampleRate").toInt());
|
||||
record_options_->channel_layout_combobox()->SetChannelLayout(OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong());
|
||||
record_options_->bit_rate_slider()->SetValue(OLIVE_CONFIG("AudioRecordingBitRate").toInt());
|
||||
record_options_->sample_format_combobox()->SetSampleFormat(static_cast<AudioParams::Format>(OLIVE_CONFIG("AudioRecordingSampleFormat").toInt()));
|
||||
recording_layout->addWidget(record_options_);
|
||||
|
||||
connect(fmt_combo, &ExportFormatComboBox::FormatChanged, audio_recording_options, &ExportAudioTab::SetFormat);
|
||||
connect(record_format_combo_, &ExportFormatComboBox::FormatChanged, record_options_, &ExportAudioTab::SetFormat);
|
||||
}
|
||||
|
||||
QHBoxLayout* refresh_layout = new QHBoxLayout();
|
||||
@@ -134,12 +139,19 @@ void PreferencesAudioTab::Accept(MultiUndoCommand *command)
|
||||
PaDeviceIndex input_device = audio_input_devices_->currentData().value<PaDeviceIndex>();
|
||||
|
||||
// Get device names, which seem to be the closest thing we have to a "unique identifier" for them
|
||||
Config::Current()[QStringLiteral("AudioOutput")] = audio_output_devices_->currentText();
|
||||
Config::Current()[QStringLiteral("AudioInput")] = audio_input_devices_->currentText();
|
||||
OLIVE_CONFIG("AudioOutput") = audio_output_devices_->currentText();
|
||||
OLIVE_CONFIG("AudioInput") = audio_input_devices_->currentText();
|
||||
|
||||
// Set devices to be used from now on
|
||||
AudioManager::instance()->SetOutputDevice(output_device);
|
||||
AudioManager::instance()->SetInputDevice(input_device);
|
||||
|
||||
OLIVE_CONFIG("AudioRecordingFormat") = record_format_combo_->GetFormat();
|
||||
OLIVE_CONFIG("AudioRecordingCodec") = record_options_->GetCodec();
|
||||
OLIVE_CONFIG("AudioRecordingSampleRate") = record_options_->sample_rate_combobox()->GetSampleRate();
|
||||
OLIVE_CONFIG("AudioRecordingChannelLayout") = QVariant::fromValue(record_options_->channel_layout_combobox()->GetChannelLayout());
|
||||
OLIVE_CONFIG("AudioRecordingBitRate") = QVariant::fromValue(record_options_->bit_rate_slider()->GetValue());
|
||||
OLIVE_CONFIG("AudioRecordingSampleFormat") = record_options_->sample_format_combobox()->GetSampleFormat();
|
||||
}
|
||||
|
||||
void PreferencesAudioTab::RefreshBackends()
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <QPushButton>
|
||||
|
||||
#include "dialog/configbase/configdialogbase.h"
|
||||
#include "dialog/export/exportaudiotab.h"
|
||||
#include "dialog/export/exportformatcombobox.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -59,6 +61,10 @@ private:
|
||||
*/
|
||||
QPushButton* refresh_devices_btn_;
|
||||
|
||||
ExportFormatComboBox *record_format_combo_;
|
||||
|
||||
ExportAudioTab *record_options_;
|
||||
|
||||
private slots:
|
||||
void RefreshBackends();
|
||||
|
||||
|
||||
@@ -95,11 +95,7 @@ void PanNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
||||
}
|
||||
}
|
||||
|
||||
if (push_job) {
|
||||
table->Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
table->Push(NodeValue::kSamples, QVariant::fromValue(job.samples()), this);
|
||||
}
|
||||
table->Push(NodeValue::kSamples, push_job ? QVariant::fromValue(job) : QVariant::fromValue(job.samples()), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ ClipBlock::ClipBlock() :
|
||||
AddInput(kMaintainAudioPitchInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
PrependInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||
SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer));
|
||||
//SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer));
|
||||
|
||||
SetEffectInput(kBufferIn);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ void TransitionBlock::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
|
||||
ShaderJobEvent(value, job);
|
||||
|
||||
job_type = NodeValue::kShaderJob;
|
||||
job_type = NodeValue::kTexture;
|
||||
push_job = QVariant::fromValue(job);
|
||||
} else if (data_type == NodeValue::kSamples) {
|
||||
// This must be an audio transition
|
||||
|
||||
@@ -101,7 +101,7 @@ void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
&& job.GetValue(kTopRightInput).data().value<QVector2D>().isNull() &&
|
||||
job.GetValue(kBottomRightInput).data().value<QVector2D>().isNull() &&
|
||||
job.GetValue(kBottomLeftInput).data().value<QVector2D>().isNull())) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
table->Push(NodeValue::kTexture, job.GetValue(kTextureInput).data(), this);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ void CropDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
|| !qIsNull(job.GetValue(kRightInput).data().toDouble())
|
||||
|| !qIsNull(job.GetValue(kTopInput).data().toDouble())
|
||||
|| !qIsNull(job.GetValue(kBottomInput).data().toDouble())) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
table->Push(NodeValue::kTexture, job.GetValue(kTextureInput).data(), this);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ void FlipDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (job.GetValue(kHorizontalInput).data().toBool() || job.GetValue(kVerticalInput).data().toBool()) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->Push(job.GetValue(kTextureInput));
|
||||
|
||||
@@ -107,7 +107,7 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
// end up with gaps in the screen that will require an alpha channel.
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
|
||||
pushed_job = true;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ void OpacityEffect::Value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
if (!qFuzzyCompare(job.GetValue(kValueInput).data().toDouble(), 1.0)) {
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
// 1.0 float is a no-op, so just push the texture
|
||||
table->Push(job.GetValue(kTextureInput));
|
||||
|
||||
@@ -35,7 +35,7 @@ BlurFilterNode::BlurFilterNode()
|
||||
{
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kMethodInput, NodeValue::kCombo, 0);
|
||||
AddInput(kMethodInput, NodeValue::kCombo, 1); // Default to gaussian
|
||||
|
||||
AddInput(kRadiusInput, NodeValue::kFloat, 10.0);
|
||||
SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0);
|
||||
@@ -118,7 +118,7 @@ void BlurFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
|
||||
} else {
|
||||
// If we're not performing the blur job, just push the texture
|
||||
|
||||
@@ -66,7 +66,7 @@ void MosaicFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globa
|
||||
if (texture
|
||||
&& job.GetValue(kHorizInput).data().toInt() != texture->width()
|
||||
&& job.GetValue(kVertInput).data().toInt() != texture->height()) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
table->Push(job.GetValue(kTextureInput));
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ void StrokeFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globa
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
if (job.GetValue(kRadiusInput).data().toDouble() > 0.0
|
||||
&& job.GetValue(kOpacityInput).data().toDouble() > 0.0) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
table->Push(job.GetValue(kTextureInput));
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ void NoiseGeneratorNode::Value(const NodeValueRow &value, const NodeGlobals &glo
|
||||
job.InsertValue(QStringLiteral("time_in"), NodeValue(NodeValue::kFloat, globals.time().in().toDouble(), this));
|
||||
|
||||
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ void PolygonGenerator::Value(const NodeValueRow &value, const NodeGlobals &globa
|
||||
job.SetRequestedFormat(VideoParams::kFormatFloat32);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
table->Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
|
||||
void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) const
|
||||
|
||||
@@ -63,9 +63,11 @@ void ShapeNode::Retranslate()
|
||||
|
||||
ShaderCode ShapeNode::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/shape.frag")));
|
||||
if (shader_id == QStringLiteral("shape")) {
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/shape.frag")));
|
||||
} else {
|
||||
return super::GetShaderCode(shader_id);
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
@@ -75,8 +77,22 @@ void ShapeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, Nod
|
||||
job.InsertValue(value);
|
||||
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
job.SetShaderID(QStringLiteral("shape"));
|
||||
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
if (!value[kBaseInput].data().isNull()) {
|
||||
// Push as merge node
|
||||
ShaderJob merge;
|
||||
|
||||
merge.SetShaderID(QStringLiteral("mrg"));
|
||||
merge.InsertValue(MergeNode::kBaseIn, value[kBaseInput]);
|
||||
merge.InsertValue(MergeNode::kBlendIn, NodeValue(NodeValue::kTexture, QVariant::fromValue(job), this));
|
||||
merge.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(merge), this);
|
||||
} else {
|
||||
// Just push generate job
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,12 +30,14 @@ namespace olive {
|
||||
|
||||
#define super Node
|
||||
|
||||
QString ShapeNodeBase::kPositionInput = QStringLiteral("pos_in");
|
||||
QString ShapeNodeBase::kSizeInput = QStringLiteral("size_in");
|
||||
QString ShapeNodeBase::kColorInput = QStringLiteral("color_in");
|
||||
const QString ShapeNodeBase::kBaseInput = QStringLiteral("base_in");
|
||||
const QString ShapeNodeBase::kPositionInput = QStringLiteral("pos_in");
|
||||
const QString ShapeNodeBase::kSizeInput = QStringLiteral("size_in");
|
||||
const QString ShapeNodeBase::kColorInput = QStringLiteral("color_in");
|
||||
|
||||
ShapeNodeBase::ShapeNodeBase(bool create_color_input)
|
||||
{
|
||||
AddInput(kBaseInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
|
||||
AddInput(kSizeInput, NodeValue::kVec2, QVector2D(100, 100));
|
||||
SetInputProperty(kSizeInput, QStringLiteral("min"), QVector2D(0, 0));
|
||||
@@ -58,6 +60,9 @@ ShapeNodeBase::ShapeNodeBase(bool create_color_input)
|
||||
for (int i=0; i<kGizmoScaleCount; i++) {
|
||||
point_gizmo_[i] = AddDraggableGizmo<PointGizmo>(pos_n_sz, PointGizmo::kAbsolute);
|
||||
}
|
||||
|
||||
SetEffectInput(kBaseInput);
|
||||
SetFlags(kVideoEffect);
|
||||
}
|
||||
|
||||
void ShapeNodeBase::Retranslate()
|
||||
@@ -102,6 +107,15 @@ void ShapeNodeBase::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlob
|
||||
poly_gizmo_->SetPolygon(QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt));
|
||||
}
|
||||
|
||||
ShaderCode ShapeNodeBase::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
if (shader_id == QStringLiteral("mrg")) {
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag"));
|
||||
}
|
||||
|
||||
return ShaderCode();
|
||||
}
|
||||
|
||||
void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/inputdragger.h"
|
||||
#include "node/math/merge/merge.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -40,9 +41,12 @@ public:
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static QString kPositionInput;
|
||||
static QString kSizeInput;
|
||||
static QString kColorInput;
|
||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||
|
||||
static const QString kBaseInput;
|
||||
static const QString kPositionInput;
|
||||
static const QString kSizeInput;
|
||||
static const QString kColorInput;
|
||||
|
||||
protected:
|
||||
PolygonGizmo *poly_gizmo() const
|
||||
|
||||
@@ -70,7 +70,7 @@ void SolidGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals
|
||||
{
|
||||
ShaderJob job;
|
||||
job.InsertValue(value);
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
|
||||
ShaderCode SolidGenerator::GetShaderCode(const QString &shader_id) const
|
||||
|
||||
@@ -95,7 +95,7 @@ void TextGeneratorV1::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
if (!job.GetValue(kTextInput).data().toString().isEmpty()) {
|
||||
table->Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ void TextGeneratorV2::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
job.SetRequestedFormat(VideoParams::kFormatFloat32);
|
||||
|
||||
if (!job.GetValue(kTextInput).data().toString().isEmpty()) {
|
||||
table->Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ void TextGeneratorV3::Retranslate()
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextInput, tr("Text"));
|
||||
SetInputName(kBaseInput, tr("Base"));
|
||||
}
|
||||
|
||||
void TextGeneratorV3::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
@@ -89,7 +90,22 @@ void TextGeneratorV3::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
job.SetColorspace(project()->color_manager()->GetDefaultInputColorSpace());
|
||||
|
||||
if (!job.GetValue(kTextInput).data().toString().isEmpty()) {
|
||||
table->Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this);
|
||||
if (!value[kBaseInput].data().isNull()) {
|
||||
// Push as merge node
|
||||
ShaderJob merge;
|
||||
|
||||
merge.SetShaderID(QStringLiteral("mrg"));
|
||||
merge.InsertValue(MergeNode::kBaseIn, value[kBaseInput]);
|
||||
merge.InsertValue(MergeNode::kBlendIn, NodeValue(NodeValue::kTexture, QVariant::fromValue(job), this));
|
||||
merge.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(merge), this);
|
||||
} else {
|
||||
// Just push generate job
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
} else if (!value[kBaseInput].data().isNull()) {
|
||||
table->Push(value[kBaseInput]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-24
@@ -58,7 +58,7 @@ QByteArray HashTraverser::GetHash(const Node *node, const Node::ValueHint &hint,
|
||||
return hash_.result();
|
||||
}
|
||||
|
||||
TexturePtr HashTraverser::ProcessVideoFootage(const FootageJob &stream, const rational &input_time)
|
||||
void HashTraverser::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time)
|
||||
{
|
||||
Hash(FileFunctions::GetUniqueFileIdentifier(stream.filename()));
|
||||
Hash(stream.loop_mode());
|
||||
@@ -69,24 +69,20 @@ TexturePtr HashTraverser::ProcessVideoFootage(const FootageJob &stream, const ra
|
||||
Hash(stream.video_params().video_type() == VideoParams::kVideoTypeStill ? 0 : input_time);
|
||||
Hash(stream.video_params().video_type());
|
||||
|
||||
TexturePtr texture = super::ProcessVideoFootage(stream, input_time);
|
||||
texture_ids_.insert(texture.get(), hash_.result());
|
||||
return texture;
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
}
|
||||
|
||||
SampleBufferPtr HashTraverser::ProcessAudioFootage(const FootageJob &stream, const TimeRange &input_time)
|
||||
void HashTraverser::ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time)
|
||||
{
|
||||
Hash(FileFunctions::GetUniqueFileIdentifier(stream.filename()));
|
||||
Hash(stream.loop_mode());
|
||||
Hash(stream.audio_params().stream_index());
|
||||
Hash(input_time);
|
||||
|
||||
SampleBufferPtr buf = super::ProcessAudioFootage(stream, input_time);
|
||||
texture_ids_.insert(buf.get(), hash_.result());
|
||||
return buf;
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
}
|
||||
|
||||
TexturePtr HashTraverser::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job)
|
||||
void HashTraverser::ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob &job)
|
||||
{
|
||||
HashGenerateJob(node, &job);
|
||||
|
||||
@@ -99,33 +95,25 @@ TexturePtr HashTraverser::ProcessShader(const Node *node, const TimeRange &range
|
||||
Hash(it.value());
|
||||
}
|
||||
|
||||
TexturePtr texture = super::ProcessShader(node, range, job);
|
||||
texture_ids_.insert(texture.get(), hash_.result());
|
||||
return texture;
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
}
|
||||
|
||||
TexturePtr HashTraverser::ProcessColorTransform(const Node *node, const ColorTransformJob &job)
|
||||
void HashTraverser::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job)
|
||||
{
|
||||
Hash(job.GetColorProcessor()->id());
|
||||
TexturePtr texture = super::ProcessColorTransform(node, job);
|
||||
texture_ids_.insert(texture.get(), hash_.result());
|
||||
return texture;
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
}
|
||||
|
||||
SampleBufferPtr HashTraverser::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
void HashTraverser::ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
{
|
||||
SampleBufferPtr buf = super::ProcessSamples(node, range, job);
|
||||
texture_ids_.insert(buf.get(), hash_.result());
|
||||
return buf;
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
}
|
||||
|
||||
TexturePtr HashTraverser::ProcessFrameGeneration(const Node *node, const GenerateJob &job)
|
||||
void HashTraverser::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
|
||||
{
|
||||
HashGenerateJob(node, &job);
|
||||
|
||||
TexturePtr texture = super::ProcessFrameGeneration(node, job);
|
||||
texture_ids_.insert(texture.get(), hash_.result());
|
||||
return texture;
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
}
|
||||
|
||||
void HashTraverser::HashGenerateJob(const Node *node, const GenerateJob *job)
|
||||
|
||||
@@ -33,17 +33,17 @@ public:
|
||||
QByteArray GetHash(const Node *node, const Node::ValueHint &hint, const VideoParams ¶ms, const TimeRange &range);
|
||||
|
||||
protected:
|
||||
virtual TexturePtr ProcessVideoFootage(const FootageJob &stream, const rational &input_time) override;
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
||||
|
||||
virtual SampleBufferPtr ProcessAudioFootage(const FootageJob &stream, const TimeRange &input_time) override;
|
||||
virtual void ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time) override;
|
||||
|
||||
virtual TexturePtr ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||
|
||||
virtual TexturePtr ProcessColorTransform(const Node *node, const ColorTransformJob& job) override;
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
||||
|
||||
virtual SampleBufferPtr ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
|
||||
virtual TexturePtr ProcessFrameGeneration(const Node *node, const GenerateJob& job) override;
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
|
||||
|
||||
private:
|
||||
void HashGenerateJob(const Node *node, const GenerateJob *job);
|
||||
|
||||
@@ -102,7 +102,7 @@ void ColorDifferenceKeyNode::Value(const NodeValueRow &value, const NodeGlobals
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ void DespillNode::Value(const NodeValueRow &value, const NodeGlobals &globals, N
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -382,7 +382,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
||||
output->Push(texture_val);
|
||||
} else {
|
||||
// Push shader job
|
||||
output->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
output->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -413,10 +413,10 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
||||
|
||||
output->Push(NodeValue::kSamples, QVariant::fromValue(job.samples()), this);
|
||||
} else {
|
||||
output->Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
|
||||
output->Push(NodeValue::kSamples, QVariant::fromValue(job), this);
|
||||
}
|
||||
} else {
|
||||
output->Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
|
||||
output->Push(NodeValue::kSamples, QVariant::fromValue(job), this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ void MergeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, Nod
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOff);
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,6 +341,8 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
||||
Track::Reference ref = GetReferenceFromRealIndex(i);
|
||||
FootageJob job(decoder_, filename(), ref.type(), GetLength(), loop_mode);
|
||||
|
||||
NodeValue::Type type;
|
||||
|
||||
if (ref.type() == Track::kVideo) {
|
||||
VideoParams vp = GetVideoParams(ref.index());
|
||||
|
||||
@@ -348,13 +350,17 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
||||
vp.set_colorspace(GetColorspaceToUse(vp));
|
||||
|
||||
job.set_video_params(vp);
|
||||
|
||||
type = NodeValue::kTexture;
|
||||
} else {
|
||||
AudioParams ap = GetAudioParams(ref.index());
|
||||
job.set_audio_params(ap);
|
||||
job.set_cache_path(project()->cache_path());
|
||||
|
||||
type = NodeValue::kSamples;
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kFootageJob, QVariant::fromValue(job), this, false, ref.ToString());
|
||||
table->Push(type, QVariant::fromValue(job), this, false, ref.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+78
-147
@@ -52,6 +52,8 @@ NodeValueRow NodeTraverser::GenerateRow(NodeValueDatabase *database, const Node
|
||||
row.insert(it.key(), value);
|
||||
}
|
||||
|
||||
PreProcessRow(range, row);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
@@ -258,7 +260,6 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const Node::ValueHint
|
||||
|
||||
if (is_enabled) {
|
||||
NodeValueRow row = GenerateRow(&database, n, range);
|
||||
//qDebug() << "FIXME: Implement pre-process of row";
|
||||
|
||||
// Generate output table
|
||||
NodeValueTable table = database.Merge();
|
||||
@@ -266,9 +267,6 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const Node::ValueHint
|
||||
// By this point, the node should have all the inputs it needs to render correctly
|
||||
n->Value(row, GenerateGlobals(video_params_, range), &table);
|
||||
|
||||
// Post-process table
|
||||
PostProcessTable(n, hint, range, table);
|
||||
|
||||
return table;
|
||||
} else {
|
||||
return database.Merge();
|
||||
@@ -289,179 +287,112 @@ NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeR
|
||||
return table;
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::ProcessVideoFootage(const FootageJob &stream, const rational &input_time)
|
||||
{
|
||||
Q_UNUSED(input_time)
|
||||
|
||||
// Create dummy texture with footage params
|
||||
return CreateDummyTexture(stream.video_params());
|
||||
}
|
||||
|
||||
SampleBufferPtr NodeTraverser::ProcessAudioFootage(const FootageJob& stream, const TimeRange &input_time)
|
||||
{
|
||||
Q_UNUSED(stream)
|
||||
Q_UNUSED(input_time)
|
||||
|
||||
return SampleBuffer::Create();
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job)
|
||||
{
|
||||
Q_UNUSED(node)
|
||||
Q_UNUSED(range)
|
||||
Q_UNUSED(job)
|
||||
|
||||
// Create dummy texture with sequence params
|
||||
VideoParams tex_params = video_params_;
|
||||
tex_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
return CreateDummyTexture(tex_params);
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::ProcessColorTransform(const Node *node, const ColorTransformJob &job)
|
||||
{
|
||||
Q_UNUSED(node)
|
||||
|
||||
return CreateDummyTexture(job.GetInputTexture()->params());
|
||||
}
|
||||
|
||||
SampleBufferPtr NodeTraverser::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
{
|
||||
Q_UNUSED(node)
|
||||
Q_UNUSED(range)
|
||||
Q_UNUSED(job)
|
||||
|
||||
return SampleBuffer::Create();
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::ProcessFrameGeneration(const Node *node, const GenerateJob &job)
|
||||
{
|
||||
Q_UNUSED(node)
|
||||
Q_UNUSED(job)
|
||||
|
||||
// Create dummy texture with sequence params
|
||||
VideoParams tex_params = video_params_;
|
||||
tex_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
return CreateDummyTexture(tex_params);
|
||||
}
|
||||
|
||||
void NodeTraverser::SaveCachedTexture(const QByteArray &hash, TexturePtr texture)
|
||||
{
|
||||
Q_UNUSED(hash)
|
||||
Q_UNUSED(texture)
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::GetCachedTexture(const QByteArray& hash)
|
||||
{
|
||||
Q_UNUSED(hash)
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QVector2D NodeTraverser::GenerateResolution() const
|
||||
{
|
||||
return QVector2D(video_params_.square_pixel_width(), video_params_.height());
|
||||
}
|
||||
|
||||
void NodeTraverser::PostProcessTable(const Node *node, const Node::ValueHint &hint, const TimeRange &range, NodeValueTable &output_params)
|
||||
void NodeTraverser::ResolveJobs(NodeValue &val, const TimeRange &range)
|
||||
{
|
||||
bool got_cached_frame = false;
|
||||
QByteArray cached_node_hash;
|
||||
if (val.type() == NodeValue::kTexture || val.type() == NodeValue::kSamples) {
|
||||
const QVariant &v = val.data();
|
||||
|
||||
// Convert footage to image/sample buffers
|
||||
/*if (CanCacheFrames() && node->GetCacheTextures()) {
|
||||
// This node is set to cache the result, see if we can retrieved a previously cached version
|
||||
cached_node_hash = RenderManager::Hash(node, hint, GetCacheVideoParams(), range.in());
|
||||
if (v.canConvert<ShaderJob>()) {
|
||||
|
||||
TexturePtr cached_frame = GetCachedTexture(cached_node_hash);
|
||||
if (cached_frame) {
|
||||
output_params.Push(NodeValue::kTexture, QVariant::fromValue(cached_frame), node);
|
||||
ShaderJob job = v.value<ShaderJob>();
|
||||
|
||||
// No more to do here
|
||||
got_cached_frame = true;
|
||||
}
|
||||
}*/
|
||||
VideoParams tex_params = GetCacheVideoParams();
|
||||
tex_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
|
||||
// Strip out any jobs or footage
|
||||
QList<NodeValue> footage_jobs_to_run;
|
||||
QList<NodeValue> shader_jobs_to_run;
|
||||
QList<NodeValue> sample_jobs_to_run;
|
||||
QList<NodeValue> generate_jobs_to_run;
|
||||
QList<NodeValue> color_transform_jobs_to_run;
|
||||
TexturePtr tex = CreateTexture(tex_params);
|
||||
|
||||
for (int i=0; i<output_params.Count(); i++) {
|
||||
const NodeValue& v = output_params.at(i);
|
||||
QList<NodeValue>* take_this_value_list = nullptr;
|
||||
ProcessShader(tex, val.source(), range, job);
|
||||
|
||||
if (v.type() == NodeValue::kFootageJob) {
|
||||
take_this_value_list = &footage_jobs_to_run;
|
||||
} else if (v.type() == NodeValue::kShaderJob) {
|
||||
take_this_value_list = &shader_jobs_to_run;
|
||||
} else if (v.type() == NodeValue::kSampleJob) {
|
||||
take_this_value_list = &sample_jobs_to_run;
|
||||
} else if (v.type() == NodeValue::kGenerateJob) {
|
||||
take_this_value_list = &generate_jobs_to_run;
|
||||
} else if (v.type() == NodeValue::kColorTransformJob) {
|
||||
take_this_value_list = &color_transform_jobs_to_run;
|
||||
}
|
||||
val.set_data(QVariant::fromValue(tex));
|
||||
|
||||
if (take_this_value_list) {
|
||||
take_this_value_list->append(output_params.TakeAt(i));
|
||||
i--;
|
||||
}
|
||||
}
|
||||
} else if (v.canConvert<GenerateJob>()) {
|
||||
|
||||
if (!got_cached_frame) {
|
||||
// Retrieve video frames
|
||||
foreach (const NodeValue& v, footage_jobs_to_run) {
|
||||
// Assume this is a VideoStream, we did a type check earlier in the function
|
||||
FootageJob job = v.data().value<FootageJob>();
|
||||
GenerateJob job = v.value<GenerateJob>();
|
||||
|
||||
VideoParams tex_params = GetCacheVideoParams();
|
||||
tex_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
if (job.GetRequestedFormat() != VideoParams::kFormatInvalid) {
|
||||
tex_params.set_format(job.GetRequestedFormat());
|
||||
}
|
||||
|
||||
TexturePtr tex = CreateTexture(tex_params);
|
||||
|
||||
ProcessFrameGeneration(tex, val.source(), job);
|
||||
|
||||
val.set_data(QVariant::fromValue(tex));
|
||||
|
||||
} else if (v.canConvert<ColorTransformJob>()) {
|
||||
|
||||
ColorTransformJob job = v.value<ColorTransformJob>();
|
||||
|
||||
VideoParams src_params = job.GetInputTexture()->params();
|
||||
src_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
|
||||
TexturePtr dest = CreateTexture(src_params);
|
||||
|
||||
ProcessColorTransform(dest, val.source(), job);
|
||||
|
||||
val.set_data(QVariant::fromValue(dest));
|
||||
|
||||
} else if (v.canConvert<FootageJob>()) {
|
||||
|
||||
FootageJob job = v.value<FootageJob>();
|
||||
|
||||
if (job.type() == Track::kVideo) {
|
||||
|
||||
rational footage_time = Footage::AdjustTimeByLoopMode(range.in(), job.loop_mode(), job.length(), job.video_params().video_type(), job.video_params().frame_rate_as_time_base());
|
||||
|
||||
TexturePtr tex;
|
||||
|
||||
if (footage_time.isNaN()) {
|
||||
// Push dummy texture
|
||||
output_params.Push(NodeValue::kTexture, QVariant::fromValue(CreateDummyTexture(job.video_params())), node, v.array(), v.tag());
|
||||
tex = CreateDummyTexture(job.video_params());
|
||||
} else {
|
||||
output_params.Push(NodeValue::kTexture, QVariant::fromValue(ProcessVideoFootage(job, footage_time)), node, v.array(), v.tag());
|
||||
VideoParams managed_params = job.video_params();
|
||||
managed_params.set_format(GetCacheVideoParams().format());
|
||||
|
||||
tex = CreateTexture(job.video_params());
|
||||
ProcessVideoFootage(tex, job, footage_time);
|
||||
}
|
||||
|
||||
val.set_data(QVariant::fromValue(tex));
|
||||
|
||||
} else if (job.type() == Track::kAudio) {
|
||||
|
||||
SampleBufferPtr buffer = CreateSampleBuffer(GetCacheAudioParams(), range.length());
|
||||
ProcessAudioFootage(buffer, job, range);
|
||||
val.set_data(QVariant::fromValue(buffer));
|
||||
|
||||
}
|
||||
|
||||
} else if (v.canConvert<SampleJob>()) {
|
||||
|
||||
SampleJob job = v.value<SampleJob>();
|
||||
SampleBufferPtr output_buffer = CreateSampleBuffer(job.samples()->audio_params(), job.samples()->sample_count());
|
||||
ProcessSamples(output_buffer, val.source(), range, job);
|
||||
val.set_data(QVariant::fromValue(output_buffer));
|
||||
|
||||
}
|
||||
|
||||
// Run shaders
|
||||
foreach (const NodeValue& v, shader_jobs_to_run) {
|
||||
output_params.Push(NodeValue::kTexture, QVariant::fromValue(ProcessShader(node, range, v.data().value<ShaderJob>())), node, v.array(), v.tag());
|
||||
}
|
||||
|
||||
// Run color transforms
|
||||
foreach (const NodeValue& v, color_transform_jobs_to_run) {
|
||||
output_params.Push(NodeValue::kTexture, QVariant::fromValue(ProcessColorTransform(node, v.data().value<ColorTransformJob>())), node, v.array(), v.tag());
|
||||
}
|
||||
|
||||
// Run generate jobs
|
||||
foreach (const NodeValue& v, generate_jobs_to_run) {
|
||||
output_params.Push(NodeValue::kTexture, QVariant::fromValue(ProcessFrameGeneration(node, v.data().value<GenerateJob>())), node, v.array(), v.tag());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve audio samples
|
||||
foreach (const NodeValue& v, footage_jobs_to_run) {
|
||||
// Assume this is an AudioStream, we did a type check earlier in the function
|
||||
FootageJob job = v.data().value<FootageJob>();
|
||||
void NodeTraverser::PreProcessRow(const TimeRange &range, NodeValueRow &row)
|
||||
{
|
||||
QByteArray cached_node_hash;
|
||||
|
||||
if (job.type() == Track::kAudio) {
|
||||
output_params.Push(NodeValue::kSamples, QVariant::fromValue(ProcessAudioFootage(job, range)), node, v.array(), v.tag());
|
||||
}
|
||||
}
|
||||
// Resolve any jobs
|
||||
for (auto it=row.begin(); it!=row.end(); it++) {
|
||||
// Jobs will almost always be submitted with one of these types
|
||||
NodeValue &val = it.value();
|
||||
|
||||
// Run any accelerated shader jobs
|
||||
foreach (const NodeValue& v, sample_jobs_to_run) {
|
||||
output_params.Push(NodeValue::kSamples, QVariant::fromValue(ProcessSamples(node, range, v.data().value<SampleJob>())), node, v.array(), v.tag());
|
||||
}
|
||||
|
||||
if (CanCacheFrames() && node->GetCacheTextures() && !got_cached_frame) {
|
||||
// Save cached texture
|
||||
SaveCachedTexture(cached_node_hash, output_params.Get(NodeValue::kTexture).value<TexturePtr>());
|
||||
ResolveJobs(val, range);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-9
@@ -66,6 +66,16 @@ public:
|
||||
video_params_ = params;
|
||||
}
|
||||
|
||||
const AudioParams& GetCacheAudioParams() const
|
||||
{
|
||||
return audio_params_;
|
||||
}
|
||||
|
||||
void SetCacheAudioParams(const AudioParams& params)
|
||||
{
|
||||
audio_params_ = params;
|
||||
}
|
||||
|
||||
static int GetChannelCountFromJob(const GenerateJob& job);
|
||||
|
||||
protected:
|
||||
@@ -73,21 +83,33 @@ protected:
|
||||
|
||||
virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange& range);
|
||||
|
||||
virtual TexturePtr ProcessVideoFootage(const FootageJob &stream, const rational &input_time);
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time){}
|
||||
|
||||
virtual SampleBufferPtr ProcessAudioFootage(const FootageJob &stream, const TimeRange &input_time);
|
||||
virtual void ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time){}
|
||||
|
||||
virtual TexturePtr ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job);
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job){}
|
||||
|
||||
virtual TexturePtr ProcessColorTransform(const Node *node, const ColorTransformJob& job);
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job){}
|
||||
|
||||
virtual SampleBufferPtr ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job);
|
||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job){}
|
||||
|
||||
virtual TexturePtr ProcessFrameGeneration(const Node *node, const GenerateJob& job);
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job){}
|
||||
|
||||
virtual TexturePtr GetCachedTexture(const QByteArray& hash);
|
||||
virtual TexturePtr CreateTexture(const VideoParams &p)
|
||||
{
|
||||
return CreateDummyTexture(p);
|
||||
}
|
||||
|
||||
virtual void SaveCachedTexture(const QByteArray& hash, TexturePtr texture);
|
||||
virtual SampleBufferPtr CreateSampleBuffer(const AudioParams ¶ms, int sample_count)
|
||||
{
|
||||
// Return dummy by default
|
||||
return SampleBuffer::Create();
|
||||
}
|
||||
|
||||
SampleBufferPtr CreateSampleBuffer(const AudioParams ¶ms, const rational &length)
|
||||
{
|
||||
return CreateSampleBuffer(params, params.time_to_samples(length));
|
||||
}
|
||||
|
||||
virtual bool CanCacheFrames()
|
||||
{
|
||||
@@ -111,13 +133,17 @@ protected:
|
||||
cancel_ = cancel;
|
||||
}
|
||||
|
||||
void ResolveJobs(NodeValue &value, const TimeRange &range);
|
||||
|
||||
private:
|
||||
void PostProcessTable(const Node *node, const Node::ValueHint &hint, const TimeRange &range, NodeValueTable &output_params);
|
||||
void PreProcessRow(const TimeRange &range, NodeValueRow &row);
|
||||
|
||||
TexturePtr CreateDummyTexture(const VideoParams &p);
|
||||
|
||||
VideoParams video_params_;
|
||||
|
||||
AudioParams audio_params_;
|
||||
|
||||
const QAtomicInt *cancel_;
|
||||
|
||||
};
|
||||
|
||||
@@ -135,13 +135,8 @@ QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value)
|
||||
|
||||
// These types have no persistent input
|
||||
case kNone:
|
||||
case kFootageJob:
|
||||
case kTexture:
|
||||
case kSamples:
|
||||
case kShaderJob:
|
||||
case kSampleJob:
|
||||
case kGenerateJob:
|
||||
case kColorTransformJob:
|
||||
case kDataTypeCount:
|
||||
break;
|
||||
}
|
||||
@@ -355,11 +350,6 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
|
||||
case kAudioParams:
|
||||
return QCoreApplication::translate("NodeValue", "Audio Parameters");
|
||||
|
||||
case kFootageJob:
|
||||
case kShaderJob:
|
||||
case kSampleJob:
|
||||
case kGenerateJob:
|
||||
case kColorTransformJob:
|
||||
case kDataTypeCount:
|
||||
break;
|
||||
}
|
||||
@@ -408,11 +398,6 @@ QString NodeValue::GetDataTypeName(Type type)
|
||||
return QStringLiteral("vparam");
|
||||
case kAudioParams:
|
||||
return QStringLiteral("aparam");
|
||||
case kFootageJob:
|
||||
case kShaderJob:
|
||||
case kSampleJob:
|
||||
case kGenerateJob:
|
||||
case kColorTransformJob:
|
||||
case kDataTypeCount:
|
||||
break;
|
||||
}
|
||||
|
||||
+5
-45
@@ -172,51 +172,6 @@ public:
|
||||
*/
|
||||
kAudioParams,
|
||||
|
||||
/**
|
||||
* Job type
|
||||
*
|
||||
* An internal type used to indicate to the renderer that a footage job needs to
|
||||
* run. This value will usually be taken from a table and a kTexture or kSamples value will be
|
||||
* pushed to take its place.
|
||||
*/
|
||||
kFootageJob,
|
||||
|
||||
/**
|
||||
* Job type
|
||||
*
|
||||
* An internal type used to indicate to the renderer that an accelerated shader job needs to
|
||||
* run. This value will usually be taken from a table and a kTexture value will be pushed to
|
||||
* take its place.
|
||||
*/
|
||||
kShaderJob,
|
||||
|
||||
/**
|
||||
* Job type
|
||||
*
|
||||
* An internal type used to indicate to the renderer that an accelerated sample job needs to
|
||||
* take place. This value will usually be taken from a table and a kSamples value will be
|
||||
* pushed to take its place.
|
||||
*/
|
||||
kSampleJob,
|
||||
|
||||
/**
|
||||
* Job type
|
||||
*
|
||||
* An internal type used to indicate to the renderer that an accelerated sample job needs to
|
||||
* take place. This value will usually be taken from a table and a kSamples value will be
|
||||
* pushed to take its place.
|
||||
*/
|
||||
kGenerateJob,
|
||||
|
||||
/**
|
||||
* Job type
|
||||
*
|
||||
* An internal type used to indicate to the renderer that an accelerated color transform job
|
||||
* needs to take place. This value will usually be taken from a table and a kTexture value will
|
||||
* be pushed to take its place.
|
||||
*/
|
||||
kColorTransformJob,
|
||||
|
||||
/**
|
||||
* End of list
|
||||
*/
|
||||
@@ -253,6 +208,11 @@ public:
|
||||
return data_;
|
||||
}
|
||||
|
||||
void set_data(const QVariant& data)
|
||||
{
|
||||
data_ = data;
|
||||
}
|
||||
|
||||
const QString& tag() const
|
||||
{
|
||||
return tag_;
|
||||
|
||||
@@ -52,6 +52,11 @@ public:
|
||||
|
||||
ProjectViewModel* model() const;
|
||||
|
||||
bool SelectItem(Node *n)
|
||||
{
|
||||
return explorer_->SelectItem(n);
|
||||
}
|
||||
|
||||
virtual void SelectAll() override;
|
||||
virtual void DeselectAll() override;
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ TimelinePanel::TimelinePanel(QWidget *parent) :
|
||||
Retranslate();
|
||||
|
||||
connect(tw, &TimelineWidget::BlockSelectionChanged, this, &TimelinePanel::BlockSelectionChanged);
|
||||
connect(tw, &TimelineWidget::RequestCaptureStart, this, &TimelinePanel::RequestCaptureStart );
|
||||
connect(tw, &TimelineWidget::RequestCaptureStart, this, &TimelinePanel::RequestCaptureStart);
|
||||
connect(tw, &TimelineWidget::RevealViewerInProject, this, &TimelinePanel::RevealViewerInProject);
|
||||
}
|
||||
|
||||
void TimelinePanel::SplitAtPlayhead()
|
||||
|
||||
@@ -114,6 +114,8 @@ signals:
|
||||
|
||||
void RequestCaptureStart(const TimeRange &time, const Track::Reference &track);
|
||||
|
||||
void RevealViewerInProject(ViewerOutput *r);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+119
-7
@@ -52,7 +52,7 @@ const QVector<uint64_t> AudioParams::kSupportedChannelLayouts = {
|
||||
AV_CH_LAYOUT_7POINT1
|
||||
};
|
||||
|
||||
const AudioParams::Format AudioParams::kInternalFormat = AudioParams::kFormatFloat32;
|
||||
const AudioParams::Format AudioParams::kInternalFormat = AudioParams::kFormatFloat32Planar;
|
||||
|
||||
bool AudioParams::operator==(const AudioParams &other) const
|
||||
{
|
||||
@@ -144,15 +144,21 @@ int AudioParams::channel_count() const
|
||||
int AudioParams::bytes_per_sample_per_channel() const
|
||||
{
|
||||
switch (format_) {
|
||||
case kFormatUnsigned8:
|
||||
case kFormatUnsigned8Packed:
|
||||
case kFormatUnsigned8Planar:
|
||||
return 1;
|
||||
case kFormatSigned16:
|
||||
case kFormatSigned16Packed:
|
||||
case kFormatSigned16Planar:
|
||||
return 2;
|
||||
case kFormatSigned32:
|
||||
case kFormatFloat32:
|
||||
case kFormatSigned32Packed:
|
||||
case kFormatSigned32Planar:
|
||||
case kFormatFloat32Packed:
|
||||
case kFormatFloat32Planar:
|
||||
return 4;
|
||||
case kFormatSigned64:
|
||||
case kFormatFloat64:
|
||||
case kFormatSigned64Packed:
|
||||
case kFormatSigned64Planar:
|
||||
case kFormatFloat64Packed:
|
||||
case kFormatFloat64Planar:
|
||||
return 8;
|
||||
case kFormatInvalid:
|
||||
case kFormatCount:
|
||||
@@ -244,4 +250,110 @@ QString AudioParams::ChannelLayoutToString(const uint64_t &layout)
|
||||
}
|
||||
}
|
||||
|
||||
QString AudioParams::FormatToString(const Format &f)
|
||||
{
|
||||
switch (f) {
|
||||
case kFormatUnsigned8Packed:
|
||||
return QCoreApplication::translate("AudioParams", "Unsigned 8-bit (Packed)");
|
||||
case kFormatSigned16Packed:
|
||||
return QCoreApplication::translate("AudioParams", "Signed 16-bit (Packed)");
|
||||
case kFormatSigned32Packed:
|
||||
return QCoreApplication::translate("AudioParams", "Signed 32-bit (Packed)");
|
||||
case kFormatSigned64Packed:
|
||||
return QCoreApplication::translate("AudioParams", "Signed 64-bit (Packed)");
|
||||
case kFormatFloat32Packed:
|
||||
return QCoreApplication::translate("AudioParams", "Float 32-bit (Packed)");
|
||||
case kFormatFloat64Packed:
|
||||
return QCoreApplication::translate("AudioParams", "Float 64-bit (Packed)");
|
||||
case kFormatUnsigned8Planar:
|
||||
return QCoreApplication::translate("AudioParams", "Unsigned 8-bit (Planar)");
|
||||
case kFormatSigned16Planar:
|
||||
return QCoreApplication::translate("AudioParams", "Signed 16-bit (Planar)");
|
||||
case kFormatSigned32Planar:
|
||||
return QCoreApplication::translate("AudioParams", "Signed 32-bit (Planar)");
|
||||
case kFormatSigned64Planar:
|
||||
return QCoreApplication::translate("AudioParams", "Signed 64-bit (Planar)");
|
||||
case kFormatFloat32Planar:
|
||||
return QCoreApplication::translate("AudioParams", "Float 32-bit (Planar)");
|
||||
case kFormatFloat64Planar:
|
||||
return QCoreApplication::translate("AudioParams", "Float 64-bit (Planar)");
|
||||
|
||||
case kFormatInvalid:
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return QCoreApplication::translate("AudioParams", "Unknown (0x%1)").arg(f, 1, 16);
|
||||
}
|
||||
|
||||
AudioParams::Format AudioParams::GetPackedEquivalent(Format fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
|
||||
// For packed input, just return input
|
||||
case kFormatUnsigned8Packed:
|
||||
case kFormatSigned16Packed:
|
||||
case kFormatSigned32Packed:
|
||||
case kFormatSigned64Packed:
|
||||
case kFormatFloat32Packed:
|
||||
case kFormatFloat64Packed:
|
||||
return fmt;
|
||||
|
||||
// Convert to packed
|
||||
case kFormatUnsigned8Planar:
|
||||
return kFormatUnsigned8Packed;
|
||||
case kFormatSigned16Planar:
|
||||
return kFormatSigned16Packed;
|
||||
case kFormatSigned32Planar:
|
||||
return kFormatSigned32Packed;
|
||||
case kFormatSigned64Planar:
|
||||
return kFormatSigned64Packed;
|
||||
case kFormatFloat32Planar:
|
||||
return kFormatFloat32Packed;
|
||||
case kFormatFloat64Planar:
|
||||
return kFormatFloat64Packed;
|
||||
|
||||
case kFormatInvalid:
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return kFormatInvalid;
|
||||
}
|
||||
|
||||
AudioParams::Format AudioParams::GetPlanarEquivalent(Format fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
|
||||
// Convert to planar
|
||||
case kFormatUnsigned8Packed:
|
||||
return kFormatUnsigned8Planar;
|
||||
case kFormatSigned16Packed:
|
||||
return kFormatSigned16Planar;
|
||||
case kFormatSigned32Packed:
|
||||
return kFormatSigned32Planar;
|
||||
case kFormatSigned64Packed:
|
||||
return kFormatSigned64Planar;
|
||||
case kFormatFloat32Packed:
|
||||
return kFormatFloat32Planar;
|
||||
case kFormatFloat64Packed:
|
||||
return kFormatFloat64Planar;
|
||||
|
||||
// For planar input, just return input
|
||||
case kFormatUnsigned8Planar:
|
||||
case kFormatSigned16Planar:
|
||||
case kFormatSigned32Planar:
|
||||
case kFormatSigned64Planar:
|
||||
case kFormatFloat32Planar:
|
||||
case kFormatFloat64Planar:
|
||||
return fmt;
|
||||
|
||||
case kFormatInvalid:
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return kFormatInvalid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,30 +35,54 @@ namespace olive {
|
||||
|
||||
class AudioParams {
|
||||
public:
|
||||
// Only append to this list (never insert) because indexes are used in serialized files
|
||||
enum Format {
|
||||
/// Invalid
|
||||
kFormatInvalid = -1,
|
||||
|
||||
/// 8-bit unsigned integer
|
||||
kFormatUnsigned8,
|
||||
kFormatUnsigned8Planar,
|
||||
|
||||
/// 16-bit signed integer
|
||||
kFormatSigned16,
|
||||
kFormatSigned16Planar,
|
||||
|
||||
/// 32-bit signed integer
|
||||
kFormatSigned32,
|
||||
kFormatSigned32Planar,
|
||||
|
||||
/// 64-bit signed integer
|
||||
kFormatSigned64,
|
||||
kFormatSigned64Planar,
|
||||
|
||||
/// 32-bit float
|
||||
kFormatFloat32,
|
||||
kFormatFloat32Planar,
|
||||
|
||||
/// 64-bit float
|
||||
kFormatFloat64,
|
||||
kFormatFloat64Planar,
|
||||
|
||||
/// 8-bit unsigned integer
|
||||
kFormatUnsigned8Packed,
|
||||
|
||||
/// 16-bit signed integer
|
||||
kFormatSigned16Packed,
|
||||
|
||||
/// 32-bit signed integer
|
||||
kFormatSigned32Packed,
|
||||
|
||||
/// 64-bit signed integer
|
||||
kFormatSigned64Packed,
|
||||
|
||||
/// 32-bit float
|
||||
kFormatFloat32Packed,
|
||||
|
||||
/// 64-bit float
|
||||
kFormatFloat64Packed,
|
||||
|
||||
/// Total format count
|
||||
kFormatCount
|
||||
kFormatCount,
|
||||
|
||||
kPlanarStart = kFormatUnsigned8Planar,
|
||||
kPackedStart = kFormatUnsigned8Packed,
|
||||
kPlanarEnd = kPackedStart,
|
||||
kPackedEnd = kFormatCount
|
||||
};
|
||||
|
||||
static const Format kInternalFormat;
|
||||
@@ -200,6 +224,11 @@ public:
|
||||
*/
|
||||
static QString ChannelLayoutToString(const uint64_t &layout);
|
||||
|
||||
static QString FormatToString(const Format &f);
|
||||
|
||||
static AudioParams::Format GetPackedEquivalent(AudioParams::Format fmt);
|
||||
static AudioParams::Format GetPlanarEquivalent(AudioParams::Format fmt);
|
||||
|
||||
private:
|
||||
void set_default_footage_parameters()
|
||||
{
|
||||
|
||||
@@ -56,10 +56,8 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
const NodeValueRow &GetValues() const
|
||||
{
|
||||
return value_map_;
|
||||
}
|
||||
const NodeValueRow &GetValues() const { return value_map_; }
|
||||
NodeValueRow &GetValues() { return value_map_; }
|
||||
|
||||
private:
|
||||
NodeValueRow value_map_;
|
||||
|
||||
@@ -518,10 +518,6 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
case NodeValue::kFile:
|
||||
case NodeValue::kVideoParams:
|
||||
case NodeValue::kAudioParams:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kBezier:
|
||||
case NodeValue::kNone:
|
||||
case NodeValue::kColorTransformJob:
|
||||
|
||||
+45
-109
@@ -50,12 +50,18 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational
|
||||
{
|
||||
ViewerOutput* viewer = Node::ValueToPtr<ViewerOutput>(ticket_->property("viewer"));
|
||||
|
||||
TimeRange range = TimeRange(time, time + frame_length);
|
||||
|
||||
NodeValueTable table;
|
||||
if (Node *texture_output = viewer->GetConnectedTextureOutput()) {
|
||||
table = GenerateTable(texture_output, viewer->GetValueHintForInput(ViewerOutput::kTextureInput), TimeRange(time, time + frame_length));
|
||||
table = GenerateTable(texture_output, viewer->GetValueHintForInput(ViewerOutput::kTextureInput), range);
|
||||
}
|
||||
|
||||
return table.Get(NodeValue::kTexture).value<TexturePtr>();
|
||||
NodeValue tex_val = table.GetWithMeta(NodeValue::kTexture);
|
||||
|
||||
ResolveJobs(tex_val, range);
|
||||
|
||||
return tex_val.data().value<TexturePtr>();
|
||||
}
|
||||
|
||||
FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time)
|
||||
@@ -133,10 +139,12 @@ void RenderProcessor::Run()
|
||||
|
||||
SetCancelPointer(&ticket_->IsCancelled());
|
||||
|
||||
SetCacheVideoParams(ticket_->property("vparam").value<VideoParams>());
|
||||
SetCacheAudioParams(ticket_->property("aparam").value<AudioParams>());
|
||||
|
||||
switch (type) {
|
||||
case RenderManager::kTypeVideo:
|
||||
{
|
||||
SetCacheVideoParams(ticket_->property("vparam").value<VideoParams>());
|
||||
rational time = ticket_->property("time").value<rational>();
|
||||
|
||||
rational frame_length = GetCacheVideoParams().frame_rate_as_time_base();
|
||||
@@ -261,7 +269,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
{
|
||||
if (track->type() == Track::kAudio) {
|
||||
|
||||
const AudioParams& audio_params = ticket_->property("aparam").value<AudioParams>();
|
||||
const AudioParams& audio_params = GetCacheAudioParams();
|
||||
|
||||
QVector<Block*> active_blocks = track->BlocksAtTimeRange(range);
|
||||
|
||||
@@ -375,11 +383,11 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
}
|
||||
}
|
||||
|
||||
TexturePtr RenderProcessor::ProcessVideoFootage(const FootageJob &stream, const rational &input_time)
|
||||
void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time)
|
||||
{
|
||||
if (ticket_->property("type").value<RenderManager::TicketType>() != RenderManager::kTypeVideo) {
|
||||
// Video cannot contribute to audio, so we do nothing here
|
||||
return super::ProcessVideoFootage(stream, input_time);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the still frame cache. On large frames such as high resolution still images, uploading
|
||||
@@ -447,12 +455,6 @@ TexturePtr RenderProcessor::ProcessVideoFootage(const FootageJob &stream, const
|
||||
|
||||
// We convert to our rendering pixel format, since that will always be float-based which
|
||||
// is necessary for correct color conversion
|
||||
VideoParams managed_params = frame->video_params();
|
||||
managed_params.set_format(render_params.format());
|
||||
managed_params.set_pixel_aspect_ratio(stream_data.pixel_aspect_ratio());
|
||||
managed_params.set_interlacing(stream_data.interlacing());
|
||||
TexturePtr value = render_ctx_->CreateTexture(managed_params);
|
||||
|
||||
ColorProcessorPtr processor = ColorProcessor::Create(color_manager,
|
||||
using_colorspace,
|
||||
color_manager->GetReferenceColorSpace());
|
||||
@@ -471,39 +473,32 @@ TexturePtr RenderProcessor::ProcessVideoFootage(const FootageJob &stream, const
|
||||
job.SetInputAlphaAssociation(kAlphaUnassociated);
|
||||
}
|
||||
|
||||
render_ctx_->BlitColorManaged(job, value.get());
|
||||
|
||||
return value;
|
||||
render_ctx_->BlitColorManaged(job, destination.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super::ProcessVideoFootage(stream, input_time);
|
||||
}
|
||||
|
||||
SampleBufferPtr RenderProcessor::ProcessAudioFootage(const FootageJob &stream, const TimeRange &input_time)
|
||||
void RenderProcessor::ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time)
|
||||
{
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream.decoder(), Decoder::CodecStream(stream.filename(), stream.audio_params().stream_index()));
|
||||
|
||||
if (decoder) {
|
||||
const AudioParams& audio_params = ticket_->property("aparam").value<AudioParams>();
|
||||
const AudioParams& audio_params = GetCacheAudioParams();
|
||||
|
||||
Decoder::RetrieveAudioData status = decoder->RetrieveAudio(input_time, audio_params,
|
||||
stream.cache_path(),
|
||||
stream.loop_mode(),
|
||||
static_cast<RenderMode::Mode>(ticket_->property("mode").toInt()));
|
||||
Decoder::RetrieveAudioStatus status = decoder->RetrieveAudio(destination,
|
||||
input_time, audio_params,
|
||||
stream.cache_path(),
|
||||
stream.loop_mode(),
|
||||
static_cast<RenderMode::Mode>(ticket_->property("mode").toInt()));
|
||||
|
||||
if (status.status == Decoder::kOK && status.samples) {
|
||||
return status.samples;
|
||||
} else if (status.status == Decoder::kWaitingForConform) {
|
||||
if (status == Decoder::kWaitingForConform) {
|
||||
ticket_->setProperty("incomplete", true);
|
||||
}
|
||||
}
|
||||
|
||||
return super::ProcessAudioFootage(stream, input_time);
|
||||
}
|
||||
|
||||
TexturePtr RenderProcessor::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job)
|
||||
void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob &job)
|
||||
{
|
||||
Q_UNUSED(range)
|
||||
|
||||
@@ -524,7 +519,7 @@ TexturePtr RenderProcessor::ProcessShader(const Node *node, const TimeRange &ran
|
||||
|
||||
if (shader.isNull()) {
|
||||
// Couldn't find or build the shader required
|
||||
return super::ProcessShader(node, range, job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,24 +527,19 @@ TexturePtr RenderProcessor::ProcessShader(const Node *node, const TimeRange &ran
|
||||
|
||||
tex_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
|
||||
TexturePtr destination = render_ctx_->CreateTexture(tex_params);
|
||||
|
||||
// Run shader
|
||||
render_ctx_->BlitToTexture(shader, job, destination.get());
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
SampleBufferPtr RenderProcessor::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
void RenderProcessor::ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
{
|
||||
if (!job.samples() || !job.samples()->is_allocated()) {
|
||||
return super::ProcessSamples(node, range, job);
|
||||
return;
|
||||
}
|
||||
|
||||
SampleBufferPtr output_buffer = SampleBuffer::CreateAllocated(job.samples()->audio_params(), job.samples()->sample_count());
|
||||
NodeValueRow value_db;
|
||||
|
||||
const AudioParams& audio_params = ticket_->property("aparam").value<AudioParams>();
|
||||
const AudioParams& audio_params = GetCacheAudioParams();
|
||||
|
||||
for (int i=0;i<job.samples()->sample_count();i++) {
|
||||
// Calculate the exact rational time at this sample
|
||||
@@ -566,62 +556,46 @@ SampleBufferPtr RenderProcessor::ProcessSamples(const Node *node, const TimeRang
|
||||
|
||||
node->ProcessSamples(value_db,
|
||||
job.samples(),
|
||||
output_buffer,
|
||||
destination,
|
||||
i);
|
||||
}
|
||||
|
||||
return output_buffer;
|
||||
}
|
||||
|
||||
TexturePtr RenderProcessor::ProcessColorTransform(const Node *node, const ColorTransformJob &job)
|
||||
void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job)
|
||||
{
|
||||
TexturePtr src = job.GetInputTexture();
|
||||
|
||||
VideoParams src_params = src->params();
|
||||
src_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
|
||||
TexturePtr dest = render_ctx_->CreateTexture(src_params);
|
||||
|
||||
render_ctx_->BlitColorManaged(job, dest.get());
|
||||
|
||||
return dest;
|
||||
render_ctx_->BlitColorManaged(job, destination.get());
|
||||
}
|
||||
|
||||
TexturePtr RenderProcessor::ProcessFrameGeneration(const Node *node, const GenerateJob &job)
|
||||
void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
|
||||
{
|
||||
FramePtr frame = Frame::Create();
|
||||
|
||||
VideoParams frame_params = GetCacheVideoParams();
|
||||
frame_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
if (job.GetRequestedFormat() != VideoParams::kFormatInvalid) {
|
||||
frame_params.set_format(job.GetRequestedFormat());
|
||||
}
|
||||
|
||||
frame->set_video_params(frame_params);
|
||||
frame->set_video_params(destination->params());
|
||||
frame->allocate();
|
||||
|
||||
node->GenerateFrame(frame, job);
|
||||
|
||||
TexturePtr texture = render_ctx_->CreateTexture(frame->video_params(),
|
||||
frame->data(),
|
||||
frame->linesize_pixels());
|
||||
|
||||
if (!job.GetColorspace().isEmpty()) {
|
||||
if (job.GetColorspace().isEmpty()) {
|
||||
// Just upload frame data straight to frame
|
||||
destination->Upload(frame->data(), frame->linesize_pixels());
|
||||
} else {
|
||||
// Convert to reference space
|
||||
TexturePtr dest = render_ctx_->CreateTexture(GetCacheVideoParams());
|
||||
|
||||
// Upload to middle texture
|
||||
TexturePtr mid = render_ctx_->CreateTexture(GetCacheVideoParams());
|
||||
mid->Upload(frame->data(), frame->linesize_pixels());
|
||||
|
||||
ColorManager* color_manager = Node::ValueToPtr<ColorManager>(ticket_->property("colormanager"));
|
||||
ColorProcessorPtr cp = ColorProcessor::Create(color_manager, job.GetColorspace(), color_manager->GetReferenceColorSpace());
|
||||
|
||||
ColorTransformJob ctj;
|
||||
|
||||
ctj.SetColorProcessor(cp);
|
||||
ctj.SetInputTexture(texture);
|
||||
ctj.SetInputTexture(mid);
|
||||
ctj.SetInputAlphaAssociation(kAlphaAssociated);
|
||||
|
||||
render_ctx_->BlitColorManaged(ctj, dest.get());
|
||||
texture = dest;
|
||||
render_ctx_->BlitColorManaged(ctj, destination.get());
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
bool RenderProcessor::CanCacheFrames()
|
||||
@@ -629,42 +603,4 @@ bool RenderProcessor::CanCacheFrames()
|
||||
return ticket_->property("type").value<RenderManager::TicketType>() == RenderManager::kTypeVideo;
|
||||
}
|
||||
|
||||
TexturePtr RenderProcessor::GetCachedTexture(const QByteArray& hash)
|
||||
{
|
||||
QString cache_dir = ticket_->property("cache").toString();
|
||||
if (cache_dir.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FramePtr f = FrameHashCache::LoadCacheFrame(cache_dir, hash);
|
||||
|
||||
if (f) {
|
||||
TexturePtr texture = render_ctx_->CreateTexture(f->video_params(), f->data(), f->linesize_pixels());
|
||||
return texture;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RenderProcessor::SaveCachedTexture(const QByteArray &hash, TexturePtr tex_var)
|
||||
{
|
||||
// FIXME: Temporarily disabled because I don't know how to ensure that the frame saved here is
|
||||
// not the main frame. If it is, it'll be saved twice which will waste a lot of cycles.
|
||||
// At least disabled, the frame will still save, and if nothing else alters the hash, it
|
||||
// will pick up automatically from GetCachedTexture.
|
||||
/*if (!tex_var.isNull()) {
|
||||
QString cache_dir = ticket_->property("cache").toString();
|
||||
|
||||
if (!cache_dir.isEmpty()) {
|
||||
TexturePtr texture = tex_var.value<TexturePtr>();
|
||||
FramePtr frame = Frame::Create();
|
||||
frame->set_video_params(texture->params());
|
||||
frame->allocate();
|
||||
render_ctx_->DownloadFromTexture(texture.get(), frame->data(), frame->linesize_pixels());
|
||||
FrameHashCache::SaveCacheFrame(cache_dir, hash, frame);
|
||||
qDebug() << "Saved mid-render frame to cache";
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,23 +44,29 @@ public:
|
||||
protected:
|
||||
virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange &range) override;
|
||||
|
||||
virtual TexturePtr ProcessVideoFootage(const FootageJob &stream, const rational &input_time) override;
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
||||
|
||||
virtual SampleBufferPtr ProcessAudioFootage(const FootageJob &stream, const TimeRange &input_time) override;
|
||||
virtual void ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time) override;
|
||||
|
||||
virtual TexturePtr ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||
|
||||
virtual SampleBufferPtr ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
|
||||
virtual TexturePtr ProcessColorTransform(const Node *node, const ColorTransformJob& job) override;
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
||||
|
||||
virtual TexturePtr ProcessFrameGeneration(const Node *node, const GenerateJob& job) override;
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
|
||||
|
||||
virtual bool CanCacheFrames() override;
|
||||
|
||||
virtual TexturePtr GetCachedTexture(const QByteArray &hash) override;
|
||||
virtual TexturePtr CreateTexture(const VideoParams &p) override
|
||||
{
|
||||
return render_ctx_->CreateTexture(p);
|
||||
}
|
||||
|
||||
virtual void SaveCachedTexture(const QByteArray& hash, TexturePtr texture) override;
|
||||
virtual SampleBufferPtr CreateSampleBuffer(const AudioParams ¶ms, int sample_count) override
|
||||
{
|
||||
return SampleBuffer::CreateAllocated(params, sample_count);
|
||||
}
|
||||
|
||||
private:
|
||||
RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader);
|
||||
|
||||
@@ -39,7 +39,8 @@ NodeParamView::NodeParamView(bool create_keyframe_view, QWidget *parent) :
|
||||
super(true, false, parent),
|
||||
last_scroll_val_(0),
|
||||
focused_node_(nullptr),
|
||||
time_target_(nullptr)
|
||||
time_target_(nullptr),
|
||||
show_all_nodes_(false)
|
||||
{
|
||||
// Create horizontal layout to place scroll area in (and keyframe editing eventually)
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
@@ -453,7 +454,7 @@ void NodeParamView::RemoveContext(Node *ctx)
|
||||
|
||||
void NodeParamView::AddNode(Node *n, Node *ctx, NodeParamViewContext *context)
|
||||
{
|
||||
if ((n->GetFlags() & Node::kDontShowInParamView) && !IsGroupMode()) {
|
||||
if ((n->GetFlags() & Node::kDontShowInParamView) && !IsGroupMode() && !show_all_nodes_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ private:
|
||||
QVector<Node*> contexts_;
|
||||
QVector<Node*> current_contexts_;
|
||||
|
||||
bool show_all_nodes_;
|
||||
|
||||
private slots:
|
||||
void UpdateGlobalScrollBar();
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "core.h"
|
||||
#include "node/node.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
@@ -86,11 +87,6 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
case NodeValue::kTexture:
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kColorTransformJob:
|
||||
case NodeValue::kVideoParams:
|
||||
case NodeValue::kAudioParams:
|
||||
case NodeValue::kDataTypeCount:
|
||||
@@ -241,11 +237,6 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
case NodeValue::kTexture:
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kColorTransformJob:
|
||||
case NodeValue::kVideoParams:
|
||||
case NodeValue::kAudioParams:
|
||||
case NodeValue::kDataTypeCount:
|
||||
@@ -396,6 +387,10 @@ void NodeParamViewWidgetBridge::CreateSliders(int count)
|
||||
T* fs = new T();
|
||||
fs->SliderBase::SetDefaultValue(GetInnerInput().GetSplitDefaultValueForTrack(i));
|
||||
fs->SetLadderElementCount(2);
|
||||
|
||||
// HACK: Force some spacing between sliders
|
||||
fs->setContentsMargins(0, 0, QtUtils::QFontMetricsWidth(fs->fontMetrics(), QStringLiteral(" ")), 0);
|
||||
|
||||
widgets_.append(fs);
|
||||
connect(fs, &T::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
}
|
||||
@@ -419,11 +414,6 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
case NodeValue::kTexture:
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kColorTransformJob:
|
||||
case NodeValue::kVideoParams:
|
||||
case NodeValue::kAudioParams:
|
||||
case NodeValue::kDataTypeCount:
|
||||
|
||||
@@ -139,10 +139,6 @@ void NodeTableView::SetTime(const rational &time)
|
||||
switch (value.type()) {
|
||||
case NodeValue::kVideoParams:
|
||||
case NodeValue::kAudioParams:
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
// These types have no string representation
|
||||
break;
|
||||
case NodeValue::kTexture:
|
||||
|
||||
@@ -97,6 +97,8 @@ ProjectExplorer::ProjectExplorer(QWidget *parent) :
|
||||
connect(tree_view_, &ProjectExplorerTreeView::customContextMenuRequested, this, &ProjectExplorer::ShowContextMenu);
|
||||
connect(list_view_, &ProjectExplorerListView::customContextMenuRequested, this, &ProjectExplorer::ShowContextMenu);
|
||||
connect(icon_view_, &ProjectExplorerIconView::customContextMenuRequested, this, &ProjectExplorer::ShowContextMenu);
|
||||
|
||||
UpdateNavBarText();
|
||||
}
|
||||
|
||||
const ProjectToolbar::ViewType &ProjectExplorer::view_type() const
|
||||
@@ -151,13 +153,7 @@ void ProjectExplorer::BrowseToFolder(const QModelIndex &index)
|
||||
list_view_->setRootIndex(index);
|
||||
|
||||
// Set navbar text to folder's name
|
||||
if (index.isValid()) {
|
||||
Folder* f = static_cast<Folder*>(sort_model_.mapToSource(index).internalPointer());
|
||||
nav_bar_->set_text(f->GetLabel());
|
||||
} else {
|
||||
// Or set it to an empty string if the index is valid (which means we're browsing to the root directory)
|
||||
nav_bar_->set_text(QString());
|
||||
}
|
||||
UpdateNavBarText();
|
||||
|
||||
// Set directory up enabled button based on whether we're in root or not
|
||||
nav_bar_->set_dir_up_enabled(index.isValid());
|
||||
@@ -246,6 +242,21 @@ QString ProjectExplorer::GetHumanReadableNodeName(Node *node)
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectExplorer::UpdateNavBarText()
|
||||
{
|
||||
QString absolute;
|
||||
|
||||
Folder* f = static_cast<Folder*>(sort_model_.mapToSource(list_view_->rootIndex()).internalPointer());
|
||||
while (f && f != project()->root()) {
|
||||
absolute.prepend(QStringLiteral("%1 / ").arg(f->GetLabel()));
|
||||
f = f->folder();
|
||||
}
|
||||
|
||||
absolute.prepend(QStringLiteral("/ "));
|
||||
|
||||
nav_bar_->set_text(absolute);
|
||||
}
|
||||
|
||||
QAbstractItemView *ProjectExplorer::CurrentView() const
|
||||
{
|
||||
return static_cast<QAbstractItemView*>(stacked_widget_->currentWidget());
|
||||
@@ -667,4 +678,32 @@ void ProjectExplorer::DeleteSelected()
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectExplorer::SelectItem(Node *n)
|
||||
{
|
||||
DeselectAll();
|
||||
|
||||
QModelIndex index = model_.CreateIndexFromItem(n);
|
||||
|
||||
if (index.isValid()) {
|
||||
index = sort_model_.mapFromSource(index);
|
||||
|
||||
QModelIndex parent = index.parent();
|
||||
if (view_type() == ProjectToolbar::TreeView) {
|
||||
// Expand all folders until this index is visible
|
||||
while (parent.isValid()) {
|
||||
tree_view_->expand(parent);
|
||||
parent = parent.parent();
|
||||
}
|
||||
} else {
|
||||
BrowseToFolder(parent);
|
||||
}
|
||||
|
||||
CurrentView()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ public:
|
||||
|
||||
void DeleteSelected();
|
||||
|
||||
bool SelectItem(Node *n);
|
||||
|
||||
public slots:
|
||||
void set_view_type(ProjectToolbar::ViewType type);
|
||||
|
||||
@@ -138,6 +140,8 @@ private:
|
||||
|
||||
static QString GetHumanReadableNodeName(Node* node);
|
||||
|
||||
void UpdateNavBarText();
|
||||
|
||||
/**
|
||||
* @brief Get the currently active QAbstractItemView
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ set(OLIVE_SOURCES
|
||||
widget/standardcombos/interlacedcombobox.h
|
||||
widget/standardcombos/pixelaspectratiocombobox.h
|
||||
widget/standardcombos/pixelformatcombobox.h
|
||||
widget/standardcombos/sampleformatcombobox.h
|
||||
widget/standardcombos/sampleratecombobox.h
|
||||
widget/standardcombos/standardcombos.h
|
||||
widget/standardcombos/videodividercombobox.h
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SAMPLEFORMATCOMBOBOX_H
|
||||
#define SAMPLEFORMATCOMBOBOX_H
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
#include "render/audioparams.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class SampleFormatComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SampleFormatComboBox(QWidget* parent = nullptr) :
|
||||
QComboBox(parent),
|
||||
attempt_to_restore_format_(true)
|
||||
{
|
||||
}
|
||||
|
||||
void SetAttemptToRestoreFormat(bool e) { attempt_to_restore_format_ = e; }
|
||||
|
||||
void SetAvailableFormats(const std::vector<AudioParams::Format> &formats)
|
||||
{
|
||||
AudioParams::Format tmp = AudioParams::kFormatInvalid;
|
||||
|
||||
if (attempt_to_restore_format_) {
|
||||
tmp = GetSampleFormat();
|
||||
}
|
||||
|
||||
clear();
|
||||
foreach (const AudioParams::Format &of, formats) {
|
||||
AddFormatItem(of);
|
||||
}
|
||||
|
||||
if (attempt_to_restore_format_) {
|
||||
SetSampleFormat(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
AudioParams::Format GetSampleFormat() const
|
||||
{
|
||||
return static_cast<AudioParams::Format>(this->currentData().toInt());
|
||||
}
|
||||
|
||||
void SetSampleFormat(AudioParams::Format fmt)
|
||||
{
|
||||
for (int i=0; i<this->count(); i++) {
|
||||
if (this->itemData(i).toInt() == fmt) {
|
||||
this->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void AddFormatItem(AudioParams::Format f)
|
||||
{
|
||||
this->addItem(AudioParams::FormatToString(f), f);
|
||||
}
|
||||
|
||||
bool attempt_to_restore_format_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SAMPLEFORMATCOMBOBOX_H
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "interlacedcombobox.h"
|
||||
#include "pixelaspectratiocombobox.h"
|
||||
#include "pixelformatcombobox.h"
|
||||
#include "sampleformatcombobox.h"
|
||||
#include "sampleratecombobox.h"
|
||||
#include "videodividercombobox.h"
|
||||
|
||||
|
||||
@@ -1071,6 +1071,14 @@ void TimelineWidget::ShowContextMenu()
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
if (ClipBlock *clip = dynamic_cast<ClipBlock*>(selected.first())) {
|
||||
if (clip->connected_viewer()) {
|
||||
QAction *reveal_in_project = menu.addAction(tr("Reveal in Project"));
|
||||
reveal_in_project->setData(reinterpret_cast<quintptr>(clip->connected_viewer()));
|
||||
connect(reveal_in_project, &QAction::triggered, this, &TimelineWidget::RevealInProject);
|
||||
}
|
||||
}
|
||||
|
||||
QAction* properties_action = menu.addAction(tr("Properties"));
|
||||
connect(properties_action, &QAction::triggered, this, &TimelineWidget::ShowSpeedDurationDialogForSelectedClips);
|
||||
}
|
||||
@@ -1215,6 +1223,15 @@ void TimelineWidget::SignalBlockSelectionChange()
|
||||
signal_block_change_timer_->start();
|
||||
}
|
||||
|
||||
void TimelineWidget::RevealInProject()
|
||||
{
|
||||
QAction *a = static_cast<QAction*>(sender());
|
||||
|
||||
ViewerOutput *item_to_reveal = reinterpret_cast<ViewerOutput*>(a->data().value<quintptr>());
|
||||
|
||||
emit RevealViewerInProject(item_to_reveal);
|
||||
}
|
||||
|
||||
void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost)
|
||||
{
|
||||
ghost_items_.append(ghost);
|
||||
|
||||
@@ -269,6 +269,8 @@ signals:
|
||||
|
||||
void RequestCaptureStart(const TimeRange &time, const Track::Reference &track);
|
||||
|
||||
void RevealViewerInProject(ViewerOutput *r);
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
@@ -410,6 +412,8 @@ private slots:
|
||||
|
||||
void SignalBlockSelectionChange();
|
||||
|
||||
void RevealInProject();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ void RecordTool::MousePress(TimelineViewMouseEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
if (t->type() != Track::kAudio) {
|
||||
if (t && t->type() != Track::kAudio) {
|
||||
// We only support audio tracks here
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ void SeekableWidget::SeekToScenePoint(qreal scene)
|
||||
return;
|
||||
}
|
||||
|
||||
rational playhead_time = SceneToTime(scene);
|
||||
rational playhead_time = qMax(rational(0), SceneToTime(scene));
|
||||
|
||||
if (Core::instance()->snapping() && GetSnapService()) {
|
||||
rational movement;
|
||||
|
||||
@@ -388,7 +388,6 @@ void ViewerWidget::StartCapture(TimelineWidget *source, const TimeRange &time, c
|
||||
SetTimeAndSignal(time.in());
|
||||
ArmForRecording();
|
||||
|
||||
recording_filename_ = QStringLiteral("/home/matt/Desktop/ass.mp3");
|
||||
recording_callback_ = source;
|
||||
recording_range_ = time;
|
||||
recording_track_ = track;
|
||||
@@ -1167,7 +1166,31 @@ void ViewerWidget::Play(bool in_to_out_only)
|
||||
in_to_out_only = false;
|
||||
}
|
||||
} else if (record_armed_) {
|
||||
if (AudioManager::instance()->StartRecording(recording_filename_, GetConnectedNode()->GetAudioParams())) {
|
||||
DisarmRecording();
|
||||
|
||||
if (GetConnectedNode()->project()->filename().isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Audio Recording"), tr("Project must be saved before you can record audio."));
|
||||
return;
|
||||
}
|
||||
|
||||
QDir audio_path(QFileInfo(GetConnectedNode()->project()->filename()).dir().filePath(tr("audio")));
|
||||
if (!audio_path.exists()) {
|
||||
audio_path.mkpath(QStringLiteral("."));
|
||||
}
|
||||
|
||||
recording_filename_ = audio_path.filePath(QStringLiteral("%1.%2").arg(
|
||||
QDateTime::currentDateTime().toString("yyyy-MM-dd hh-mm-ss"),
|
||||
ExportFormat::GetExtension(static_cast<ExportFormat::Format>(Config::Current()[QStringLiteral("AudioRecordingFormat")].toInt())))
|
||||
);
|
||||
|
||||
AudioParams ap(OLIVE_CONFIG("AudioRecordingSampleRate").toInt(), OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong(), static_cast<AudioParams::Format>(OLIVE_CONFIG("AudioRecordingSampleFormat").toInt()));
|
||||
|
||||
EncodingParams encode_param;
|
||||
encode_param.EnableAudio(ap, static_cast<ExportCodec::Codec>(OLIVE_CONFIG("AudioRecordingCodec").toInt()));
|
||||
encode_param.SetFilename(recording_filename_);
|
||||
encode_param.set_audio_bit_rate(OLIVE_CONFIG("AudioRecordingBitRate").toInt() * 1000);
|
||||
|
||||
if (AudioManager::instance()->StartRecording(encode_param)) {
|
||||
recording_ = true;
|
||||
controls_->SetPauseButtonRecordingState(true);
|
||||
recording_callback_->EnableRecordingOverlay(TimelineCoordinate(recording_range_.in(), recording_track_));
|
||||
@@ -1175,8 +1198,6 @@ void ViewerWidget::Play(bool in_to_out_only)
|
||||
QMessageBox::critical(this, tr("Audio Recording"), tr("Failed to start audio recording"));
|
||||
return;
|
||||
}
|
||||
|
||||
DisarmRecording();
|
||||
}
|
||||
|
||||
PlayInternal(1, in_to_out_only);
|
||||
|
||||
@@ -479,6 +479,15 @@ void MainWindow::ShowWelcomeDialog()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::RevealViewerInProject(ViewerOutput *r)
|
||||
{
|
||||
foreach (ProjectPanel *p, project_panels_) {
|
||||
if (p->project() == r->project() && p->SelectItem(r)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
void MainWindow::ShowNouveauWarning()
|
||||
{
|
||||
@@ -557,6 +566,7 @@ TimelinePanel* MainWindow::AppendTimelinePanel()
|
||||
connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime);
|
||||
connect(panel, &TimelinePanel::RequestCaptureStart, sequence_viewer_panel_, &SequenceViewerPanel::StartCapture);
|
||||
connect(panel, &TimelinePanel::BlockSelectionChanged, this, &MainWindow::TimelinePanelSelectionChanged);
|
||||
connect(panel, &TimelinePanel::RevealViewerInProject, this, &MainWindow::RevealViewerInProject);
|
||||
connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTime);
|
||||
connect(curve_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTime);
|
||||
connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, panel, &TimelinePanel::SetTime);
|
||||
|
||||
@@ -195,6 +195,8 @@ private slots:
|
||||
|
||||
void ShowWelcomeDialog();
|
||||
|
||||
void RevealViewerInProject(ViewerOutput *r);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user