export: fixed many export formats including still image and image sequence export
Still incomplete, but does a lot towards #1345
This commit is contained in:
+92
-3
@@ -22,10 +22,15 @@
|
|||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "common/timecodefunctions.h"
|
||||||
#include "ffmpeg/ffmpegencoder.h"
|
#include "ffmpeg/ffmpegencoder.h"
|
||||||
|
#include "oiio/oiioencoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
const QRegularExpression Encoder::kImageSequenceContainsDigits = QRegularExpression(QStringLiteral("\\[[#]+\\]"));
|
||||||
|
const QRegularExpression Encoder::kImageSequenceRemoveDigits = QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
|
||||||
|
|
||||||
Encoder::Encoder(const EncodingParams ¶ms) :
|
Encoder::Encoder(const EncodingParams ¶ms) :
|
||||||
params_(params)
|
params_(params)
|
||||||
{
|
{
|
||||||
@@ -36,6 +41,47 @@ const EncodingParams &Encoder::params() const
|
|||||||
return params_;
|
return params_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Encoder::GetFilenameForFrame(const rational &frame)
|
||||||
|
{
|
||||||
|
if (params().video_is_image_sequence()) {
|
||||||
|
// Transform!
|
||||||
|
int64_t frame_index = Timecode::time_to_timestamp(frame, params().video_params().frame_rate_as_time_base());
|
||||||
|
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
|
||||||
|
QString frame_index_str = QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
|
||||||
|
|
||||||
|
QString f = params_.filename();
|
||||||
|
f.replace(kImageSequenceContainsDigits, frame_index_str);
|
||||||
|
return f;
|
||||||
|
} else {
|
||||||
|
// Keep filename
|
||||||
|
return params_.filename();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
|
||||||
|
{
|
||||||
|
int start = filename.indexOf(kImageSequenceContainsDigits);
|
||||||
|
int digit_count = 0;
|
||||||
|
for (int i=start+1; i<filename.size(); i++) {
|
||||||
|
if (filename.at(i) == '#') {
|
||||||
|
digit_count++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return digit_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Encoder::FilenameContainsDigitPlaceholder(const QString& filename)
|
||||||
|
{
|
||||||
|
return filename.contains(kImageSequenceContainsDigits);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Encoder::FilenameRemoveDigitPlaceholder(QString filename)
|
||||||
|
{
|
||||||
|
return filename.remove(kImageSequenceRemoveDigits);
|
||||||
|
}
|
||||||
|
|
||||||
void Encoder::WriteAudio(AudioParams pcm_info, const QString &pcm_filename)
|
void Encoder::WriteAudio(AudioParams pcm_info, const QString &pcm_filename)
|
||||||
{
|
{
|
||||||
QFile f(pcm_filename);
|
QFile f(pcm_filename);
|
||||||
@@ -49,6 +95,7 @@ EncodingParams::EncodingParams() :
|
|||||||
video_max_bit_rate_(0),
|
video_max_bit_rate_(0),
|
||||||
video_buffer_size_(0),
|
video_buffer_size_(0),
|
||||||
video_threads_(0),
|
video_threads_(0),
|
||||||
|
video_is_image_sequence_(false),
|
||||||
audio_enabled_(false),
|
audio_enabled_(false),
|
||||||
audio_bit_rate_(0)
|
audio_bit_rate_(0)
|
||||||
{
|
{
|
||||||
@@ -242,11 +289,53 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
|
|||||||
writer->writeEndElement(); // audio
|
writer->writeEndElement(); // audio
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder* Encoder::CreateFromID(const QString &id, const EncodingParams& params)
|
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params)
|
||||||
{
|
{
|
||||||
Q_UNUSED(id)
|
switch (id) {
|
||||||
|
case kEncoderTypeNone:
|
||||||
|
break;
|
||||||
|
case kEncoderTypeFFmpeg:
|
||||||
|
return new FFmpegEncoder(params);
|
||||||
|
case kEncoderTypeOIIO:
|
||||||
|
return new OIIOEncoder(params);
|
||||||
|
}
|
||||||
|
|
||||||
return new FFmpegEncoder(params);
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
|
||||||
|
{
|
||||||
|
switch (f) {
|
||||||
|
case ExportFormat::kFormatDNxHD:
|
||||||
|
case ExportFormat::kFormatMatroska:
|
||||||
|
case ExportFormat::kFormatQuickTime:
|
||||||
|
case ExportFormat::kFormatMPEG4:
|
||||||
|
case ExportFormat::kFormatWAV:
|
||||||
|
case ExportFormat::kFormatAIFF:
|
||||||
|
case ExportFormat::kFormatMP3:
|
||||||
|
case ExportFormat::kFormatFLAC:
|
||||||
|
case ExportFormat::kFormatOgg:
|
||||||
|
case ExportFormat::kFormatWebM:
|
||||||
|
return kEncoderTypeFFmpeg;
|
||||||
|
case ExportFormat::kFormatOpenEXR:
|
||||||
|
case ExportFormat::kFormatPNG:
|
||||||
|
case ExportFormat::kFormatTIFF:
|
||||||
|
return kEncoderTypeOIIO;
|
||||||
|
case ExportFormat::kFormatCount:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kEncoderTypeNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms)
|
||||||
|
{
|
||||||
|
return CreateFromID(GetTypeFromFormat(f), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||||
|
{
|
||||||
|
return QStringList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-1
@@ -22,6 +22,7 @@
|
|||||||
#define ENCODER_H
|
#define ENCODER_H
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
@@ -53,6 +54,10 @@ public:
|
|||||||
void set_video_buffer_size(const int64_t& sz);
|
void set_video_buffer_size(const int64_t& sz);
|
||||||
void set_video_threads(const int& threads);
|
void set_video_threads(const int& threads);
|
||||||
void set_video_pix_fmt(const QString& s);
|
void set_video_pix_fmt(const QString& s);
|
||||||
|
void set_video_is_image_sequence(bool s)
|
||||||
|
{
|
||||||
|
video_is_image_sequence_ = s;
|
||||||
|
}
|
||||||
|
|
||||||
const QString& filename() const;
|
const QString& filename() const;
|
||||||
|
|
||||||
@@ -66,6 +71,10 @@ public:
|
|||||||
const int64_t& video_buffer_size() const;
|
const int64_t& video_buffer_size() const;
|
||||||
const int& video_threads() const;
|
const int& video_threads() const;
|
||||||
const QString& video_pix_fmt() const;
|
const QString& video_pix_fmt() const;
|
||||||
|
bool video_is_image_sequence() const
|
||||||
|
{
|
||||||
|
return video_is_image_sequence_;
|
||||||
|
}
|
||||||
|
|
||||||
bool audio_enabled() const;
|
bool audio_enabled() const;
|
||||||
const ExportCodec::Codec &audio_codec() const;
|
const ExportCodec::Codec &audio_codec() const;
|
||||||
@@ -99,6 +108,7 @@ private:
|
|||||||
int64_t video_buffer_size_;
|
int64_t video_buffer_size_;
|
||||||
int video_threads_;
|
int video_threads_;
|
||||||
QString video_pix_fmt_;
|
QString video_pix_fmt_;
|
||||||
|
bool video_is_image_sequence_;
|
||||||
|
|
||||||
bool audio_enabled_;
|
bool audio_enabled_;
|
||||||
ExportCodec::Codec audio_codec_;
|
ExportCodec::Codec audio_codec_;
|
||||||
@@ -115,6 +125,12 @@ class Encoder : public QObject
|
|||||||
public:
|
public:
|
||||||
Encoder(const EncodingParams& params);
|
Encoder(const EncodingParams& params);
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
kEncoderTypeNone = -1,
|
||||||
|
kEncoderTypeFFmpeg,
|
||||||
|
kEncoderTypeOIIO
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a Encoder instance using a Encoder ID
|
* @brief Create a Encoder instance using a Encoder ID
|
||||||
*
|
*
|
||||||
@@ -122,7 +138,13 @@ public:
|
|||||||
*
|
*
|
||||||
* A Encoder instance or nullptr if a Decoder with this ID does not exist
|
* A Encoder instance or nullptr if a Decoder with this ID does not exist
|
||||||
*/
|
*/
|
||||||
static Encoder *CreateFromID(const QString& id, const EncodingParams ¶ms);
|
static Encoder *CreateFromID(Type id, const EncodingParams ¶ms);
|
||||||
|
|
||||||
|
static Type GetTypeFromFormat(ExportFormat::Format f);
|
||||||
|
|
||||||
|
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms);
|
||||||
|
|
||||||
|
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
|
||||||
|
|
||||||
const EncodingParams& params() const;
|
const EncodingParams& params() const;
|
||||||
|
|
||||||
@@ -136,6 +158,16 @@ public:
|
|||||||
return error_;
|
return error_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString GetFilenameForFrame(const rational& frame);
|
||||||
|
|
||||||
|
static int GetImageSequencePlaceholderDigitCount(const QString& filename);
|
||||||
|
|
||||||
|
static bool FilenameContainsDigitPlaceholder(const QString &filename);
|
||||||
|
static QString FilenameRemoveDigitPlaceholder(QString filename);
|
||||||
|
|
||||||
|
static const QRegularExpression kImageSequenceContainsDigits;
|
||||||
|
static const QRegularExpression kImageSequenceRemoveDigits;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual bool Open() = 0;
|
virtual bool Open() = 0;
|
||||||
|
|
||||||
|
|||||||
+12
-43
@@ -52,6 +52,14 @@ QString ExportCodec::GetCodecName(ExportCodec::Codec c)
|
|||||||
return tr("AAC");
|
return tr("AAC");
|
||||||
case kCodecPCM:
|
case kCodecPCM:
|
||||||
return tr("PCM (Uncompressed)");
|
return tr("PCM (Uncompressed)");
|
||||||
|
case kCodecFLAC:
|
||||||
|
return tr("FLAC");
|
||||||
|
case kCodecOpus:
|
||||||
|
return tr("Opus");
|
||||||
|
case kCodecVorbis:
|
||||||
|
return tr("Vorbis");
|
||||||
|
case kCodecVP9:
|
||||||
|
return tr("VP9");
|
||||||
case kCodecCount:
|
case kCodecCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -70,6 +78,10 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
|
|||||||
case kCodecMP3:
|
case kCodecMP3:
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
case kCodecPCM:
|
case kCodecPCM:
|
||||||
|
case kCodecVorbis:
|
||||||
|
case kCodecOpus:
|
||||||
|
case kCodecFLAC:
|
||||||
|
case kCodecVP9:
|
||||||
return false;
|
return false;
|
||||||
case kCodecOpenEXR:
|
case kCodecOpenEXR:
|
||||||
case kCodecPNG:
|
case kCodecPNG:
|
||||||
@@ -82,47 +94,4 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList ExportCodec::GetPixelFormatsForCodec(ExportCodec::Codec c)
|
|
||||||
{
|
|
||||||
QStringList pix_fmts;
|
|
||||||
|
|
||||||
AVCodec* codec_info = nullptr;
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
case kCodecH264:
|
|
||||||
codec_info = avcodec_find_encoder(AV_CODEC_ID_H264);
|
|
||||||
break;
|
|
||||||
case kCodecDNxHD:
|
|
||||||
codec_info = avcodec_find_encoder(AV_CODEC_ID_DNXHD);
|
|
||||||
break;
|
|
||||||
case kCodecProRes:
|
|
||||||
codec_info = avcodec_find_encoder(AV_CODEC_ID_PRORES);
|
|
||||||
break;
|
|
||||||
case kCodecH265:
|
|
||||||
codec_info = avcodec_find_encoder(AV_CODEC_ID_HEVC);
|
|
||||||
break;
|
|
||||||
case kCodecOpenEXR:
|
|
||||||
case kCodecPNG:
|
|
||||||
case kCodecTIFF:
|
|
||||||
// FIXME: Add these in (these will most likely use an OIIOEncoder which doesn't exist yet)
|
|
||||||
break;
|
|
||||||
case kCodecMP2:
|
|
||||||
case kCodecMP3:
|
|
||||||
case kCodecAAC:
|
|
||||||
case kCodecPCM:
|
|
||||||
case kCodecCount:
|
|
||||||
// These are audio or invalid codecs and therefore have no pixel formats
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codec_info) {
|
|
||||||
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
|
|
||||||
const char* pix_fmt_name = av_get_pix_fmt_name(codec_info->pix_fmts[i]);
|
|
||||||
pix_fmts.append(pix_fmt_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pix_fmts;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class ExportCodec : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum Codec {
|
enum Codec {
|
||||||
|
// Video codecs
|
||||||
kCodecDNxHD,
|
kCodecDNxHD,
|
||||||
kCodecH264,
|
kCodecH264,
|
||||||
kCodecH265,
|
kCodecH265,
|
||||||
@@ -40,11 +41,16 @@ public:
|
|||||||
kCodecPNG,
|
kCodecPNG,
|
||||||
kCodecProRes,
|
kCodecProRes,
|
||||||
kCodecTIFF,
|
kCodecTIFF,
|
||||||
|
kCodecVP9,
|
||||||
|
|
||||||
|
// Audio codecs
|
||||||
kCodecMP2,
|
kCodecMP2,
|
||||||
kCodecMP3,
|
kCodecMP3,
|
||||||
kCodecAAC,
|
kCodecAAC,
|
||||||
kCodecPCM,
|
kCodecPCM,
|
||||||
|
kCodecOpus,
|
||||||
|
kCodecVorbis,
|
||||||
|
kCodecFLAC,
|
||||||
|
|
||||||
kCodecCount
|
kCodecCount
|
||||||
};
|
};
|
||||||
@@ -53,8 +59,6 @@ public:
|
|||||||
|
|
||||||
static bool IsCodecAStillImage(Codec c);
|
static bool IsCodecAStillImage(Codec c);
|
||||||
|
|
||||||
static QStringList GetPixelFormatsForCodec(Codec c);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-20
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "exportformat.h"
|
#include "exportformat.h"
|
||||||
|
|
||||||
|
#include "encoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
||||||
@@ -39,6 +41,19 @@ QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
|||||||
return tr("TIFF");
|
return tr("TIFF");
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return tr("QuickTime");
|
return tr("QuickTime");
|
||||||
|
case kFormatWAV:
|
||||||
|
return tr("Wave Audio");
|
||||||
|
case kFormatAIFF:
|
||||||
|
return tr("AIFF");
|
||||||
|
case kFormatMP3:
|
||||||
|
return tr("MP3");
|
||||||
|
case kFormatFLAC:
|
||||||
|
return tr("FLAC");
|
||||||
|
case kFormatOgg:
|
||||||
|
return tr("Ogg");
|
||||||
|
case kFormatWebM:
|
||||||
|
return tr("WebM");
|
||||||
|
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -63,25 +78,18 @@ QString ExportFormat::GetExtension(ExportFormat::Format f)
|
|||||||
return QStringLiteral("tiff");
|
return QStringLiteral("tiff");
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return QStringLiteral("mov");
|
return QStringLiteral("mov");
|
||||||
case kFormatCount:
|
case kFormatWAV:
|
||||||
break;
|
return QStringLiteral("wav");
|
||||||
}
|
case kFormatAIFF:
|
||||||
|
return QStringLiteral("aiff");
|
||||||
return QString();
|
case kFormatMP3:
|
||||||
}
|
return QStringLiteral("mp3");
|
||||||
|
case kFormatFLAC:
|
||||||
QString ExportFormat::GetEncoder(ExportFormat::Format f)
|
return QStringLiteral("flac");
|
||||||
{
|
case kFormatOgg:
|
||||||
switch (f) {
|
return QStringLiteral("ogg");
|
||||||
case kFormatDNxHD:
|
case kFormatWebM:
|
||||||
case kFormatMatroska:
|
return QStringLiteral("webm");
|
||||||
case kFormatQuickTime:
|
|
||||||
case kFormatMPEG4:
|
|
||||||
return QStringLiteral("ffmpeg");
|
|
||||||
case kFormatOpenEXR:
|
|
||||||
case kFormatPNG:
|
|
||||||
case kFormatTIFF:
|
|
||||||
return QStringLiteral("oiio");
|
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -106,6 +114,14 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
|
|||||||
return {ExportCodec::kCodecTIFF};
|
return {ExportCodec::kCodecTIFF};
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH265, ExportCodec::kCodecProRes};
|
return {ExportCodec::kCodecH264, ExportCodec::kCodecH265, ExportCodec::kCodecProRes};
|
||||||
|
case kFormatWebM:
|
||||||
|
return {ExportCodec::kCodecVP9};
|
||||||
|
case kFormatOgg:
|
||||||
|
case kFormatWAV:
|
||||||
|
case kFormatAIFF:
|
||||||
|
case kFormatMP3:
|
||||||
|
case kFormatFLAC:
|
||||||
|
return {};
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -119,15 +135,31 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
|||||||
case kFormatDNxHD:
|
case kFormatDNxHD:
|
||||||
return {ExportCodec::kCodecPCM};
|
return {ExportCodec::kCodecPCM};
|
||||||
case kFormatMatroska:
|
case kFormatMatroska:
|
||||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus};
|
||||||
case kFormatMPEG4:
|
case kFormatMPEG4:
|
||||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
||||||
|
case kFormatWebM:
|
||||||
|
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus};
|
||||||
case kFormatOpenEXR:
|
case kFormatOpenEXR:
|
||||||
case kFormatPNG:
|
case kFormatPNG:
|
||||||
case kFormatTIFF:
|
case kFormatTIFF:
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
||||||
|
case kFormatWAV:
|
||||||
|
return {ExportCodec::kCodecPCM};
|
||||||
|
case kFormatAIFF:
|
||||||
|
return {ExportCodec::kCodecPCM};
|
||||||
|
case kFormatMP3:
|
||||||
|
return {ExportCodec::kCodecMP3};
|
||||||
|
case kFormatFLAC:
|
||||||
|
return {ExportCodec::kCodecFLAC};
|
||||||
|
case kFormatOgg:
|
||||||
|
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM};
|
||||||
|
|
||||||
|
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -135,4 +167,17 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, ExportCodec::Codec c)
|
||||||
|
{
|
||||||
|
Encoder* e = Encoder::CreateFromFormat(f, EncodingParams());
|
||||||
|
QStringList list;
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
list = e->GetPixelFormatsForCodec(c);
|
||||||
|
delete e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,16 +41,23 @@ public:
|
|||||||
kFormatQuickTime,
|
kFormatQuickTime,
|
||||||
kFormatPNG,
|
kFormatPNG,
|
||||||
kFormatTIFF,
|
kFormatTIFF,
|
||||||
|
kFormatWAV,
|
||||||
|
kFormatAIFF,
|
||||||
|
kFormatMP3,
|
||||||
|
kFormatFLAC,
|
||||||
|
kFormatOgg,
|
||||||
|
kFormatWebM,
|
||||||
|
|
||||||
kFormatCount
|
kFormatCount
|
||||||
};
|
};
|
||||||
|
|
||||||
static QString GetName(Format f);
|
static QString GetName(Format f);
|
||||||
static QString GetExtension(Format f);
|
static QString GetExtension(Format f);
|
||||||
static QString GetEncoder(Format f);
|
|
||||||
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
|
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
|
||||||
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
|
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
|
||||||
|
|
||||||
|
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,59 @@ FFmpegEncoder::FFmpegEncoder(const EncodingParams ¶ms) :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||||
|
{
|
||||||
|
QStringList pix_fmts;
|
||||||
|
|
||||||
|
AVCodec* codec_info = nullptr;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case ExportCodec::kCodecH264:
|
||||||
|
codec_info = avcodec_find_encoder(AV_CODEC_ID_H264);
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecDNxHD:
|
||||||
|
codec_info = avcodec_find_encoder(AV_CODEC_ID_DNXHD);
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecProRes:
|
||||||
|
codec_info = avcodec_find_encoder(AV_CODEC_ID_PRORES);
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecH265:
|
||||||
|
codec_info = avcodec_find_encoder(AV_CODEC_ID_HEVC);
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecVP9:
|
||||||
|
codec_info = avcodec_find_encoder(AV_CODEC_ID_VP9);
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecOpenEXR:
|
||||||
|
codec_info = avcodec_find_encoder(AV_CODEC_ID_EXR);
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecPNG:
|
||||||
|
codec_info = avcodec_find_encoder(AV_CODEC_ID_PNG);
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecTIFF:
|
||||||
|
codec_info = avcodec_find_encoder(AV_CODEC_ID_TIFF);
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecMP2:
|
||||||
|
case ExportCodec::kCodecMP3:
|
||||||
|
case ExportCodec::kCodecAAC:
|
||||||
|
case ExportCodec::kCodecPCM:
|
||||||
|
case ExportCodec::kCodecFLAC:
|
||||||
|
case ExportCodec::kCodecOpus:
|
||||||
|
case ExportCodec::kCodecVorbis:
|
||||||
|
case ExportCodec::kCodecCount:
|
||||||
|
// These are audio or invalid codecs and therefore have no pixel formats
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codec_info) {
|
||||||
|
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
|
||||||
|
const char* pix_fmt_name = av_get_pix_fmt_name(codec_info->pix_fmts[i]);
|
||||||
|
pix_fmts.append(pix_fmt_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pix_fmts;
|
||||||
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::Open()
|
bool FFmpegEncoder::Open()
|
||||||
{
|
{
|
||||||
if (open_) {
|
if (open_) {
|
||||||
@@ -447,6 +500,18 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
|||||||
case ExportCodec::kCodecPCM:
|
case ExportCodec::kCodecPCM:
|
||||||
codec_id = AV_CODEC_ID_PCM_S16LE;
|
codec_id = AV_CODEC_ID_PCM_S16LE;
|
||||||
break;
|
break;
|
||||||
|
case ExportCodec::kCodecVP9:
|
||||||
|
codec_id = AV_CODEC_ID_VP9;
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecOpus:
|
||||||
|
codec_id = AV_CODEC_ID_OPUS;
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecVorbis:
|
||||||
|
codec_id = AV_CODEC_ID_VORBIS;
|
||||||
|
break;
|
||||||
|
case ExportCodec::kCodecFLAC:
|
||||||
|
codec_id = AV_CODEC_ID_FLAC;
|
||||||
|
break;
|
||||||
case ExportCodec::kCodecCount:
|
case ExportCodec::kCodecCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class FFmpegEncoder : public Encoder
|
|||||||
public:
|
public:
|
||||||
FFmpegEncoder(const EncodingParams ¶ms);
|
FFmpegEncoder(const EncodingParams ¶ms);
|
||||||
|
|
||||||
|
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
|
||||||
|
|
||||||
virtual bool Open() override;
|
virtual bool Open() override;
|
||||||
|
|
||||||
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
|
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
|
||||||
|
|||||||
@@ -18,5 +18,7 @@ set(OLIVE_SOURCES
|
|||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
codec/oiio/oiiodecoder.cpp
|
codec/oiio/oiiodecoder.cpp
|
||||||
codec/oiio/oiiodecoder.h
|
codec/oiio/oiiodecoder.h
|
||||||
|
codec/oiio/oiioencoder.cpp
|
||||||
|
codec/oiio/oiioencoder.h
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "oiioencoder.h"
|
||||||
|
|
||||||
|
#include "common/oiioutils.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
OIIOEncoder::OIIOEncoder(const EncodingParams ¶ms) :
|
||||||
|
Encoder(params)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OIIOEncoder::Open()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
|
||||||
|
{
|
||||||
|
std::string filename = GetFilenameForFrame(time).toStdString();
|
||||||
|
|
||||||
|
auto output = OIIO::ImageOutput::create(filename);
|
||||||
|
if (!output) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
|
||||||
|
OIIO::ImageSpec spec(frame->width(), frame->height(), frame->channel_count(), type);
|
||||||
|
|
||||||
|
if (!output->open(filename, spec)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!output->write_image(type, frame->data(), OIIO::AutoStride, frame->linesize_bytes())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!output->close()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OIIOEncoder::WriteAudio(AudioParams pcm_info, QIODevice *file)
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void OIIOEncoder::Close()
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 OIIOENCODER_H
|
||||||
|
#define OIIOENCODER_H
|
||||||
|
|
||||||
|
#include "codec/encoder.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
class OIIOEncoder : public Encoder
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
OIIOEncoder(const EncodingParams ¶ms);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
virtual bool Open() override;
|
||||||
|
|
||||||
|
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
|
||||||
|
virtual void WriteAudio(olive::AudioParams pcm_info,
|
||||||
|
QIODevice *file) override;
|
||||||
|
|
||||||
|
virtual void Close() override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OIIOENCODER_H
|
||||||
@@ -38,4 +38,24 @@ QFrame *QtUtils::CreateHorizontalLine()
|
|||||||
return horizontal_line;
|
return horizontal_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int QtUtils::MessageBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &message, QMessageBox::StandardButtons buttons)
|
||||||
|
{
|
||||||
|
QMessageBox b(parent);
|
||||||
|
b.setIcon(icon);
|
||||||
|
b.setWindowModality(Qt::WindowModal);
|
||||||
|
b.setWindowTitle(title);
|
||||||
|
b.setText(message);
|
||||||
|
|
||||||
|
uint mask = QMessageBox::FirstButton;
|
||||||
|
while (mask <= QMessageBox::LastButton) {
|
||||||
|
uint sb = buttons & mask;
|
||||||
|
if (sb) {
|
||||||
|
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
|
||||||
|
}
|
||||||
|
mask <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.exec();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
@@ -47,6 +48,8 @@ public:
|
|||||||
|
|
||||||
static QFrame* CreateHorizontalLine();
|
static QFrame* CreateHorizontalLine();
|
||||||
|
|
||||||
|
static int MessageBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,14 @@ opentime::RationalTime rational::toRationalTime(double framerate) const
|
|||||||
|
|
||||||
rational rational::flipped() const
|
rational rational::flipped() const
|
||||||
{
|
{
|
||||||
return rational(denom_, numer_);
|
rational r = *this;
|
||||||
|
r.flip();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rational::flip()
|
||||||
|
{
|
||||||
|
std::swap(denom_, numer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rational::isNull() const
|
bool rational::isNull() const
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public:
|
|||||||
|
|
||||||
// Produce "flipped" version
|
// Produce "flipped" version
|
||||||
rational flipped() const;
|
rational flipped() const;
|
||||||
|
void flip();
|
||||||
|
|
||||||
// Returns whether the rational is valid but equal to zero or not
|
// Returns whether the rational is valid but equal to zero or not
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -31,6 +31,11 @@
|
|||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
H264Section::H264Section(QWidget *parent) :
|
H264Section::H264Section(QWidget *parent) :
|
||||||
|
H264Section(H264CRFSection::kDefaultH264CRF, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
H264Section::H264Section(int default_crf, QWidget *parent) :
|
||||||
CodecSection(parent)
|
CodecSection(parent)
|
||||||
{
|
{
|
||||||
QGridLayout* layout = new QGridLayout(this);
|
QGridLayout* layout = new QGridLayout(this);
|
||||||
@@ -54,7 +59,7 @@ H264Section::H264Section(QWidget *parent) :
|
|||||||
compression_method_stack_ = new QStackedWidget();
|
compression_method_stack_ = new QStackedWidget();
|
||||||
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
|
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
|
||||||
|
|
||||||
crf_section_ = new H264CRFSection();
|
crf_section_ = new H264CRFSection(default_crf);
|
||||||
compression_method_stack_->addWidget(crf_section_);
|
compression_method_stack_->addWidget(crf_section_);
|
||||||
|
|
||||||
bitrate_section_ = new H264BitRateSection();
|
bitrate_section_ = new H264BitRateSection();
|
||||||
@@ -107,7 +112,7 @@ void H264Section::AddOpts(EncodingParams *params)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
H264CRFSection::H264CRFSection(QWidget *parent) :
|
H264CRFSection::H264CRFSection(int default_crf, QWidget *parent) :
|
||||||
QWidget(parent)
|
QWidget(parent)
|
||||||
{
|
{
|
||||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||||
@@ -116,15 +121,15 @@ H264CRFSection::H264CRFSection(QWidget *parent) :
|
|||||||
crf_slider_ = new QSlider(Qt::Horizontal);
|
crf_slider_ = new QSlider(Qt::Horizontal);
|
||||||
crf_slider_->setMinimum(kMinimumCRF);
|
crf_slider_->setMinimum(kMinimumCRF);
|
||||||
crf_slider_->setMaximum(kMaximumCRF);
|
crf_slider_->setMaximum(kMaximumCRF);
|
||||||
crf_slider_->setValue(kDefaultCRF);
|
crf_slider_->setValue(default_crf);
|
||||||
layout->addWidget(crf_slider_);
|
layout->addWidget(crf_slider_);
|
||||||
|
|
||||||
IntegerSlider* crf_input = new IntegerSlider();
|
IntegerSlider* crf_input = new IntegerSlider();
|
||||||
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH")));
|
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH")));
|
||||||
crf_input->SetMinimum(kMinimumCRF);
|
crf_input->SetMinimum(kMinimumCRF);
|
||||||
crf_input->SetMaximum(kMaximumCRF);
|
crf_input->SetMaximum(kMaximumCRF);
|
||||||
crf_input->SetValue(kDefaultCRF);
|
crf_input->SetValue(default_crf);
|
||||||
crf_input->SetDefaultValue(kDefaultCRF);
|
crf_input->SetDefaultValue(default_crf);
|
||||||
layout->addWidget(crf_input);
|
layout->addWidget(crf_input);
|
||||||
|
|
||||||
connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue);
|
connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue);
|
||||||
@@ -211,4 +216,9 @@ int64_t H264FileSizeSection::GetFileSize() const
|
|||||||
return qRound64(file_size_->GetValue() * 1024.0 * 1024.0 * 8.0);
|
return qRound64(file_size_->GetValue() * 1024.0 * 1024.0 * 8.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
H265Section::H265Section(QWidget *parent) :
|
||||||
|
H264Section(H264CRFSection::kDefaultH265CRF, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,15 +33,15 @@ class H264CRFSection : public QWidget
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
H264CRFSection(QWidget* parent = nullptr);
|
H264CRFSection(int default_crf, QWidget* parent = nullptr);
|
||||||
|
|
||||||
int GetValue() const;
|
int GetValue() const;
|
||||||
|
|
||||||
|
static const int kDefaultH264CRF = 23;
|
||||||
|
static const int kDefaultH265CRF = 28;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kMinimumCRF = 0;
|
static const int kMinimumCRF = 0;
|
||||||
|
|
||||||
static const int kDefaultCRF = 23;
|
|
||||||
|
|
||||||
static const int kMaximumCRF = 51;
|
static const int kMaximumCRF = 51;
|
||||||
|
|
||||||
QSlider* crf_slider_;
|
QSlider* crf_slider_;
|
||||||
@@ -98,6 +98,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
H264Section(QWidget* parent = nullptr);
|
H264Section(QWidget* parent = nullptr);
|
||||||
|
H264Section(int default_crf, QWidget* parent);
|
||||||
|
|
||||||
virtual void AddOpts(EncodingParams* params) override;
|
virtual void AddOpts(EncodingParams* params) override;
|
||||||
|
|
||||||
@@ -112,6 +113,13 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class H265Section : public H264Section
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
H265Section(QWidget* parent = nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // H264SECTION_H
|
#endif // H264SECTION_H
|
||||||
|
|||||||
@@ -36,12 +36,22 @@ ImageSection::ImageSection(QWidget* parent) :
|
|||||||
layout->addWidget(new QLabel(tr("Image Sequence:")), row, 0);
|
layout->addWidget(new QLabel(tr("Image Sequence:")), row, 0);
|
||||||
|
|
||||||
image_sequence_checkbox_ = new QCheckBox();
|
image_sequence_checkbox_ = new QCheckBox();
|
||||||
layout->addWidget(new QCheckBox(), row, 1);
|
connect(image_sequence_checkbox_, &QCheckBox::toggled, this, &ImageSection::ImageSequenceCheckBoxToggled);
|
||||||
|
layout->addWidget(image_sequence_checkbox_, row, 1);
|
||||||
|
|
||||||
|
row++;
|
||||||
|
|
||||||
|
layout->addWidget(new QLabel(tr("Frame to Export:")), row, 0);
|
||||||
|
|
||||||
|
frame_slider_ = new TimeSlider();
|
||||||
|
frame_slider_->SetMinimum(0);
|
||||||
|
frame_slider_->SetValue(0);
|
||||||
|
layout->addWidget(frame_slider_, row, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QCheckBox *ImageSection::image_sequence_checkbox() const
|
void ImageSection::ImageSequenceCheckBoxToggled(bool e)
|
||||||
{
|
{
|
||||||
return image_sequence_checkbox_;
|
frame_slider_->setEnabled(!e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
|
||||||
#include "codecsection.h"
|
#include "codecsection.h"
|
||||||
|
#include "widget/slider/timeslider.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
@@ -33,11 +34,34 @@ class ImageSection : public CodecSection
|
|||||||
public:
|
public:
|
||||||
ImageSection(QWidget* parent = nullptr);
|
ImageSection(QWidget* parent = nullptr);
|
||||||
|
|
||||||
QCheckBox* image_sequence_checkbox() const;
|
bool IsImageSequenceChecked() const
|
||||||
|
{
|
||||||
|
return image_sequence_checkbox_->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimebase(const rational& r)
|
||||||
|
{
|
||||||
|
frame_slider_->SetTimebase(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t GetTimestamp() const
|
||||||
|
{
|
||||||
|
return frame_slider_->GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimestamp(int64_t t)
|
||||||
|
{
|
||||||
|
frame_slider_->SetValue(t);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QCheckBox* image_sequence_checkbox_;
|
QCheckBox* image_sequence_checkbox_;
|
||||||
|
|
||||||
|
TimeSlider* frame_slider_;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ImageSequenceCheckBoxToggled(bool e);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+146
-66
@@ -30,6 +30,7 @@
|
|||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include "common/digit.h"
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "dialog/task/task.h"
|
#include "dialog/task/task.h"
|
||||||
@@ -134,19 +135,19 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
|||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
QTabWidget* preferences_tabs = new QTabWidget();
|
preferences_tabs_ = new QTabWidget();
|
||||||
QScrollArea* video_area = new QScrollArea();
|
QScrollArea* video_area = new QScrollArea();
|
||||||
color_manager_ = viewer_node_->project()->color_manager();
|
color_manager_ = viewer_node_->project()->color_manager();
|
||||||
video_tab_ = new ExportVideoTab(color_manager_);
|
video_tab_ = new ExportVideoTab(color_manager_);
|
||||||
video_area->setWidgetResizable(true);
|
video_area->setWidgetResizable(true);
|
||||||
video_area->setWidget(video_tab_);
|
video_area->setWidget(video_tab_);
|
||||||
preferences_tabs->addTab(video_area, tr("Video"));
|
preferences_tabs_->addTab(video_area, tr("Video"));
|
||||||
QScrollArea* audio_area = new QScrollArea();
|
QScrollArea* audio_area = new QScrollArea();
|
||||||
audio_tab_ = new ExportAudioTab();
|
audio_tab_ = new ExportAudioTab();
|
||||||
audio_area->setWidgetResizable(true);
|
audio_area->setWidgetResizable(true);
|
||||||
audio_area->setWidget(audio_tab_);
|
audio_area->setWidget(audio_tab_);
|
||||||
preferences_tabs->addTab(audio_area, tr("Audio"));
|
preferences_tabs_->addTab(audio_area, tr("Audio"));
|
||||||
preferences_layout->addWidget(preferences_tabs, row, 0, 1, 4);
|
preferences_layout->addWidget(preferences_tabs_, row, 0, 1, 4);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
@@ -165,6 +166,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
|||||||
preview_layout->addWidget(new QLabel(tr("Preview")));
|
preview_layout->addWidget(new QLabel(tr("Preview")));
|
||||||
preview_viewer_ = new ViewerWidget();
|
preview_viewer_ = new ViewerWidget();
|
||||||
preview_viewer_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
preview_viewer_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
connect(preview_viewer_, &ViewerWidget::TimeChanged, video_tab_, &ExportVideoTab::SetTimestamp);
|
||||||
preview_layout->addWidget(preview_viewer_);
|
preview_layout->addWidget(preview_viewer_);
|
||||||
splitter->addWidget(preview_area);
|
splitter->addWidget(preview_area);
|
||||||
|
|
||||||
@@ -176,17 +178,31 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
|||||||
|
|
||||||
// Populate combobox formats
|
// Populate combobox formats
|
||||||
for (int i=0; i<ExportFormat::kFormatCount; i++) {
|
for (int i=0; i<ExportFormat::kFormatCount; i++) {
|
||||||
format_combobox_->addItem(ExportFormat::GetName(static_cast<ExportFormat::Format>(i)));
|
QString format_name = ExportFormat::GetName(static_cast<ExportFormat::Format>(i));
|
||||||
|
|
||||||
|
bool inserted = false;
|
||||||
|
|
||||||
|
for (int j=0; j<format_combobox_->count(); j++) {
|
||||||
|
if (format_combobox_->itemText(j) > format_name) {
|
||||||
|
format_combobox_->insertItem(j, format_name, i);
|
||||||
|
inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inserted) {
|
||||||
|
format_combobox_->addItem(format_name, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
previously_selected_format_ = ExportFormat::kFormatMPEG4;
|
previously_selected_format_ = ExportFormat::kFormatMPEG4;
|
||||||
format_combobox_->setCurrentIndex(ExportFormat::kFormatMPEG4);
|
SetCurrentFormat(ExportFormat::kFormatMPEG4);
|
||||||
connect(format_combobox_,
|
connect(format_combobox_,
|
||||||
static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||||
this,
|
this,
|
||||||
&ExportDialog::FormatChanged);
|
&ExportDialog::FormatChanged);
|
||||||
FormatChanged(ExportFormat::kFormatMPEG4);
|
FormatChanged(format_combobox_->currentIndex());
|
||||||
|
|
||||||
VideoParams vp = viewer_node_->GetVideoParams();
|
VideoParams vp = viewer_node_->GetVideoParams();
|
||||||
AudioParams ap = viewer_node_->GetAudioParams();
|
AudioParams ap = viewer_node_->GetAudioParams();
|
||||||
@@ -195,9 +211,9 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
|||||||
video_tab_->width_slider()->SetDefaultValue(vp.width());
|
video_tab_->width_slider()->SetDefaultValue(vp.width());
|
||||||
video_tab_->height_slider()->SetValue(vp.height());
|
video_tab_->height_slider()->SetValue(vp.height());
|
||||||
video_tab_->height_slider()->SetDefaultValue(vp.height());
|
video_tab_->height_slider()->SetDefaultValue(vp.height());
|
||||||
video_tab_->frame_rate_combobox()->SetFrameRate(vp.frame_rate());
|
video_tab_->SetSelectedFrameRate(vp.frame_rate());
|
||||||
video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(vp.pixel_aspect_ratio());
|
video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(vp.pixel_aspect_ratio());
|
||||||
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(Config::Current()["OnlinePixelFormat"].toInt()));
|
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(Config::Current()[QStringLiteral("OnlinePixelFormat")].toInt()));
|
||||||
video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
|
video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
|
||||||
audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate());
|
audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate());
|
||||||
audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout());
|
audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout());
|
||||||
@@ -228,6 +244,10 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
|||||||
&ExportVideoTab::ColorSpaceChanged,
|
&ExportVideoTab::ColorSpaceChanged,
|
||||||
preview_viewer_,
|
preview_viewer_,
|
||||||
static_cast<void(ViewerWidget::*)(const ColorTransform&)>(&ViewerWidget::SetColorTransform));
|
static_cast<void(ViewerWidget::*)(const ColorTransform&)>(&ViewerWidget::SetColorTransform));
|
||||||
|
connect(video_tab_,
|
||||||
|
&ExportVideoTab::ImageSequenceCheckBoxChanged,
|
||||||
|
this,
|
||||||
|
&ExportDialog::ImageSequenceCheckBoxChanged);
|
||||||
|
|
||||||
// Set viewer to view the node
|
// Set viewer to view the node
|
||||||
preview_viewer_->ConnectViewerNode(viewer_node_);
|
preview_viewer_->ConnectViewerNode(viewer_node_);
|
||||||
@@ -236,36 +256,35 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
|||||||
preview_viewer_->SetColorTransform(video_tab_->CurrentOCIOColorSpace());
|
preview_viewer_->SetColorTransform(video_tab_->CurrentOCIOColorSpace());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExportFormat::Format ExportDialog::GetSelectedFormat() const
|
||||||
|
{
|
||||||
|
return static_cast<ExportFormat::Format>(format_combobox_->currentData().toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
rational ExportDialog::GetSelectedTimebase() const
|
||||||
|
{
|
||||||
|
return video_tab_->GetSelectedFrameRate().flipped();
|
||||||
|
}
|
||||||
|
|
||||||
void ExportDialog::StartExport()
|
void ExportDialog::StartExport()
|
||||||
{
|
{
|
||||||
if (!video_enabled_->isChecked() && !audio_enabled_->isChecked()) {
|
if (!video_enabled_->isChecked() && !audio_enabled_->isChecked()) {
|
||||||
QMessageBox b(this);
|
QtUtils::MessageBox(this, QMessageBox::Critical, tr("Invalid parameters"),
|
||||||
b.setIcon(QMessageBox::Critical);
|
tr("Both video and audio are disabled. There's nothing to export."));
|
||||||
b.setWindowModality(Qt::WindowModal);
|
|
||||||
b.setWindowTitle(tr("Invalid parameters"));
|
|
||||||
b.setText(tr("Both video and audio are disabled. There's nothing to export."));
|
|
||||||
b.addButton(QMessageBox::Ok);
|
|
||||||
b.exec();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate if the entered filename contains the correct extension (the extension is necessary
|
// Validate if the entered filename contains the correct extension (the extension is necessary
|
||||||
// for both FFmpeg and OIIO to determine the output format)
|
// for both FFmpeg and OIIO to determine the output format)
|
||||||
QString necessary_ext = QStringLiteral(".%1").arg(ExportFormat::GetExtension(static_cast<ExportFormat::Format>(format_combobox_->currentIndex())));
|
QString necessary_ext = QStringLiteral(".%1").arg(ExportFormat::GetExtension(GetSelectedFormat()));
|
||||||
QString proposed_filename = filename_edit_->text().trimmed();
|
QString proposed_filename = filename_edit_->text().trimmed();
|
||||||
|
|
||||||
// If it doesn't, see if the user wants to append it automatically. If not, we don't abort the export.
|
// If it doesn't, see if the user wants to append it automatically. If not, we don't abort the export.
|
||||||
if (!proposed_filename.endsWith(necessary_ext, Qt::CaseInsensitive)) {
|
if (!proposed_filename.endsWith(necessary_ext, Qt::CaseInsensitive)) {
|
||||||
QMessageBox b(this);
|
if (QtUtils::MessageBox(this, QMessageBox::Warning, tr("Invalid filename"),
|
||||||
b.setIcon(QMessageBox::Warning);
|
tr("The filename must contain the extension \"%1\". Would you like to append it "
|
||||||
b.setWindowModality(Qt::WindowModal);
|
"automatically?").arg(necessary_ext),
|
||||||
b.setWindowTitle(tr("Invalid filename"));
|
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||||
b.setText(tr("The filename must contain the extension \"%1\". Would you like to append it "
|
|
||||||
"automatically?").arg(necessary_ext));
|
|
||||||
b.addButton(QMessageBox::Yes);
|
|
||||||
b.addButton(QMessageBox::No);
|
|
||||||
|
|
||||||
if (b.exec() == QMessageBox::Yes) {
|
|
||||||
filename_edit_->setText(proposed_filename.append(necessary_ext));
|
filename_edit_->setText(proposed_filename.append(necessary_ext));
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
@@ -278,43 +297,50 @@ void ExportDialog::StartExport()
|
|||||||
|
|
||||||
// If the directory does not exist, try to create it
|
// If the directory does not exist, try to create it
|
||||||
if (!QDir(file_info.path()).mkpath(QStringLiteral("."))) {
|
if (!QDir(file_info.path()).mkpath(QStringLiteral("."))) {
|
||||||
QMessageBox b(this);
|
QtUtils::MessageBox(this, QMessageBox::Critical, tr("Failed to create output directory"),
|
||||||
b.setIcon(QMessageBox::Critical);
|
tr("The intended output directory doesn't exist and Olive couldn't create it. "
|
||||||
b.setWindowModality(Qt::WindowModal);
|
"Please choose a different filename."));
|
||||||
b.setWindowTitle(tr("Failed to create output directory"));
|
|
||||||
b.setText(tr("The intended output directory doesn't exist and Olive couldn't create it. "
|
|
||||||
"Please choose a different filename."));
|
|
||||||
b.addButton(QMessageBox::Ok);
|
|
||||||
b.exec();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate if this is an image sequence and if the filename contains enough digits
|
||||||
|
if (video_tab_->IsImageSequenceSet()) {
|
||||||
|
// Ensure filename contains digits
|
||||||
|
if (!Encoder::FilenameContainsDigitPlaceholder(proposed_filename)) {
|
||||||
|
QtUtils::MessageBox(this, QMessageBox::Critical, tr("Invalid filename"),
|
||||||
|
tr("Export is set to an image sequence, but the filename does not have a section for digits "
|
||||||
|
"(formatted as [#####] where the amount of # is the amount of digits)."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t frame_count = GetExportLengthInTimebaseUnits();
|
||||||
|
int64_t needed_digit_count = GetDigitCount(frame_count);
|
||||||
|
int current_digit_count = Encoder::GetImageSequencePlaceholderDigitCount(proposed_filename);
|
||||||
|
if (current_digit_count < needed_digit_count) {
|
||||||
|
QtUtils::MessageBox(this, QMessageBox::Critical, tr("Invalid filename"),
|
||||||
|
tr("Filename doesn't contain enough digits for the amount of frames "
|
||||||
|
"this export will need (need %1 for %n frame(s)).", nullptr, frame_count)
|
||||||
|
.arg(QString::number(needed_digit_count)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate if the file exists and whether the user wishes to overwrite it
|
// Validate if the file exists and whether the user wishes to overwrite it
|
||||||
if (file_info.exists()) {
|
if (file_info.exists()) {
|
||||||
QMessageBox b(this);
|
if (QtUtils::MessageBox(this, QMessageBox::Warning, tr("Confirm Overwrite"),
|
||||||
b.setIcon(QMessageBox::Warning);
|
tr("The file \"%1\" already exists. Do you want to overwrite it?")
|
||||||
b.setWindowModality(Qt::WindowModal);
|
.arg(proposed_filename),
|
||||||
b.setWindowTitle(tr("Confirm Overwrite"));
|
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
|
||||||
b.setText(tr("The file \"%1\" already exists. Do you want to overwrite it?")
|
|
||||||
.arg(proposed_filename));
|
|
||||||
b.addButton(QMessageBox::Yes);
|
|
||||||
b.addButton(QMessageBox::No);
|
|
||||||
|
|
||||||
if (b.exec() == QMessageBox::No) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate video resolution
|
// Validate video resolution
|
||||||
if (video_enabled_->isChecked()
|
if (video_enabled_->isChecked()
|
||||||
&& video_tab_->GetSelectedCodec() == ExportCodec::kCodecH264
|
&& (video_tab_->GetSelectedCodec() == ExportCodec::kCodecH264 || video_tab_->GetSelectedCodec() == ExportCodec::kCodecH265)
|
||||||
&& (video_tab_->width_slider()->GetValue()%2 != 0 || video_tab_->height_slider()->GetValue()%2 != 0)) {
|
&& (video_tab_->width_slider()->GetValue()%2 != 0 || video_tab_->height_slider()->GetValue()%2 != 0)) {
|
||||||
QMessageBox b(this);
|
QtUtils::MessageBox(this, QMessageBox::Critical, tr("Invalid Parameters"),
|
||||||
b.setIcon(QMessageBox::Critical);
|
tr("Width and height must be multiples of 2."));
|
||||||
b.setWindowModality(Qt::WindowModal);
|
|
||||||
b.setWindowTitle(tr("Invalid Parameters"));
|
|
||||||
b.setText(tr("Width and height must be multiples of 2."));
|
|
||||||
b.exec();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +362,29 @@ void ExportDialog::ExportFinished()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExportDialog::ImageSequenceCheckBoxChanged(bool e)
|
||||||
|
{
|
||||||
|
QFileInfo current_fileinfo(filename_edit_->text());
|
||||||
|
|
||||||
|
QString basename = current_fileinfo.completeBaseName();
|
||||||
|
QString suffix = current_fileinfo.suffix();
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
if (!Encoder::FilenameContainsDigitPlaceholder(basename)) {
|
||||||
|
basename.append(QStringLiteral("_[#####]"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
basename = Encoder::FilenameRemoveDigitPlaceholder(basename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set filename
|
||||||
|
if (!suffix.isEmpty()) {
|
||||||
|
basename.append('.');
|
||||||
|
basename.append(suffix);
|
||||||
|
}
|
||||||
|
filename_edit_->setText(current_fileinfo.dir().filePath(basename));
|
||||||
|
}
|
||||||
|
|
||||||
void ExportDialog::closeEvent(QCloseEvent *e)
|
void ExportDialog::closeEvent(QCloseEvent *e)
|
||||||
{
|
{
|
||||||
preview_viewer_->ConnectViewerNode(nullptr);
|
preview_viewer_->ConnectViewerNode(nullptr);
|
||||||
@@ -345,7 +394,7 @@ void ExportDialog::closeEvent(QCloseEvent *e)
|
|||||||
|
|
||||||
void ExportDialog::BrowseFilename()
|
void ExportDialog::BrowseFilename()
|
||||||
{
|
{
|
||||||
ExportFormat::Format f = static_cast<ExportFormat::Format>(format_combobox_->currentIndex());
|
ExportFormat::Format f = GetSelectedFormat();
|
||||||
|
|
||||||
QString browsed_fn = QFileDialog::getSaveFileName(this,
|
QString browsed_fn = QFileDialog::getSaveFileName(this,
|
||||||
"",
|
"",
|
||||||
@@ -365,7 +414,7 @@ void ExportDialog::FormatChanged(int index)
|
|||||||
{
|
{
|
||||||
QString current_filename = filename_edit_->text().trimmed();
|
QString current_filename = filename_edit_->text().trimmed();
|
||||||
QString previously_selected_ext = ExportFormat::GetExtension(previously_selected_format_);
|
QString previously_selected_ext = ExportFormat::GetExtension(previously_selected_format_);
|
||||||
ExportFormat::Format current_format = static_cast<ExportFormat::Format>(index);
|
ExportFormat::Format current_format = static_cast<ExportFormat::Format>(format_combobox_->itemData(index).toInt());
|
||||||
QString currently_selected_ext = ExportFormat::GetExtension(current_format);
|
QString currently_selected_ext = ExportFormat::GetExtension(current_format);
|
||||||
|
|
||||||
// If the previous extension was added, remove it
|
// If the previous extension was added, remove it
|
||||||
@@ -381,15 +430,13 @@ void ExportDialog::FormatChanged(int index)
|
|||||||
previously_selected_format_ = current_format;
|
previously_selected_format_ = current_format;
|
||||||
|
|
||||||
// Update video and audio comboboxes
|
// Update video and audio comboboxes
|
||||||
video_tab_->codec_combobox()->clear();
|
bool has_video_codecs = video_tab_->SetFormat(current_format);
|
||||||
foreach (ExportCodec::Codec vcodec, ExportFormat::GetVideoCodecs(current_format)) {
|
video_enabled_->setChecked(has_video_codecs);
|
||||||
video_tab_->codec_combobox()->addItem(ExportCodec::GetCodecName(vcodec), vcodec);
|
video_enabled_->setEnabled(has_video_codecs);
|
||||||
}
|
|
||||||
|
|
||||||
audio_tab_->codec_combobox()->clear();
|
bool has_audio_codecs = audio_tab_->SetFormat(current_format);
|
||||||
foreach (ExportCodec::Codec acodec, ExportFormat::GetAudioCodecs(current_format)) {
|
audio_enabled_->setChecked(has_audio_codecs);
|
||||||
audio_tab_->codec_combobox()->addItem(ExportCodec::GetCodecName(acodec), acodec);
|
audio_enabled_->setEnabled(has_audio_codecs);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::ResolutionChanged()
|
void ExportDialog::ResolutionChanged()
|
||||||
@@ -449,7 +496,7 @@ ExportParams ExportDialog::GenerateParams() const
|
|||||||
{
|
{
|
||||||
VideoParams video_render_params(static_cast<int>(video_tab_->width_slider()->GetValue()),
|
VideoParams video_render_params(static_cast<int>(video_tab_->width_slider()->GetValue()),
|
||||||
static_cast<int>(video_tab_->height_slider()->GetValue()),
|
static_cast<int>(video_tab_->height_slider()->GetValue()),
|
||||||
video_tab_->frame_rate_combobox()->GetFrameRate().flipped(),
|
GetSelectedTimebase(),
|
||||||
video_tab_->pixel_format_field()->GetPixelFormat(),
|
video_tab_->pixel_format_field()->GetPixelFormat(),
|
||||||
VideoParams::kInternalChannelCount,
|
VideoParams::kInternalChannelCount,
|
||||||
video_tab_->pixel_aspect_combobox()->GetPixelAspectRatio(),
|
video_tab_->pixel_aspect_combobox()->GetPixelAspectRatio(),
|
||||||
@@ -461,10 +508,15 @@ ExportParams ExportDialog::GenerateParams() const
|
|||||||
AudioParams::kInternalFormat);
|
AudioParams::kInternalFormat);
|
||||||
|
|
||||||
ExportParams params;
|
ExportParams params;
|
||||||
|
params.set_encoder(Encoder::GetTypeFromFormat(GetSelectedFormat()));
|
||||||
params.SetFilename(filename_edit_->text().trimmed());
|
params.SetFilename(filename_edit_->text().trimmed());
|
||||||
params.SetExportLength(viewer_node_->GetLength());
|
params.SetExportLength(viewer_node_->GetLength());
|
||||||
|
|
||||||
if (range_combobox_->currentIndex() == kRangeInToOut) {
|
if (ExportCodec::IsCodecAStillImage(video_tab_->GetSelectedCodec()) && !video_tab_->IsImageSequenceSet()) {
|
||||||
|
// Exporting as image without exporting image sequence, only export one frame
|
||||||
|
rational export_time = Timecode::timestamp_to_time(video_tab_->GetStillImageTime(), GetSelectedTimebase());
|
||||||
|
params.set_custom_range(TimeRange(export_time, export_time));
|
||||||
|
} else if (range_combobox_->currentIndex() == kRangeInToOut) {
|
||||||
// Assume if this combobox is enabled, workarea is enabled - a check that we make in this dialog's constructor
|
// Assume if this combobox is enabled, workarea is enabled - a check that we make in this dialog's constructor
|
||||||
params.set_custom_range(viewer_node_->GetTimelinePoints()->workarea()->range());
|
params.set_custom_range(viewer_node_->GetTimelinePoints()->workarea()->range());
|
||||||
}
|
}
|
||||||
@@ -479,11 +531,15 @@ ExportParams ExportDialog::GenerateParams() const
|
|||||||
|
|
||||||
params.set_video_threads(video_tab_->threads());
|
params.set_video_threads(video_tab_->threads());
|
||||||
|
|
||||||
video_tab_->GetCodecSection()->AddOpts(¶ms);
|
if (video_tab_->isVisible()) {
|
||||||
|
video_tab_->GetCodecSection()->AddOpts(¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
params.set_color_transform(video_tab_->CurrentOCIOColorSpace());
|
params.set_color_transform(video_tab_->CurrentOCIOColorSpace());
|
||||||
|
|
||||||
params.set_video_pix_fmt(video_tab_->pix_fmt());
|
params.set_video_pix_fmt(video_tab_->pix_fmt());
|
||||||
|
|
||||||
|
params.set_video_is_image_sequence(video_tab_->IsImageSequenceSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio_enabled_->isChecked()) {
|
if (audio_enabled_->isChecked()) {
|
||||||
@@ -496,6 +552,30 @@ ExportParams ExportDialog::GenerateParams() const
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExportDialog::SetCurrentFormat(ExportFormat::Format format)
|
||||||
|
{
|
||||||
|
for (int i=0; i<format_combobox_->count(); i++) {
|
||||||
|
if (format_combobox_->itemData(i).toInt() == format) {
|
||||||
|
format_combobox_->setCurrentIndex(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rational ExportDialog::GetExportLength() const
|
||||||
|
{
|
||||||
|
if (range_combobox_->currentIndex() == kRangeInToOut) {
|
||||||
|
return viewer_node_->GetTimelinePoints()->workarea()->range().length();
|
||||||
|
} else {
|
||||||
|
return viewer_node_->GetLength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ExportDialog::GetExportLengthInTimebaseUnits() const
|
||||||
|
{
|
||||||
|
return Timecode::time_to_timestamp(GetExportLength(), GetSelectedTimebase());
|
||||||
|
}
|
||||||
|
|
||||||
void ExportDialog::UpdateViewerDimensions()
|
void ExportDialog::UpdateViewerDimensions()
|
||||||
{
|
{
|
||||||
preview_viewer_->SetViewerResolution(static_cast<int>(video_tab_->width_slider()->GetValue()),
|
preview_viewer_->SetViewerResolution(static_cast<int>(video_tab_->width_slider()->GetValue()),
|
||||||
@@ -509,7 +589,7 @@ void ExportDialog::UpdateViewerDimensions()
|
|||||||
vp.height(),
|
vp.height(),
|
||||||
static_cast<int>(video_tab_->width_slider()->GetValue()),
|
static_cast<int>(video_tab_->width_slider()->GetValue()),
|
||||||
static_cast<int>(video_tab_->height_slider()->GetValue())
|
static_cast<int>(video_tab_->height_slider()->GetValue())
|
||||||
);
|
);
|
||||||
|
|
||||||
preview_viewer_->SetMatrix(transform);
|
preview_viewer_->SetMatrix(transform);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ class ExportDialog : public QDialog
|
|||||||
public:
|
public:
|
||||||
ExportDialog(ViewerOutput* viewer_node, QWidget* parent = nullptr);
|
ExportDialog(ViewerOutput* viewer_node, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
ExportFormat::Format GetSelectedFormat() const;
|
||||||
|
|
||||||
|
rational GetSelectedTimebase() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void closeEvent(QCloseEvent *e) override;
|
virtual void closeEvent(QCloseEvent *e) override;
|
||||||
|
|
||||||
@@ -51,15 +55,22 @@ private:
|
|||||||
|
|
||||||
ExportParams GenerateParams() const;
|
ExportParams GenerateParams() const;
|
||||||
|
|
||||||
|
void SetCurrentFormat(ExportFormat::Format format);
|
||||||
|
|
||||||
ViewerOutput* viewer_node_;
|
ViewerOutput* viewer_node_;
|
||||||
|
|
||||||
ExportFormat::Format previously_selected_format_;
|
ExportFormat::Format previously_selected_format_;
|
||||||
|
|
||||||
|
rational GetExportLength() const;
|
||||||
|
int64_t GetExportLengthInTimebaseUnits() const;
|
||||||
|
|
||||||
enum RangeSelection {
|
enum RangeSelection {
|
||||||
kRangeEntireSequence,
|
kRangeEntireSequence,
|
||||||
kRangeInToOut
|
kRangeInToOut
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QTabWidget* preferences_tabs_;
|
||||||
|
|
||||||
QComboBox* range_combobox_;
|
QComboBox* range_combobox_;
|
||||||
|
|
||||||
QCheckBox* video_enabled_;
|
QCheckBox* video_enabled_;
|
||||||
@@ -92,6 +103,8 @@ private slots:
|
|||||||
|
|
||||||
void ExportFinished();
|
void ExportFinished();
|
||||||
|
|
||||||
|
void ImageSequenceCheckBoxChanged(bool e);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,4 +75,15 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) :
|
|||||||
outer_layout->addStretch();
|
outer_layout->addStretch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ExportAudioTab::SetFormat(ExportFormat::Format format)
|
||||||
|
{
|
||||||
|
QList<ExportCodec::Codec> acodecs = ExportFormat::GetAudioCodecs(format);
|
||||||
|
setEnabled(!acodecs.isEmpty());
|
||||||
|
codec_combobox()->clear();
|
||||||
|
foreach (ExportCodec::Codec acodec, acodecs) {
|
||||||
|
codec_combobox()->addItem(ExportCodec::GetCodecName(acodec), acodec);
|
||||||
|
}
|
||||||
|
return acodecs.size();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
#include "codec/exportformat.h"
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
#include "widget/standardcombos/standardcombos.h"
|
#include "widget/standardcombos/standardcombos.h"
|
||||||
|
|
||||||
@@ -36,6 +37,8 @@ class ExportAudioTab : public QWidget
|
|||||||
public:
|
public:
|
||||||
ExportAudioTab(QWidget* parent = nullptr);
|
ExportAudioTab(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
int SetFormat(ExportFormat::Format format);
|
||||||
|
|
||||||
QComboBox* codec_combobox() const
|
QComboBox* codec_combobox() const
|
||||||
{
|
{
|
||||||
return codec_combobox_;
|
return codec_combobox_;
|
||||||
|
|||||||
@@ -49,6 +49,26 @@ ExportVideoTab::ExportVideoTab(ColorManager* color_manager, QWidget *parent) :
|
|||||||
outer_layout->addStretch();
|
outer_layout->addStretch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ExportVideoTab::SetFormat(ExportFormat::Format format)
|
||||||
|
{
|
||||||
|
format_ = format;
|
||||||
|
|
||||||
|
QList<ExportCodec::Codec> vcodecs = ExportFormat::GetVideoCodecs(format);
|
||||||
|
setEnabled(!vcodecs.isEmpty());
|
||||||
|
codec_combobox()->clear();
|
||||||
|
foreach (ExportCodec::Codec vcodec, vcodecs) {
|
||||||
|
codec_combobox()->addItem(ExportCodec::GetCodecName(vcodec), vcodec);
|
||||||
|
}
|
||||||
|
return vcodecs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExportVideoTab::IsImageSequenceSet() const
|
||||||
|
{
|
||||||
|
ImageSection* img_section = dynamic_cast<ImageSection*>(codec_stack_->currentWidget());
|
||||||
|
|
||||||
|
return (img_section && img_section->IsImageSequenceChecked());
|
||||||
|
}
|
||||||
|
|
||||||
QWidget* ExportVideoTab::SetupResolutionSection()
|
QWidget* ExportVideoTab::SetupResolutionSection()
|
||||||
{
|
{
|
||||||
int row = 0;
|
int row = 0;
|
||||||
@@ -99,6 +119,7 @@ QWidget* ExportVideoTab::SetupResolutionSection()
|
|||||||
layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0);
|
layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0);
|
||||||
|
|
||||||
frame_rate_combobox_ = new FrameRateComboBox();
|
frame_rate_combobox_ = new FrameRateComboBox();
|
||||||
|
connect(frame_rate_combobox_, &FrameRateComboBox::FrameRateChanged, this, &ExportVideoTab::UpdateFrameRate);
|
||||||
layout->addWidget(frame_rate_combobox_, row, 1);
|
layout->addWidget(frame_rate_combobox_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
@@ -161,6 +182,9 @@ QWidget *ExportVideoTab::SetupCodecSection()
|
|||||||
h264_section_ = new H264Section();
|
h264_section_ = new H264Section();
|
||||||
codec_stack_->addWidget(h264_section_);
|
codec_stack_->addWidget(h264_section_);
|
||||||
|
|
||||||
|
h265_section_ = new H265Section();
|
||||||
|
codec_stack_->addWidget(h265_section_);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
QPushButton* advanced_btn = new QPushButton(tr("Advanced"));
|
QPushButton* advanced_btn = new QPushButton(tr("Advanced"));
|
||||||
@@ -178,7 +202,7 @@ void ExportVideoTab::MaintainAspectRatioChanged(bool val)
|
|||||||
void ExportVideoTab::OpenAdvancedDialog()
|
void ExportVideoTab::OpenAdvancedDialog()
|
||||||
{
|
{
|
||||||
// Find export formats compatible with this encoder
|
// Find export formats compatible with this encoder
|
||||||
QStringList pixel_formats = ExportCodec::GetPixelFormatsForCodec(GetSelectedCodec());
|
QStringList pixel_formats = ExportFormat::GetPixelFormatsForCodec(format_, GetSelectedCodec());
|
||||||
|
|
||||||
ExportAdvancedVideoDialog d(pixel_formats, this);
|
ExportAdvancedVideoDialog d(pixel_formats, this);
|
||||||
|
|
||||||
@@ -191,19 +215,51 @@ void ExportVideoTab::OpenAdvancedDialog()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExportVideoTab::UpdateFrameRate(rational r)
|
||||||
|
{
|
||||||
|
// Convert frame rate to timebase
|
||||||
|
r.flip();
|
||||||
|
|
||||||
|
for (int i=0; i<codec_stack_->count(); i++) {
|
||||||
|
ImageSection* img = dynamic_cast<ImageSection*>(codec_stack_->widget(i));
|
||||||
|
if (img) {
|
||||||
|
img->SetTimebase(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ExportVideoTab::VideoCodecChanged()
|
void ExportVideoTab::VideoCodecChanged()
|
||||||
{
|
{
|
||||||
ExportCodec::Codec codec = GetSelectedCodec();
|
ExportCodec::Codec codec = GetSelectedCodec();
|
||||||
|
|
||||||
if (codec == ExportCodec::kCodecH264) {
|
if (codec == ExportCodec::kCodecH264) {
|
||||||
SetCodecSection(h264_section());
|
SetCodecSection(h264_section_);
|
||||||
|
} else if (codec == ExportCodec::kCodecH265) {
|
||||||
|
SetCodecSection(h265_section_);
|
||||||
} else if (ExportCodec::IsCodecAStillImage(codec)) {
|
} else if (ExportCodec::IsCodecAStillImage(codec)) {
|
||||||
SetCodecSection(image_section());
|
SetCodecSection(image_section_);
|
||||||
|
} else {
|
||||||
|
SetCodecSection(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default pixel format
|
// Set default pixel format
|
||||||
pix_fmt_ = ExportCodec::GetPixelFormatsForCodec(codec).first();
|
QStringList pix_fmts = ExportFormat::GetPixelFormatsForCodec(format_, codec);
|
||||||
|
if (!pix_fmts.isEmpty()) {
|
||||||
|
pix_fmt_ = pix_fmts.first();
|
||||||
|
} else {
|
||||||
|
pix_fmt_.clear();
|
||||||
|
}
|
||||||
qDebug() << "Set default pix fmt" << pix_fmt_;
|
qDebug() << "Set default pix fmt" << pix_fmt_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExportVideoTab::SetTimestamp(int64_t timestamp)
|
||||||
|
{
|
||||||
|
for (int i=0; i<codec_stack_->count(); i++) {
|
||||||
|
ImageSection* img = dynamic_cast<ImageSection*>(codec_stack_->widget(i));
|
||||||
|
if (img) {
|
||||||
|
img->SetTimestamp(timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ class ExportVideoTab : public QWidget
|
|||||||
public:
|
public:
|
||||||
ExportVideoTab(ColorManager* color_manager, QWidget* parent = nullptr);
|
ExportVideoTab(ColorManager* color_manager, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
int SetFormat(ExportFormat::Format format);
|
||||||
|
|
||||||
|
bool IsImageSequenceSet() const;
|
||||||
|
|
||||||
|
int64_t GetStillImageTime() const
|
||||||
|
{
|
||||||
|
return image_section_->GetTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
ExportCodec::Codec GetSelectedCodec() const
|
ExportCodec::Codec GetSelectedCodec() const
|
||||||
{
|
{
|
||||||
return static_cast<ExportCodec::Codec>(codec_combobox()->currentData().toInt());
|
return static_cast<ExportCodec::Codec>(codec_combobox()->currentData().toInt());
|
||||||
@@ -71,9 +80,15 @@ public:
|
|||||||
return scaling_method_combobox_;
|
return scaling_method_combobox_;
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameRateComboBox* frame_rate_combobox() const
|
rational GetSelectedFrameRate() const
|
||||||
{
|
{
|
||||||
return frame_rate_combobox_;
|
return frame_rate_combobox_->GetFrameRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSelectedFrameRate(const rational& fr)
|
||||||
|
{
|
||||||
|
frame_rate_combobox_->SetFrameRate(fr);
|
||||||
|
UpdateFrameRate(fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CurrentOCIOColorSpace()
|
QString CurrentOCIOColorSpace()
|
||||||
@@ -88,17 +103,12 @@ public:
|
|||||||
|
|
||||||
void SetCodecSection(CodecSection* section)
|
void SetCodecSection(CodecSection* section)
|
||||||
{
|
{
|
||||||
codec_stack_->setCurrentWidget(section);
|
if (section) {
|
||||||
}
|
codec_stack_->setVisible(true);
|
||||||
|
codec_stack_->setCurrentWidget(section);
|
||||||
ImageSection* image_section() const
|
} else {
|
||||||
{
|
codec_stack_->setVisible(false);
|
||||||
return image_section_;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
H264Section* h264_section() const
|
|
||||||
{
|
|
||||||
return h264_section_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InterlacedComboBox* interlaced_combobox() const
|
InterlacedComboBox* interlaced_combobox() const
|
||||||
@@ -128,9 +138,13 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void VideoCodecChanged();
|
void VideoCodecChanged();
|
||||||
|
|
||||||
|
void SetTimestamp(int64_t timestamp);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ColorSpaceChanged(const QString& colorspace);
|
void ColorSpaceChanged(const QString& colorspace);
|
||||||
|
|
||||||
|
void ImageSequenceCheckBoxChanged(bool e);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* SetupResolutionSection();
|
QWidget* SetupResolutionSection();
|
||||||
QWidget* SetupColorSection();
|
QWidget* SetupColorSection();
|
||||||
@@ -144,6 +158,7 @@ private:
|
|||||||
QStackedWidget* codec_stack_;
|
QStackedWidget* codec_stack_;
|
||||||
ImageSection* image_section_;
|
ImageSection* image_section_;
|
||||||
H264Section* h264_section_;
|
H264Section* h264_section_;
|
||||||
|
H264Section* h265_section_;
|
||||||
|
|
||||||
ColorSpaceChooser* color_space_chooser_;
|
ColorSpaceChooser* color_space_chooser_;
|
||||||
|
|
||||||
@@ -160,11 +175,15 @@ private:
|
|||||||
|
|
||||||
QString pix_fmt_;
|
QString pix_fmt_;
|
||||||
|
|
||||||
|
ExportFormat::Format format_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void MaintainAspectRatioChanged(bool val);
|
void MaintainAspectRatioChanged(bool val);
|
||||||
|
|
||||||
void OpenAdvancedDialog();
|
void OpenAdvancedDialog();
|
||||||
|
|
||||||
|
void UpdateFrameRate(rational r);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ ExportParams::ExportParams() :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &ExportParams::encoder() const
|
const Encoder::Type &ExportParams::encoder() const
|
||||||
{
|
{
|
||||||
return encoder_id_;
|
return encoder_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportParams::set_encoder(const QString &id)
|
void ExportParams::set_encoder(const Encoder::Type &id)
|
||||||
{
|
{
|
||||||
encoder_id_ = id;
|
encoder_id_ = id;
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ void ExportParams::Save(QXmlStreamWriter *writer) const
|
|||||||
{
|
{
|
||||||
writer->writeStartElement(QStringLiteral("export"));
|
writer->writeStartElement(QStringLiteral("export"));
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("encoder"), encoder_id_);
|
writer->writeTextElement(QStringLiteral("encoder"), QString::number(encoder_id_));
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
|
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ public:
|
|||||||
|
|
||||||
ExportParams();
|
ExportParams();
|
||||||
|
|
||||||
const QString& encoder() const;
|
const Encoder::Type& encoder() const;
|
||||||
void set_encoder(const QString& id);
|
void set_encoder(const Encoder::Type& id);
|
||||||
|
|
||||||
bool has_custom_range() const;
|
bool has_custom_range() const;
|
||||||
const TimeRange& custom_range() const;
|
const TimeRange& custom_range() const;
|
||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
virtual void Save(QXmlStreamWriter* writer) const override;
|
virtual void Save(QXmlStreamWriter* writer) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString encoder_id_;
|
Encoder::Type encoder_id_;
|
||||||
|
|
||||||
VideoScalingMethod video_scaling_method_;
|
VideoScalingMethod video_scaling_method_;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user