Files
oak-editor/app/dialog/sequence/sequencedialogpresettab.cpp
T
Mike-Solar 3a3ead6fc0 app: route all FFmpeg access through the ffmpeg_bridge C API
Replace every direct FFmpeg call in the editor and the render worker
with the pure C ffmpeg_bridge API, wrapped in thin C++ adapters that
preserve the original interfaces:

- avframeptr.h: olive::AVFrame adapter around FBFrame handles
- ffmpegutils: format conversion helpers on FB_* constants; the int
  overload is renamed GetCompatibleBridgePixelFormat to avoid a silent
  overload-resolution trap with the PixelFormat enum
- ffmpegdecoder/ffmpegencoder: rewritten as handle-based adapters over
  FBDecoder/FBProbe/FBEncoder/FBScaler/FBResampler
- audioprocessor: FBAudioGraph push/pull adapter
- pluginrenderer/OliveClip: sws/pixdesc usage converted to FBScaler and
  fb_pix_fmt_* queries
- AudioParams/channel layouts are plain uint64_t masks everywhere

Build integration: the root project no longer links FFMPEG directly;
only ffmpeg_bridge does. Binaries resolve the bridge library at runtime
via @loader_path inside the macOS app bundle (copied there post-build)
and via $ORIGIN/../ffmpeg_bridge/bin on Linux; on Windows the DLL is
installed next to the executables, so packages on all three platforms
ship the bridge library.

ffmpeg_bridge gains the extra API the adapters need:
fb_frame_make_writable, fb_decoder_get_format_duration,
fb_resampler_convert_frame, FB_PIX_FMT_YUV440P, SRT validation in
fb_probe_read_subtitle_stream, packed/planar fixes in
fb_encoder_write_audio, and a component-size fix in
fb_pix_fmt_component_size.
2026-07-15 23:00:06 +08:00

300 lines
9.4 KiB
C++

