diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index e8a0b7844..b5c64f81c 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -463,8 +463,9 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled) if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { bool image_is_still = false; - ImageStream::Interlacing interlacing = ImageStream::kInterlaceNone; rational pixel_aspect_ratio; + rational frame_rate; + VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone; { // Read at least two frames to get more information about this video stream @@ -479,15 +480,19 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled) // Check if video is interlaced and what field dominance it has if so if (frame->interlaced_frame) { if (frame->top_field_first) { - interlacing = ImageStream::kInterlacedTopFirst; + interlacing = VideoParams::kInterlacedTopFirst; } else { - interlacing = ImageStream::kInterlacedBottomFirst; + interlacing = VideoParams::kInterlacedBottomFirst; } } pixel_aspect_ratio = av_guess_sample_aspect_ratio(instance.fmt_ctx(), instance.stream(), frame); + + frame_rate = av_guess_frame_rate(instance.fmt_ctx(), + instance.stream(), + frame); } // Read second frame @@ -521,7 +526,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled) } else { VideoStreamPtr video_stream = std::make_shared(); - video_stream->set_frame_rate(av_guess_frame_rate(fmt_ctx, avstream, nullptr)); + video_stream->set_frame_rate(frame_rate); video_stream->set_start_time(avstream->start_time); image_stream = video_stream; @@ -783,9 +788,10 @@ FramePtr FFmpegDecoder::BuffersToNativeFrame(int divider, int width, int height, copy->set_video_params(VideoParams(width, height, native_pix_fmt_, + std::static_pointer_cast(stream())->pixel_aspect_ratio(), + std::static_pointer_cast(stream())->interlacing(), divider)); copy->set_timestamp(Timecode::timestamp_to_time(ts, time_base_)); - copy->set_sample_aspect_ratio(std::static_pointer_cast(stream())->pixel_aspect_ratio()); copy->allocate(); // Convert frame to RGB/A for the rest of the pipeline diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 84349c8d1..562d4f82e 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -138,6 +138,17 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time) encoded_frame->height = frame->height(); encoded_frame->format = video_codec_ctx_->pix_fmt; + // Set interlacing + if (frame->video_params().interlacing() != VideoParams::kInterlaceNone) { + encoded_frame->interlaced_frame = 1; + + if (frame->video_params().interlacing() == VideoParams::kInterlacedTopFirst) { + encoded_frame->top_field_first = 1; + } else { + encoded_frame->top_field_first = 0; + } + } + error_code = av_frame_get_buffer(encoded_frame, 0); if (error_code < 0) { FFmpegError("Failed to create AVFrame buffer", error_code); @@ -443,10 +454,28 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV if (type == AVMEDIA_TYPE_VIDEO) { codec_ctx->width = params().video_params().width(); codec_ctx->height = params().video_params().height(); - codec_ctx->sample_aspect_ratio = {1, 1}; + codec_ctx->sample_aspect_ratio = params().video_params().pixel_aspect_ratio().toAVRational(); codec_ctx->time_base = params().video_params().time_base().toAVRational(); codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8()); + if (params().video_params().interlacing() != VideoParams::kInterlaceNone) { + // FIXME: I actually don't know what these flags do, the documentation helpfully doesn't + // explain them at all. I hope using both of them is the right thing to do. + codec_ctx->flags |= AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME; + + + if (params().video_params().interlacing() == VideoParams::kInterlacedTopFirst) { + codec_ctx->field_order = AV_FIELD_TT; + } else { + codec_ctx->field_order = AV_FIELD_BB; + + if (codec_id == AV_CODEC_ID_H264) { + // For some reason, FFmpeg doesn't set libx264's bff flag so we have to do it ourselves + av_opt_set(video_codec_ctx_->priv_data, "x264opts", "bff=1", AV_OPT_SEARCH_CHILDREN); + } + } + } + // Set custom options { QHash::const_iterator i; diff --git a/app/codec/frame.cpp b/app/codec/frame.cpp index bc821b038..4ca93f42b 100644 --- a/app/codec/frame.cpp +++ b/app/codec/frame.cpp @@ -27,8 +27,7 @@ OLIVE_NAMESPACE_ENTER Frame::Frame() : - timestamp_(0), - sample_aspect_ratio_(1) + timestamp_(0) { } @@ -106,20 +105,6 @@ void Frame::set_pixel(int x, int y, const Color &c) c.toData(data_.data() + byte_offset, video_params().format()); } -const rational &Frame::sample_aspect_ratio() const -{ - return sample_aspect_ratio_; -} - -void Frame::set_sample_aspect_ratio(const rational &aspect_ratio) -{ - if (aspect_ratio.isNull()) { - sample_aspect_ratio_ = 1; - } else { - sample_aspect_ratio_ = aspect_ratio; - } -} - const rational &Frame::timestamp() const { return timestamp_; diff --git a/app/codec/frame.h b/app/codec/frame.h index daea040ce..7f49948a8 100644 --- a/app/codec/frame.h +++ b/app/codec/frame.h @@ -57,9 +57,6 @@ public: bool contains_pixel(int x, int y) const; void set_pixel(int x, int y, const Color& c); - const rational& sample_aspect_ratio() const; - void set_sample_aspect_ratio(const rational& sample_aspect_ratio); - /** * @brief Get frame's timestamp. * @@ -116,8 +113,6 @@ private: int64_t native_timestamp_; - rational sample_aspect_ratio_; - int linesize_; }; diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp index 238c7c6b8..6a5e9f0c8 100644 --- a/app/codec/oiio/oiiodecoder.cpp +++ b/app/codec/oiio/oiiodecoder.cpp @@ -118,16 +118,14 @@ bool OIIODecoder::Probe(Footage *f, const QAtomicInt *cancelled) image_stream->set_width(in->spec().width); 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); + image_stream->set_pixel_aspect_ratio(GetPixelAspectRatioFromOIIO(in->spec())); // Images will always have just one stream image_stream->set_index(0); // OIIO automatically premultiplies alpha - // FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this likely reduces the - // fidelity? + // FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this + // likely reduces the fidelity? image_stream->set_premultiplied_alpha(true); // Get stats for this image and dump them into the Footage file @@ -181,6 +179,8 @@ FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider frame->set_video_params(VideoParams(buffer_->spec().width, buffer_->spec().height, pix_fmt_, + GetPixelAspectRatioFromOIIO(buffer_->spec()), + VideoParams::kInterlaceNone, // FIXME: Does OIIO deinterlace for us? divider)); frame->allocate(); @@ -294,6 +294,11 @@ PixelFormat::Format OIIODecoder::GetFormatFromOIIOBasetype(const OIIO::ImageSpec } } +rational OIIODecoder::GetPixelAspectRatioFromOIIO(const OpenImageIO_v2_1::ImageSpec &spec) +{ + return rational::fromDouble(spec.extra_attribs.get_float("PixelAspectRatio", 1)); +} + bool OIIODecoder::FileTypeIsSupported(const QString& fn) { // We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG) diff --git a/app/codec/oiio/oiiodecoder.h b/app/codec/oiio/oiiodecoder.h index be0b6fca2..1b0a052c0 100644 --- a/app/codec/oiio/oiiodecoder.h +++ b/app/codec/oiio/oiiodecoder.h @@ -57,6 +57,8 @@ public: static PixelFormat::Format GetFormatFromOIIOBasetype(const OIIO::ImageSpec& spec); + static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec); + private: #if OIIO_VERSION < 10903 OIIO::ImageInput* image_; diff --git a/app/common/ratiodialog.cpp b/app/common/ratiodialog.cpp index 35bf48820..cb01147e3 100644 --- a/app/common/ratiodialog.cpp +++ b/app/common/ratiodialog.cpp @@ -70,7 +70,8 @@ double GetFloatRatioFromUser(QWidget* parent, if (numer_ok && denom_ok - && num > 0) { + && num > 0 + && den > 0) { // Exit loop and set this ratio if (ok_in) { *ok_in = true; diff --git a/app/config/config.cpp b/app/config/config.cpp index 2f5f96a88..24bce9245 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -107,7 +107,9 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeParam::kInt, 1920); SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeParam::kInt, 1080); + SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeParam::kRational, QVariant::fromValue(rational(1))); SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeParam::kRational, QVariant::fromValue(rational(1001, 30000))); + SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeParam::kInt, VideoParams::kInterlaceNone); SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeParam::kInt, 48000); SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeParam::kInt, QVariant::fromValue(static_cast(AV_CH_LAYOUT_STEREO))); SetEntryInternal(QStringLiteral("DefaultSequencePreviewFormat"), NodeParam::kInt, PixelFormat::PIX_FMT_RGBA16F); @@ -158,7 +160,7 @@ void Config::Load() double config_fr = value.toDouble(); - QList supported_frame_rates = Core::SupportedFrameRates(); + const QVector& supported_frame_rates = VideoParams::kSupportedFrameRates; rational match = supported_frame_rates.first(); double match_diff = qAbs(match.toDouble() - config_fr); diff --git a/app/core.cpp b/app/core.cpp index 5f483f3ba..1a5d14fbc 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -171,6 +171,7 @@ void Core::DeclareTypesForQt() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } @@ -868,90 +869,6 @@ bool Core::CloseAllExceptActiveProject() return true; } -QList Core::SupportedFrameRates() -{ - QList frame_rates; - - frame_rates.append(rational(10, 1)); // 10 FPS - frame_rates.append(rational(15, 1)); // 15 FPS - frame_rates.append(rational(24000, 1001)); // 23.976 FPS - frame_rates.append(rational(24, 1)); // 24 FPS - frame_rates.append(rational(25, 1)); // 25 FPS - frame_rates.append(rational(30000, 1001)); // 29.97 FPS - frame_rates.append(rational(30, 1)); // 30 FPS - frame_rates.append(rational(48000, 1001)); // 47.952 FPS - frame_rates.append(rational(48, 1)); // 48 FPS - frame_rates.append(rational(50, 1)); // 50 FPS - frame_rates.append(rational(60000, 1001)); // 59.94 FPS - frame_rates.append(rational(60, 1)); // 60 FPS - - return frame_rates; -} - -QList Core::SupportedSampleRates() -{ - QList sample_rates; - - sample_rates.append(8000); // 8000 Hz - sample_rates.append(11025); // 11025 Hz - sample_rates.append(16000); // 16000 Hz - sample_rates.append(22050); // 22050 Hz - sample_rates.append(24000); // 24000 Hz - sample_rates.append(32000); // 32000 Hz - sample_rates.append(44100); // 44100 Hz - sample_rates.append(48000); // 48000 Hz - sample_rates.append(88200); // 88200 Hz - sample_rates.append(96000); // 96000 Hz - - return sample_rates; -} - -QList Core::SupportedChannelLayouts() -{ - QList channel_layouts; - - channel_layouts.append(AV_CH_LAYOUT_MONO); - channel_layouts.append(AV_CH_LAYOUT_STEREO); - channel_layouts.append(AV_CH_LAYOUT_2_1); - channel_layouts.append(AV_CH_LAYOUT_5POINT1); - channel_layouts.append(AV_CH_LAYOUT_7POINT1); - - return channel_layouts; -} - -QList Core::SupportedDividers() -{ - return {1, 2, 3, 4, 6, 8, 12, 16}; -} - -QString Core::FrameRateToString(const rational &frame_rate) -{ - return tr("%1 FPS").arg(frame_rate.toDouble()); -} - -QString Core::SampleRateToString(const int &sample_rate) -{ - return tr("%1 Hz").arg(sample_rate); -} - -QString Core::ChannelLayoutToString(const uint64_t &layout) -{ - switch (layout) { - case AV_CH_LAYOUT_MONO: - return tr("Mono"); - case AV_CH_LAYOUT_STEREO: - return tr("Stereo"); - case AV_CH_LAYOUT_2_1: - return tr("2.1"); - case AV_CH_LAYOUT_5POINT1: - return tr("5.1"); - case AV_CH_LAYOUT_7POINT1: - return tr("7.1"); - default: - return tr("Unknown (0x%1)").arg(layout, 1, 16); - } -} - QString Core::GetProjectFilter() { return QStringLiteral("%1 (*.ove)").arg(tr("Olive Project")); diff --git a/app/core.h b/app/core.h index 1ead89b5c..01d941f0a 100644 --- a/app/core.h +++ b/app/core.h @@ -169,42 +169,6 @@ public: static QString PasteStringFromClipboard(); - /** - * @brief Return a list of supported frame rates in rational form - * - * These rationals can be flipped to create a timebase in this frame rate. - */ - static QList SupportedFrameRates(); - - /** - * @brief Return a list of supported sample rates in integer form - */ - static QList SupportedSampleRates(); - /** - * @brief Return a list of supported channel layouts as or'd flags - */ - static QList SupportedChannelLayouts(); - - /** - * @brief Return a list of supported dividers - */ - static QList SupportedDividers(); - - /** - * @brief Convert rational frame rate (i.e. flipped timebase) to a user-friendly string - */ - static QString FrameRateToString(const rational& frame_rate); - - /** - * @brief Convert integer sample rate to a user-friendly string - */ - static QString SampleRateToString(const int &sample_rate); - - /** - * @brief Convert channel layout to a user-friendly string - */ - static QString ChannelLayoutToString(const uint64_t &layout); - /** * @brief Recursively count files in a file/directory list */ diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index b23950cfc..9e59c94d6 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -177,9 +177,11 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : video_tab_->width_slider()->SetDefaultValue(viewer_node_->video_params().width()); video_tab_->height_slider()->SetValue(viewer_node_->video_params().height()); video_tab_->height_slider()->SetDefaultValue(viewer_node_->video_params().height()); - video_tab_->set_frame_rate(viewer_node_->video_params().time_base().flipped()); - audio_tab_->set_sample_rate(viewer_node_->audio_params().sample_rate()); - audio_tab_->set_channel_layout(viewer_node_->audio_params().channel_layout()); + video_tab_->frame_rate_combobox()->SetFrameRate(viewer_node_->video_params().time_base().flipped()); + video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(viewer_node_->video_params().pixel_aspect_ratio()); + video_tab_->interlaced_combobox()->SetInterlaceMode(viewer_node_->video_params().interlacing()); + audio_tab_->sample_rate_combobox()->SetSampleRate(viewer_node_->audio_params().sample_rate()); + audio_tab_->channel_layout_combobox()->SetChannelLayout(viewer_node_->audio_params().channel_layout()); video_aspect_ratio_ = static_cast(viewer_node_->video_params().width()) / static_cast(viewer_node_->video_params().height()); @@ -423,12 +425,14 @@ ExportParams ExportDialog::GenerateParams() const VideoParams video_render_params(static_cast(video_tab_->width_slider()->GetValue()), static_cast(video_tab_->height_slider()->GetValue()), - video_tab_->frame_rate().flipped(), + video_tab_->frame_rate_combobox()->GetFrameRate().flipped(), PixelFormat::instance()->GetConfiguredFormatForMode(render_mode), + video_tab_->pixel_aspect_combobox()->GetPixelAspectRatio(), + video_tab_->interlaced_combobox()->GetInterlaceMode(), render_mode); AudioParams audio_render_params(audio_tab_->sample_rate_combobox()->currentData().toInt(), - audio_tab_->channel_layout_combobox()->currentData().toULongLong(), + audio_tab_->channel_layout_combobox()->GetChannelLayout(), SampleFormat::kInternalFormat); ExportParams params; @@ -462,8 +466,8 @@ ExportParams ExportDialog::GenerateParams() const void ExportDialog::UpdateViewerDimensions() { - preview_viewer_->SetOverrideSize(static_cast(video_tab_->width_slider()->GetValue()), - static_cast(video_tab_->height_slider()->GetValue())); + preview_viewer_->SetViewerResolution(static_cast(video_tab_->width_slider()->GetValue()), + static_cast(video_tab_->height_slider()->GetValue())); QMatrix4x4 transform = ExportParams::GenerateMatrix(static_cast(video_tab_->scaling_method_combobox()->currentData().toInt()), diff --git a/app/dialog/export/exportaudiotab.cpp b/app/dialog/export/exportaudiotab.cpp index d83cdf39f..c6f4ae003 100644 --- a/app/dialog/export/exportaudiotab.cpp +++ b/app/dialog/export/exportaudiotab.cpp @@ -46,22 +46,14 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) : layout->addWidget(new QLabel(tr("Sample Rate:")), row, 0); - sample_rate_combobox_ = new QComboBox(); - sample_rates_ = Core::SupportedSampleRates(); - foreach (const int& sr, sample_rates_) { - sample_rate_combobox_->addItem(Core::SampleRateToString(sr), sr); - } + sample_rate_combobox_ = new SampleRateComboBox(); layout->addWidget(sample_rate_combobox_, row, 1); row++; layout->addWidget(new QLabel(tr("Channel Layout:")), row, 0); - channel_layout_combobox_ = new QComboBox(); - channel_layouts_ = Core::SupportedChannelLayouts(); - foreach (const uint64_t& ch_layout, channel_layouts_) { - channel_layout_combobox_->addItem(Core::ChannelLayoutToString(ch_layout), QVariant::fromValue(ch_layout)); - } + channel_layout_combobox_ = new ChannelLayoutComboBox(); layout->addWidget(channel_layout_combobox_, row, 1); row++; @@ -72,29 +64,4 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) : outer_layout->addStretch(); } -QComboBox *ExportAudioTab::codec_combobox() const -{ - return codec_combobox_; -} - -QComboBox *ExportAudioTab::sample_rate_combobox() const -{ - return sample_rate_combobox_; -} - -QComboBox *ExportAudioTab::channel_layout_combobox() const -{ - return channel_layout_combobox_; -} - -void ExportAudioTab::set_sample_rate(int rate) -{ - sample_rate_combobox_->setCurrentIndex(sample_rates_.indexOf(rate)); -} - -void ExportAudioTab::set_channel_layout(uint64_t layout) -{ - channel_layout_combobox_->setCurrentIndex(channel_layouts_.indexOf(layout)); -} - OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/export/exportaudiotab.h b/app/dialog/export/exportaudiotab.h index b14a1e3b9..924d0b655 100644 --- a/app/dialog/export/exportaudiotab.h +++ b/app/dialog/export/exportaudiotab.h @@ -25,6 +25,7 @@ #include #include "common/define.h" +#include "widget/standardcombos/standardcombos.h" OLIVE_NAMESPACE_ENTER @@ -33,20 +34,26 @@ class ExportAudioTab : public QWidget public: ExportAudioTab(QWidget* parent = nullptr); - QComboBox* codec_combobox() const; - QComboBox* sample_rate_combobox() const; - QComboBox* channel_layout_combobox() const; + QComboBox* codec_combobox() const + { + return codec_combobox_; + } - void set_sample_rate(int rate); - void set_channel_layout(uint64_t layout); + SampleRateComboBox* sample_rate_combobox() const + { + return sample_rate_combobox_; + } + + ChannelLayoutComboBox* channel_layout_combobox() const + { + return channel_layout_combobox_; + } private: QComboBox* codec_combobox_; - QComboBox* sample_rate_combobox_; - QComboBox* channel_layout_combobox_; + SampleRateComboBox* sample_rate_combobox_; + ChannelLayoutComboBox* channel_layout_combobox_; - QList sample_rates_; - QList channel_layouts_; }; OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/export/exportvideotab.cpp b/app/dialog/export/exportvideotab.cpp index 298e5bc11..9d62c360f 100644 --- a/app/dialog/export/exportvideotab.cpp +++ b/app/dialog/export/exportvideotab.cpp @@ -49,71 +49,6 @@ ExportVideoTab::ExportVideoTab(ColorManager* color_manager, QWidget *parent) : outer_layout->addStretch(); } -ExportCodec::Codec ExportVideoTab::GetSelectedCodec() const -{ - return static_cast(codec_combobox()->currentData().toInt()); -} - -QComboBox *ExportVideoTab::codec_combobox() const -{ - return codec_combobox_; -} - -IntegerSlider *ExportVideoTab::width_slider() const -{ - return width_slider_; -} - -IntegerSlider *ExportVideoTab::height_slider() const -{ - return height_slider_; -} - -QCheckBox *ExportVideoTab::maintain_aspect_checkbox() const -{ - return maintain_aspect_checkbox_; -} - -QComboBox *ExportVideoTab::scaling_method_combobox() const -{ - return scaling_method_combobox_; -} - -const rational &ExportVideoTab::frame_rate() const -{ - return frame_rates_.at(frame_rate_combobox_->currentIndex()); -} - -void ExportVideoTab::set_frame_rate(const rational &frame_rate) -{ - frame_rate_combobox_->setCurrentIndex(frame_rates_.indexOf(frame_rate)); -} - -QString ExportVideoTab::CurrentOCIOColorSpace() -{ - return color_space_chooser_->input(); -} - -CodecSection *ExportVideoTab::GetCodecSection() const -{ - return static_cast(codec_stack_->currentWidget()); -} - -void ExportVideoTab::SetCodecSection(CodecSection *section) -{ - codec_stack_->setCurrentWidget(section); -} - -ImageSection *ExportVideoTab::image_section() const -{ - return image_section_; -} - -H264Section *ExportVideoTab::h264_section() const -{ - return h264_section_; -} - QWidget* ExportVideoTab::SetupResolutionSection() { int row = 0; @@ -163,14 +98,23 @@ QWidget* ExportVideoTab::SetupResolutionSection() layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0); - frame_rate_combobox_ = new QComboBox(); - frame_rates_ = Core::SupportedFrameRates(); - foreach (const rational& fr, frame_rates_) { - frame_rate_combobox_->addItem(Core::FrameRateToString(fr)); - } - + frame_rate_combobox_ = new FrameRateComboBox(); layout->addWidget(frame_rate_combobox_, row, 1); + row++; + + layout->addWidget(new QLabel(tr("Pixel Aspect Ratio:")), row, 0); + + pixel_aspect_combobox_ = new PixelAspectRatioComboBox(); + layout->addWidget(pixel_aspect_combobox_, row, 1); + + row++; + + layout->addWidget(new QLabel(tr("Interlacing:")), row, 0); + + interlaced_combobox_ = new InterlacedComboBox(); + layout->addWidget(interlaced_combobox_, row, 1); + return resolution_group; } diff --git a/app/dialog/export/exportvideotab.h b/app/dialog/export/exportvideotab.h index dd67e2ea7..fe35d884b 100644 --- a/app/dialog/export/exportvideotab.h +++ b/app/dialog/export/exportvideotab.h @@ -31,6 +31,7 @@ #include "render/colormanager.h" #include "widget/colorwheel/colorspacechooser.h" #include "widget/slider/integerslider.h" +#include "widget/standardcombos/standardcombos.h" OLIVE_NAMESPACE_ENTER @@ -40,24 +41,75 @@ class ExportVideoTab : public QWidget public: ExportVideoTab(ColorManager* color_manager, QWidget* parent = nullptr); - ExportCodec::Codec GetSelectedCodec() const; + ExportCodec::Codec GetSelectedCodec() const + { + return static_cast(codec_combobox()->currentData().toInt()); + } - QComboBox* codec_combobox() const; + QComboBox* codec_combobox() const + { + return codec_combobox_; + } - IntegerSlider* width_slider() const; - IntegerSlider* height_slider() const; - QCheckBox* maintain_aspect_checkbox() const; - QComboBox* scaling_method_combobox() const; + IntegerSlider* width_slider() const + { + return width_slider_; + } - const rational& frame_rate() const; - void set_frame_rate(const rational& frame_rate); + IntegerSlider* height_slider() const + { + return height_slider_; + } - QString CurrentOCIOColorSpace(); + QCheckBox* maintain_aspect_checkbox() const + { + return maintain_aspect_checkbox_; + } - CodecSection* GetCodecSection() const; - void SetCodecSection(CodecSection* section); - ImageSection* image_section() const; - H264Section* h264_section() const; + QComboBox* scaling_method_combobox() const + { + return scaling_method_combobox_; + } + + FrameRateComboBox* frame_rate_combobox() const + { + return frame_rate_combobox_; + } + + QString CurrentOCIOColorSpace() + { + return color_space_chooser_->input(); + } + + CodecSection* GetCodecSection() const + { + return static_cast(codec_stack_->currentWidget()); + } + + void SetCodecSection(CodecSection* section) + { + codec_stack_->setCurrentWidget(section); + } + + ImageSection* image_section() const + { + return image_section_; + } + + H264Section* h264_section() const + { + return h264_section_; + } + + InterlacedComboBox* interlaced_combobox() const + { + return interlaced_combobox_; + } + + PixelAspectRatioComboBox* pixel_aspect_combobox() const + { + return pixel_aspect_combobox_; + } const int& threads() const { @@ -80,7 +132,7 @@ private: QWidget* SetupCodecSection(); QComboBox* codec_combobox_; - QComboBox* frame_rate_combobox_; + FrameRateComboBox* frame_rate_combobox_; QCheckBox* maintain_aspect_checkbox_; QComboBox* scaling_method_combobox_; @@ -93,10 +145,11 @@ private: IntegerSlider* width_slider_; IntegerSlider* height_slider_; - QList frame_rates_; - ColorManager* color_manager_; + InterlacedComboBox* interlaced_combobox_; + PixelAspectRatioComboBox* pixel_aspect_combobox_; + int threads_; QString pix_fmt_; diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp index 04212ddf8..17f4e83eb 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp @@ -28,7 +28,7 @@ #include namespace OCIO = OCIO_NAMESPACE::v1; -#include "common/ratiodialog.h" +#include "core.h" #include "project/item/footage/footage.h" #include "project/project.h" #include "undo/undostack.h" @@ -45,45 +45,16 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) : video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0); - pixel_aspect_combo_ = new QComboBox(); + pixel_aspect_combo_ = new PixelAspectRatioComboBox(); + pixel_aspect_combo_->SetPixelAspectRatio(stream->pixel_aspect_ratio()); 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; icount(); i++) { - if (pixel_aspect_combo_->itemData(i).value() == 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(&QComboBox::currentIndexChanged), - this, &VideoStreamProperties::PixelAspectComboBoxChanged); - row++; video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0); - video_interlace_combo_ = new QComboBox(); - - // These must match the Interlacing enum in ImageStream - video_interlace_combo_->addItem(tr("None (Progressive)")); - video_interlace_combo_->addItem(tr("Top-Field First")); - video_interlace_combo_->addItem(tr("Bottom-Field First")); - - video_interlace_combo_->setCurrentIndex(stream->interlacing()); + video_interlace_combo_ = new InterlacedComboBox(); + video_interlace_combo_->SetInterlaceMode(stream->interlacing()); video_layout->addWidget(video_interlace_combo_, row, 1); @@ -153,14 +124,14 @@ void VideoStreamProperties::Accept(QUndoCommand *parent) if (video_premultiply_alpha_->isChecked() != stream_->premultiplied_alpha() || set_colorspace != stream_->colorspace(false) - || static_cast(video_interlace_combo_->currentIndex()) != stream_->interlacing() - || pixel_aspect_combo_->currentData().value() != stream_->pixel_aspect_ratio()) { + || static_cast(video_interlace_combo_->currentIndex()) != stream_->interlacing() + || pixel_aspect_combo_->GetPixelAspectRatio() != stream_->pixel_aspect_ratio()) { new VideoStreamChangeCommand(stream_, video_premultiply_alpha_->isChecked(), set_colorspace, - static_cast(video_interlace_combo_->currentIndex()), - pixel_aspect_combo_->currentData().value(), + static_cast(video_interlace_combo_->currentIndex()), + pixel_aspect_combo_->GetPixelAspectRatio(), parent); } @@ -199,46 +170,10 @@ bool VideoStreamProperties::IsImageSequence(ImageStream *stream) return (stream->type() == Stream::kVideo && static_cast(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, + VideoParams::Interlacing interlacing, const rational &pixel_ar, QUndoCommand *parent) : UndoCommand(parent), diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.h b/app/dialog/footageproperties/streamproperties/videostreamproperties.h index a1d534cee..5926fb77b 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.h +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.h @@ -28,6 +28,7 @@ #include "streamproperties.h" #include "undo/undocommand.h" #include "widget/slider/integerslider.h" +#include "widget/standardcombos/standardcombos.h" OLIVE_NAMESPACE_ENTER @@ -43,12 +44,6 @@ 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 */ @@ -67,7 +62,7 @@ private: /** * @brief Setting for video interlacing */ - QComboBox* video_interlace_combo_; + InterlacedComboBox* video_interlace_combo_; /** * @brief Sets the start index for image sequences @@ -82,14 +77,14 @@ private: /** * @brief Sets the pixel aspect ratio of the stream */ - QComboBox* pixel_aspect_combo_; + PixelAspectRatioComboBox* pixel_aspect_combo_; class VideoStreamChangeCommand : public UndoCommand { public: VideoStreamChangeCommand(ImageStreamPtr stream, bool premultiplied, QString colorspace, - ImageStream::Interlacing interlacing, + VideoParams::Interlacing interlacing, const rational& pixel_ar, QUndoCommand* parent = nullptr); @@ -104,12 +99,12 @@ private: bool new_premultiplied_; QString new_colorspace_; - ImageStream::Interlacing new_interlacing_; + VideoParams::Interlacing new_interlacing_; rational new_pixel_ar_; bool old_premultiplied_; QString old_colorspace_; - ImageStream::Interlacing old_interlacing_; + VideoParams::Interlacing old_interlacing_; rational old_pixel_ar_; }; @@ -138,9 +133,6 @@ private: }; -private slots: - void PixelAspectComboBoxChanged(int index); - }; OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/sequence/sequence.cpp b/app/dialog/sequence/sequence.cpp index 7dd12122b..3006c2b73 100644 --- a/app/dialog/sequence/sequence.cpp +++ b/app/dialog/sequence/sequence.cpp @@ -102,24 +102,17 @@ void SequenceDialog::accept() return; } - // Get the rational at the combobox's index (which will be correct provided AddFrameRate() was used at all time) - rational video_time_base = parameter_tab_->GetSelectedVideoFrameRate().flipped(); - - // Get the rational at the combobox's index (which will be correct provided AddFrameRate() was used at all time) - int audio_sample_rate = parameter_tab_->GetSelectedAudioSampleRate(); - - // Get the audio channel layout value - uint64_t channels = parameter_tab_->GetSelectedAudioChannelLayout(); - // Generate video and audio parameter structs from data VideoParams video_params = VideoParams(parameter_tab_->GetSelectedVideoWidth(), parameter_tab_->GetSelectedVideoHeight(), - video_time_base, + parameter_tab_->GetSelectedVideoFrameRate().flipped(), parameter_tab_->GetSelectedPreviewFormat(), + parameter_tab_->GetSelectedVideoPixelAspect(), + parameter_tab_->GetSelectedVideoInterlacingMode(), parameter_tab_->GetSelectedPreviewResolution()); - AudioParams audio_params = AudioParams(audio_sample_rate, - channels, + AudioParams audio_params = AudioParams(parameter_tab_->GetSelectedAudioSampleRate(), + parameter_tab_->GetSelectedAudioChannelLayout(), SampleFormat::kInternalFormat); if (make_undoable_) { diff --git a/app/dialog/sequence/sequencedialogparametertab.cpp b/app/dialog/sequence/sequencedialogparametertab.cpp index 1c8742675..98689b16c 100644 --- a/app/dialog/sequence/sequencedialogparametertab.cpp +++ b/app/dialog/sequence/sequencedialogparametertab.cpp @@ -35,8 +35,16 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg video_layout->addWidget(video_height_field_, row, 1); row++; video_layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0); - video_frame_rate_field_ = new QComboBox(); + video_frame_rate_field_ = new FrameRateComboBox(); video_layout->addWidget(video_frame_rate_field_, row, 1); + row++; + video_layout->addWidget(new QLabel(tr("Pixel Aspect Ratio:")), row, 0); + video_pixel_aspect_field_ = new PixelAspectRatioComboBox(); + video_layout->addWidget(video_pixel_aspect_field_, row, 1); + row++; + video_layout->addWidget(new QLabel(tr("Interlacing:"))); + video_interlaced_field_ = new InterlacedComboBox(); + video_layout->addWidget(video_interlaced_field_, row, 1); layout->addWidget(video_group); row = 0; @@ -46,11 +54,11 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg audio_group->setTitle(tr("Audio")); QGridLayout* audio_layout = new QGridLayout(audio_group); audio_layout->addWidget(new QLabel(tr("Sample Rate:")), row, 0); - audio_sample_rate_field_ = new QComboBox(); + audio_sample_rate_field_ = new SampleRateComboBox(); audio_layout->addWidget(audio_sample_rate_field_, row, 1); row++; audio_layout->addWidget(new QLabel(tr("Channels:")), row, 0); - audio_channels_field_ = new QComboBox(); + audio_channels_field_ = new ChannelLayoutComboBox(); audio_layout->addWidget(audio_channels_field_, row, 1); layout->addWidget(audio_group); @@ -61,83 +69,29 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg preview_group->setTitle(tr("Preview")); QGridLayout* preview_layout = new QGridLayout(preview_group); preview_layout->addWidget(new QLabel(tr("Resolution:")), row, 0); - preview_resolution_field_ = new QComboBox(); + preview_resolution_field_ = new VideoDividerComboBox(); preview_layout->addWidget(preview_resolution_field_, row, 1); preview_resolution_label_ = new QLabel(); preview_layout->addWidget(preview_resolution_label_, row, 2); row++; preview_layout->addWidget(new QLabel(tr("Format:")), row, 0); - preview_format_field_ = new QComboBox(); + preview_format_field_ = new PixelFormatComboBox(true, true); preview_layout->addWidget(preview_format_field_, row, 1, 1, 2); layout->addWidget(preview_group); - // Set up available frame rates - frame_rate_list_ = Core::SupportedFrameRates(); - foreach (const rational& fr, frame_rate_list_) { - video_frame_rate_field_->addItem(Core::FrameRateToString(fr)); - } - - // Set up available sample rates - sample_rate_list_ = Core::SupportedSampleRates(); - foreach (const int& sr, sample_rate_list_) { - audio_sample_rate_field_->addItem(Core::SampleRateToString(sr)); - } - - // Set up available channel layouts - channel_layout_list_ = Core::SupportedChannelLayouts(); - foreach (const uint64_t& ch_layout, channel_layout_list_) { - audio_channels_field_->addItem(Core::ChannelLayoutToString(ch_layout), QVariant::fromValue(ch_layout)); - } - - // Set up preview dividers - divider_list_ = Core::SupportedDividers(); - foreach (int d, divider_list_) { - QString name; - - if (d == 1) { - name = tr("Full"); - } else { - name = tr("1/%1").arg(d); - } - - preview_resolution_field_->addItem(name); - } - connect(preview_resolution_field_, static_cast(&QComboBox::currentIndexChanged), - this, &SequenceDialogParameterTab::UpdatePreviewResolutionLabel); - - // Set up preview formats - for (int i=0;i(i); - - // We always render with an alpha channel internally - if (PixelFormat::FormatHasAlphaChannel(pix_fmt) - && PixelFormat::FormatIsFloat(pix_fmt)) { - preview_format_field_->addItem(PixelFormat::GetName(pix_fmt)); - - preview_format_list_.append(pix_fmt); - } - } - // Set values based on input sequence video_width_field_->SetValue(sequence->video_params().width()); video_height_field_->SetValue(sequence->video_params().height()); + video_frame_rate_field_->SetFrameRate(sequence->video_params().time_base().flipped()); + video_pixel_aspect_field_->SetPixelAspectRatio(sequence->video_params().pixel_aspect_ratio()); + video_interlaced_field_->SetInterlaceMode(sequence->video_params().interlacing()); + preview_resolution_field_->SetDivider(sequence->video_params().divider()); + preview_format_field_->SetPixelFormat(sequence->video_params().format()); + audio_sample_rate_field_->SetSampleRate(sequence->audio_params().sample_rate()); + audio_channels_field_->SetChannelLayout(sequence->audio_params().channel_layout()); - int frame_rate_index = frame_rate_list_.indexOf(sequence->video_params().time_base().flipped()); - video_frame_rate_field_->setCurrentIndex(frame_rate_index); - - int sample_rate_index = sample_rate_list_.indexOf(sequence->audio_params().sample_rate()); - audio_sample_rate_field_->setCurrentIndex(sample_rate_index); - - for (int i=0;icount();i++) { - if (audio_channels_field_->itemData(i).toULongLong() == sequence->audio_params().channel_layout()) { - audio_channels_field_->setCurrentIndex(i); - break; - } - } - - preview_resolution_field_->setCurrentIndex(divider_list_.indexOf(sequence->video_params().divider())); - - preview_format_field_->setCurrentIndex(preview_format_list_.indexOf(sequence->video_params().format())); + connect(preview_resolution_field_, static_cast(&QComboBox::currentIndexChanged), + this, &SequenceDialogParameterTab::UpdatePreviewResolutionLabel); layout->addStretch(); @@ -148,62 +102,31 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg UpdatePreviewResolutionLabel(); } -int SequenceDialogParameterTab::GetSelectedVideoWidth() const -{ - return video_width_field_->GetValue(); -} - -int SequenceDialogParameterTab::GetSelectedVideoHeight() const -{ - return video_height_field_->GetValue(); -} - -const rational &SequenceDialogParameterTab::GetSelectedVideoFrameRate() const -{ - return frame_rate_list_.at(video_frame_rate_field_->currentIndex()); -} - -int SequenceDialogParameterTab::GetSelectedAudioSampleRate() const -{ - return sample_rate_list_.at(audio_sample_rate_field_->currentIndex()); -} - -uint64_t SequenceDialogParameterTab::GetSelectedAudioChannelLayout() const -{ - return audio_channels_field_->currentData().toULongLong(); -} - -int SequenceDialogParameterTab::GetSelectedPreviewResolution() const -{ - return divider_list_.at(preview_resolution_field_->currentIndex()); -} - -PixelFormat::Format SequenceDialogParameterTab::GetSelectedPreviewFormat() const -{ - return preview_format_list_.at(preview_format_field_->currentIndex()); -} - void SequenceDialogParameterTab::PresetChanged(const SequencePreset &preset) { video_width_field_->SetValue(preset.width()); video_height_field_->SetValue(preset.height()); - video_frame_rate_field_->setCurrentIndex(frame_rate_list_.indexOf(preset.frame_rate())); - audio_sample_rate_field_->setCurrentIndex(sample_rate_list_.indexOf(preset.sample_rate())); - audio_channels_field_->setCurrentIndex(channel_layout_list_.indexOf(preset.channel_layout())); - preview_resolution_field_->setCurrentIndex(divider_list_.indexOf(preset.preview_divider())); - preview_format_field_->setCurrentIndex(preview_format_list_.indexOf(preset.preview_format())); + video_frame_rate_field_->SetFrameRate(preset.frame_rate()); + video_pixel_aspect_field_->SetPixelAspectRatio(preset.pixel_aspect()); + video_interlaced_field_->SetInterlaceMode(preset.interlacing()); + audio_sample_rate_field_->SetSampleRate(preset.sample_rate()); + audio_channels_field_->SetChannelLayout(preset.channel_layout()); + preview_resolution_field_->SetDivider(preset.preview_divider()); + preview_format_field_->SetPixelFormat(preset.preview_format()); } void SequenceDialogParameterTab::SavePresetClicked() { emit SaveParametersAsPreset({QString(), - static_cast(video_width_field_->GetValue()), - static_cast(video_height_field_->GetValue()), - frame_rate_list_.at(video_frame_rate_field_->currentIndex()), - sample_rate_list_.at(audio_sample_rate_field_->currentIndex()), - channel_layout_list_.at(audio_channels_field_->currentIndex()), - divider_list_.at(preview_resolution_field_->currentIndex()), - preview_format_list_.at(preview_format_field_->currentIndex())}); + GetSelectedVideoWidth(), + GetSelectedVideoHeight(), + GetSelectedVideoFrameRate(), + GetSelectedVideoPixelAspect(), + GetSelectedVideoInterlacingMode(), + GetSelectedAudioSampleRate(), + GetSelectedAudioChannelLayout(), + GetSelectedPreviewResolution(), + GetSelectedPreviewFormat()}); } void SequenceDialogParameterTab::UpdatePreviewResolutionLabel() @@ -211,7 +134,9 @@ void SequenceDialogParameterTab::UpdatePreviewResolutionLabel() VideoParams test_param(video_width_field_->GetValue(), video_height_field_->GetValue(), PixelFormat::PIX_FMT_INVALID, - divider_list_.at(preview_resolution_field_->currentIndex())); + rational(1), + VideoParams::kInterlaceNone, + preview_resolution_field_->currentData().toInt()); preview_resolution_label_->setText(tr("(%1x%2)").arg(QString::number(test_param.effective_width()), QString::number(test_param.effective_height()))); diff --git a/app/dialog/sequence/sequencedialogparametertab.h b/app/dialog/sequence/sequencedialogparametertab.h index 2baab8fb2..794abe505 100644 --- a/app/dialog/sequence/sequencedialogparametertab.h +++ b/app/dialog/sequence/sequencedialogparametertab.h @@ -8,6 +8,7 @@ #include "project/item/sequence/sequence.h" #include "sequencepreset.h" #include "widget/slider/integerslider.h" +#include "widget/standardcombos/standardcombos.h" OLIVE_NAMESPACE_ENTER @@ -17,19 +18,50 @@ class SequenceDialogParameterTab : public QWidget public: SequenceDialogParameterTab(Sequence* sequence, QWidget* parent = nullptr); - int GetSelectedVideoWidth() const; + int GetSelectedVideoWidth() const + { + return video_width_field_->GetValue(); + } - int GetSelectedVideoHeight() const; + int GetSelectedVideoHeight() const + { + return video_height_field_->GetValue(); + } - const rational& GetSelectedVideoFrameRate() const; + rational GetSelectedVideoFrameRate() const + { + return video_frame_rate_field_->GetFrameRate(); + } - int GetSelectedAudioSampleRate() const; + rational GetSelectedVideoPixelAspect() const + { + return video_pixel_aspect_field_->GetPixelAspectRatio(); + } - uint64_t GetSelectedAudioChannelLayout() const; + VideoParams::Interlacing GetSelectedVideoInterlacingMode() const + { + return video_interlaced_field_->GetInterlaceMode(); + } - int GetSelectedPreviewResolution() const; + int GetSelectedAudioSampleRate() const + { + return audio_sample_rate_field_->GetSampleRate(); + } - PixelFormat::Format GetSelectedPreviewFormat() const; + uint64_t GetSelectedAudioChannelLayout() const + { + return audio_channels_field_->GetChannelLayout(); + } + + int GetSelectedPreviewResolution() const + { + return preview_resolution_field_->GetDivider(); + } + + PixelFormat::Format GetSelectedPreviewFormat() const + { + return preview_format_field_->GetPixelFormat(); + } public slots: void PresetChanged(const SequencePreset& preset); @@ -42,27 +74,21 @@ private: IntegerSlider* video_height_field_; - QComboBox* video_frame_rate_field_; + FrameRateComboBox* video_frame_rate_field_; - QComboBox* audio_sample_rate_field_; + PixelAspectRatioComboBox* video_pixel_aspect_field_; - QComboBox* audio_channels_field_; + InterlacedComboBox* video_interlaced_field_; - QComboBox* preview_resolution_field_; + SampleRateComboBox* audio_sample_rate_field_; + + ChannelLayoutComboBox* audio_channels_field_; + + VideoDividerComboBox* preview_resolution_field_; QLabel* preview_resolution_label_; - QComboBox* preview_format_field_; - - QList frame_rate_list_; - - QList sample_rate_list_; - - QList channel_layout_list_; - - QList divider_list_; - - QList preview_format_list_; + PixelFormatComboBox* preview_format_field_; private slots: void SavePresetClicked(); diff --git a/app/dialog/sequence/sequencedialogpresettab.cpp b/app/dialog/sequence/sequencedialogpresettab.cpp index 8bf6837dd..44a2af0f9 100644 --- a/app/dialog/sequence/sequencedialogpresettab.cpp +++ b/app/dialog/sequence/sequencedialogpresettab.cpp @@ -31,6 +31,7 @@ #include "common/filefunctions.h" #include "node/input.h" +#include "render/videoparams.h" #include "ui/icons/icons.h" #include "widget/menu/menu.h" @@ -40,6 +41,8 @@ const int kDataIsPreset = Qt::UserRole; const int kDataPresetIsCustomRole = Qt::UserRole + 1; const int kDataPresetDataRole = Qt::UserRole + 2; +const PixelFormat::Format kDefaultPreviewFormat = PixelFormat::PIX_FMT_RGBA16F; + SequenceDialogPresetTab::SequenceDialogPresetTab(QWidget* parent) : QWidget(parent), PresetManager(this, QStringLiteral("sequencepresets")) @@ -65,8 +68,12 @@ SequenceDialogPresetTab::SequenceDialogPresetTab(QWidget* parent) : preset_tree_->addTopLevelItem(CreateHDPresetFolder(tr("1080p"), 1920, 1080, 3)); preset_tree_->addTopLevelItem(CreateHDPresetFolder(tr("720p"), 1280, 720, 2)); - preset_tree_->addTopLevelItem(CreateSDPresetFolder(tr("NTSC"), 720, 480, rational(30000, 1001), 1)); - preset_tree_->addTopLevelItem(CreateSDPresetFolder(tr("PAL"), 720, 576, rational(25, 1), 1)); + preset_tree_->addTopLevelItem(CreateSDPresetFolder(tr("NTSC"), 720, 480, rational(30000, 1001), + VideoParams::kPixelAspectNTSCStandard, + VideoParams::kPixelAspectNTSCWidescreen, 1)); + preset_tree_->addTopLevelItem(CreateSDPresetFolder(tr("PAL"), 720, 576, rational(25, 1), + VideoParams::kPixelAspectPALStandard, + VideoParams::kPixelAspectPALWidescreen, 1)); // Load custom presets for (int i=0;iaddTopLevelItem(parent); @@ -145,18 +162,22 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateSDPresetFolder(const QString &na width, height, frame_rate, + standard_par, + VideoParams::kInterlacedTopFirst, 48000, AV_CH_LAYOUT_STEREO, divider, - PixelFormat::PIX_FMT_RGBA16F)); + kDefaultPreviewFormat)); AddStandardItem(parent, SequencePreset::Create(tr("%1 Widescreen").arg(name), width, height, frame_rate, + wide_par, + VideoParams::kInterlacedTopFirst, 48000, AV_CH_LAYOUT_STEREO, divider, - PixelFormat::PIX_FMT_RGBA16F)); + kDefaultPreviewFormat)); return parent; } diff --git a/app/dialog/sequence/sequencedialogpresettab.h b/app/dialog/sequence/sequencedialogpresettab.h index df5ba5596..a2ef8a95d 100644 --- a/app/dialog/sequence/sequencedialogpresettab.h +++ b/app/dialog/sequence/sequencedialogpresettab.h @@ -49,7 +49,7 @@ private: QTreeWidgetItem *CreateHDPresetFolder(const QString& name, int width, int height, int divider); - QTreeWidgetItem *CreateSDPresetFolder(const QString& name, int width, int height, const rational &frame_rate, int divider); + QTreeWidgetItem *CreateSDPresetFolder(const QString& name, int width, int height, const rational &frame_rate, const rational& standard_par, const rational& wide_par, int divider); QTreeWidgetItem* GetSelectedItem(); QTreeWidgetItem* GetSelectedCustomPreset(); diff --git a/app/dialog/sequence/sequencepreset.h b/app/dialog/sequence/sequencepreset.h index 4b0dc776e..6b134b8f1 100644 --- a/app/dialog/sequence/sequencepreset.h +++ b/app/dialog/sequence/sequencepreset.h @@ -27,6 +27,7 @@ #include "common/xmlutils.h" #include "dialog/sequence/presetmanager.h" #include "render/pixelformat.h" +#include "render/videoparams.h" OLIVE_NAMESPACE_ENTER @@ -38,6 +39,8 @@ public: int width, int height, const rational& frame_rate, + const rational& pixel_aspect, + VideoParams::Interlacing interlacing, int sample_rate, uint64_t channel_layout, int preview_divider, @@ -45,6 +48,8 @@ public: width_(width), height_(height), frame_rate_(frame_rate), + pixel_aspect_(pixel_aspect), + interlacing_(interlacing), sample_rate_(sample_rate), channel_layout_(channel_layout), preview_divider_(preview_divider), @@ -57,13 +62,16 @@ public: int width, int height, const rational& frame_rate, + const rational& pixel_aspect, + VideoParams::Interlacing interlacing, int sample_rate, uint64_t channel_layout, int preview_divider, PixelFormat::Format preview_format) { - return std::make_shared(name, width, height, frame_rate, sample_rate, - channel_layout, preview_divider, preview_format); + return std::make_shared(name, width, height, frame_rate, pixel_aspect, + interlacing, sample_rate, channel_layout, + preview_divider, preview_format); } virtual void Load(QXmlStreamReader* reader) override @@ -77,6 +85,10 @@ public: height_ = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("framerate")) { frame_rate_ = rational::fromString(reader->readElementText()); + } else if (reader->name() == QStringLiteral("pixelaspect")) { + pixel_aspect_ = rational::fromString(reader->readElementText()); + } else if (reader->name() == QStringLiteral("interlacing")) { + interlacing_ = static_cast(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("samplerate")) { sample_rate_ = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("chlayout")) { @@ -97,6 +109,8 @@ public: writer->writeTextElement(QStringLiteral("width"), QString::number(width_)); writer->writeTextElement(QStringLiteral("height"), QString::number(height_)); writer->writeTextElement(QStringLiteral("framerate"), frame_rate_.toString()); + writer->writeTextElement(QStringLiteral("pixelaspect"), pixel_aspect_.toString()); + writer->writeTextElement(QStringLiteral("interlacing_"), QString::number(interlacing_)); writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate_)); writer->writeTextElement(QStringLiteral("chlayout"), QString::number(channel_layout_)); writer->writeTextElement(QStringLiteral("divider"), QString::number(preview_divider_)); @@ -118,6 +132,16 @@ public: return frame_rate_; } + const rational& pixel_aspect() const + { + return pixel_aspect_; + } + + VideoParams::Interlacing interlacing() const + { + return interlacing_; + } + int sample_rate() const { return sample_rate_; @@ -142,6 +166,8 @@ private: int width_; int height_; rational frame_rate_; + rational pixel_aspect_; + VideoParams::Interlacing interlacing_; int sample_rate_; uint64_t channel_layout_; int preview_divider_; diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 0d96a049e..93280bf65 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -136,6 +136,7 @@ void ViewerOutput::set_video_params(const VideoParams &video) { bool size_changed = video_params_.width() != video.width() || video_params_.height() != video.height(); bool timebase_changed = video_params_.time_base() != video.time_base(); + bool pixel_aspect_changed = video_params_.pixel_aspect_ratio() != video.pixel_aspect_ratio(); video_params_ = video; @@ -143,6 +144,10 @@ void ViewerOutput::set_video_params(const VideoParams &video) emit SizeChanged(video_params_.width(), video_params_.height()); } + if (pixel_aspect_changed) { + emit PixelAspectChanged(video_params_.pixel_aspect_ratio()); + } + if (timebase_changed) { video_frame_cache_.SetTimebase(video_params_.time_base()); emit TimebaseChanged(video_params_.time_base()); diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index c2b1c49e1..2d67c9800 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -131,6 +131,8 @@ signals: void SizeChanged(int width, int height); + void PixelAspectChanged(const rational& pixel_aspect); + void VideoParamsChanged(); void AudioParamsChanged(); diff --git a/app/project/item/footage/imagestream.cpp b/app/project/item/footage/imagestream.cpp index 908ac8adc..a4a53df0a 100644 --- a/app/project/item/footage/imagestream.cpp +++ b/app/project/item/footage/imagestream.cpp @@ -29,7 +29,7 @@ OLIVE_NAMESPACE_ENTER ImageStream::ImageStream() : premultiplied_alpha_(false), - interlacing_(kInterlaceNone), + interlacing_(VideoParams::kInterlaceNone), pixel_aspect_ratio_(1) { set_type(kImage); diff --git a/app/project/item/footage/imagestream.h b/app/project/item/footage/imagestream.h index 47f59f3f2..dfa01bab9 100644 --- a/app/project/item/footage/imagestream.h +++ b/app/project/item/footage/imagestream.h @@ -22,6 +22,7 @@ #define IMAGESTREAM_H #include "render/pixelformat.h" +#include "render/videoparams.h" #include "stream.h" OLIVE_NAMESPACE_ENTER @@ -75,18 +76,12 @@ public: QString get_colorspace_match_string() const; - enum Interlacing { - kInterlaceNone, - kInterlacedTopFirst, - kInterlacedBottomFirst - }; - - Interlacing interlacing() const + VideoParams::Interlacing interlacing() const { return interlacing_; } - void set_interlacing(Interlacing i) + void set_interlacing(VideoParams::Interlacing i) { interlacing_ = i; @@ -122,7 +117,7 @@ private: int height_; bool premultiplied_alpha_; QString colorspace_; - Interlacing interlacing_; + VideoParams::Interlacing interlacing_; PixelFormat::Format format_; diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index 9e89a0aaa..fcf76c8dd 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -65,7 +65,8 @@ void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const if (reader->name() == QStringLiteral("video")) { int video_width = 0, video_height = 0, preview_div = 1; - rational video_timebase; + rational video_timebase, video_pixel_aspect; + VideoParams::Interlacing video_interlacing = VideoParams::kInterlaceNone; PixelFormat::Format preview_format = PixelFormat::PIX_FMT_INVALID; while (XMLReadNextStartElement(reader)) { @@ -83,12 +84,17 @@ void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const preview_div = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("format")) { preview_format = static_cast(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("pixelaspect")) { + video_pixel_aspect = rational::fromString(reader->readElementText()); + } else if (reader->name() == QStringLiteral("interlacing")) { + video_interlacing = static_cast(reader->readElementText().toInt()); } else { reader->skipCurrentElement(); } } - set_video_params(VideoParams(video_width, video_height, video_timebase, preview_format, preview_div)); + set_video_params(VideoParams(video_width, video_height, video_timebase, preview_format, + video_pixel_aspect, video_interlacing, preview_div)); } else if (reader->name() == QStringLiteral("audio")) { int rate = 0; uint64_t layout = 0; @@ -156,6 +162,8 @@ void Sequence::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("width"), QString::number(video_params().width())); writer->writeTextElement(QStringLiteral("height"), QString::number(video_params().height())); writer->writeTextElement(QStringLiteral("timebase"), video_params().time_base().toString()); + writer->writeTextElement(QStringLiteral("pixelaspect"), video_params().pixel_aspect_ratio().toString()); + writer->writeTextElement(QStringLiteral("interlacing"), QString::number(video_params().interlacing())); writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params().divider())); writer->writeTextElement(QStringLiteral("format"), QString::number(video_params().format())); @@ -245,6 +253,8 @@ void Sequence::set_default_parameters() height, Config::Current()["DefaultSequenceFrameRate"].value(), static_cast(Config::Current()["DefaultSequencePreviewFormat"].toInt()), + Config::Current()["DefaultSequencePixelAspect"].value(), + Config::Current()["DefaultSequenceInterlacing"].value(), VideoParams::generate_auto_divider(width, height))); set_audio_params(AudioParams(Config::Current()["DefaultSequenceAudioFrequency"].toInt(), Config::Current()["DefaultSequenceAudioLayout"].toULongLong(), @@ -269,6 +279,8 @@ void Sequence::set_parameters_from_footage(const QList footage) vs->height(), vs->frame_rate().flipped(), static_cast(Config::Current()["DefaultSequencePreviewFormat"].toInt()), + vs->pixel_aspect_ratio(), + vs->interlacing(), VideoParams::generate_auto_divider(vs->width(), vs->height()))); found_video_params = true; } @@ -284,6 +296,8 @@ void Sequence::set_parameters_from_footage(const QList footage) is->height(), video_params().time_base(), static_cast(Config::Current()["DefaultSequencePreviewFormat"].toInt()), + is->pixel_aspect_ratio(), + is->interlacing(), VideoParams::generate_auto_divider(is->width(), is->height()))); } break; diff --git a/app/render/audioparams.cpp b/app/render/audioparams.cpp index 35c51aedf..90ab792d9 100644 --- a/app/render/audioparams.cpp +++ b/app/render/audioparams.cpp @@ -24,8 +24,31 @@ extern "C" { #include } +#include + OLIVE_NAMESPACE_ENTER +const QVector AudioParams::kSupportedSampleRates = { + 8000, // 8000 Hz + 11025, // 11025 Hz + 16000, // 16000 Hz + 22050, // 22050 Hz + 24000, // 24000 Hz + 32000, // 32000 Hz + 44100, // 44100 Hz + 48000, // 48000 Hz + 88200, // 88200 Hz + 96000 // 96000 Hz +}; + +const QVector AudioParams::kSupportedChannelLayouts = { + AV_CH_LAYOUT_MONO, + AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_2_1, + AV_CH_LAYOUT_5POINT1, + AV_CH_LAYOUT_7POINT1 +}; + int AudioParams::time_to_bytes(const double &time) const { Q_ASSERT(is_valid()); @@ -127,4 +150,27 @@ bool AudioParams::is_valid() const && format_ != SampleFormat::SAMPLE_FMT_COUNT); } +QString AudioParams::SampleRateToString(const int &sample_rate) +{ + return QCoreApplication::translate("AudioParams", "%1 Hz").arg(sample_rate); +} + +QString AudioParams::ChannelLayoutToString(const uint64_t &layout) +{ + switch (layout) { + case AV_CH_LAYOUT_MONO: + return QCoreApplication::translate("AudioParams", "Mono"); + case AV_CH_LAYOUT_STEREO: + return QCoreApplication::translate("AudioParams", "Stereo"); + case AV_CH_LAYOUT_2_1: + return QCoreApplication::translate("AudioParams", "2.1"); + case AV_CH_LAYOUT_5POINT1: + return QCoreApplication::translate("AudioParams", "5.1"); + case AV_CH_LAYOUT_7POINT1: + return QCoreApplication::translate("AudioParams", "7.1"); + default: + return QCoreApplication::translate("AudioParams", "Unknown (0x%1)").arg(layout, 1, 16); + } +} + OLIVE_NAMESPACE_EXIT diff --git a/app/render/audioparams.h b/app/render/audioparams.h index 617685a5e..56028a579 100644 --- a/app/render/audioparams.h +++ b/app/render/audioparams.h @@ -80,6 +80,19 @@ public: bool operator==(const AudioParams& other) const; bool operator!=(const AudioParams& other) const; + static const QVector kSupportedChannelLayouts; + static const QVector kSupportedSampleRates; + + /** + * @brief Convert integer sample rate to a user-friendly string + */ + static QString SampleRateToString(const int &sample_rate); + + /** + * @brief Convert channel layout to a user-friendly string + */ + static QString ChannelLayoutToString(const uint64_t &layout); + private: int sample_rate_; diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index 4b580711a..224f52c41 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -149,22 +149,26 @@ 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) { + rational true_pixel_aspect_ratio = frame_params.pixel_aspect_ratio() / params.pixel_aspect_ratio(); + + if (true_pixel_aspect_ratio != 1) { int new_width = frame_params.width(); int new_height = frame_params.height(); // Scale the frame in a way that does not reduce the resolution - if (frame->sample_aspect_ratio() > 1) { + if (frame_params.pixel_aspect_ratio() > 1) { // Make wider - new_width = qRound(static_cast(new_width) * frame->sample_aspect_ratio().toDouble()); + new_width = qRound(static_cast(new_width) * frame_params.pixel_aspect_ratio().toDouble()); } else { // Make taller - new_height = qRound(static_cast(new_height) / frame->sample_aspect_ratio().toDouble()); + new_height = qRound(static_cast(new_height) / frame_params.pixel_aspect_ratio().toDouble()); } frame_params = VideoParams(new_width, new_height, frame_params.format(), + frame_params.pixel_aspect_ratio(), + frame_params.interlacing(), frame_params.divider()); } @@ -178,6 +182,8 @@ QVariant OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const Video VideoParams dest_params(frame_params.width(), frame_params.height(), texture_fmt, + frame_params.pixel_aspect_ratio(), + frame_params.interlacing(), frame_params.divider()); // Create destination texture @@ -420,6 +426,8 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node, params.height(), params.time_base(), output_format, + params.pixel_aspect_ratio(), + params.interlacing(), params.divider()); int real_iteration_count; diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index 252cb1210..1f7902190 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -127,6 +127,8 @@ void RenderWorker::RenderFrame(RenderTicketPtr ticket, ViewerOutput* viewer, con video_params_.height(), video_params_.time_base(), output_format, + video_params_.pixel_aspect_ratio(), + video_params_.interlacing(), video_params_.divider())); frame->set_timestamp(time); frame->allocate(); @@ -295,6 +297,8 @@ QVariant RenderWorker::ProcessFrameGeneration(const Node* node, const GenerateJo video_params_.height(), video_params_.time_base(), output_fmt, + video_params_.pixel_aspect_ratio(), + video_params_.interlacing(), video_params_.divider())); frame->allocate(); @@ -316,6 +320,8 @@ QVariant RenderWorker::GetCachedFrame(const Node* node, const rational& time) f->height() * video_params_.divider(), f->video_params().time_base(), f->video_params().format(), + f->video_params().pixel_aspect_ratio(), + f->video_params().interlacing(), video_params_.divider())); return CachedFrameToTexture(f); diff --git a/app/render/pixelformat.cpp b/app/render/pixelformat.cpp index 4c1ee999d..01abf0aff 100644 --- a/app/render/pixelformat.cpp +++ b/app/render/pixelformat.cpp @@ -263,8 +263,8 @@ FramePtr PixelFormat::ConvertPixelFormat(FramePtr frame, const PixelFormat::Form // Create a destination frame with the same parameters FramePtr converted = Frame::Create(); converted->set_video_params(VideoParams(frame->video_params().width(), - frame->video_params().height(), - dest_format)); + frame->video_params().height(), + dest_format)); converted->set_timestamp(frame->timestamp()); converted->allocate(); diff --git a/app/render/videoparams.cpp b/app/render/videoparams.cpp index 8f59f41bd..8e20a717e 100644 --- a/app/render/videoparams.cpp +++ b/app/render/videoparams.cpp @@ -26,28 +26,70 @@ OLIVE_NAMESPACE_ENTER +const rational VideoParams::kPixelAspectSquare(1); +const rational VideoParams::kPixelAspectNTSCStandard(8, 9); +const rational VideoParams::kPixelAspectNTSCWidescreen(32, 27); +const rational VideoParams::kPixelAspectPALStandard(16, 15); +const rational VideoParams::kPixelAspectPALWidescreen(64, 45); +const rational VideoParams::kPixelAspect1080Anamorphic(4, 3); + +const QVector VideoParams::kSupportedFrameRates = { + rational(10, 1), // 10 FPS + rational(15, 1), // 15 FPS + rational(24000, 1001), // 23.976 FPS + rational(24, 1), // 24 FPS + rational(25, 1), // 25 FPS + rational(30000, 1001), // 29.97 FPS + rational(30, 1), // 30 FPS + rational(48000, 1001), // 47.952 FPS + rational(48, 1), // 48 FPS + rational(50, 1), // 50 FPS + rational(60000, 1001), // 59.94 FPS + rational(60, 1) // 60 FPS +}; + +const QVector VideoParams::kSupportedDividers = {1, 2, 3, 4, 6, 8, 12, 16}; + +const QVector VideoParams::kStandardPixelAspects = { + VideoParams::kPixelAspectSquare, + VideoParams::kPixelAspectNTSCStandard, + VideoParams::kPixelAspectNTSCWidescreen, + VideoParams::kPixelAspectPALStandard, + VideoParams::kPixelAspectPALWidescreen, + VideoParams::kPixelAspect1080Anamorphic +}; + VideoParams::VideoParams() : - format_(PixelFormat::PIX_FMT_INVALID) + width_(0), + height_(0), + format_(PixelFormat::PIX_FMT_INVALID), + interlacing_(Interlacing::kInterlaceNone) { } -VideoParams::VideoParams(const int &width, const int &height, const PixelFormat::Format &format, const int& divider) : +VideoParams::VideoParams(const int &width, const int &height, const PixelFormat::Format &format, const rational& pixel_aspect_ratio, const Interlacing &interlacing, const int& divider) : width_(width), height_(height), format_(format), + pixel_aspect_ratio_(pixel_aspect_ratio), + interlacing_(interlacing), divider_(divider) { calculate_effective_size(); + validate_pixel_aspect_ratio(); } -VideoParams::VideoParams(const int &width, const int &height, const rational &time_base, const PixelFormat::Format &format, const int ÷r) : +VideoParams::VideoParams(const int &width, const int &height, const rational &time_base, const PixelFormat::Format &format, const rational& pixel_aspect_ratio, const Interlacing &interlacing, const int ÷r) : width_(width), height_(height), time_base_(time_base), format_(format), + pixel_aspect_ratio_(pixel_aspect_ratio), + interlacing_(interlacing), divider_(divider) { calculate_effective_size(); + validate_pixel_aspect_ratio(); } int VideoParams::generate_auto_divider(qint64 width, qint64 height) @@ -60,16 +102,14 @@ int VideoParams::generate_auto_divider(qint64 width, qint64 height) double squared_divider = double(megapixels) / double(target_res); double divider = qSqrt(squared_divider); - QList supported_dividers = Core::SupportedDividers(); - - if (divider <= supported_dividers.first()) { - return supported_dividers.first(); - } else if (divider >= supported_dividers.last()) { - return supported_dividers.last(); + if (divider <= kSupportedDividers.first()) { + return kSupportedDividers.first(); + } else if (divider >= kSupportedDividers.last()) { + return kSupportedDividers.last(); } else { - for (int i=1; i= prev_divider && divider <= next_divider) { double prev_diff = qAbs(prev_divider - divider); @@ -94,6 +134,7 @@ bool VideoParams::operator==(const VideoParams &rhs) const && height() == rhs.height() && time_base() == rhs.time_base() && format() == rhs.format() + && pixel_aspect_ratio() == rhs.pixel_aspect_ratio() && divider() == rhs.divider(); } @@ -109,12 +150,49 @@ void VideoParams::calculate_effective_size() effective_height_ = qCeil(height() / divider_ * 0.5) * 2; } +void VideoParams::validate_pixel_aspect_ratio() +{ + if (pixel_aspect_ratio_.isNull()) { + pixel_aspect_ratio_ = 1; + } +} + bool VideoParams::is_valid() const { return (width() > 0 && height() > 0 + && !pixel_aspect_ratio_.isNull() && format_ != PixelFormat::PIX_FMT_INVALID && format_ != PixelFormat::PIX_FMT_COUNT); } +QString VideoParams::FrameRateToString(const rational &frame_rate) +{ + return QCoreApplication::translate("VideoParams", "%1 FPS").arg(frame_rate.toDouble()); +} + +QStringList VideoParams::GetStandardPixelAspectRatioNames() +{ + QStringList strings = { + QCoreApplication::translate("VideoParams", "Square Pixels (%1)"), + QCoreApplication::translate("VideoParams", "NTSC Standard (%1)"), + QCoreApplication::translate("VideoParams", "NTSC Widescreen (%1)"), + QCoreApplication::translate("VideoParams", "PAL Standard (%1)"), + QCoreApplication::translate("VideoParams", "PAL Widescreen (%1)"), + QCoreApplication::translate("VideoParams", "HD Anamorphic 1080 (%1)") + }; + + // Format each + for (int i=0; i kSupportedFrameRates; + static const QVector kStandardPixelAspects; + static const QVector kSupportedDividers; + + /** + * @brief Convert rational frame rate (i.e. flipped timebase) to a user-friendly string + */ + static QString FrameRateToString(const rational& frame_rate); + + static QStringList GetStandardPixelAspectRatioNames(); + static QString FormatPixelAspectRatioString(const QString& format, const rational& ratio); + private: void calculate_effective_size(); + void validate_pixel_aspect_ratio(); + int width_; int height_; rational time_base_; PixelFormat::Format format_; + rational pixel_aspect_ratio_; + + Interlacing interlacing_; + int divider_; int effective_width_; int effective_height_; @@ -92,5 +137,6 @@ private: OLIVE_NAMESPACE_EXIT Q_DECLARE_METATYPE(OLIVE_NAMESPACE::VideoParams) +Q_DECLARE_METATYPE(OLIVE_NAMESPACE::VideoParams::Interlacing) #endif // VIDEOPARAMS_H diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt index 01ba47053..1a05cbe55 100644 --- a/app/widget/CMakeLists.txt +++ b/app/widget/CMakeLists.txt @@ -41,6 +41,7 @@ add_subdirectory(projecttoolbar) add_subdirectory(resizablescrollbar) add_subdirectory(scope) add_subdirectory(slider) +add_subdirectory(standardcombos) add_subdirectory(taskview) add_subdirectory(timebased) add_subdirectory(timelinewidget) diff --git a/app/widget/nodetableview/nodetabletraverser.cpp b/app/widget/nodetableview/nodetabletraverser.cpp index 1ca0e1080..77d92afb6 100644 --- a/app/widget/nodetableview/nodetabletraverser.cpp +++ b/app/widget/nodetableview/nodetabletraverser.cpp @@ -29,7 +29,8 @@ QVariant NodeTableTraverser::ProcessVideoFootage(StreamPtr stream, const rationa return QVariant::fromValue(VideoParams(video_stream->width(), video_stream->height(), video_stream->timebase(), - video_stream->format())); + video_stream->format(), + video_stream->pixel_aspect_ratio())); } QVariant NodeTableTraverser::ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time) diff --git a/app/widget/standardcombos/CMakeLists.txt b/app/widget/standardcombos/CMakeLists.txt new file mode 100644 index 000000000..cbfac1d51 --- /dev/null +++ b/app/widget/standardcombos/CMakeLists.txt @@ -0,0 +1,28 @@ +# 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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/standardcombos/channellayoutcombobox.h + widget/standardcombos/frameratecombobox.h + widget/standardcombos/interlacedcombobox.h + widget/standardcombos/pixelaspectratiocombobox.h + widget/standardcombos/pixelformatcombobox.h + widget/standardcombos/sampleratecombobox.h + widget/standardcombos/standardcombos.h + widget/standardcombos/videodividercombobox.h + PARENT_SCOPE +) diff --git a/app/widget/standardcombos/channellayoutcombobox.h b/app/widget/standardcombos/channellayoutcombobox.h new file mode 100644 index 000000000..7deec22e6 --- /dev/null +++ b/app/widget/standardcombos/channellayoutcombobox.h @@ -0,0 +1,62 @@ +/*** + + 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 . + +***/ + +#ifndef CHANNELLAYOUTCOMBOBOX_H +#define CHANNELLAYOUTCOMBOBOX_H + +#include + +#include "render/audioparams.h" + +OLIVE_NAMESPACE_ENTER + +class ChannelLayoutComboBox : public QComboBox +{ + Q_OBJECT +public: + ChannelLayoutComboBox(QWidget* parent = nullptr) : + QComboBox(parent) + { + foreach (const uint64_t& ch_layout, AudioParams::kSupportedChannelLayouts) { + this->addItem(AudioParams::ChannelLayoutToString(ch_layout), + QVariant::fromValue(ch_layout)); + } + } + + uint64_t GetChannelLayout() const + { + return this->currentData().toULongLong(); + } + + void SetChannelLayout(uint64_t ch) + { + for (int i=0; icount(); i++) { + if (this->itemData(i).toULongLong() == ch) { + this->setCurrentIndex(i); + break; + } + } + } + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // CHANNELLAYOUTCOMBOBOX_H diff --git a/app/widget/standardcombos/frameratecombobox.h b/app/widget/standardcombos/frameratecombobox.h new file mode 100644 index 000000000..00eefe7aa --- /dev/null +++ b/app/widget/standardcombos/frameratecombobox.h @@ -0,0 +1,62 @@ +/*** + + 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 . + +***/ + +#ifndef FRAMERATECOMBOBOX_H +#define FRAMERATECOMBOBOX_H + +#include + +#include "common/rational.h" +#include "render/videoparams.h" + +OLIVE_NAMESPACE_ENTER + +class FrameRateComboBox : public QComboBox +{ + Q_OBJECT +public: + FrameRateComboBox(QWidget* parent = nullptr) : + QComboBox(parent) + { + foreach (const rational& fr, VideoParams::kSupportedFrameRates) { + this->addItem(VideoParams::FrameRateToString(fr), QVariant::fromValue(fr)); + } + } + + rational GetFrameRate() const + { + return this->currentData().value(); + } + + void SetFrameRate(const rational& r) + { + for (int i=0; icount(); i++) { + if (this->itemData(i).value() == r) { + this->setCurrentIndex(i); + break; + } + } + } + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // FRAMERATECOMBOBOX_H diff --git a/app/widget/standardcombos/interlacedcombobox.h b/app/widget/standardcombos/interlacedcombobox.h new file mode 100644 index 000000000..1bf54806b --- /dev/null +++ b/app/widget/standardcombos/interlacedcombobox.h @@ -0,0 +1,57 @@ +/*** + + 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 . + +***/ + +#ifndef INTERLACEDCOMBOBOX_H +#define INTERLACEDCOMBOBOX_H + +#include + +#include "render/videoparams.h" + +OLIVE_NAMESPACE_ENTER + +class InterlacedComboBox : public QComboBox +{ + Q_OBJECT +public: + InterlacedComboBox(QWidget* parent = nullptr) : + QComboBox(parent) + { + // These must match the Interlacing enum in VideoParams + this->addItem(tr("None (Progressive)")); + this->addItem(tr("Top-Field First")); + this->addItem(tr("Bottom-Field First")); + } + + VideoParams::Interlacing GetInterlaceMode() const + { + return static_cast(this->currentIndex()); + } + + void SetInterlaceMode(VideoParams::Interlacing mode) + { + this->setCurrentIndex(mode); + } + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // INTERLACEDCOMBOBOX_H diff --git a/app/widget/standardcombos/pixelaspectratiocombobox.h b/app/widget/standardcombos/pixelaspectratiocombobox.h new file mode 100644 index 000000000..a5e2d02b9 --- /dev/null +++ b/app/widget/standardcombos/pixelaspectratiocombobox.h @@ -0,0 +1,127 @@ +/*** + + 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 . + +***/ + +#ifndef PIXELASPECTRATIOCOMBOBOX_H +#define PIXELASPECTRATIOCOMBOBOX_H + +#include + +#include "common/ratiodialog.h" +#include "render/videoparams.h" + +OLIVE_NAMESPACE_ENTER + +class PixelAspectRatioComboBox : public QComboBox +{ + Q_OBJECT +public: + PixelAspectRatioComboBox(QWidget* parent = nullptr) : + QComboBox(parent), + dont_prompt_custom_par_(false) + { + QStringList par_names = VideoParams::GetStandardPixelAspectRatioNames(); + for (int i=0; iaddItem(par_names.at(i), + QVariant::fromValue(ratio)); + } + + // 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 + this->addItem(QString()); + UpdateCustomItem(rational()); + + // Pick up index signal to query for custom aspect ratio if requested + connect(this, static_cast(&QComboBox::currentIndexChanged), + this, &PixelAspectRatioComboBox::IndexChanged); + } + + rational GetPixelAspectRatio() const + { + return this->currentData().value(); + } + + void SetPixelAspectRatio(const rational& r) + { + // Determine which index to select on startup + for (int i=0; icount(); i++) { + if (this->itemData(i).value() == r) { + this->setCurrentIndex(i); + return; + } + } + + // Must not have found the ratio, so it must be custom + UpdateCustomItem(r); + dont_prompt_custom_par_ = true; + this->setCurrentIndex(this->count() - 1); + dont_prompt_custom_par_ = false; + } + +private slots: + void IndexChanged(int index) + { + if (dont_prompt_custom_par_) { + return; + } + + // Detect if custom was selected, in which case query what the new AR should be + if (index == this->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)); + } + } + } + +private: + void UpdateCustomItem(const rational &ratio) + { + const int custom_index = this->count() - 1; + + if (ratio.isNull()) { + this->setItemText(custom_index, + tr("Custom...")); + + // Use 1:1 to prevent any real chance of the PAR being set to 0 + this->setItemData(custom_index, + QVariant::fromValue(rational(1))); + } else { + this->setItemText(custom_index, + VideoParams::FormatPixelAspectRatioString(tr("Custom (%1)"), ratio)); + this->setItemData(custom_index, + QVariant::fromValue(ratio)); + } + } + + bool dont_prompt_custom_par_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // PIXELASPECTRATIOCOMBOBOX_H diff --git a/app/widget/standardcombos/pixelformatcombobox.h b/app/widget/standardcombos/pixelformatcombobox.h new file mode 100644 index 000000000..48638a14b --- /dev/null +++ b/app/widget/standardcombos/pixelformatcombobox.h @@ -0,0 +1,67 @@ +/*** + + 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 . + +***/ + +#ifndef PIXELFORMATCOMBOBOX_H +#define PIXELFORMATCOMBOBOX_H + +#include + +#include "render/pixelformat.h" + +OLIVE_NAMESPACE_ENTER + +class PixelFormatComboBox : public QComboBox +{ + Q_OBJECT +public: + PixelFormatComboBox(bool alpha_only, bool float_only, QWidget* parent = nullptr) : + QComboBox(parent) + { + // Set up preview formats + for (int i=0;i(i); + + if ((!alpha_only || PixelFormat::FormatHasAlphaChannel(pix_fmt)) + && (!float_only || PixelFormat::FormatIsFloat(pix_fmt))) { + this->addItem(PixelFormat::GetName(pix_fmt), pix_fmt); + } + } + } + + PixelFormat::Format GetPixelFormat() const + { + return static_cast(this->currentData().toInt()); + } + + void SetPixelFormat(PixelFormat::Format fmt) + { + for (int i=0; icount(); i++) { + if (this->itemData(i).toInt() == fmt) { + this->setCurrentIndex(i); + break; + } + } + } + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // PIXELFORMATCOMBOBOX_H diff --git a/app/widget/standardcombos/sampleratecombobox.h b/app/widget/standardcombos/sampleratecombobox.h new file mode 100644 index 000000000..e7300517f --- /dev/null +++ b/app/widget/standardcombos/sampleratecombobox.h @@ -0,0 +1,61 @@ +/*** + + 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 . + +***/ + +#ifndef SAMPLERATECOMBOBOX_H +#define SAMPLERATECOMBOBOX_H + +#include + +#include "render/audioparams.h" + +OLIVE_NAMESPACE_ENTER + +class SampleRateComboBox : public QComboBox +{ + Q_OBJECT +public: + SampleRateComboBox(QWidget* parent = nullptr) : + QComboBox(parent) + { + foreach (int sr, AudioParams::kSupportedSampleRates) { + this->addItem(AudioParams::SampleRateToString(sr), sr); + } + } + + int GetSampleRate() const + { + return this->currentData().toInt(); + } + + void SetSampleRate(int rate) + { + for (int i=0; icount(); i++) { + if (this->itemData(i).toInt() == rate) { + this->setCurrentIndex(i); + break; + } + } + } + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // SAMPLERATECOMBOBOX_H diff --git a/app/widget/standardcombos/standardcombos.h b/app/widget/standardcombos/standardcombos.h new file mode 100644 index 000000000..e59aedab8 --- /dev/null +++ b/app/widget/standardcombos/standardcombos.h @@ -0,0 +1,32 @@ +/*** + + 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 . + +***/ + +#ifndef STANDARDCOMBOS_H +#define STANDARDCOMBOS_H + +#include "channellayoutcombobox.h" +#include "frameratecombobox.h" +#include "interlacedcombobox.h" +#include "pixelaspectratiocombobox.h" +#include "pixelformatcombobox.h" +#include "sampleratecombobox.h" +#include "videodividercombobox.h" + +#endif // STANDARDCOMBOS_H diff --git a/app/widget/standardcombos/videodividercombobox.h b/app/widget/standardcombos/videodividercombobox.h new file mode 100644 index 000000000..89b6c1b6a --- /dev/null +++ b/app/widget/standardcombos/videodividercombobox.h @@ -0,0 +1,69 @@ +/*** + + 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 . + +***/ + +#ifndef VIDEODIVIDERCOMBOBOX_H +#define VIDEODIVIDERCOMBOBOX_H + +#include + +#include "render/videoparams.h" + +OLIVE_NAMESPACE_ENTER + +class VideoDividerComboBox : public QComboBox +{ + Q_OBJECT +public: + VideoDividerComboBox(QWidget* parent = nullptr) : + QComboBox(parent) + { + foreach (int d, VideoParams::kSupportedDividers) { + QString name; + + if (d == 1) { + name = tr("Full"); + } else { + name = tr("1/%1").arg(d); + } + + this->addItem(name, d); + } + } + + int GetDivider() const + { + return this->currentData().toInt(); + } + + void SetDivider(int d) + { + for (int i=0; icount(); i++) { + if (this->itemData(i).toInt() == d) { + this->setCurrentIndex(i); + break; + } + } + } + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // VIDEODIVIDERCOMBOBOX_H diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 4cfab0008..c4df185ce 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -88,6 +88,8 @@ void FootageViewerWidget::SetFootage(Footage *footage) video_stream->height(), video_stream->frame_rate().flipped(), static_cast(Config::Current()["DefaultSequencePreviewFormat"].toInt()), + video_stream->pixel_aspect_ratio(), + video_stream->interlacing(), VideoParams::generate_auto_divider(video_stream->width(), video_stream->height()))); NodeParam::ConnectEdge(video_node_->output(), viewer_node_->texture_input()); } else { @@ -98,6 +100,8 @@ void FootageViewerWidget::SetFootage(Footage *footage) height, Config::Current()["DefaultSequenceFrameRate"].value(), static_cast(Config::Current()["DefaultSequencePreviewFormat"].toInt()), + Config::Current()["DefaultSequencePixelAspect"].value(), + Config::Current()["DefaultSequenceInterlacing"].value(), VideoParams::generate_auto_divider(width, height))); } diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 3c1cc304c..b6777049f 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -164,7 +164,8 @@ void ViewerWidget::TimeChangedEvent(const int64_t &i) void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) { - connect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot); + connect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SetViewerResolution); + connect(n, &ViewerOutput::PixelAspectChanged, this, &ViewerWidget::SetViewerPixelAspect); connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); connect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererVideoParameters); connect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters); @@ -176,7 +177,8 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) n->audio_playback_cache()->SetParameters(n->audio_params()); - SizeChangedSlot(n->video_params().width(), n->video_params().height()); + SetViewerResolution(n->video_params().width(), n->video_params().height()); + SetViewerPixelAspect(n->video_params().pixel_aspect_ratio()); last_length_ = rational(); LengthChangedSlot(n->GetLength()); @@ -213,7 +215,8 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) { PauseInternal(); - disconnect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot); + disconnect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SetViewerResolution); + disconnect(n, &ViewerOutput::PixelAspectChanged, this, &ViewerWidget::SetViewerPixelAspect); disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); disconnect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererVideoParameters); disconnect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters); @@ -224,7 +227,7 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) ruler()->SetPlaybackCache(nullptr); // Effectively disables the viewer and clears the state - SizeChangedSlot(0, 0); + SetViewerResolution(0, 0); display_widget_->DisconnectColorManager(); foreach (ViewerWindow* window, windows_) { @@ -280,11 +283,6 @@ void ViewerWidget::SetColorMenuEnabled(bool enabled) color_menu_enabled_ = enabled; } -void ViewerWidget::SetOverrideSize(int width, int height) -{ - SizeChangedSlot(width, height); -} - void ViewerWidget::SetMatrix(const QMatrix4x4 &mat) { display_widget_->SetMatrix(mat); @@ -1061,7 +1059,7 @@ void ViewerWidget::PlaybackTimerUpdate() } } -void ViewerWidget::SizeChangedSlot(int width, int height) +void ViewerWidget::SetViewerResolution(int width, int height) { sizer_->SetChildSize(width, height); @@ -1070,6 +1068,13 @@ void ViewerWidget::SizeChangedSlot(int width, int height) } } +void ViewerWidget::SetViewerPixelAspect(const rational &ratio) +{ + sizer_->SetPixelAspectRatio(ratio); + + // FIXME: Update windows too +} + void ViewerWidget::LengthChangedSlot(const rational &length) { if (last_length_ != length) { diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 6e171e6e3..419aa8c65 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -73,8 +73,6 @@ public: */ void SetColorMenuEnabled(bool enabled); - void SetOverrideSize(int width, int height); - void SetMatrix(const QMatrix4x4& mat); /** @@ -124,6 +122,10 @@ public slots: void CacheSequenceInOut(); + void SetViewerResolution(int width, int height); + + void SetViewerPixelAspect(const rational& ratio); + signals: /** * @brief Wrapper for ViewerGLWidget::CursorColor() @@ -257,8 +259,6 @@ private: private slots: void PlaybackTimerUpdate(); - void SizeChangedSlot(int width, int height); - void LengthChangedSlot(const rational& length); void UpdateRendererVideoParameters(); diff --git a/app/widget/viewer/viewersizer.cpp b/app/widget/viewer/viewersizer.cpp index 8f492d488..75f85fd85 100644 --- a/app/widget/viewer/viewersizer.cpp +++ b/app/widget/viewer/viewersizer.cpp @@ -27,7 +27,9 @@ OLIVE_NAMESPACE_ENTER ViewerSizer::ViewerSizer(QWidget *parent) : QWidget(parent), widget_(nullptr), - aspect_ratio_(0), + width_(0), + height_(0), + pixel_aspect_(1), zoom_(0) { } @@ -51,11 +53,12 @@ void ViewerSizer::SetChildSize(int width, int height) width_ = width; height_ = height; - if (!width_ || !height_) { - aspect_ratio_ = 0; - } else { - aspect_ratio_ = static_cast(width_) / static_cast(height_); - } + UpdateSize(); +} + +void ViewerSizer::SetPixelAspectRatio(const rational &pixel_aspect) +{ + pixel_aspect_ = pixel_aspect; UpdateSize(); } @@ -81,7 +84,7 @@ void ViewerSizer::UpdateSize() } // If the aspect ratio is 0, the widget is always hidden - if (qIsNull(aspect_ratio_)) { + if (!width_ || !height_) { widget_->setVisible(false); return; } @@ -91,6 +94,8 @@ void ViewerSizer::UpdateSize() QSize child_size; QMatrix4x4 child_matrix; + double sequence_aspect_ratio = static_cast(width_) / static_cast(height_) * pixel_aspect_.toDouble(); + if (zoom_ <= 0) { // If zoom is 0, we auto-fit @@ -98,12 +103,12 @@ void ViewerSizer::UpdateSize() child_size = size(); - if (our_aspect_ratio > aspect_ratio_) { + if (our_aspect_ratio > sequence_aspect_ratio) { // This container is wider than the image, scale by height - child_size = QSize(qRound(child_size.height() * aspect_ratio_), height()); + child_size = QSize(qRound(child_size.height() * sequence_aspect_ratio), height()); } else { // This container is taller than the image, scale by width - child_size = QSize(width(), qRound(child_size.width() / aspect_ratio_)); + child_size = QSize(width(), qRound(child_size.width() / sequence_aspect_ratio)); } } else { diff --git a/app/widget/viewer/viewersizer.h b/app/widget/viewer/viewersizer.h index 2363f28cf..a6dbfc34f 100644 --- a/app/widget/viewer/viewersizer.h +++ b/app/widget/viewer/viewersizer.h @@ -24,6 +24,7 @@ #include #include "common/define.h" +#include "common/rational.h" OLIVE_NAMESPACE_ENTER @@ -56,6 +57,11 @@ public: */ void SetChildSize(int width, int height); + /** + * @brief Set pixel aspect ratio + */ + void SetPixelAspectRatio(const rational& pixel_aspect); + /** * @brief Set the zoom value of the child widget * @@ -91,10 +97,7 @@ private: int width_; int height_; - /** - * @brief Aspect ratio calculated from the size provided by SetChildSize() - */ - double aspect_ratio_; + rational pixel_aspect_; /** * @brief Internal zoom value