implemented core subtitle support

This commit is contained in:
itsmattkc
2021-05-18 12:42:47 +10:00
parent 4adaedb304
commit cc21ed37a7
37 changed files with 898 additions and 47 deletions
+25 -1
View File
@@ -91,7 +91,8 @@ EncodingParams::EncodingParams() :
video_threads_(0),
video_is_image_sequence_(false),
audio_enabled_(false),
audio_bit_rate_(0)
audio_bit_rate_(0),
subtitles_enabled_(false)
{
}
@@ -114,6 +115,13 @@ void EncodingParams::EnableAudio(const AudioParams &audio_params, const ExportCo
audio_codec_ = acodec;
}
void EncodingParams::EnableSubtitles(const SubtitleParams::Encoding &encoding, const ExportCodec::Codec &scodec)
{
subtitles_enabled_ = true;
subtitles_encoding_ = encoding;
subtitles_codec_ = scodec;
}
void EncodingParams::set_video_option(const QString &key, const QString &value)
{
video_opts_.insert(key, value);
@@ -219,6 +227,21 @@ const AudioParams &EncodingParams::audio_params() const
return audio_params_;
}
bool EncodingParams::subtitles_enabled() const
{
return subtitles_enabled_;
}
SubtitleParams::Encoding EncodingParams::subtitles_encoding() const
{
return subtitles_encoding_;
}
ExportCodec::Codec EncodingParams::subtitles_codec() const
{
return subtitles_codec_;
}
const rational &EncodingParams::GetExportLength() const
{
return export_length_;
@@ -310,6 +333,7 @@ Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
case ExportFormat::kFormatFLAC:
case ExportFormat::kFormatOgg:
case ExportFormat::kFormatWebM:
case ExportFormat::kFormatSRT:
return kEncoderTypeFFmpeg;
case ExportFormat::kFormatOpenEXR:
case ExportFormat::kFormatPNG:
+12
View File
@@ -31,7 +31,9 @@
#include "codec/frame.h"
#include "codec/samplebuffer.h"
#include "common/timerange.h"
#include "node/block/subtitle/subtitle.h"
#include "render/audioparams.h"
#include "render/subtitleparams.h"
#include "render/videoparams.h"
namespace olive {
@@ -47,6 +49,7 @@ public:
void EnableVideo(const VideoParams& video_params, const ExportCodec::Codec& vcodec);
void EnableAudio(const AudioParams& audio_params, const ExportCodec::Codec &acodec);
void EnableSubtitles(const SubtitleParams::Encoding &encoding, const ExportCodec::Codec &scodec);
void set_video_option(const QString& key, const QString& value);
void set_video_bit_rate(const int64_t& rate);
@@ -91,6 +94,10 @@ public:
audio_bit_rate_ = b;
}
bool subtitles_enabled() const;
SubtitleParams::Encoding subtitles_encoding() const;
ExportCodec::Codec subtitles_codec() const;
const rational& GetExportLength() const;
void SetExportLength(const rational& GetExportLength);
@@ -116,6 +123,10 @@ private:
AudioParams audio_params_;
int64_t audio_bit_rate_;
bool subtitles_enabled_;
ExportCodec::Codec subtitles_codec_;
SubtitleParams::Encoding subtitles_encoding_;
rational export_length_;
};
@@ -174,6 +185,7 @@ public slots:
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) = 0;
virtual bool WriteAudio(olive::SampleBufferPtr audio) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
virtual void Close() = 0;
+30
View File
@@ -60,6 +60,8 @@ QString ExportCodec::GetCodecName(ExportCodec::Codec c)
return tr("Vorbis");
case kCodecVP9:
return tr("VP9");
case kCodecSRT:
return tr("SubRip SRT");
case kCodecCount:
break;
}
@@ -82,6 +84,7 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
case kCodecOpus:
case kCodecFLAC:
case kCodecVP9:
case kCodecSRT:
return false;
case kCodecOpenEXR:
case kCodecPNG:
@@ -94,4 +97,31 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
return false;
}
SubtitleParams::Encoding ExportCodec::GetDefaultSubtitleEncoding(Codec c)
{
switch (c) {
case kCodecSRT:
return SubtitleParams::kWindows1252;
case kCodecDNxHD:
case kCodecH264:
case kCodecH265:
case kCodecProRes:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecPCM:
case kCodecVorbis:
case kCodecOpus:
case kCodecFLAC:
case kCodecVP9:
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
case kCodecCount:
break;
}
return SubtitleParams::kEncodingInvalid;
}
}
+6
View File
@@ -25,6 +25,7 @@
#include <QString>
#include "common/define.h"
#include "render/subtitleparams.h"
namespace olive {
@@ -52,6 +53,9 @@ public:
kCodecVorbis,
kCodecFLAC,
// Subtitle codecs
kCodecSRT,
kCodecCount
};
@@ -59,6 +63,8 @@ public:
static bool IsCodecAStillImage(Codec c);
static SubtitleParams::Encoding GetDefaultSubtitleEncoding(Codec c);
};
}
+39 -8
View File
@@ -53,6 +53,8 @@ QString ExportFormat::GetName(olive::ExportFormat::Format f)
return tr("Ogg");
case kFormatWebM:
return tr("WebM");
case kFormatSRT:
return tr("SubRip SRT");
case kFormatCount:
break;
@@ -90,6 +92,8 @@ QString ExportFormat::GetExtension(ExportFormat::Format f)
return QStringLiteral("ogg");
case kFormatWebM:
return QStringLiteral("webm");
case kFormatSRT:
return QStringLiteral("srt");
case kFormatCount:
break;
}
@@ -121,7 +125,7 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
return {};
case kFormatSRT:
case kFormatCount:
break;
}
@@ -132,22 +136,19 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
{
switch (f) {
// Video/audio formats
case kFormatDNxHD:
return {ExportCodec::kCodecPCM};
case kFormatMatroska:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus};
case kFormatMPEG4:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3};
case kFormatQuickTime:
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 kFormatPNG:
case kFormatTIFF:
return {};
// Audio only formats
case kFormatWAV:
return {ExportCodec::kCodecPCM};
case kFormatAIFF:
@@ -159,9 +160,39 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
case kFormatOgg:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM};
// Video only formats
case kFormatOpenEXR:
case kFormatPNG:
case kFormatTIFF:
case kFormatSRT:
case kFormatCount:
break;
}
return {};
}
QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
{
switch (f) {
case kFormatDNxHD:
case kFormatMPEG4:
case kFormatOpenEXR:
case kFormatQuickTime:
case kFormatPNG:
case kFormatTIFF:
case kFormatWAV:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatOgg:
case kFormatWebM:
case kFormatCount:
break;
case kFormatMatroska:
case kFormatSRT:
return {ExportCodec::kCodecSRT};
}
return {};
+2
View File
@@ -47,6 +47,7 @@ public:
kFormatFLAC,
kFormatOgg,
kFormatWebM,
kFormatSRT,
kFormatCount
};
@@ -55,6 +56,7 @@ public:
static QString GetExtension(Format f);
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
+7 -2
View File
@@ -272,7 +272,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
if (decoder
&& (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|| avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)) {
|| avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO
|| avstream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -367,7 +368,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
desc.AddVideoStream(stream);
} else {
} else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
// Create an audio stream object
uint64_t channel_layout = avstream->codecpar->channel_layout;
@@ -412,6 +413,10 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
stream.set_duration(avstream->duration);
desc.AddAudioStream(stream);
} else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
qDebug() << "Subtitle probing: Stub";
}
}
+95 -4
View File
@@ -27,6 +27,7 @@ extern "C" {
#include <QFile>
#include "common/ffmpegutils.h"
#include "common/timecodefunctions.h"
namespace olive {
@@ -83,6 +84,7 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
case ExportCodec::kCodecFLAC:
case ExportCodec::kCodecOpus:
case ExportCodec::kCodecVorbis:
case ExportCodec::kCodecSRT:
case ExportCodec::kCodecCount:
// These are audio or invalid codecs and therefore have no pixel formats
break;
@@ -178,6 +180,13 @@ bool FFmpegEncoder::Open()
}
}
// Initialize a subtitle stream if it's enabled
if (params().subtitles_enabled()) {
if (!InitializeStream(AVMEDIA_TYPE_SUBTITLE, &subtitle_stream_, &subtitle_codec_ctx_, params().subtitles_codec())) {
return false;
}
}
av_dump_format(fmt_ctx_, 0, filename_c_str, 1);
// Open output file for writing
@@ -347,6 +356,76 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio)
return result;
}
QString GetAssTime(const rational &time)
{
int64_t total_centiseconds = qRound64(time.toDouble() * 100);
int64_t cs = total_centiseconds % 100;
int64_t ss = (total_centiseconds / 100) % 60;
int64_t mm = (total_centiseconds / 6000) % 60;
int64_t hh = total_centiseconds / 360000;
return QStringLiteral("%1:%2:%3.%4").arg(
QString::number(hh),
QStringLiteral("%1").arg(mm, 2, 10, QLatin1Char('0')),
QStringLiteral("%1").arg(ss, 2, 10, QLatin1Char('0')),
QStringLiteral("%1").arg(cs, 2, 10, QLatin1Char('0'))
);
}
bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
{
AVSubtitle subtitle;
memset(&subtitle, 0, sizeof(subtitle));
AVSubtitleRect rect;
memset(&rect, 0, sizeof(rect));
QString ass_line = QStringLiteral("Dialogue: 0,%1,%2,Default,,0,0,0,,%3").arg(
GetAssTime(sub_block->in()),
GetAssTime(sub_block->out()),
sub_block->GetText()
);
QByteArray utf8_sub = sub_block->GetText().toUtf8();
QByteArray utf8_ass = ass_line.toUtf8();
rect.type = SUBTITLE_ASS;
rect.text = utf8_sub.data();
rect.ass = utf8_ass.data();
AVSubtitleRect *rect_array = &rect;
subtitle.num_rects = 1;
subtitle.rects = &rect_array;
subtitle.pts = Timecode::time_to_timestamp(sub_block->in(), av_get_time_base_q(), true);
subtitle.end_display_time = qRound64(sub_block->length().toDouble() * 1000);
QVector<uint8_t> out_buf(1024 * 1024);
int sub_sz = avcodec_encode_subtitle(subtitle_codec_ctx_, out_buf.data(), out_buf.size(), &subtitle);
if (sub_sz < 0) {
return false;
}
AVPacket *pkt = av_packet_alloc();
pkt->stream_index = subtitle_stream_->index;
pkt->data = out_buf.data();
pkt->size = sub_sz;
pkt->pts = subtitle.pts;
pkt->duration = subtitle.end_display_time;
pkt->dts = pkt->pts;
av_packet_rescale_ts(pkt, av_get_time_base_q(), subtitle_stream_->time_base);
av_interleaved_write_frame(fmt_ctx_, pkt);
av_packet_free(&pkt);
return true;
}
/*
void FFmpegEncoder::WriteAudio(AudioParams pcm_info, QIODevice* file)
{
@@ -464,7 +543,7 @@ void FFmpegEncoder::FFmpegError(const QString& context, int error_code)
char err[1024];
av_strerror(error_code, err, 1024);
QString formatted_err = tr("%1: %2 %3").arg(context, formatted_err, QString::number(error_code));
QString formatted_err = tr("%1: %2 %3").arg(context, err, QString::number(error_code));
qDebug() << formatted_err;
SetError(formatted_err);
}
@@ -516,8 +595,8 @@ fail:
bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AVCodecContext** codec_ctx_ptr, const ExportCodec::Codec& codec)
{
if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
SetError(tr("Cannot initialize a stream that is not a video or audio type"));
if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) {
SetError(tr("Cannot initialize a stream that is not a video, audio, or subtitle type"));
return false;
}
@@ -570,6 +649,9 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
case ExportCodec::kCodecFLAC:
codec_id = AV_CODEC_ID_FLAC;
break;
case ExportCodec::kCodecSRT:
codec_id = AV_CODEC_ID_SUBRIP;
break;
case ExportCodec::kCodecCount:
break;
}
@@ -648,7 +730,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
}
}
} else {
} else if (type == AVMEDIA_TYPE_AUDIO) {
// Assume audio stream
codec_ctx->sample_rate = params().audio_params().sample_rate();
@@ -661,6 +743,15 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
codec_ctx->bit_rate = params().audio_bit_rate();
}
} else if (type == AVMEDIA_TYPE_SUBTITLE) {
codec_ctx->time_base = av_get_time_base_q();
QByteArray ass_header = SubtitleParams::GenerateASSHeader().toUtf8();
codec_ctx->subtitle_header = new uint8_t[ass_header.size()];
memcpy(codec_ctx->subtitle_header, ass_header.constData(), ass_header.size());
codec_ctx->subtitle_header_size = ass_header.size();
}
if (!SetupCodecContext(stream, codec_ctx, encoder)) {
+5
View File
@@ -46,6 +46,8 @@ public:
virtual bool WriteAudio(olive::SampleBufferPtr audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
virtual VideoParams::Format GetDesiredPixelFormat() const override
@@ -90,6 +92,9 @@ private:
int audio_frame_offset_;
int audio_write_count_;
AVStream* subtitle_stream_;
AVCodecContext* subtitle_codec_ctx_;
bool open_;
};
+5
View File
@@ -68,6 +68,11 @@ bool OIIOEncoder::WriteAudio(SampleBufferPtr audio)
return false;
}
bool OIIOEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
{
return false;
}
void OIIOEncoder::Close()
{
// Do nothing
+1
View File
@@ -36,6 +36,7 @@ public slots:
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
virtual bool WriteAudio(SampleBufferPtr audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
+6 -4
View File
@@ -18,13 +18,15 @@ add_subdirectory(codec)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
dialog/export/export.h
dialog/export/export.cpp
dialog/export/exportadvancedvideodialog.h
dialog/export/export.h
dialog/export/exportadvancedvideodialog.cpp
dialog/export/exportaudiotab.h
dialog/export/exportadvancedvideodialog.h
dialog/export/exportaudiotab.cpp
dialog/export/exportvideotab.h
dialog/export/exportaudiotab.h
dialog/export/exportsubtitlestab.cpp
dialog/export/exportsubtitlestab.h
dialog/export/exportvideotab.cpp
dialog/export/exportvideotab.h
PARENT_SCOPE
)
+29 -12
View File
@@ -124,29 +124,30 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
QHBoxLayout* av_enabled_layout = new QHBoxLayout();
video_enabled_ = new QCheckBox(tr("Export Video"));
video_enabled_->setChecked(true);
av_enabled_layout->addWidget(video_enabled_);
audio_enabled_ = new QCheckBox(tr("Export Audio"));
audio_enabled_->setChecked(true);
av_enabled_layout->addWidget(audio_enabled_);
subtitles_enabled_ = new QCheckBox(tr("Export Subtitle"));
av_enabled_layout->addWidget(subtitles_enabled_);
preferences_layout->addLayout(av_enabled_layout, row, 0, 1, 4);
row++;
preferences_tabs_ = new QTabWidget();
QScrollArea* video_area = new QScrollArea();
color_manager_ = viewer_node_->project()->color_manager();
video_tab_ = new ExportVideoTab(color_manager_);
video_area->setWidgetResizable(true);
video_area->setWidget(video_tab_);
preferences_tabs_->addTab(video_area, tr("Video"));
QScrollArea* audio_area = new QScrollArea();
AddPreferencesTab(video_tab_, tr("Video"));
audio_tab_ = new ExportAudioTab();
audio_area->setWidgetResizable(true);
audio_area->setWidget(audio_tab_);
preferences_tabs_->addTab(audio_area, tr("Audio"));
AddPreferencesTab(audio_tab_, tr("Audio"));
subtitle_tab_ = new ExportSubtitlesTab();
AddPreferencesTab(subtitle_tab_, tr("Subtitles"));
preferences_layout->addWidget(preferences_tabs_, row, 0, 1, 4);
row++;
@@ -268,9 +269,9 @@ rational ExportDialog::GetSelectedTimebase() const
void ExportDialog::StartExport()
{
if (!video_enabled_->isChecked() && !audio_enabled_->isChecked()) {
if (!video_enabled_->isChecked() && !audio_enabled_->isChecked() && !subtitles_enabled_->isChecked()) {
QtUtils::MessageBox(this, QMessageBox::Critical, tr("Invalid parameters"),
tr("Both video and audio are disabled. There's nothing to export."));
tr("Video, audio, and subtitles are disabled. There's nothing to export."));
return;
}
@@ -392,6 +393,14 @@ void ExportDialog::closeEvent(QCloseEvent *e)
QDialog::closeEvent(e);
}
void ExportDialog::AddPreferencesTab(QWidget *inner_widget, const QString &title)
{
QScrollArea* scroll_area = new QScrollArea();
scroll_area->setWidgetResizable(true);
scroll_area->setWidget(inner_widget);
preferences_tabs_->addTab(scroll_area, title);
}
void ExportDialog::BrowseFilename()
{
ExportFormat::Format f = GetSelectedFormat();
@@ -437,6 +446,10 @@ void ExportDialog::FormatChanged(int index)
bool has_audio_codecs = audio_tab_->SetFormat(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);
}
void ExportDialog::ResolutionChanged()
@@ -549,6 +562,10 @@ ExportParams ExportDialog::GenerateParams() const
params.set_audio_bit_rate(audio_tab_->bit_rate_slider()->GetValue() * 1000);
}
if (subtitles_enabled_->isChecked()) {
params.EnableSubtitles(subtitle_tab_->GetSubtitleEncoding(), subtitle_tab_->GetSubtitleCodec());
}
return params;
}
+5
View File
@@ -30,6 +30,7 @@
#include "codec/exportcodec.h"
#include "codec/exportformat.h"
#include "exportaudiotab.h"
#include "exportsubtitlestab.h"
#include "exportvideotab.h"
#include "task/export/export.h"
#include "widget/viewer/viewer.h"
@@ -50,6 +51,8 @@ protected:
virtual void closeEvent(QCloseEvent *e) override;
private:
void AddPreferencesTab(QWidget *inner_widget, const QString &title);
void LoadPresets();
void SetDefaultFilename();
@@ -75,6 +78,7 @@ private:
QCheckBox* video_enabled_;
QCheckBox* audio_enabled_;
QCheckBox* subtitles_enabled_;
ViewerWidget* preview_viewer_;
QLineEdit* filename_edit_;
@@ -82,6 +86,7 @@ private:
ExportVideoTab* video_tab_;
ExportAudioTab* audio_tab_;
ExportSubtitlesTab* subtitle_tab_;
double video_aspect_ratio_;
+47
View File
@@ -0,0 +1,47 @@
#include "exportsubtitlestab.h"
#include <QGridLayout>
#include <QLabel>
namespace olive {
ExportSubtitlesTab::ExportSubtitlesTab(QWidget *parent) :
QWidget(parent)
{
QVBoxLayout* outer_layout = new QVBoxLayout(this);
QGridLayout* layout = new QGridLayout();
outer_layout->addLayout(layout);
int row = 0;
layout->addWidget(new QLabel(tr("Codec:")), row, 0);
codec_combobox_ = new QComboBox();
layout->addWidget(codec_combobox_, row, 1);
row++;
layout->addWidget(new QLabel(tr("Encoding:")), row, 0);
encoding_combobox_ = new QComboBox();
for (int i=0; i<SubtitleParams::kEncodingCount; i++) {
encoding_combobox_->addItem(SubtitleParams::GetEncodingName(static_cast<SubtitleParams::Encoding>(i)), i);
}
layout->addWidget(encoding_combobox_, row, 1);
outer_layout->addStretch();
}
int ExportSubtitlesTab::SetFormat(ExportFormat::Format format)
{
auto scodecs = ExportFormat::GetSubtitleCodecs(format);
setEnabled(!scodecs.isEmpty());
codec_combobox_->clear();
foreach (ExportCodec::Codec scodec, scodecs) {
codec_combobox_->addItem(ExportCodec::GetCodecName(scodec), scodec);
}
return scodecs.size();
}
}
+57
View File
@@ -0,0 +1,57 @@
/***
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 EXPORTSUBTITLESTAB_H
#define EXPORTSUBTITLESTAB_H
#include <QComboBox>
#include "codec/exportformat.h"
#include "render/subtitleparams.h"
namespace olive {
class ExportSubtitlesTab : public QWidget
{
public:
ExportSubtitlesTab(QWidget *parent = nullptr);
int SetFormat(ExportFormat::Format format);
ExportCodec::Codec GetSubtitleCodec()
{
return static_cast<ExportCodec::Codec>(codec_combobox_->currentData().toInt());
}
SubtitleParams::Encoding GetSubtitleEncoding()
{
return static_cast<SubtitleParams::Encoding>(encoding_combobox_->currentData().toInt());
}
private:
QComboBox *codec_combobox_;
QComboBox *encoding_combobox_;
};
}
#endif // EXPORTSUBTITLESTAB_H
+1 -1
View File
@@ -74,7 +74,7 @@ QWidget* ExportVideoTab::SetupResolutionSection()
int row = 0;
QGroupBox* resolution_group = new QGroupBox();
resolution_group->setTitle(tr("Basic"));
resolution_group->setTitle(tr("General"));
QGridLayout* layout = new QGridLayout(resolution_group);
+1
View File
@@ -16,6 +16,7 @@
add_subdirectory(clip)
add_subdirectory(gap)
add_subdirectory(subtitle)
add_subdirectory(transition)
set(OLIVE_SOURCES
+7 -3
View File
@@ -26,9 +26,11 @@ namespace olive {
const QString ClipBlock::kBufferIn = QStringLiteral("buffer_in");
ClipBlock::ClipBlock()
ClipBlock::ClipBlock(bool create_buffer_in)
{
AddInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
if (create_buffer_in) {
AddInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
}
}
Node *ClipBlock::copy() const
@@ -108,7 +110,9 @@ void ClipBlock::Retranslate()
{
super::Retranslate();
SetInputName(kBufferIn, tr("Buffer"));
if (HasInputWithID(kBufferIn)) {
SetInputName(kBufferIn, tr("Buffer"));
}
}
void ClipBlock::Hash(const QString &out, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const
+1 -1
View File
@@ -32,7 +32,7 @@ class ClipBlock : public Block
{
Q_OBJECT
public:
ClipBlock();
ClipBlock(bool create_buffer_in = true);
NODE_DEFAULT_DESTRUCTOR(ClipBlock)
+22
View File
@@ -0,0 +1,22 @@
# 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/block/subtitle/subtitle.cpp
node/block/subtitle/subtitle.h
PARENT_SCOPE
)
+62
View File
@@ -0,0 +1,62 @@
/***
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 "subtitle.h"
namespace olive {
#define super ClipBlock
const QString SubtitleBlock::kTextIn = QStringLiteral("text_in");
SubtitleBlock::SubtitleBlock() :
super(false)
{
AddInput(kTextIn, NodeValue::kText, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
}
Node *SubtitleBlock::copy() const
{
return new SubtitleBlock();
}
QString SubtitleBlock::Name() const
{
return tr("Subtitle");
}
QString SubtitleBlock::id() const
{
return QStringLiteral("org.olivevideoeditor.Olive.subtitle");
}
QString SubtitleBlock::Description() const
{
return tr("A time-based node representing a single subtitle element for a certain period of time.");
}
void SubtitleBlock::Retranslate()
{
super::Retranslate();
SetInputName(kTextIn, tr("Text"));
}
}
+60
View File
@@ -0,0 +1,60 @@
/***
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 SUBTITLEBLOCK_H
#define SUBTITLEBLOCK_H
#include "node/block/clip/clip.h"
namespace olive {
class SubtitleBlock : public ClipBlock
{
Q_OBJECT
public:
SubtitleBlock();
NODE_DEFAULT_DESTRUCTOR(SubtitleBlock)
virtual Node* copy() const override;
virtual QString Name() const override;
virtual QString id() const override;
virtual QString Description() const override;
virtual void Retranslate() override;
static const QString kTextIn;
QString GetText() const
{
return GetStandardValue(kTextIn).toString();
}
void SetText(const QString &text)
{
SetStandardValue(kTextIn, text);
}
};
}
#endif // SUBTITLEBLOCK_H
+3
View File
@@ -26,6 +26,7 @@
#include "audio/volume/volume.h"
#include "block/clip/clip.h"
#include "block/gap/gap.h"
#include "block/subtitle/subtitle.h"
#include "block/transition/crossdissolve/crossdissolvetransition.h"
#include "block/transition/diptocolor/diptocolortransition.h"
#include "distort/crop/cropdistortnode.h"
@@ -236,6 +237,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new ValueNode();
case kTimeRemapNode:
return new TimeRemapNode();
case kSubtitleBlock:
return new SubtitleBlock();
case kInternalNodeCount:
break;
+1
View File
@@ -58,6 +58,7 @@ public:
kProjectSequence,
kValueNode,
kTimeRemapNode,
kSubtitleBlock,
// Count value
kInternalNodeCount
+2
View File
@@ -53,6 +53,8 @@ set(OLIVE_SOURCES
render/renderprocessor.h
render/shadercode.h
render/stillimagecache.h
render/subtitleparams.cpp
render/subtitleparams.h
render/texture.cpp
render/texture.h
render/videoparams.cpp
+188
View File
@@ -0,0 +1,188 @@
/***
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 "subtitleparams.h"
#include <QCoreApplication>
namespace olive {
QString SubtitleParams::GetEncodingName(Encoding encoding)
{
switch (encoding) {
case kISO8859_1:
return QCoreApplication::translate("SubtitleParams", "ASCII/ISO 8859-1");
case kWindows1252:
return QCoreApplication::translate("SubtitleParams", "Windows-1252");
case kUTF8:
return QCoreApplication::translate("SubtitleParams", "UTF-8");
case kUTF8WithBOM:
return QCoreApplication::translate("SubtitleParams", "UTF-8 with BOM");
case kUTF16LE:
return QCoreApplication::translate("SubtitleParams", "UTF-16LE");
case kUTF16BE:
return QCoreApplication::translate("SubtitleParams", "UTF-16BE");
case kEncodingInvalid:
case kEncodingCount:
break;
}
return QCoreApplication::translate("SubtitleParams", "Unknown");
}
bool SubtitleParams::EncodingHasUnicodeBOM(Encoding encoding)
{
switch (encoding) {
case kUTF8WithBOM:
case kUTF16LE:
case kUTF16BE:
return true;
case kISO8859_1:
case kWindows1252:
case kUTF8:
case kEncodingInvalid:
case kEncodingCount:
break;
}
return false;
}
QByteArray SubtitleParams::GetUnicodeBOM(Encoding encoding)
{
QByteArray arr;
if (encoding == kUTF8WithBOM) {
arr.resize(3);
arr[0] = 0xEF;
arr[1] = 0xBB;
arr[2] = 0xBF;
} else if (encoding == kUTF16LE) {
arr.resize(2);
arr[0] = 0xFF;
arr[1] = 0xFE;
} else if (encoding == kUTF16BE) {
arr.resize(2);
arr[0] = 0xFE;
arr[1] = 0xFF;
}
return arr;
}
const char *SubtitleParams::GetQTextStreamCodec(Encoding encoding)
{
switch (encoding) {
case SubtitleParams::kISO8859_1:
return "ISO 8859-1";
case SubtitleParams::kWindows1252:
return "Windows-1252";
case SubtitleParams::kUTF8:
case SubtitleParams::kUTF8WithBOM:
return "UTF-8";
case SubtitleParams::kUTF16LE:
return "UTF-16LE";
case SubtitleParams::kUTF16BE:
return "UTF-16BE";
case SubtitleParams::kEncodingInvalid:
case SubtitleParams::kEncodingCount:
break;
}
return nullptr;
}
QString SubtitleParams::GenerateASSHeader()
{
// NOTE: We'll probably implement more customization as we support ASS better. Right now, we only
// natively support SRT and only make this header because FFmpeg requires it.
static const int kAssDefaultPlayResX = 384;
static const int kAssDefaultPlayResY = 288;
static const QString kAssDefaultFont = QStringLiteral("Arial");
static const int kAssDefaultFontSize = 16;
static const int kAssDefaultPrimaryColor = 0xFFFFFF; // White
static const int kAssDefaultSecondaryColor = 0xFFFFFF; // White
static const int kAssDefaultOutlineColor = 0x000000; // Black
static const int kAssDefaultBackColor = 0x000000; // Black
static const int kAssBold = 0;
static const int kAssItalic = 0;
static const int kAssUnderline = 0;
static const int kAssStrike = 0;
static const int kAssBorderStyle = 1;
static const int kAssAlignment = 2;
static const QString kFormatHeader = QStringLiteral(
"[Script Info]\r\n"
"; Script generated by %1 %2\r\n"
"ScriptType: v4.00+\r\n"
"PlayResX: %3\r\n"
"PlayResY: %4\r\n"
"ScaledBorderAndShadow: yes\r\n"
"\r\n"
/* ASSv4 header */
"[V4+ Styles]\r\n"
"Format: Name, "
"Fontname, Fontsize, "
"PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
"Bold, Italic, Underline, StrikeOut, "
"ScaleX, ScaleY, "
"Spacing, Angle, "
"BorderStyle, Outline, Shadow, "
"Alignment, MarginL, MarginR, MarginV, "
"Encoding\r\n"
"Style: "
"Default," /* Name */
"%5,%6," /* Font{name,size} */
"&H%7,&H%8,&H%9,&H%10," /* {Primary,Secondary,Outline,Back}Colour */
"%11,%12,%13,%14," /* Bold, Italic, Underline, StrikeOut */
"100,100," /* Scale{X,Y} */
"0,0," /* Spacing, Angle */
"%15,1,0," /* BorderStyle, Outline, Shadow */
"%16,10,10,10," /* Alignment, Margin[LRV] */
"0\r\n" /* Encoding */
"\r\n"
"[Events]\r\n"
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n"
);
return kFormatHeader.arg(QCoreApplication::applicationName(),
QCoreApplication::applicationVersion(),
QString::number(kAssDefaultPlayResX),
QString::number(kAssDefaultPlayResY),
kAssDefaultFont,
QString::number(kAssDefaultFontSize),
QString::number(kAssDefaultPrimaryColor, 16),
QString::number(kAssDefaultSecondaryColor, 16),
QString::number(kAssDefaultOutlineColor, 16),
QString::number(kAssDefaultBackColor, 16),
QString::number(kAssBold),
QString::number(kAssItalic),
QString::number(kAssUnderline),
QString::number(kAssStrike),
QString::number(kAssBorderStyle),
QString::number(kAssAlignment)
);
}
}
+55
View File
@@ -0,0 +1,55 @@
/***
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 SUBTITLEPARAMS_H
#define SUBTITLEPARAMS_H
#include <QString>
namespace olive {
class SubtitleParams {
public:
enum Encoding {
kEncodingInvalid = -1,
kISO8859_1,
kWindows1252,
kUTF8,
kUTF8WithBOM,
kUTF16LE,
kUTF16BE,
kEncodingCount
};
static QString GetEncodingName(Encoding encoding);
static bool EncodingHasUnicodeBOM(Encoding encoding);
static QByteArray GetUnicodeBOM(Encoding encoding);
static const char *GetQTextStreamCodec(Encoding encoding);
static QString GenerateASSHeader();
};
}
#endif // SUBTITLEPARAMS_H
+12 -2
View File
@@ -56,7 +56,7 @@ bool ExportTask::Run()
}
if (!encoder_->Open()) {
SetError(tr("Failed to open file"));
SetError(tr("Failed to open file: %1").arg(encoder_->GetError()));
encoder_->deleteLater();
return false;
}
@@ -102,6 +102,7 @@ bool ExportTask::Run()
// Start render process
TimeRangeList video_range, audio_range;
TimeRange subtitle_range;
if (params_.video_enabled()) {
video_range = {range};
@@ -111,7 +112,11 @@ bool ExportTask::Run()
audio_range = {range};
}
Render(color_manager_, video_range, audio_range, RenderMode::kOnline, nullptr,
if (params_.subtitles_enabled()) {
subtitle_range = range;
}
Render(color_manager_, video_range, audio_range, subtitle_range, RenderMode::kOnline, nullptr,
video_force_size, video_force_matrix, encoder_->GetDesiredPixelFormat(),
color_processor_);
@@ -191,6 +196,11 @@ void ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples
}
}
void ExportTask::EncodeSubtitle(const SubtitleBlock *sub)
{
encoder_->WriteSubtitle(sub);
}
void ExportTask::WriteAudioLoop(const TimeRange& time, SampleBufferPtr samples)
{
encoder_->WriteAudio(samples);
+2
View File
@@ -42,6 +42,8 @@ protected:
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples, qint64 job_time) override;
virtual void EncodeSubtitle(const SubtitleBlock *sub) override;
virtual bool TwoStepFrameRendering() const override
{
return false;
+1
View File
@@ -67,6 +67,7 @@ bool PreCacheTask::Run()
Render(project_->color_manager(),
video_range,
TimeRangeList(),
TimeRange(),
RenderMode::kOnline,
viewer()->video_frame_cache());
+51 -1
View File
@@ -21,6 +21,7 @@
#include "render.h"
#include "common/timecodefunctions.h"
#include "node/project/sequence/sequence.h"
#include "render/rendermanager.h"
namespace olive {
@@ -40,7 +41,7 @@ RenderTask::~RenderTask()
bool RenderTask::Render(ColorManager* manager,
const TimeRangeList& video_range,
const TimeRangeList &audio_range,
const TimeRangeList &audio_range, const TimeRange &subtitle_range,
RenderMode::Mode mode,
FrameHashCache* cache, const QSize &force_size,
const QMatrix4x4 &force_matrix, VideoParams::Format force_format,
@@ -129,6 +130,50 @@ bool RenderTask::Render(ColorManager* manager,
mode, cache, force_size, force_matrix, force_format, force_color_output);
}
// Subtitle loop, loops over all blocks in sequence on all tracks
if (!subtitle_range.length().isNull()) {
Sequence *sequence = dynamic_cast<Sequence*>(viewer_);
if (sequence) {
TrackList *list = sequence->track_list(Track::kSubtitle);
QVector<int> block_indexes(list->GetTrackCount(), 0);
QVector<int> tracks_to_push;
do {
tracks_to_push.clear();
for (int i=0; i<block_indexes.size(); i++) {
Track *this_track = list->GetTrackAt(i);
int &this_block_index = block_indexes[i];
if (this_block_index >= this_track->Blocks().size()) {
continue;
}
Block *this_block = this_track->Blocks().at(this_block_index);
Track *compare_track = tracks_to_push.isEmpty() ? nullptr : list->GetTrackAt(tracks_to_push.first());
const int &compare_block_index = tracks_to_push.isEmpty() ? -1 : block_indexes.at(tracks_to_push.first());
Block *compare_block = compare_track ? compare_track->Blocks().at(compare_block_index) : nullptr;
if (!compare_track || compare_block->in() >= this_block->in()) {
if (compare_track && compare_block->in() != this_block->in()) {
tracks_to_push.clear();
}
tracks_to_push.append(i);
}
}
for (int i=0; i<tracks_to_push.size(); i++) {
Track *this_track = list->GetTrackAt(tracks_to_push.at(i));
Block *this_block = this_track->Blocks().at(block_indexes.at(tracks_to_push.at(i)));
if (const SubtitleBlock *sub = dynamic_cast<const SubtitleBlock*>(this_block)) {
EncodeSubtitle(sub);
}
block_indexes[tracks_to_push.at(i)]++;
}
} while (!tracks_to_push.isEmpty());
}
}
finished_watcher_mutex_.lock();
while (!IsCancelled()) {
@@ -238,6 +283,11 @@ void RenderTask::DownloadFrame(QThread *thread, FramePtr frame, const QByteArray
hash));
}
void RenderTask::EncodeSubtitle(const SubtitleBlock *subtitle)
{
Q_UNUSED(subtitle)
}
void RenderTask::PrepareWatcher(RenderTicketWatcher *watcher, QThread *thread)
{
watcher->moveToThread(thread);
+5 -1
View File
@@ -23,6 +23,7 @@
#include <QtConcurrent/QtConcurrent>
#include "node/block/subtitle/subtitle.h"
#include "node/color/colormanager/colormanager.h"
#include "node/output/viewer/viewer.h"
#include "task/task.h"
@@ -41,7 +42,8 @@ public:
protected:
bool Render(ColorManager *manager, const TimeRangeList &video_range,
const TimeRangeList &audio_range, RenderMode::Mode mode,
const TimeRangeList &audio_range, const TimeRange &subtitle_range,
RenderMode::Mode mode,
FrameHashCache *cache, const QSize& force_size = QSize(0, 0),
const QMatrix4x4& force_matrix = QMatrix4x4(),
VideoParams::Format force_format = VideoParams::kFormatInvalid,
@@ -53,6 +55,8 @@ protected:
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples, qint64 job_time) = 0;
virtual void EncodeSubtitle(const SubtitleBlock *subtitle);
ViewerOutput* viewer() const
{
return viewer_;
+5
View File
@@ -96,6 +96,9 @@ public:
/// An audio clip with a sine connected to it
kAddableTone,
/// A subtitle clip
kAddableSubtitle,
kAddableCount
};
@@ -112,6 +115,8 @@ public:
return QCoreApplication::translate("Tool", "Title");
case kAddableTone:
return QCoreApplication::translate("Tool", "Tone");
case kAddableSubtitle:
return QCoreApplication::translate("Tool", "Subtitle");
case kAddableCount:
break;
}
+9 -4
View File
@@ -1230,11 +1230,11 @@ public:
if (timeline_->type() == Track::kVideo) {
relevant_input = ViewerOutput::kTextureInput;
} else {
} else if (timeline_->type() == Track::kAudio) {
relevant_input = ViewerOutput::kSamplesInput;
}
if (!timeline_->parent()->IsInputConnected(relevant_input)) {
if (!relevant_input.isEmpty() && !timeline_->parent()->IsInputConnected(relevant_input)) {
direct_ = NodeInput(timeline_->parent(), relevant_input);
Node::ConnectEdge(track_, direct_);
@@ -1289,15 +1289,20 @@ private:
merge_ = new MergeNode();
base_ = NodeInput(merge_, MergeNode::kBaseIn);
blend_ = NodeInput(merge_, MergeNode::kBlendIn);
} else {
} else if (timeline_->type() == Track::kAudio) {
merge_ = new MathNode();
base_ = NodeInput(merge_, MathNode::kParamAIn);
blend_ = NodeInput(merge_, MathNode::kParamBIn);
} else {
merge_ = nullptr;
}
merge_->setParent(&memory_manager_);
} else {
merge_ = nullptr;
}
if (merge_) {
merge_->setParent(&memory_manager_);
}
}
TrackList* timeline_;
+26 -2
View File
@@ -77,7 +77,6 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
// Create list of TimelineViews - these MUST correspond to the ViewType enum
view_splitter_ = new QSplitter(Qt::Vertical);
view_splitter_->setChildrenCollapsible(false);
vert_layout->addWidget(view_splitter_);
// Video view
@@ -86,6 +85,9 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
// Audio view
views_.append(new TimelineAndTrackView(Qt::AlignTop));
// Subtitle view
views_.append(new TimelineAndTrackView(Qt::AlignTop));
// Create tools
tools_.resize(olive::Tool::kCount);
tools_.fill(nullptr);
@@ -155,7 +157,17 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
}
// Split viewer 50/50
view_splitter_->setSizes({INT_MAX, INT_MAX});
QList<int> view_sizes;
view_sizes.reserve(views_.size());
view_sizes.append(height()/2); // Video
view_sizes.append(height()/2); // Audio
view_sizes.append(0); // Subtitle (hidden by default)
view_splitter_->setSizes(view_sizes);
// Video and audio are not collapsible, subtitle is
view_splitter_->setCollapsible(Track::kVideo, false);
view_splitter_->setCollapsible(Track::kAudio, false);
view_splitter_->setCollapsible(Track::kSubtitle, true);
// FIXME: Magic number
SetScale(90.0);
@@ -786,11 +798,23 @@ void TimelineWidget::ViewMouseMoved(TimelineViewMouseEvent *event)
if (hover_tool) {
hover_tool->HoverMove(event);
// Special cast for subtitle adding - ensure section is visible
if (dynamic_cast<AddTool*>(hover_tool)
&& Core::instance()->GetSelectedAddableObject() == Tool::kAddableSubtitle) {
QList<int> sz = view_splitter_->sizes();
int &subtitle_section_height = sz[Track::kSubtitle];
if (subtitle_section_height == 0) {
subtitle_section_height = height() / Track::kCount;
view_splitter_->setSizes(sz);
}
}
}
}
}
}
void TimelineWidget::ViewMouseReleased(TimelineViewMouseEvent *event)
{
if (active_tool_) {
+13 -1
View File
@@ -20,6 +20,7 @@
#include "add.h"
#include "core.h"
#include "node/block/subtitle/subtitle.h"
#include "node/factory.h"
#include "node/generator/solid/solid.h"
#include "node/generator/text/text.h"
@@ -54,6 +55,9 @@ void AddTool::MousePress(TimelineViewMouseEvent *event)
case olive::Tool::kAddableTone:
add_type = Track::kAudio;
break;
case olive::Tool::kAddableSubtitle:
add_type = Track::kSubtitle;
break;
case olive::Tool::kAddableEmpty:
// Leave as "none", which means this block can be placed on any track
break;
@@ -93,7 +97,12 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event)
if (!ghost_->GetAdjustedLength().isNull()) {
MultiUndoCommand* command = new MultiUndoCommand();
ClipBlock* clip = new ClipBlock();
ClipBlock* clip;
if (Core::instance()->GetSelectedAddableObject() == olive::Tool::kAddableSubtitle) {
clip = new SubtitleBlock();
} else {
clip = new ClipBlock();
}
clip->set_length_and_media_out(ghost_->GetAdjustedLength());
clip->SetLabel(olive::Tool::GetAddableObjectName(Core::instance()->GetSelectedAddableObject()));
@@ -140,6 +149,9 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event)
// Not implemented yet
qWarning() << "Unimplemented add object:" << Core::instance()->GetSelectedAddableObject();
break;
case olive::Tool::kAddableSubtitle:
// The block itself is the node we want
break;
case olive::Tool::kAddableCount:
// Invalid value, do nothing
break;