/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "sequencedialogpresettab.h"
#include <QDir>
#include <QGroupBox>
#include <QInputDialog>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QSplitter>
#include <QTreeWidgetItem>
#include <QXmlStreamWriter>
#include "config/config.h"
#include "render/videoparams.h"
#include "ui/icons/icons.h"
#include "widget/menu/menu.h"
namespace olive
{
const int kDataIsPreset = Qt::UserRole;
const int kDataPresetIsCustomRole = Qt::UserRole + 1;
const int kDataPresetDataRole = Qt::UserRole + 2;
SequenceDialogPresetTab::SequenceDialogPresetTab(QWidget *parent)
: QWidget(parent)
, PresetManager<SequencePreset>(this, QStringLiteral("sequencepresets"))
{
QVBoxLayout *outer_layout = new QVBoxLayout(this);
outer_layout->setContentsMargins(0, 0, 0, 0);
preset_tree_ = new QTreeWidget();
preset_tree_->setColumnCount(1);
preset_tree_->setHeaderLabel(tr("Preset"));
preset_tree_->setContextMenuPolicy(Qt::CustomContextMenu);
connect(preset_tree_, &QTreeWidget::customContextMenuRequested, this,
&SequenceDialogPresetTab::ShowContextMenu);
outer_layout->addWidget(preset_tree_);
connect(preset_tree_, &QTreeWidget::currentItemChanged, this,
&SequenceDialogPresetTab::SelectedItemChanged);
connect(preset_tree_, &QTreeWidget::itemDoubleClicked, this,
&SequenceDialogPresetTab::ItemDoubleClicked);
// Add "my presets" folder
my_presets_folder_ = CreateFolder(tr("My Presets"));
preset_tree_->addTopLevelItem(my_presets_folder_);
// Add presets
preset_tree_->addTopLevelItem(
CreateHDPresetFolder(tr("4K UHD"), 3840, 2160, 2));
preset_tree_->addTopLevelItem(
CreateHDPresetFolder(tr("1080p"), 1920, 1080, 1));
preset_tree_->addTopLevelItem(
CreateHDPresetFolder(tr("720p"), 1280, 720, 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; i < GetNumberOfPresets(); i++) {
AddCustomItem(my_presets_folder_, GetPreset(i), i);
}
}
void SequenceDialogPresetTab::SaveParametersAsPreset(SequencePreset preset)
{
PresetPtr preset_ptr = std::make_shared<SequencePreset>(preset);
// If replaced, no need to make another item. If not saved, shared ptr will delete itself
if (SavePreset(preset_ptr) == kAppended) {
AddCustomItem(my_presets_folder_, preset_ptr, GetNumberOfPresets() - 1);
}
}
QTreeWidgetItem *SequenceDialogPresetTab::CreateFolder(const QString &name)
{
QTreeWidgetItem *folder = new QTreeWidgetItem();
folder->setText(0, name);
folder->setIcon(0, icon::Folder);
return folder;
}
QTreeWidgetItem *
SequenceDialogPresetTab::CreateHDPresetFolder(const QString &name, int width,
int height, int divider)
{
const PixelFormat default_format = static_cast<PixelFormat::Format>(
OLIVE_CONFIG("OfflinePixelFormat").toInt());
const bool default_autocache = false;
QTreeWidgetItem *parent = CreateFolder(name);
const uint64_t layout = kChannelLayoutStereo;
AddStandardItem(parent,
std::make_shared<SequencePreset>(
tr("%1 23.976 FPS").arg(name), width, height,
rational(24000, 1001), VideoParams::kPixelAspectSquare,
VideoParams::kInterlaceNone, 48000, layout, divider,
default_format, default_autocache));
AddStandardItem(parent,
std::make_shared<SequencePreset>(
tr("%1 25 FPS").arg(name), width, height,
rational(25, 1), VideoParams::kPixelAspectSquare,
VideoParams::kInterlaceNone, 48000, layout, divider,
default_format, default_autocache));
AddStandardItem(parent,
std::make_shared<SequencePreset>(
tr("%1 29.97 FPS").arg(name), width, height,
rational(30000, 1001), VideoParams::kPixelAspectSquare,
VideoParams::kInterlaceNone, 48000, layout, divider,
default_format, default_autocache));
AddStandardItem(parent,
std::make_shared<SequencePreset>(
tr("%1 50 FPS").arg(name), width, height,
rational(50, 1), VideoParams::kPixelAspectSquare,
VideoParams::kInterlaceNone, 48000, layout, divider,
default_format, default_autocache));
AddStandardItem(parent,
std::make_shared<SequencePreset>(
tr("%1 59.94 FPS").arg(name), width, height,
rational(60000, 1001), VideoParams::kPixelAspectSquare,
VideoParams::kInterlaceNone, 48000, layout, divider,
default_format, default_autocache));
return parent;
}
QTreeWidgetItem *SequenceDialogPresetTab::CreateSDPresetFolder(
const QString &name, int width, int height, const rational &frame_rate,
const rational &standard_par, const rational &wide_par, int divider)
{
const PixelFormat default_format = static_cast<PixelFormat::Format>(
OLIVE_CONFIG("OfflinePixelFormat").toInt());
const bool default_autocache = false;
QTreeWidgetItem *parent = CreateFolder(name);
preset_tree_->addTopLevelItem(parent);
const uint64_t layout = kChannelLayoutStereo;
AddStandardItem(
parent, std::make_shared<SequencePreset>(
tr("%1 Standard").arg(name), width, height, frame_rate,
standard_par, VideoParams::kInterlacedBottomFirst, 48000,
layout, divider, default_format, default_autocache));
AddStandardItem(
parent, std::make_shared<SequencePreset>(
tr("%1 Widescreen").arg(name), width, height, frame_rate,
wide_par, VideoParams::kInterlacedBottomFirst, 48000,
layout, divider, default_format, default_autocache));
return parent;
}
QTreeWidgetItem *SequenceDialogPresetTab::GetSelectedItem()
{
QList<QTreeWidgetItem *> selected_items = preset_tree_->selectedItems();
if (selected_items.isEmpty()) {
return nullptr;
} else {
return selected_items.first();
}
}
QTreeWidgetItem *SequenceDialogPresetTab::GetSelectedCustomPreset()
{
QTreeWidgetItem *sel = GetSelectedItem();
if (sel && sel->data(0, kDataIsPreset).toBool() &&
sel->data(0, kDataPresetIsCustomRole).toBool()) {
return sel;
}
return nullptr;
}
void SequenceDialogPresetTab::AddStandardItem(QTreeWidgetItem *folder,
PresetPtr preset,
const QString &description)
{
int index = default_preset_data_.size();
default_preset_data_.append(preset);
AddItemInternal(folder, preset, false, index, description);
}
void SequenceDialogPresetTab::AddCustomItem(QTreeWidgetItem *folder,
PresetPtr preset, int index,
const QString &description)
{
AddItemInternal(folder, preset, true, index, description);
}
void SequenceDialogPresetTab::AddItemInternal(QTreeWidgetItem *folder,
PresetPtr preset, bool is_custom,
int index,
const QString &description)
{
QTreeWidgetItem *item = new QTreeWidgetItem();
item->setText(0, preset->GetName());
item->setIcon(0, icon::Video);
item->setToolTip(0, description);
item->setData(0, kDataIsPreset, true);
item->setData(0, kDataPresetIsCustomRole, is_custom);
item->setData(0, kDataPresetDataRole, index);
folder->addChild(item);
}
void SequenceDialogPresetTab::SelectedItemChanged(QTreeWidgetItem *current,
QTreeWidgetItem *previous)
{
Q_UNUSED(previous)
if (current->data(0, kDataIsPreset).toBool()) {
int preset_index = current->data(0, kDataPresetDataRole).toInt();
PresetPtr preset_data =
(current->data(0, kDataPresetIsCustomRole).toBool()) ?
GetPreset(preset_index) :
default_preset_data_.at(preset_index);
emit PresetChanged(*static_cast<SequencePreset *>(preset_data.get()));
}
}
void SequenceDialogPresetTab::ItemDoubleClicked(QTreeWidgetItem *item,
int column)
{
Q_UNUSED(column)
if (item->data(0, kDataIsPreset).toBool()) {
emit PresetAccepted();
}
}
void SequenceDialogPresetTab::ShowContextMenu()
{
QTreeWidgetItem *sel = GetSelectedCustomPreset();
if (sel) {
Menu m(this);
QAction *delete_action = m.addAction(tr("Delete Preset"));
connect(delete_action, &QAction::triggered, this,
&SequenceDialogPresetTab::DeleteSelectedPreset);
m.exec(QCursor::pos());
}
}
void SequenceDialogPresetTab::DeleteSelectedPreset()
{
QTreeWidgetItem *sel = GetSelectedCustomPreset();
if (sel) {
int preset_index = sel->data(0, kDataPresetDataRole).toInt();
// Shift all items whose index was after this preset forward
for (int i = 0; i < my_presets_folder_->childCount(); i++) {
QTreeWidgetItem *custom_item = my_presets_folder_->child(i);
int this_item_index =
custom_item->data(0, kDataPresetDataRole).toInt();
if (this_item_index > preset_index) {
custom_item->setData(0, kDataPresetDataRole,
this_item_index - 1);
}
}
// Remove the preset
DeletePreset(preset_index);
// Delete the item
delete sel;
}
}
}