various: allow setting footage aspect ratios
We had support for detecting aspect ratios and respecting them in the render, but this allows people to not only see the aspect ratio in use, but also override it with their own.
This commit is contained in:
@@ -114,8 +114,6 @@ bool FFmpegDecoder::Open()
|
||||
native_pix_fmt_ = GetNativePixelFormat(ideal_pix_fmt_);
|
||||
|
||||
Q_ASSERT(native_pix_fmt_ != PixelFormat::PIX_FMT_INVALID);
|
||||
|
||||
aspect_ratio_ = our_instance->sample_aspect_ratio();
|
||||
}
|
||||
|
||||
time_base_ = our_instance->stream()->time_base;
|
||||
@@ -465,6 +463,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
|
||||
|
||||
bool image_is_still = false;
|
||||
ImageStream::Interlacing interlacing = ImageStream::kInterlaceNone;
|
||||
rational pixel_aspect_ratio;
|
||||
|
||||
{
|
||||
// Read at least two frames to get more information about this video stream
|
||||
@@ -484,6 +483,10 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
|
||||
interlacing = ImageStream::kInterlacedBottomFirst;
|
||||
}
|
||||
}
|
||||
|
||||
pixel_aspect_ratio = av_guess_sample_aspect_ratio(instance.fmt_ctx(),
|
||||
instance.stream(),
|
||||
frame);
|
||||
}
|
||||
|
||||
// Read second frame
|
||||
@@ -527,6 +530,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
|
||||
image_stream->set_height(avstream->codecpar->height);
|
||||
image_stream->set_format(GetNativePixelFormat(FFmpegCommon::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(avstream->codecpar->format))));
|
||||
image_stream->set_interlacing(interlacing);
|
||||
image_stream->set_pixel_aspect_ratio(pixel_aspect_ratio);
|
||||
|
||||
str = image_stream;
|
||||
|
||||
@@ -825,7 +829,7 @@ FramePtr FFmpegDecoder::BuffersToNativeFrame(int divider, int width, int height,
|
||||
native_pix_fmt_,
|
||||
divider));
|
||||
copy->set_timestamp(Timecode::timestamp_to_time(ts, time_base_));
|
||||
copy->set_sample_aspect_ratio(aspect_ratio_);
|
||||
copy->set_sample_aspect_ratio(std::static_pointer_cast<ImageStream>(stream())->pixel_aspect_ratio());
|
||||
copy->allocate();
|
||||
|
||||
// Convert frame to RGB/A for the rest of the pipeline
|
||||
@@ -1262,16 +1266,6 @@ void FFmpegDecoderInstance::TruncateCacheRangeTo(const qint64 &t)
|
||||
}
|
||||
}
|
||||
|
||||
rational FFmpegDecoderInstance::sample_aspect_ratio() const
|
||||
{
|
||||
return av_guess_sample_aspect_ratio(fmt_ctx_, avstream_, nullptr);
|
||||
}
|
||||
|
||||
AVStream *FFmpegDecoderInstance::stream() const
|
||||
{
|
||||
return avstream_;
|
||||
}
|
||||
|
||||
FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_index) :
|
||||
fmt_ctx_(nullptr),
|
||||
opts_(nullptr),
|
||||
|
||||
@@ -64,8 +64,15 @@ public:
|
||||
void RemoveFramesBefore(const qint64& t);
|
||||
void TruncateCacheRangeTo(const qint64& t);
|
||||
|
||||
rational sample_aspect_ratio() const;
|
||||
AVStream* stream() const;
|
||||
AVFormatContext* fmt_ctx() const
|
||||
{
|
||||
return fmt_ctx_;
|
||||
}
|
||||
|
||||
AVStream* stream() const
|
||||
{
|
||||
return avstream_;
|
||||
}
|
||||
|
||||
void ClearFrameCache();
|
||||
|
||||
@@ -191,7 +198,6 @@ private:
|
||||
PixelFormat::Format native_pix_fmt_;
|
||||
|
||||
rational time_base_;
|
||||
rational aspect_ratio_;
|
||||
int64_t start_time_;
|
||||
|
||||
static QHash< Stream*, QList<FFmpegDecoderInstance*> > instance_map_;
|
||||
|
||||
+5
-1
@@ -113,7 +113,11 @@ const rational &Frame::sample_aspect_ratio() const
|
||||
|
||||
void Frame::set_sample_aspect_ratio(const rational &aspect_ratio)
|
||||
{
|
||||
sample_aspect_ratio_ = aspect_ratio;
|
||||
if (aspect_ratio.isNull()) {
|
||||
sample_aspect_ratio_ = 1;
|
||||
} else {
|
||||
sample_aspect_ratio_ = aspect_ratio;
|
||||
}
|
||||
}
|
||||
|
||||
const rational &Frame::timestamp() const
|
||||
|
||||
@@ -119,6 +119,9 @@ bool OIIODecoder::Probe(Footage *f, const QAtomicInt *cancelled)
|
||||
image_stream->set_height(in->spec().height);
|
||||
image_stream->set_format(GetFormatFromOIIOBasetype(in->spec()));
|
||||
|
||||
// FIXME: Haven't looked, does OIIO report pixel aspect ratio somewhere?
|
||||
image_stream->set_pixel_aspect_ratio(1);
|
||||
|
||||
// Images will always have just one stream
|
||||
image_stream->set_index(0);
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ set(OLIVE_SOURCES
|
||||
common/qtutils.h
|
||||
common/qtutils.cpp
|
||||
common/range.h
|
||||
common/ratiodialog.h
|
||||
common/ratiodialog.cpp
|
||||
common/rational.h
|
||||
common/rational.cpp
|
||||
common/threadedobject.h
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 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 "ratiodialog.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QMessageBox>
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
double GetFloatRatioFromUser(QWidget* parent,
|
||||
const QString& title,
|
||||
bool* ok_in)
|
||||
{
|
||||
QString s;
|
||||
|
||||
forever {
|
||||
bool ok;
|
||||
|
||||
s = QInputDialog::getText(parent,
|
||||
title,
|
||||
QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
||||
QLineEdit::Normal,
|
||||
s,
|
||||
&ok);
|
||||
|
||||
if (!ok) {
|
||||
// User cancelled dialog, do nothing
|
||||
if (ok_in) {
|
||||
*ok_in = false;
|
||||
}
|
||||
return qSNaN();
|
||||
}
|
||||
|
||||
QStringList ratio_components = s.split(QRegExp(QStringLiteral(":|;|\\/")));
|
||||
|
||||
if (ratio_components.size() == 1) {
|
||||
bool float_ok;
|
||||
|
||||
double flt = ratio_components.at(0).toDouble(&float_ok);
|
||||
|
||||
if (float_ok && flt > 0) {
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return flt;
|
||||
}
|
||||
} else if (ratio_components.size() == 2) {
|
||||
bool numer_ok, denom_ok;
|
||||
|
||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||
|
||||
if (numer_ok
|
||||
&& denom_ok
|
||||
&& num > 0) {
|
||||
// Exit loop and set this ratio
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
}
|
||||
|
||||
QMessageBox::warning(parent,
|
||||
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
||||
QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||
"rational fraction with a ':' or a '/' separator.").arg(s),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,36 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 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 RATIODIALOG_H
|
||||
#define RATIODIALOG_H
|
||||
|
||||
#include <QInputDialog>
|
||||
|
||||
#include "common/rational.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
double GetFloatRatioFromUser(QWidget* parent,
|
||||
const QString& title,
|
||||
bool* ok_in);
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // RATIODIALOG_H
|
||||
@@ -22,11 +22,13 @@
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QInputDialog>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace OCIO = OCIO_NAMESPACE::v1;
|
||||
|
||||
#include "common/ratiodialog.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "project/project.h"
|
||||
#include "undo/undostack.h"
|
||||
@@ -41,6 +43,37 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) :
|
||||
|
||||
int row = 0;
|
||||
|
||||
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
|
||||
|
||||
pixel_aspect_combo_ = new QComboBox();
|
||||
video_layout->addWidget(pixel_aspect_combo_, row, 1);
|
||||
|
||||
AddPixelAspectRatio(tr("Square Pixels"), rational(1));
|
||||
AddPixelAspectRatio(tr("NTSC Standard"), rational(8, 9));
|
||||
AddPixelAspectRatio(tr("NTSC Widescreen"), rational(32, 27));
|
||||
AddPixelAspectRatio(tr("PAL Standard"), rational(16, 15));
|
||||
AddPixelAspectRatio(tr("PAL Widescreen"), rational(64, 45));
|
||||
AddPixelAspectRatio(tr("HD Anamorphic 1080"), rational(4, 3));
|
||||
|
||||
// Always add custom item last, much of the logic relies on this. Set this to the current AR so
|
||||
// that if none of the above are ==, it will eventually select this item
|
||||
AddPixelAspectRatio(QString(), rational());
|
||||
UpdateCustomItem(stream->pixel_aspect_ratio());
|
||||
|
||||
// Determine which index to select on startup
|
||||
for (int i=0; i<pixel_aspect_combo_->count(); i++) {
|
||||
if (pixel_aspect_combo_->itemData(i).value<rational>() == stream->pixel_aspect_ratio()) {
|
||||
pixel_aspect_combo_->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pick up index signal to query for custom aspect ratio if requested
|
||||
connect(pixel_aspect_combo_, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &VideoStreamProperties::PixelAspectComboBoxChanged);
|
||||
|
||||
row++;
|
||||
|
||||
video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
|
||||
|
||||
video_interlace_combo_ = new QComboBox();
|
||||
@@ -119,12 +152,15 @@ void VideoStreamProperties::Accept(QUndoCommand *parent)
|
||||
}
|
||||
|
||||
if (video_premultiply_alpha_->isChecked() != stream_->premultiplied_alpha()
|
||||
|| set_colorspace != stream_->colorspace(false)) {
|
||||
|| set_colorspace != stream_->colorspace(false)
|
||||
|| static_cast<ImageStream::Interlacing>(video_interlace_combo_->currentIndex()) != stream_->interlacing()
|
||||
|| pixel_aspect_combo_->currentData().value<rational>() != stream_->pixel_aspect_ratio()) {
|
||||
|
||||
new VideoStreamChangeCommand(stream_,
|
||||
video_premultiply_alpha_->isChecked(),
|
||||
set_colorspace,
|
||||
static_cast<ImageStream::Interlacing>(video_interlace_combo_->currentIndex()),
|
||||
pixel_aspect_combo_->currentData().value<rational>(),
|
||||
parent);
|
||||
}
|
||||
|
||||
@@ -163,16 +199,54 @@ bool VideoStreamProperties::IsImageSequence(ImageStream *stream)
|
||||
return (stream->type() == Stream::kVideo && static_cast<VideoStream*>(stream)->is_image_sequence());
|
||||
}
|
||||
|
||||
void VideoStreamProperties::AddPixelAspectRatio(const QString &name, const rational &ratio)
|
||||
{
|
||||
pixel_aspect_combo_->addItem(GetPixelAspectRatioItemText(name, ratio),
|
||||
QVariant::fromValue(ratio));
|
||||
}
|
||||
|
||||
QString VideoStreamProperties::GetPixelAspectRatioItemText(const QString &name, const rational &ratio)
|
||||
{
|
||||
return tr("%1 (%2)").arg(name, QString::number(ratio.toDouble(), 'f', 4));
|
||||
}
|
||||
|
||||
void VideoStreamProperties::UpdateCustomItem(const rational &ratio)
|
||||
{
|
||||
const int custom_index = pixel_aspect_combo_->count() - 1;
|
||||
|
||||
pixel_aspect_combo_->setItemText(custom_index,
|
||||
GetPixelAspectRatioItemText(tr("Custom"), ratio));
|
||||
pixel_aspect_combo_->setItemData(custom_index,
|
||||
QVariant::fromValue(ratio));
|
||||
}
|
||||
|
||||
void VideoStreamProperties::PixelAspectComboBoxChanged(int index)
|
||||
{
|
||||
// Detect if custom was selected, in which case query what the new AR should be
|
||||
if (index == pixel_aspect_combo_->count() - 1) {
|
||||
// Query for custom pixel aspect ratio
|
||||
bool ok;
|
||||
|
||||
double custom_ratio = GetFloatRatioFromUser(this, tr("Set Custom Pixel Aspect Ratio"), &ok);
|
||||
|
||||
if (ok) {
|
||||
UpdateCustomItem(rational::fromDouble(custom_ratio));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(ImageStreamPtr stream,
|
||||
bool premultiplied,
|
||||
QString colorspace,
|
||||
ImageStream::Interlacing interlacing,
|
||||
const rational &pixel_ar,
|
||||
QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
stream_(stream),
|
||||
new_premultiplied_(premultiplied),
|
||||
new_colorspace_(colorspace),
|
||||
new_interlacing_(interlacing)
|
||||
new_interlacing_(interlacing),
|
||||
new_pixel_ar_(pixel_ar)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -186,10 +260,12 @@ void VideoStreamProperties::VideoStreamChangeCommand::redo_internal()
|
||||
old_premultiplied_ = stream_->premultiplied_alpha();
|
||||
old_colorspace_ = stream_->colorspace(false);
|
||||
old_interlacing_ = stream_->interlacing();
|
||||
old_pixel_ar_ = stream_->pixel_aspect_ratio();
|
||||
|
||||
stream_->set_premultiplied_alpha(new_premultiplied_);
|
||||
stream_->set_colorspace(new_colorspace_);
|
||||
stream_->set_interlacing(new_interlacing_);
|
||||
stream_->set_pixel_aspect_ratio(new_pixel_ar_);
|
||||
}
|
||||
|
||||
void VideoStreamProperties::VideoStreamChangeCommand::undo_internal()
|
||||
@@ -197,6 +273,7 @@ void VideoStreamProperties::VideoStreamChangeCommand::undo_internal()
|
||||
stream_->set_premultiplied_alpha(old_premultiplied_);
|
||||
stream_->set_colorspace(old_colorspace_);
|
||||
stream_->set_interlacing(old_interlacing_);
|
||||
stream_->set_pixel_aspect_ratio(old_pixel_ar_);
|
||||
}
|
||||
|
||||
VideoStreamProperties::ImageSequenceChangeCommand::ImageSequenceChangeCommand(VideoStreamPtr video_stream, int64_t start_index, int64_t duration, QUndoCommand *parent) :
|
||||
|
||||
@@ -43,6 +43,12 @@ public:
|
||||
private:
|
||||
static bool IsImageSequence(ImageStream* stream);
|
||||
|
||||
void AddPixelAspectRatio(const QString& name, const rational& ratio);
|
||||
|
||||
static QString GetPixelAspectRatioItemText(const QString& name, const rational& ratio);
|
||||
|
||||
void UpdateCustomItem(const rational& ratio);
|
||||
|
||||
/**
|
||||
* @brief Attached video stream
|
||||
*/
|
||||
@@ -73,12 +79,18 @@ private:
|
||||
*/
|
||||
IntegerSlider* imgseq_end_time_;
|
||||
|
||||
/**
|
||||
* @brief Sets the pixel aspect ratio of the stream
|
||||
*/
|
||||
QComboBox* pixel_aspect_combo_;
|
||||
|
||||
class VideoStreamChangeCommand : public UndoCommand {
|
||||
public:
|
||||
VideoStreamChangeCommand(ImageStreamPtr stream,
|
||||
bool premultiplied,
|
||||
QString colorspace,
|
||||
ImageStream::Interlacing interlacing,
|
||||
const rational& pixel_ar,
|
||||
QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
@@ -93,10 +105,12 @@ private:
|
||||
bool new_premultiplied_;
|
||||
QString new_colorspace_;
|
||||
ImageStream::Interlacing new_interlacing_;
|
||||
rational new_pixel_ar_;
|
||||
|
||||
bool old_premultiplied_;
|
||||
QString old_colorspace_;
|
||||
ImageStream::Interlacing old_interlacing_;
|
||||
rational old_pixel_ar_;
|
||||
|
||||
};
|
||||
|
||||
@@ -123,6 +137,10 @@ private:
|
||||
int64_t old_duration_;
|
||||
|
||||
};
|
||||
|
||||
private slots:
|
||||
void PixelAspectComboBoxChanged(int index);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -242,38 +242,53 @@ QIcon Footage::icon()
|
||||
|
||||
QString Footage::duration()
|
||||
{
|
||||
if (streams_.isEmpty()) {
|
||||
return QString();
|
||||
// Find longest stream duration
|
||||
|
||||
StreamPtr longest_stream = nullptr;
|
||||
rational longest;
|
||||
|
||||
foreach (StreamPtr stream, streams_) {
|
||||
if (stream->type() == Stream::kVideo || stream->type() == Stream::kAudio) {
|
||||
rational this_stream_dur = Timecode::timestamp_to_time(stream->duration(),
|
||||
stream->timebase());
|
||||
|
||||
if (this_stream_dur > longest) {
|
||||
longest_stream = stream;
|
||||
longest = this_stream_dur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (streams_.first()->type() == Stream::kVideo) {
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(streams_.first());
|
||||
if (longest_stream) {
|
||||
if (longest_stream->type() == Stream::kVideo) {
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(longest_stream);
|
||||
|
||||
int64_t duration = video_stream->duration();
|
||||
rational frame_rate_timebase = video_stream->frame_rate().flipped();
|
||||
int64_t duration = video_stream->duration();
|
||||
rational frame_rate_timebase = video_stream->frame_rate().flipped();
|
||||
|
||||
if (video_stream->timebase() != frame_rate_timebase) {
|
||||
// Convert from timebase to frame rate
|
||||
rational duration_time = Timecode::timestamp_to_time(duration, video_stream->timebase());
|
||||
duration = Timecode::time_to_timestamp(duration_time, frame_rate_timebase);
|
||||
if (video_stream->timebase() != frame_rate_timebase) {
|
||||
// Convert from timebase to frame rate
|
||||
rational duration_time = Timecode::timestamp_to_time(duration, video_stream->timebase());
|
||||
duration = Timecode::time_to_timestamp(duration_time, frame_rate_timebase);
|
||||
}
|
||||
|
||||
return Timecode::timestamp_to_timecode(duration,
|
||||
frame_rate_timebase,
|
||||
Core::instance()->GetTimecodeDisplay());
|
||||
} else if (longest_stream->type() == Stream::kAudio) {
|
||||
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(longest_stream);
|
||||
|
||||
// If we're showing in a timecode, we prefer showing audio in seconds instead
|
||||
Timecode::Display display = Core::instance()->GetTimecodeDisplay();
|
||||
if (display == Timecode::kTimecodeDropFrame
|
||||
|| display == Timecode::kTimecodeNonDropFrame) {
|
||||
display = Timecode::kTimecodeSeconds;
|
||||
}
|
||||
|
||||
return Timecode::timestamp_to_timecode(longest_stream->duration(),
|
||||
longest_stream->timebase(),
|
||||
display);
|
||||
}
|
||||
|
||||
return Timecode::timestamp_to_timecode(duration,
|
||||
frame_rate_timebase,
|
||||
Core::instance()->GetTimecodeDisplay());
|
||||
} else if (streams_.first()->type() == Stream::kAudio) {
|
||||
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(streams_.first());
|
||||
|
||||
// If we're showing in a timecode, we prefer showing audio in seconds instead
|
||||
Timecode::Display display = Core::instance()->GetTimecodeDisplay();
|
||||
if (display == Timecode::kTimecodeDropFrame
|
||||
|| display == Timecode::kTimecodeNonDropFrame) {
|
||||
display = Timecode::kTimecodeSeconds;
|
||||
}
|
||||
|
||||
return Timecode::timestamp_to_timecode(streams_.first()->duration(),
|
||||
streams_.first()->timebase(),
|
||||
display);
|
||||
}
|
||||
|
||||
return QString();
|
||||
@@ -285,15 +300,13 @@ QString Footage::rate()
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (streams_.first()->type() == Stream::kVideo) {
|
||||
// Return the timebase as a frame rate
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(streams_.first());
|
||||
|
||||
if (HasStreamsOfType(Stream::kVideo)) {
|
||||
// This is a video editor, prioritize video streams
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(get_first_stream_of_type(Stream::kVideo));
|
||||
return QCoreApplication::translate("Footage", "%1 FPS").arg(video_stream->frame_rate().toDouble());
|
||||
} else if (streams_.first()->type() == Stream::kAudio) {
|
||||
// Return the sample rate
|
||||
} else if (HasStreamsOfType(Stream::kAudio)) {
|
||||
// No video streams, return audio
|
||||
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(streams_.first());
|
||||
|
||||
return QCoreApplication::translate("Footage", "%1 Hz").arg(audio_stream->sample_rate());
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ OLIVE_NAMESPACE_ENTER
|
||||
|
||||
ImageStream::ImageStream() :
|
||||
premultiplied_alpha_(false),
|
||||
interlacing_(kInterlaceNone)
|
||||
interlacing_(kInterlaceNone),
|
||||
pixel_aspect_ratio_(1)
|
||||
{
|
||||
set_type(kImage);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,23 @@ public:
|
||||
emit ParametersChanged();
|
||||
}
|
||||
|
||||
const rational& pixel_aspect_ratio() const
|
||||
{
|
||||
return pixel_aspect_ratio_;
|
||||
}
|
||||
|
||||
void set_pixel_aspect_ratio(const rational& r)
|
||||
{
|
||||
// Auto-correct null aspect ratio to 1:1
|
||||
if (r.isNull()) {
|
||||
pixel_aspect_ratio_ = 1;
|
||||
} else {
|
||||
pixel_aspect_ratio_ = r;
|
||||
}
|
||||
|
||||
emit ParametersChanged();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void FootageSetEvent(Footage*) override;
|
||||
|
||||
@@ -109,6 +126,8 @@ private:
|
||||
|
||||
PixelFormat::Format format_;
|
||||
|
||||
rational pixel_aspect_ratio_;
|
||||
|
||||
private slots:
|
||||
void ColorConfigChanged();
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ QVariant OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const Video
|
||||
VideoParams frame_params = frame->video_params();
|
||||
|
||||
// Check frame aspect ratio
|
||||
if (frame->sample_aspect_ratio() != 1 && frame->sample_aspect_ratio() != 0) {
|
||||
if (frame->sample_aspect_ratio() != 1) {
|
||||
int new_width = frame_params.width();
|
||||
int new_height = frame_params.height();
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "audio/audiomanager.h"
|
||||
#include "common/power.h"
|
||||
#include "common/ratiodialog.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "config/config.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
@@ -397,6 +398,8 @@ FramePtr DecodeCachedImage(const QString &fn, const rational& time)
|
||||
|
||||
if (frame) {
|
||||
frame->set_timestamp(time);
|
||||
} else {
|
||||
qWarning() << "Tried to load cached frame from file but it was null";
|
||||
}
|
||||
|
||||
return frame;
|
||||
@@ -743,45 +746,14 @@ void ViewerWidget::ContextMenuSetSafeMargins()
|
||||
|
||||
void ViewerWidget::ContextMenuSetCustomSafeMargins()
|
||||
{
|
||||
QString s;
|
||||
bool ok;
|
||||
|
||||
forever {
|
||||
bool ok;
|
||||
double new_ratio = GetFloatRatioFromUser(this,
|
||||
tr("Safe Margins"),
|
||||
&ok);
|
||||
|
||||
s = QInputDialog::getText(this,
|
||||
tr("Safe Margins"),
|
||||
tr("Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
||||
QLineEdit::Normal,
|
||||
s,
|
||||
&ok);
|
||||
|
||||
if (!ok) {
|
||||
// User cancelled dialog, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList ratio_components = s.split(QRegExp(QStringLiteral(":|;|\\/")));
|
||||
|
||||
if (ratio_components.size() == 2) {
|
||||
bool numer_ok, denom_ok;
|
||||
|
||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||
|
||||
if (numer_ok
|
||||
&& denom_ok
|
||||
&& num > 0) {
|
||||
// Exit loop and set this ratio
|
||||
context_menu_widget_->SetSafeMargins(ViewerSafeMarginInfo(true, num / den));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QMessageBox::warning(this,
|
||||
tr("Invalid custom ratio"),
|
||||
tr("Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||
"rational fraction with a ':' or a '/' separator.").arg(s),
|
||||
QMessageBox::Ok);
|
||||
if (ok) {
|
||||
context_menu_widget_->SetSafeMargins(ViewerSafeMarginInfo(true, new_ratio));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user