export: allow exporting subtitles to sidecar files
This commit is contained in:
+30
-1
@@ -93,7 +93,8 @@ EncodingParams::EncodingParams() :
|
||||
video_color_range_(kYUVDefault),
|
||||
audio_enabled_(false),
|
||||
audio_bit_rate_(0),
|
||||
subtitles_enabled_(false)
|
||||
subtitles_enabled_(false),
|
||||
subtitles_are_sidecar_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -117,6 +118,29 @@ void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
|
||||
subtitles_codec_ = scodec;
|
||||
}
|
||||
|
||||
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec)
|
||||
{
|
||||
subtitles_enabled_ = true;
|
||||
subtitles_are_sidecar_ = true;
|
||||
subtitle_sidecar_fmt_ = sfmt;
|
||||
subtitles_codec_ = scodec;
|
||||
}
|
||||
|
||||
void EncodingParams::DisableVideo()
|
||||
{
|
||||
video_enabled_ = false;
|
||||
}
|
||||
|
||||
void EncodingParams::DisableAudio()
|
||||
{
|
||||
audio_enabled_ = false;
|
||||
}
|
||||
|
||||
void EncodingParams::DisableSubtitles()
|
||||
{
|
||||
subtitles_enabled_ = false;
|
||||
}
|
||||
|
||||
void EncodingParams::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("filename"), filename_);
|
||||
@@ -217,6 +241,11 @@ Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams
|
||||
return CreateFromID(GetTypeFromFormat(f), params);
|
||||
}
|
||||
|
||||
Encoder *Encoder::CreateFromParams(const EncodingParams ¶ms)
|
||||
{
|
||||
return CreateFromFormat(params.format(), params);
|
||||
}
|
||||
|
||||
QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||
{
|
||||
return QStringList();
|
||||
|
||||
@@ -58,6 +58,14 @@ public:
|
||||
void EnableVideo(const VideoParams& video_params, const ExportCodec::Codec& vcodec);
|
||||
void EnableAudio(const AudioParams& audio_params, const ExportCodec::Codec &acodec);
|
||||
void EnableSubtitles(const ExportCodec::Codec &scodec);
|
||||
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec);
|
||||
|
||||
void DisableVideo();
|
||||
void DisableAudio();
|
||||
void DisableSubtitles();
|
||||
|
||||
const ExportFormat::Format &format() const { return format_; }
|
||||
void set_format(const ExportFormat::Format &format) { format_ = format; }
|
||||
|
||||
void set_video_option(const QString& key, const QString& value) { video_opts_.insert(key, value); }
|
||||
void set_video_bit_rate(const int64_t& rate) { video_bit_rate_ = rate; }
|
||||
@@ -92,6 +100,8 @@ public:
|
||||
void set_audio_bit_rate(const int64_t& b) { audio_bit_rate_ = b; }
|
||||
|
||||
bool subtitles_enabled() const { return subtitles_enabled_; }
|
||||
bool subtitles_are_sidecar() const { return subtitles_are_sidecar_; }
|
||||
ExportFormat::Format subtitle_sidecar_fmt() const { return subtitle_sidecar_fmt_; }
|
||||
ExportCodec::Codec subtitles_codec() const { return subtitles_codec_; }
|
||||
|
||||
const rational& GetExportLength() const { return export_length_; }
|
||||
@@ -101,6 +111,7 @@ public:
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
ExportFormat::Format format_;
|
||||
|
||||
bool video_enabled_;
|
||||
ExportCodec::Codec video_codec_;
|
||||
@@ -121,6 +132,8 @@ private:
|
||||
int64_t audio_bit_rate_;
|
||||
|
||||
bool subtitles_enabled_;
|
||||
bool subtitles_are_sidecar_;
|
||||
ExportFormat::Format subtitle_sidecar_fmt_;
|
||||
ExportCodec::Codec subtitles_codec_;
|
||||
|
||||
rational export_length_;
|
||||
@@ -152,6 +165,8 @@ public:
|
||||
|
||||
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms);
|
||||
|
||||
static Encoder *CreateFromParams(const EncodingParams ¶ms);
|
||||
|
||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
|
||||
virtual std::vector<AudioParams::Format> GetSampleFormatsForCodec(ExportCodec::Codec c) const;
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
#include "common/digit.h"
|
||||
#include "common/qtutils.h"
|
||||
#include "core.h"
|
||||
#include "dialog/task/task.h"
|
||||
#include "node/project/project.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
@@ -250,6 +249,12 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
||||
preview_viewer_->ConnectViewerNode(viewer_node_);
|
||||
preview_viewer_->SetColorMenuEnabled(false);
|
||||
preview_viewer_->SetColorTransform(video_tab_->CurrentOCIOColorSpace());
|
||||
|
||||
// We don't check if the codec supports subtitles because we can always export to a sidecar file
|
||||
bool has_subtitle_codecs = SequenceHasSubtitles();
|
||||
connect(subtitles_enabled_, &QCheckBox::toggled, subtitle_tab_, &QWidget::setEnabled);
|
||||
subtitles_enabled_->setChecked(has_subtitle_codecs);
|
||||
subtitles_enabled_->setEnabled(has_subtitle_codecs);
|
||||
}
|
||||
|
||||
rational ExportDialog::GetSelectedTimebase() const
|
||||
@@ -445,9 +450,9 @@ void ExportDialog::FormatChanged(ExportFormat::Format current_format)
|
||||
audio_enabled_->setChecked(has_audio_codecs);
|
||||
audio_enabled_->setEnabled(has_audio_codecs);
|
||||
|
||||
bool has_subtitle_codecs = subtitle_tab_->SetFormat(current_format);
|
||||
subtitles_enabled_->setChecked(has_subtitle_codecs);
|
||||
subtitles_enabled_->setEnabled(has_subtitle_codecs);
|
||||
if (subtitles_enabled_->isEnabled()) {
|
||||
subtitle_tab_->SetFormat(current_format);
|
||||
}
|
||||
}
|
||||
|
||||
void ExportDialog::ResolutionChanged()
|
||||
@@ -503,6 +508,20 @@ void ExportDialog::SetDefaultFilename()
|
||||
filename_edit_->setText(file_location);
|
||||
}
|
||||
|
||||
bool ExportDialog::SequenceHasSubtitles() const
|
||||
{
|
||||
if (Sequence *s = dynamic_cast<Sequence*>(viewer_node_)) {
|
||||
TrackList *tl = s->track_list(Track::kSubtitle);
|
||||
for (Track *t : tl->GetTracks()) {
|
||||
if (!t->IsMuted() && !t->Blocks().empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ExportParams ExportDialog::GenerateParams() const
|
||||
{
|
||||
VideoParams video_render_params(static_cast<int>(video_tab_->width_slider()->GetValue()),
|
||||
@@ -519,7 +538,7 @@ ExportParams ExportDialog::GenerateParams() const
|
||||
audio_tab_->sample_format_combobox()->GetSampleFormat());
|
||||
|
||||
ExportParams params;
|
||||
params.set_encoder(Encoder::GetTypeFromFormat(format_combobox_->GetFormat()));
|
||||
params.set_format(format_combobox_->GetFormat());
|
||||
params.SetFilename(filename_edit_->text().trimmed());
|
||||
params.SetExportLength(viewer_node_->GetLength());
|
||||
|
||||
@@ -561,8 +580,15 @@ ExportParams ExportDialog::GenerateParams() const
|
||||
params.set_audio_bit_rate(audio_tab_->bit_rate_slider()->GetValue() * 1000);
|
||||
}
|
||||
|
||||
if (subtitles_enabled_->isChecked()) {
|
||||
params.EnableSubtitles(subtitle_tab_->GetSubtitleCodec());
|
||||
if (subtitles_enabled_->isEnabled()
|
||||
&& subtitles_enabled_->isChecked()) {
|
||||
if (!subtitle_tab_->GetSidecarEnabled()) {
|
||||
// Export subtitles embedded in container
|
||||
params.EnableSubtitles(subtitle_tab_->GetSubtitleCodec());
|
||||
} else {
|
||||
// Export subtitles to a sidecar file
|
||||
params.EnableSidecarSubtitles(subtitle_tab_->GetSidecarFormat(), subtitle_tab_->GetSubtitleCodec());
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
|
||||
@@ -63,6 +63,8 @@ private:
|
||||
void LoadPresets();
|
||||
void SetDefaultFilename();
|
||||
|
||||
bool SequenceHasSubtitles() const;
|
||||
|
||||
ExportParams GenerateParams() const;
|
||||
|
||||
ViewerOutput* viewer_node_;
|
||||
|
||||
@@ -46,6 +46,13 @@ ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent) :
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case kShowSubtitlesOnly:
|
||||
if (!ExportFormat::GetVideoCodecs(f).isEmpty()
|
||||
|| ExportFormat::GetSubtitleCodecs(f).isEmpty()
|
||||
|| !ExportFormat::GetAudioCodecs(f).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
QString format_name = ExportFormat::GetName(f);
|
||||
|
||||
@@ -34,7 +34,8 @@ public:
|
||||
enum Mode {
|
||||
kShowAllFormats,
|
||||
kShowAudioOnly,
|
||||
kShowVideoOnly
|
||||
kShowVideoOnly,
|
||||
kShowSubtitlesOnly
|
||||
};
|
||||
|
||||
ExportFormatComboBox(Mode mode, QWidget *parent = nullptr);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "exportsubtitlestab.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -15,22 +14,46 @@ ExportSubtitlesTab::ExportSubtitlesTab(QWidget *parent) :
|
||||
|
||||
int row = 0;
|
||||
|
||||
sidecar_checkbox_ = new QCheckBox(tr("Export to sidecar file"));
|
||||
layout->addWidget(sidecar_checkbox_, row, 0, 1, 2);
|
||||
|
||||
row++;
|
||||
|
||||
sidecar_format_label_ = new QLabel(tr("Sidecar Format:"));
|
||||
sidecar_format_label_->setVisible(false);
|
||||
layout->addWidget(sidecar_format_label_, row, 0);
|
||||
|
||||
sidecar_format_combobox_ = new ExportFormatComboBox(ExportFormatComboBox::kShowSubtitlesOnly);
|
||||
sidecar_format_combobox_->setVisible(false);
|
||||
layout->addWidget(sidecar_format_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Codec:")), row, 0);
|
||||
|
||||
codec_combobox_ = new QComboBox();
|
||||
layout->addWidget(codec_combobox_, row, 1);
|
||||
|
||||
outer_layout->addStretch();
|
||||
|
||||
connect(sidecar_checkbox_, &QCheckBox::toggled, sidecar_format_label_, &QWidget::setVisible);
|
||||
connect(sidecar_checkbox_, &QCheckBox::toggled, sidecar_format_combobox_, &QWidget::setVisible);
|
||||
}
|
||||
|
||||
int ExportSubtitlesTab::SetFormat(ExportFormat::Format format)
|
||||
{
|
||||
auto scodecs = ExportFormat::GetSubtitleCodecs(format);
|
||||
setEnabled(!scodecs.isEmpty());
|
||||
|
||||
sidecar_checkbox_->setChecked(scodecs.empty());
|
||||
sidecar_checkbox_->setEnabled(!scodecs.empty());
|
||||
|
||||
scodecs = ExportFormat::GetSubtitleCodecs(sidecar_format_combobox_->GetFormat());
|
||||
|
||||
codec_combobox_->clear();
|
||||
foreach (ExportCodec::Codec scodec, scodecs) {
|
||||
codec_combobox_->addItem(ExportCodec::GetCodecName(scodec), scodec);
|
||||
}
|
||||
|
||||
return scodecs.size();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
#ifndef EXPORTSUBTITLESTAB_H
|
||||
#define EXPORTSUBTITLESTAB_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
|
||||
#include "codec/exportformat.h"
|
||||
#include "render/subtitleparams.h"
|
||||
#include "dialog/export/exportformatcombobox.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -34,6 +36,12 @@ class ExportSubtitlesTab : public QWidget
|
||||
public:
|
||||
ExportSubtitlesTab(QWidget *parent = nullptr);
|
||||
|
||||
bool GetSidecarEnabled() const { return sidecar_checkbox_->isChecked(); }
|
||||
void SetSidecarEnabled(bool e) { sidecar_checkbox_->setEnabled(e); }
|
||||
|
||||
ExportFormat::Format GetSidecarFormat() const { return sidecar_format_combobox_->GetFormat(); }
|
||||
void SetSidecarFormat(ExportFormat::Format f) { sidecar_format_combobox_->SetFormat(f); }
|
||||
|
||||
int SetFormat(ExportFormat::Format format);
|
||||
|
||||
ExportCodec::Codec GetSubtitleCodec()
|
||||
@@ -42,6 +50,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
QCheckBox *sidecar_checkbox_;
|
||||
|
||||
QLabel *sidecar_format_label_;
|
||||
ExportFormatComboBox *sidecar_format_combobox_;
|
||||
|
||||
QComboBox *codec_combobox_;
|
||||
|
||||
};
|
||||
|
||||
@@ -58,7 +58,14 @@ bool ExportTask::Run()
|
||||
params_.SetFilename(FileFunctions::GetSafeTemporaryFilename(real_filename));
|
||||
}
|
||||
|
||||
encoder_ = Encoder::CreateFromID(params_.encoder(), params_);
|
||||
// If we're exporting to a sidecar subtitle file, disable the subtitles in the main encoder
|
||||
bool subtitles_enabled = params_.subtitles_enabled();
|
||||
ExportParams sidecar_params = params_;
|
||||
if (subtitles_enabled && params_.subtitles_are_sidecar()) {
|
||||
params_.DisableSubtitles();
|
||||
}
|
||||
|
||||
encoder_ = std::shared_ptr<Encoder>(Encoder::CreateFromParams(params_));
|
||||
|
||||
if (!encoder_) {
|
||||
SetError(tr("Failed to create encoder"));
|
||||
@@ -67,10 +74,38 @@ bool ExportTask::Run()
|
||||
|
||||
if (!encoder_->Open()) {
|
||||
SetError(tr("Failed to open file: %1").arg(encoder_->GetError()));
|
||||
encoder_->deleteLater();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (subtitles_enabled && params_.subtitles_are_sidecar()) {
|
||||
// Construct sidecar params
|
||||
sidecar_params.DisableVideo();
|
||||
sidecar_params.DisableAudio();
|
||||
|
||||
QString sidecar_filename;
|
||||
{
|
||||
QFileInfo fi(real_filename);
|
||||
sidecar_filename = fi.completeBaseName();
|
||||
sidecar_filename.append('.');
|
||||
sidecar_filename.append(ExportFormat::GetExtension(sidecar_params.subtitle_sidecar_fmt()));
|
||||
sidecar_filename = fi.dir().filePath(sidecar_filename);
|
||||
}
|
||||
sidecar_params.SetFilename(sidecar_filename);
|
||||
|
||||
subtitle_encoder_ = std::shared_ptr<Encoder>(Encoder::CreateFromFormat(sidecar_params.subtitle_sidecar_fmt(), sidecar_params));
|
||||
if (!subtitle_encoder_) {
|
||||
SetError(tr("Failed to create subtitle encoder"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subtitle_encoder_->Open()) {
|
||||
SetError(tr("Failed to open subtitle sidecar file: %1").arg(sidecar_filename));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
subtitle_encoder_ = encoder_;
|
||||
}
|
||||
|
||||
if (params_.has_custom_range()) {
|
||||
// Render custom range only
|
||||
range = params_.custom_range();
|
||||
@@ -121,7 +156,7 @@ bool ExportTask::Run()
|
||||
audio_range = {range};
|
||||
}
|
||||
|
||||
if (params_.subtitles_enabled()) {
|
||||
if (subtitles_enabled) {
|
||||
subtitle_range = range;
|
||||
}
|
||||
|
||||
@@ -132,13 +167,18 @@ bool ExportTask::Run()
|
||||
bool success = true;
|
||||
|
||||
encoder_->Close();
|
||||
|
||||
if (!encoder_->GetError().isEmpty()) {
|
||||
SetError(encoder_->GetError());
|
||||
success = false;
|
||||
}
|
||||
|
||||
delete encoder_;
|
||||
if (subtitle_encoder_ != encoder_) {
|
||||
subtitle_encoder_->Close();
|
||||
if (!subtitle_encoder_->GetError().isEmpty()) {
|
||||
SetError(subtitle_encoder_->GetError());
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If cancelled, delete the file we made, which is always a file we created since we write to a
|
||||
// temp file during the actual encoding process
|
||||
@@ -209,8 +249,8 @@ bool ExportTask::AudioDownloaded(const TimeRange &range, const SampleBuffer &sam
|
||||
|
||||
bool ExportTask::EncodeSubtitle(const SubtitleBlock *sub)
|
||||
{
|
||||
if (!encoder_->WriteSubtitle(sub)) {
|
||||
SetError(encoder_->GetError());
|
||||
if (!subtitle_encoder_->WriteSubtitle(sub)) {
|
||||
SetError(subtitle_encoder_->GetError());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
@@ -60,7 +60,9 @@ private:
|
||||
|
||||
ExportParams params_;
|
||||
|
||||
Encoder* encoder_;
|
||||
std::shared_ptr<Encoder> encoder_;
|
||||
|
||||
std::shared_ptr<Encoder> subtitle_encoder_;
|
||||
|
||||
ColorProcessorPtr color_processor_;
|
||||
|
||||
|
||||
@@ -28,16 +28,6 @@ ExportParams::ExportParams() :
|
||||
{
|
||||
}
|
||||
|
||||
const Encoder::Type &ExportParams::encoder() const
|
||||
{
|
||||
return encoder_id_;
|
||||
}
|
||||
|
||||
void ExportParams::set_encoder(const Encoder::Type &id)
|
||||
{
|
||||
encoder_id_ = id;
|
||||
}
|
||||
|
||||
bool ExportParams::has_custom_range() const
|
||||
{
|
||||
return has_custom_range_;
|
||||
@@ -104,8 +94,6 @@ void ExportParams::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("export"));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("encoder"), QString::number(encoder_id_));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_));
|
||||
|
||||
@@ -39,9 +39,6 @@ public:
|
||||
|
||||
ExportParams();
|
||||
|
||||
const Encoder::Type& encoder() const;
|
||||
void set_encoder(const Encoder::Type& id);
|
||||
|
||||
bool has_custom_range() const;
|
||||
const TimeRange& custom_range() const;
|
||||
void set_custom_range(const TimeRange& custom_range);
|
||||
@@ -59,8 +56,6 @@ public:
|
||||
virtual void Save(QXmlStreamWriter* writer) const override;
|
||||
|
||||
private:
|
||||
Encoder::Type encoder_id_;
|
||||
|
||||
VideoScalingMethod video_scaling_method_;
|
||||
|
||||
bool has_custom_range_;
|
||||
|
||||
@@ -95,8 +95,7 @@ bool RenderTask::Render(ColorManager* manager,
|
||||
|
||||
// Subtitle loop, loops over all blocks in sequence on all tracks
|
||||
if (!subtitle_range.length().isNull()) {
|
||||
Sequence *sequence = dynamic_cast<Sequence*>(viewer_);
|
||||
if (sequence) {
|
||||
if (Sequence *sequence = dynamic_cast<Sequence*>(viewer_)) {
|
||||
TrackList *list = sequence->track_list(Track::kSubtitle);
|
||||
QVector<int> block_indexes(list->GetTrackCount(), 0);
|
||||
|
||||
@@ -106,6 +105,10 @@ bool RenderTask::Render(ColorManager* manager,
|
||||
|
||||
for (int i=0; i<block_indexes.size(); i++) {
|
||||
Track *this_track = list->GetTrackAt(i);
|
||||
if (this_track->IsMuted()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int &this_block_index = block_indexes[i];
|
||||
if (this_block_index >= this_track->Blocks().size()) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user