use frame rate and timebase less interchangeably

This commit is contained in:
itsmattkc
2021-04-07 13:08:25 +10:00
parent 898ea25e89
commit bb32048c65
22 changed files with 187 additions and 85 deletions
+2 -2
View File
@@ -219,7 +219,7 @@ void FFmpegEncoder::WriteAudio(AudioParams pcm_info, QIODevice* file)
// If not, use another frame size
if (params().video_enabled()) {
// If we're encoding video, use enough samples to cover roughly one frame of video
maximum_frame_samples = params().audio_params().time_to_samples(params().video_params().time_base());
maximum_frame_samples = params().audio_params().time_to_samples(params().video_params().frame_rate_as_time_base());
} else {
// If no video, just use an arbitrary number
maximum_frame_samples = 256;
@@ -484,7 +484,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
codec_ctx->width = params().video_params().width();
codec_ctx->height = params().video_params().height();
codec_ctx->sample_aspect_ratio = params().video_params().pixel_aspect_ratio().toAVRational();
codec_ctx->time_base = params().video_params().time_base().toAVRational();
codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational();
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
if (params().video_params().interlacing() != VideoParams::kInterlaceNone) {
+8
View File
@@ -281,11 +281,19 @@ int64_t Timecode::time_to_timestamp(const double &time, const rational &timebase
int64_t Timecode::rescale_timestamp(const int64_t &ts, const rational &source, const rational &dest)
{
if (source == dest) {
return ts;
}
return qRound64(static_cast<double>(ts) * source.toDouble() / dest.toDouble());
}
int64_t Timecode::rescale_timestamp_ceil(const int64_t &ts, const rational &source, const rational &dest)
{
if (source == dest) {
return ts;
}
return qCeil(static_cast<double>(ts) * source.toDouble() / dest.toDouble());
}
+1 -1
View File
@@ -195,7 +195,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
video_tab_->width_slider()->SetDefaultValue(vp.width());
video_tab_->height_slider()->SetValue(vp.height());
video_tab_->height_slider()->SetDefaultValue(vp.height());
video_tab_->frame_rate_combobox()->SetFrameRate(vp.time_base().flipped());
video_tab_->frame_rate_combobox()->SetFrameRate(vp.frame_rate());
video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(vp.pixel_aspect_ratio());
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(Config::Current()["OnlinePixelFormat"].toInt()));
video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
+8 -31
View File
@@ -92,30 +92,13 @@ QString ViewerOutput::Description() const
QString ViewerOutput::duration() const
{
/*rational timeline_length = GetLength();
rational timebase = GetVideoParams().time_base();
int64_t timestamp = Timecode::time_to_timestamp(timeline_length, timebase);
return Timecode::timestamp_to_timecode(timestamp, timebase, Core::instance()->GetTimecodeDisplay());*/
// Try video first
VideoParams video = GetFirstEnabledVideoStream();
if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) {
int64_t duration = video.duration();
rational frame_rate_timebase = video.frame_rate_as_time_base();
rational frame_rate_timebase = video.frame_rate().flipped();
if (frame_rate_timebase.isNull()) {
frame_rate_timebase = video.time_base();
}
if (video.time_base() != frame_rate_timebase) {
// Convert from timebase to frame rate
duration = Timecode::rescale_timestamp_ceil(duration, video.time_base(), frame_rate_timebase);
}
return Timecode::timestamp_to_timecode(duration,
return Timecode::timestamp_to_timecode(Timecode::rescale_timestamp_ceil(video.duration(), video.time_base(), frame_rate_timebase),
frame_rate_timebase,
Core::instance()->GetTimecodeDisplay());
}
@@ -147,13 +130,7 @@ QString ViewerOutput::rate() const
VideoParams video_stream = GetFirstEnabledVideoStream();
if (video_stream.video_type() != VideoParams::kVideoTypeStill) {
rational using_tb = video_stream.frame_rate();
if (using_tb.isNull()) {
using_tb = video_stream.time_base().flipped();
}
return tr("%1 FPS").arg(using_tb.toDouble());
return tr("%1 FPS").arg(video_stream.frame_rate().toDouble());
}
} else if (HasEnabledAudioStreams()) {
// No video streams, return audio
@@ -414,7 +391,7 @@ void ViewerOutput::InputValueChangedEvent(const QString &input, int element)
VideoParams new_video_params = GetVideoParams();
bool size_changed = cached_video_params_.width() != new_video_params.width() || cached_video_params_.height() != new_video_params.height();
bool timebase_changed = cached_video_params_.time_base() != new_video_params.time_base();
bool frame_rate_changed = cached_video_params_.frame_rate() != new_video_params.frame_rate();
bool pixel_aspect_changed = cached_video_params_.pixel_aspect_ratio() != new_video_params.pixel_aspect_ratio();
bool interlacing_changed = cached_video_params_.interlacing() != new_video_params.interlacing();
@@ -430,9 +407,9 @@ void ViewerOutput::InputValueChangedEvent(const QString &input, int element)
emit InterlacingChanged(new_video_params.interlacing());
}
if (timebase_changed) {
video_frame_cache_.SetTimebase(new_video_params.time_base());
emit TimebaseChanged(new_video_params.time_base());
if (frame_rate_changed) {
video_frame_cache_.SetTimebase(new_video_params.frame_rate_as_time_base());
emit FrameRateChanged(new_video_params.frame_rate());
}
emit VideoParamsChanged();
@@ -479,7 +456,7 @@ void ViewerOutput::set_parameters_from_footage(const QVector<ViewerOutput *> foo
// prioritize
using_timebase = GetVideoParams().time_base();
} else {
using_timebase = s.frame_rate().flipped();
using_timebase = s.frame_rate_as_time_base();
found_video_params = true;
}
+1 -1
View File
@@ -150,7 +150,7 @@ public:
static const uint64_t kVideoParamEditMask;
signals:
void TimebaseChanged(const rational&);
void FrameRateChanged(const rational&);
void LengthChanged(const rational& length);
+33
View File
@@ -123,6 +123,39 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
AddStream(Track::kVideo, QVariant::fromValue(footage_info.GetVideoStreams().at(i)));
}
if (!footage_info.GetVideoStreams().isEmpty()) {
// FIXME: This will break on multiple video streams. Currently we don't have
// infrastructure for different properties per element. We'll see if this becomes
// a problem.
VideoParams vp = footage_info.GetVideoStreams().first();
uint64_t video_param_mask = 0;
video_param_mask |= VideoParamEdit::kEnabled;
video_param_mask |= VideoParamEdit::kColorspace;
video_param_mask |= VideoParamEdit::kPixelAspect;
video_param_mask |= VideoParamEdit::kInterlacing;
video_param_mask |= VideoParamEdit::kFrameRateIsArbitrary;
if (vp.channel_count() == VideoParams::kRGBAChannelCount) {
// Add premultiplied setting if this footage has an alpha channel
video_param_mask |= VideoParamEdit::kPremultipliedAlpha;
}
if (vp.video_type() == VideoParams::kVideoTypeVideo) {
// This is video, ensure that the frame rate does not overwrite the timebase
video_param_mask |= VideoParamEdit::kFrameRateIsNotTimebase;
} else {
// This is not a video, so it's either a still image or an image sequence
video_param_mask |= VideoParamEdit::kIsImageSequence;
video_param_mask |= VideoParamEdit::kStartTime;
video_param_mask |= VideoParamEdit::kEndTime;
video_param_mask |= VideoParamEdit::kFrameRate;
}
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(video_param_mask));
}
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
AddStream(Track::kAudio, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
}
+7 -5
View File
@@ -74,6 +74,7 @@ public:
format_(format)
{
set_default_footage_parameters();
timebase_ = sample_rate_as_time_base();
}
int sample_rate() const
@@ -98,11 +99,7 @@ public:
rational time_base() const
{
if (timebase_.isNull()) {
return rational(1, sample_rate());
} else {
return timebase_;
}
return timebase_;
}
void set_time_base(const rational& timebase)
@@ -110,6 +107,11 @@ public:
timebase_ = timebase;
}
rational sample_rate_as_time_base() const
{
return rational(1, sample_rate());
}
Format format() const
{
return format_;
+2
View File
@@ -56,6 +56,7 @@ void FrameHashCache::SetHash(const rational &time, const QByteArray &hash, const
if (job.range.Contains(time)
&& job_time < job.job_time) {
// Hash here has changed since this frame started rendering, discard it
qDebug() << "Discarded hash because old";
return;
}
}
@@ -71,6 +72,7 @@ void FrameHashCache::SetHash(const rational &time, const QByteArray &hash, const
void FrameHashCache::SetTimebase(const rational &tb)
{
qDebug() << "Hello timebase" << tb;
timebase_ = tb;
}
+1 -1
View File
@@ -62,7 +62,7 @@ void RenderProcessor::Run()
NodeOutput texture_output = viewer->GetConnectedTextureOutput();
if (texture_output.IsValid()) {
table = GenerateTable(texture_output.node(), texture_output.output(),
TimeRange(time, time + video_params.time_base()));
TimeRange(time, time + video_params.frame_rate_as_time_base()));
}
TexturePtr texture = table.Get(NodeValue::kTexture).value<TexturePtr>();
+2 -1
View File
@@ -112,7 +112,8 @@ VideoParams::VideoParams(int width, int height, const rational &time_base, Forma
channel_count_(nb_channels),
pixel_aspect_ratio_(pixel_aspect_ratio),
interlacing_(interlacing),
divider_(divider)
divider_(divider),
frame_rate_(time_base.flipped())
{
calculate_effective_size();
validate_pixel_aspect_ratio();
+5
View File
@@ -122,6 +122,11 @@ public:
time_base_ = r;
}
rational frame_rate_as_time_base() const
{
return frame_rate_.flipped();
}
int divider() const
{
return divider_;
+1 -1
View File
@@ -163,7 +163,7 @@ void ExportTask::FrameDownloaded(FramePtr f, const QByteArray &hash, const QVect
forever {
rational real_time = Timecode::timestamp_to_time(frame_time_,
viewer()->GetVideoParams().time_base());
viewer()->GetVideoParams().frame_rate_as_time_base());
if (!time_map_.contains(real_time)) {
break;
+1 -1
View File
@@ -51,7 +51,7 @@ bool RenderTask::Render(ColorManager* manager,
double progress_counter = 0;
double total_length = 0;
double video_frame_sz = video_params().time_base().toDouble();
double video_frame_sz = video_params().frame_rate_as_time_base().toDouble();
// Store real time before any rendering takes place
qint64 job_time = QDateTime::currentMSecsSinceEpoch();
+24 -12
View File
@@ -74,46 +74,58 @@ ViewerOutput *TimeBasedWidget::GetConnectedNode() const
void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node)
{
// Ignore no-op
if (viewer_node_ == node) {
return;
}
if (viewer_node_) {
// Call potential derivative functions for disconnecting the viewer node
DisconnectNodeInternal(viewer_node_);
// Disconnect length changed signal
disconnect(viewer_node_, &ViewerOutput::LengthChanged, this, &TimeBasedWidget::UpdateMaximumScroll);
disconnect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &TimeBasedWidget::SetTimebase);
if (auto_set_timebase_) {
SetTimebase(rational());
}
// Reset timebase to null
SetTimebase(rational());
// Disconnect ruler and scrollbar from timeline points
ruler()->ConnectTimelinePoints(nullptr);
scrollbar_->ConnectTimelinePoints(nullptr);
}
// Set viewer node
viewer_node_ = node;
// Call derivatives
ConnectedNodeChanged(viewer_node_);
if (viewer_node_) {
// Connect length changed signal
connect(viewer_node_, &ViewerOutput::LengthChanged, this, &TimeBasedWidget::UpdateMaximumScroll);
// Connect ruler and scrollbar to timeline points
ruler()->ConnectTimelinePoints(viewer_node_->GetTimelinePoints());
scrollbar_->ConnectTimelinePoints(viewer_node_->GetTimelinePoints());
// If we're setting the timebase, set it automatically based on the video and audio parameters
if (auto_set_timebase_) {
if (!viewer_node_->GetVideoParams().time_base().isNull()) {
SetTimebase(viewer_node_->GetVideoParams().time_base());
} else if (viewer_node_->GetAudioParams().sample_rate() > 0) {
SetTimebase(viewer_node_->GetAudioParams().time_base());
} else {
SetTimebase(rational());
}
rational video_tb = viewer_node_->GetVideoParams().frame_rate_as_time_base();
connect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &TimeBasedWidget::SetTimebase);
if (!video_tb.isNull()) {
SetTimebase(video_tb);
} else {
rational audio_tb = viewer_node_->GetAudioParams().sample_rate_as_time_base();
if (!audio_tb.isNull()) {
SetTimebase(audio_tb);
} else {
SetTimebase(rational());
}
}
}
// Call derivatives
ConnectNodeInternal(viewer_node_);
}
+1 -3
View File
@@ -224,11 +224,10 @@ void TimelineWidget::ConnectNodeInternal(ViewerOutput *n)
connect(s, &Sequence::TrackAdded, this, &TimelineWidget::AddTrack);
connect(s, &Sequence::TrackRemoved, this, &TimelineWidget::RemoveTrack);
connect(n, &ViewerOutput::TimebaseChanged, this, &TimelineWidget::SetTimebase);
ruler()->SetPlaybackCache(n->video_frame_cache());
SetTimebase(n->GetVideoParams().time_base());
SetTimebase(n->GetVideoParams().frame_rate_as_time_base());
for (int i=0;i<views_.size();i++) {
Track::Type track_type = static_cast<Track::Type>(i);
@@ -253,7 +252,6 @@ void TimelineWidget::DisconnectNodeInternal(ViewerOutput *n)
disconnect(s, &Sequence::TrackAdded, this, &TimelineWidget::AddTrack);
disconnect(s, &Sequence::TrackRemoved, this, &TimelineWidget::RemoveTrack);
disconnect(n, &ViewerOutput::TimebaseChanged, this, &TimelineWidget::SetTimebase);
DeselectAll();
+1 -1
View File
@@ -281,7 +281,7 @@ void ImportTool::PrepGhosts(const rational& frame, const int& track_index)
if (parent()->GetConnectedNode()) {
FootageToGhosts(frame,
dragged_footage_,
parent()->timebase(),
parent()->GetConnectedNode()->GetVideoParams().time_base(),
track_index);
}
}
+25 -15
View File
@@ -97,6 +97,10 @@ VideoParamEdit::VideoParamEdit(QWidget* parent) :
connect(frame_rate_combobox_, static_cast<void (FrameRateComboBox::*)(int)>(&FrameRateComboBox::currentIndexChanged), this, &VideoParamEdit::Changed);
layout->addWidget(frame_rate_combobox_, row, 1);
frame_rate_slider_ = new FloatSlider();
connect(frame_rate_slider_, &FloatSlider::ValueChanged, this, &VideoParamEdit::Changed);
layout->addWidget(frame_rate_slider_, row, 1);
row++;
// Pixel Aspect Ratio
@@ -215,7 +219,8 @@ void VideoParamEdit::SetParameterMask(uint64_t mask)
depth_slider_->setVisible(mask & kDepth);
frame_rate_lbl_->setVisible(mask & kFrameRate);
frame_rate_combobox_->setVisible(mask & kFrameRate);
frame_rate_combobox_->setVisible((mask & kFrameRate) && (mask & ~kFrameRateIsArbitrary));
frame_rate_slider_->setVisible((mask & kFrameRate) && (mask & kFrameRateIsArbitrary));
pixel_aspect_lbl_->setVisible(mask & kPixelAspect);
pixel_aspect_combobox_->setVisible(mask & kPixelAspect);
@@ -263,12 +268,22 @@ VideoParams VideoParamEdit::GetVideoParams() const
p.set_height(height_slider_->GetValue());
p.set_depth(depth_slider_->GetValue());
p.set_frame_rate(frame_rate_combobox_->GetFrameRate());
if (mask_ & kFrameRateIsNotTimebase) {
// Frame rate editor will only edit the frame rate
p.set_time_base(timebase_temp_);
} else {
p.set_time_base(frame_rate_combobox_->GetFrameRate().flipped());
{
rational using_frame_rate;
if (mask_ & kFrameRateIsArbitrary) {
using_frame_rate = rational::fromDouble(frame_rate_slider_->GetValue());
} else {
using_frame_rate = frame_rate_combobox_->GetFrameRate();
}
p.set_frame_rate(using_frame_rate);
if (mask_ & kFrameRateIsNotTimebase) {
// Frame rate editor will only edit the frame rate
p.set_time_base(timebase_temp_);
} else {
p.set_time_base(using_frame_rate.flipped());
}
}
p.set_pixel_aspect_ratio(pixel_aspect_combobox_->GetPixelAspectRatio());
@@ -295,14 +310,9 @@ void VideoParamEdit::SetVideoParams(const VideoParams &p)
height_slider_->SetValue(p.height());
depth_slider_->SetValue(p.depth());
if (mask_ & kFrameRateIsNotTimebase) {
// Frame rate editor will only edit the frame rate
frame_rate_combobox_->SetFrameRate(p.frame_rate());
timebase_temp_ = p.time_base();
} else {
// Frame rate editor will edit both frame rate and time base
frame_rate_combobox_->SetFrameRate(p.time_base().flipped());
}
frame_rate_combobox_->SetFrameRate(p.frame_rate());
frame_rate_slider_->SetValue(p.frame_rate().toDouble());
timebase_temp_ = p.time_base();
pixel_aspect_combobox_->SetPixelAspectRatio(p.pixel_aspect_ratio());
interlaced_combobox_->SetInterlaceMode(p.interlacing());
@@ -27,6 +27,7 @@
#include "render/colormanager.h"
#include "render/videoparams.h"
#include "widget/slider/floatslider.h"
#include "widget/slider/integerslider.h"
#include "widget/standardcombos/frameratecombobox.h"
#include "widget/standardcombos/interlacedcombobox.h"
@@ -60,6 +61,7 @@ public:
kPremultipliedAlpha = 0x2000,
kColorspace = 0x4000,
kFrameRateIsNotTimebase = 0x8000,
kFrameRateIsArbitrary = 0x10000
};
void SetParameterMask(uint64_t mask);
@@ -143,6 +145,7 @@ private:
IntegerSlider* depth_slider_;
QLabel* frame_rate_lbl_;
FrameRateComboBox* frame_rate_combobox_;
FloatSlider* frame_rate_slider_;
QLabel* pixel_aspect_lbl_;
PixelAspectRatioComboBox* pixel_aspect_combobox_;
QLabel* interlaced_lbl_;
+38
View File
@@ -71,10 +71,13 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
stack_->addWidget(sizer_);
display_widget_ = new ViewerDisplayWidget();
display_widget_->setAcceptDrops(true);
connect(display_widget_, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
connect(display_widget_, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor);
connect(display_widget_, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged);
connect(display_widget_, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged);
connect(display_widget_, &ViewerDisplayWidget::DragEntered, this, &ViewerWidget::DragEntered);
connect(display_widget_, &ViewerDisplayWidget::Dropped, this, &ViewerWidget::Dropped);
connect(sizer_, &ViewerSizer::RequestScale, display_widget_, &ViewerDisplayWidget::SetMatrixZoom);
connect(sizer_, &ViewerSizer::RequestTranslate, display_widget_, &ViewerDisplayWidget::SetMatrixTranslate);
connect(display_widget_, &ViewerDisplayWidget::HandDragMoved, sizer_, &ViewerSizer::HandDragMove);
@@ -118,6 +121,8 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
SetAutoMaxScrollBar(true);
instances_.append(this);
setAcceptDrops(true);
}
ViewerWidget::~ViewerWidget()
@@ -1214,4 +1219,37 @@ void ViewerWidget::ViewerShiftedRange(const rational &from, const rational &to)
}
}
void ViewerWidget::DragEntered(QDragEnterEvent* event)
{
if (event->mimeData()->formats().contains(QStringLiteral("application/x-oliveprojectitemdata"))) {
event->accept();
}
}
void ViewerWidget::Dropped(QDropEvent *event)
{
QByteArray mimedata = event->mimeData()->data(QStringLiteral("application/x-oliveprojectitemdata"));
QDataStream stream(&mimedata, QIODevice::ReadOnly);
// Variables to deserialize into
quintptr item_ptr = 0;
QVector<Track::Reference> enabled_streams;
while (!stream.atEnd()) {
stream >> enabled_streams >> item_ptr;
// We only need the one item
break;
}
if (item_ptr) {
Node* item = reinterpret_cast<Node*>(item_ptr);
ViewerOutput* viewer = dynamic_cast<ViewerOutput*>(item);
if (viewer) {
ConnectViewerNode(viewer);
}
}
}
}
+4
View File
@@ -288,6 +288,10 @@ private slots:
void TimeChangedFromWaveform(qint64 t);
void DragEntered(QDragEnterEvent* event);
void Dropped(QDropEvent* event);
};
}
+16 -7
View File
@@ -278,20 +278,29 @@ void ViewerDisplayWidget::mouseReleaseEvent(QMouseEvent *event)
void ViewerDisplayWidget::dragEnterEvent(QDragEnterEvent *event)
{
emit DragEntered();
super::dragEnterEvent(event);
emit DragEntered(event);
if (!event->isAccepted()) {
super::dragEnterEvent(event);
}
}
void ViewerDisplayWidget::dragLeaveEvent(QDragLeaveEvent *event)
{
emit DragLeft();
super::dragLeaveEvent(event);
emit DragLeft(event);
if (!event->isAccepted()) {
super::dragLeaveEvent(event);
}
}
void ViewerDisplayWidget::dropEvent(QDropEvent *event)
{
emit Dropped();
super::dropEvent(event);
emit Dropped(event);
if (!event->isAccepted()) {
super::dropEvent(event);
}
}
void ViewerDisplayWidget::OnPaint()
@@ -345,7 +354,7 @@ void ViewerDisplayWidget::OnPaint()
rational node_time = GetGizmoTime();
gizmo_db_ = gt.GenerateDatabase(gizmos_, TimeRange(node_time,
node_time + gizmo_params_.time_base()));
node_time + gizmo_params_.frame_rate_as_time_base()));
QPainter p(inner_widget());
p.setWorldTransform(GenerateGizmoTransform());
+3 -3
View File
@@ -170,11 +170,11 @@ signals:
*/
void CursorColor(const Color& reference, const Color& display);
void DragEntered();
void DragEntered(QDragEnterEvent* event);
void DragLeft();
void DragLeft(QDragLeaveEvent* event);
void Dropped();
void Dropped(QDropEvent* event);
protected:
/**