media: implement importing SRT files

This commit is contained in:
itsmattkc
2022-05-11 13:48:59 -07:00
parent f17c119c29
commit 83e7707025
19 changed files with 517 additions and 76 deletions
+5 -1
View File
@@ -43,7 +43,11 @@ SubtitleBlock::SubtitleBlock()
QString SubtitleBlock::Name() const
{
return tr("Subtitle");
if (GetText().isEmpty()) {
return tr("Subtitle");
} else {
return GetText();
}
}
QString SubtitleBlock::id() const
+39
View File
@@ -28,6 +28,7 @@ namespace olive {
const QString ViewerOutput::kVideoParamsInput = QStringLiteral("video_param_in");
const QString ViewerOutput::kAudioParamsInput = QStringLiteral("audio_param_in");
const QString ViewerOutput::kSubtitleParamsInput = QStringLiteral("subtitle_param_in");
const QString ViewerOutput::kTextureInput = QStringLiteral("tex_in");
const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in");
const QString ViewerOutput::kVideoAutoCacheInput = QStringLiteral("video_autocache_in");
@@ -48,6 +49,8 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
AddInput(kSubtitleParamsInput, NodeValue::kSubtitleParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
if (create_buffer_inputs) {
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
@@ -100,6 +103,7 @@ QString ViewerOutput::duration() const
// Get first enabled streams
VideoParams video = GetFirstEnabledVideoStream();
AudioParams audio = GetFirstEnabledAudioStream();
SubtitleParams sub = GetFirstEnabledSubtitleStream();
if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) {
// Prioritize video
@@ -113,6 +117,8 @@ QString ViewerOutput::duration() const
}
using_timebase = audio.sample_rate_as_time_base();
} else if (sub.is_valid()) {
using_timebase = OLIVE_CONFIG("DefaultSequenceFrameRate").value<rational>();
}
if (using_timebase.isNull()) {
@@ -151,6 +157,11 @@ bool ViewerOutput::HasEnabledAudioStreams() const
return GetFirstEnabledAudioStream().is_valid();
}
bool ViewerOutput::HasEnabledSubtitleStreams() const
{
return GetFirstEnabledSubtitleStream().is_valid();
}
VideoParams ViewerOutput::GetFirstEnabledVideoStream() const
{
int sz = GetVideoStreamCount();
@@ -181,6 +192,21 @@ AudioParams ViewerOutput::GetFirstEnabledAudioStream() const
return AudioParams();
}
SubtitleParams ViewerOutput::GetFirstEnabledSubtitleStream() const
{
int sz = GetSubtitleStreamCount();
for (int i=0; i<sz; i++) {
SubtitleParams sp = GetSubtitleParams(i);
if (sp.enabled()) {
return sp;
}
}
return SubtitleParams();
}
void ViewerOutput::set_default_parameters()
{
int width = OLIVE_CONFIG("DefaultSequenceWidth").toInt();
@@ -276,6 +302,16 @@ QVector<Track::Reference> ViewerOutput::GetEnabledStreamsAsReferences() const
}
}
{
int sp_sz = GetSubtitleStreamCount();
for (int i=0; i<sp_sz; i++) {
if (GetSubtitleParams(i).enabled()) {
refs.append(Track::Reference(Track::kSubtitle, i));
}
}
}
return refs;
}
@@ -285,6 +321,7 @@ void ViewerOutput::Retranslate()
SetInputName(kVideoParamsInput, tr("Video Parameters"));
SetInputName(kAudioParamsInput, tr("Audio Parameters"));
SetInputName(kSubtitleParamsInput, tr("Subtitle Parameters"));
if (HasInputWithID(kTextureInput)) {
SetInputName(kTextureInput, tr("Texture"));
@@ -520,6 +557,8 @@ int ViewerOutput::AddStream(Track::Type type, const QVariant& value)
id = kVideoParamsInput;
} else if (type == Track::kAudio) {
id = kAudioParamsInput;
} else if (type == Track::kSubtitle) {
id = kSubtitleParamsInput;
} else {
return -1;
}
+26 -2
View File
@@ -27,7 +27,7 @@
#include "render/audioparams.h"
#include "render/audioplaybackcache.h"
#include "render/framehashcache.h"
#include "render/videoparams.h"
#include "render/subtitleparams.h"
#include "render/videoparams.h"
#include "timeline/timelinepoints.h"
@@ -88,6 +88,17 @@ public:
}
}
SubtitleParams GetSubtitleParams(int index = 0) const
{
// This check isn't strictly necessary (GetStandardValue will return a null VideoParams anyway),
// but it does suppress a warning message that we don't need
if (index < InputArraySize(kSubtitleParamsInput)) {
return GetStandardValue(kSubtitleParamsInput, index).value<SubtitleParams>();
} else {
return SubtitleParams();
}
}
void SetVideoParams(const VideoParams &video, int index = 0)
{
SetStandardValue(kVideoParamsInput, QVariant::fromValue(video), index);
@@ -98,6 +109,11 @@ public:
SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio), index);
}
void SetSubtitleParams(const SubtitleParams &subs, int index = 0)
{
SetStandardValue(kSubtitleParamsInput, QVariant::fromValue(subs), index);
}
int GetVideoStreamCount() const
{
return InputArraySize(kVideoParamsInput);
@@ -108,16 +124,23 @@ public:
return InputArraySize(kAudioParamsInput);
}
int GetSubtitleStreamCount() const
{
return InputArraySize(kSubtitleParamsInput);
}
int GetTotalStreamCount() const
{
return GetVideoStreamCount() + GetAudioStreamCount();
return GetVideoStreamCount() + GetAudioStreamCount() + GetSubtitleStreamCount();
}
bool HasEnabledVideoStreams() const;
bool HasEnabledAudioStreams() const;
bool HasEnabledSubtitleStreams() const;
VideoParams GetFirstEnabledVideoStream() const;
AudioParams GetFirstEnabledAudioStream() const;
SubtitleParams GetFirstEnabledSubtitleStream() const;
const rational &GetLength() const { return last_length_; }
const rational &GetVideoLength() const { return video_length_; }
@@ -191,6 +214,7 @@ public:
static const QString kVideoParamsInput;
static const QString kAudioParamsInput;
static const QString kSubtitleParamsInput;
static const QString kTextureInput;
static const QString kSamplesInput;
+45 -4
View File
@@ -121,6 +121,10 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
AddStream(Track::kAudio, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
}
for (int i=0; i<footage_info.GetSubtitleStreams().size(); i++) {
AddStream(Track::kSubtitle, QVariant::fromValue(footage_info.GetSubtitleStreams().at(i)));
}
SetValid();
}
@@ -146,6 +150,12 @@ rational Footage::VerifyLengthInternal(Track::Type type) const
if (first_stream.is_valid()) {
return Timecode::timestamp_to_time(first_stream.duration(), first_stream.time_base());
}
} else if (type == Track::kSubtitle) {
SubtitleParams first_stream = GetFirstEnabledSubtitleStream();
if (first_stream.is_valid()) {
return first_stream.duration();
}
}
return 0;
@@ -165,6 +175,7 @@ void Footage::Clear()
// Clear all dynamically created inputs
InputArrayResize(kVideoParamsInput, 0);
InputArrayResize(kAudioParamsInput, 0);
InputArrayResize(kSubtitleParamsInput, 0);
// Clear decoder link
decoder_.clear();
@@ -205,13 +216,19 @@ void Footage::set_timestamp(const qint64 &t)
int Footage::GetStreamIndex(Track::Type type, int index) const
{
if (type == Track::kVideo) {
switch (type) {
case Track::kVideo:
return GetVideoParams(index).stream_index();
} else if (type == Track::kAudio) {
case Track::kAudio:
return GetAudioParams(index).stream_index();
} else {
return -1;
case Track::kSubtitle:
return GetSubtitleParams(index).stream_index();
case Track::kNone:
case Track::kCount:
break;
}
return -1;
}
Track::Reference Footage::GetReferenceFromRealIndex(int real_index) const
@@ -229,6 +246,12 @@ Track::Reference Footage::GetReferenceFromRealIndex(int real_index) const
}
}
for (int i=0; i<GetSubtitleStreamCount(); i++) {
if (GetSubtitleParams(i).stream_index() == real_index) {
return Track::Reference(Track::kSubtitle, i);
}
}
return Track::Reference();
}
@@ -249,6 +272,8 @@ QIcon Footage::icon() const
return icon::Audio;
} else if (s.is_valid() && s.video_type() == VideoParams::kVideoTypeStill) {
return icon::Image;
} else if (HasEnabledSubtitleStreams()) {
return icon::TextSmallCaps; // FIXME: Procure icon
}
}
@@ -275,6 +300,12 @@ QString Footage::DescribeAudioStream(const AudioParams &params)
QString::number(params.sample_rate()));
}
QString Footage::DescribeSubtitleStream(const SubtitleParams &params)
{
return tr("%1: Subtitle")
.arg(QString::number(params.stream_index()));
}
void Footage::Hash(QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
{
super::Hash(hash, globals, video_params);
@@ -475,6 +506,16 @@ void Footage::UpdateTooltip()
}
}
int sp_sz = GetSubtitleStreamCount();
for (int i=0; i<sp_sz; i++) {
SubtitleParams p = GetSubtitleParams(i);
if (p.enabled()) {
tip.append("\n");
tip.append(DescribeSubtitleStream(p));
}
}
SetToolTip(tip);
} else {
SetToolTip(tr("Invalid"));
+1
View File
@@ -173,6 +173,7 @@ public:
static QString DescribeVideoStream(const VideoParams& params);
static QString DescribeAudioStream(const AudioParams& params);
static QString DescribeSubtitleStream(const SubtitleParams& params);
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
@@ -67,6 +67,10 @@ bool FootageDescription::Load(const QString &filename)
AudioParams ap;
ap.Load(&reader);
AddAudioStream(ap);
} else if (reader.name() == QStringLiteral("subtitle")) {
SubtitleParams sp;
sp.Load(&reader);
AddSubtitleStream(sp);
} else {
reader.skipCurrentElement();
}
@@ -124,6 +128,12 @@ bool FootageDescription::Save(const QString &filename) const
writer.writeEndElement(); // audio
}
foreach (const SubtitleParams& sp, subtitle_streams_) {
writer.writeStartElement(QStringLiteral("subtitle"));
sp.Save(&writer);
writer.writeEndElement(); // audio
}
writer.writeEndElement(); // streams
writer.writeEndElement(); // streamcache
+31 -3
View File
@@ -23,6 +23,7 @@
#include "node/output/track/track.h"
#include "render/audioparams.h"
#include "render/subtitleparams.h"
#include "render/videoparams.h"
namespace olive {
@@ -37,7 +38,7 @@ public:
bool IsValid() const
{
return !decoder_.isEmpty() && (!video_streams_.isEmpty() || !audio_streams_.isEmpty());
return !decoder_.isEmpty() && (!video_streams_.isEmpty() || !audio_streams_.isEmpty() || !subtitle_streams_.isEmpty());
}
const QString& decoder() const
@@ -59,12 +60,21 @@ public:
audio_streams_.append(audio_params);
}
void AddSubtitleStream(const SubtitleParams& sub_params)
{
Q_ASSERT(!HasStreamIndex(sub_params.stream_index()));
subtitle_streams_.append(sub_params);
}
Track::Type GetTypeOfStream(int index)
{
if (StreamIsVideo(index)) {
return Track::kVideo;
} else if (StreamIsAudio(index)) {
return Track::kAudio;
} else if (StreamIsSubtitle(index)) {
return Track::kSubtitle;
} else {
return Track::kNone;
}
@@ -92,9 +102,20 @@ public:
return false;
}
bool StreamIsSubtitle(int index) const
{
foreach (const SubtitleParams& sp, subtitle_streams_) {
if (sp.stream_index() == index) {
return true;
}
}
return false;
}
bool HasStreamIndex(int index) const
{
return StreamIsVideo(index) || StreamIsAudio(index);
return StreamIsVideo(index) || StreamIsAudio(index) || StreamIsSubtitle(index);
}
bool Load(const QString& filename);
@@ -111,8 +132,13 @@ public:
return audio_streams_;
}
const QVector<SubtitleParams>& GetSubtitleStreams() const
{
return subtitle_streams_;
}
private:
static constexpr unsigned kFootageMetaVersion = 1;
static constexpr unsigned kFootageMetaVersion = 2;
QString decoder_;
@@ -120,6 +146,8 @@ private:
QVector<AudioParams> audio_streams_;
QVector<SubtitleParams> subtitle_streams_;
};
}
+6
View File
@@ -29,6 +29,7 @@
#include "common/bezier.h"
#include "common/tohex.h"
#include "render/audioparams.h"
#include "render/subtitleparams.h"
#include "render/videoparams.h"
#include "render/color.h"
@@ -130,6 +131,7 @@ QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value)
return value.value<AudioParams>().toBytes();
// These types have no persistent input
case kSubtitleParams:
case kNone:
case kTexture:
case kSamples:
@@ -345,6 +347,8 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
return QCoreApplication::translate("NodeValue", "Video Parameters");
case kAudioParams:
return QCoreApplication::translate("NodeValue", "Audio Parameters");
case kSubtitleParams:
return QCoreApplication::translate("NodeValue", "Subtitle Parameters");
case kDataTypeCount:
break;
@@ -394,6 +398,8 @@ QString NodeValue::GetDataTypeName(Type type)
return QStringLiteral("vparam");
case kAudioParams:
return QStringLiteral("aparam");
case kSubtitleParams:
return QStringLiteral("sparam");
case kDataTypeCount:
break;
}
+7
View File
@@ -178,6 +178,13 @@ public:
*/
kAudioParams,
/**
* Subtitle Parameters type
*
* Resolves to `SubtitleParams`
*/
kSubtitleParams,
/**
* End of list
*/