app: migrate all engine access to the C ABI facade (nm U _ZN5olive = 0)
Every app module now reaches liboakengine exclusively through oakengine_* C calls, EngineEventBridge subscriptions and app-side handle headers (cliphandle/keyframehandle/nodevaluehandle/oakvaluehelper). Direct C++ command construction, engine signal connect()s, and engine type usage in MOC-visible signatures are gone: 557 -> 0 undefined olive:: symbols in oak-editor.
This commit is contained in:
@@ -26,6 +26,8 @@ set(OLIVE_SOURCES
|
||||
widget/viewer/viewerdisplay.h
|
||||
widget/viewer/viewerpreventsleep.cpp
|
||||
widget/viewer/viewerpreventsleep.h
|
||||
widget/viewer/viewerplaybacktimer.cpp
|
||||
widget/viewer/viewerplaybacktimer.h
|
||||
widget/viewer/viewerqueue.h
|
||||
widget/viewer/viewersafemargininfo.h
|
||||
widget/viewer/viewersizer.cpp
|
||||
@@ -34,5 +36,7 @@ set(OLIVE_SOURCES
|
||||
widget/viewer/viewertexteditor.h
|
||||
widget/viewer/viewerwindow.cpp
|
||||
widget/viewer/viewerwindow.h
|
||||
widget/viewer/vieweroutpututils.h
|
||||
widget/viewer/vieweroutpututils.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -25,8 +25,10 @@
|
||||
#include <QPainter>
|
||||
#include <QtMath>
|
||||
|
||||
#include "config/config.h"
|
||||
#include "common/configwrapper.h"
|
||||
#include "engineeventbridge.h"
|
||||
#include "timeline/timelinecommon.h"
|
||||
#include "widget/viewer/vieweroutpututils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -36,6 +38,8 @@ namespace olive
|
||||
AudioWaveformView::AudioWaveformView(QWidget *parent)
|
||||
: super(parent)
|
||||
, playback_(nullptr)
|
||||
, waveform_bridge_(nullptr)
|
||||
, waveform_subscription_(0)
|
||||
{
|
||||
setAutoFillBackground(true);
|
||||
setBackgroundRole(QPalette::Base);
|
||||
@@ -46,6 +50,13 @@ AudioWaveformView::AudioWaveformView(QWidget *parent)
|
||||
// originates from the center. But we're leaving it top/left for now since it was just
|
||||
// ported from a QWidget's paintEvent.
|
||||
setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
|
||||
// The connected-waveform notification arrives through the liboakengine
|
||||
// event C ABI (replaces the old ViewerOutput::connected_waveform_changed
|
||||
// connect); set_viewer() only subscribes/unsubscribes on the handle.
|
||||
waveform_bridge_ = new EngineEventBridge(this);
|
||||
connect(waveform_bridge_, &EngineEventBridge::viewer_connected_waveform_changed,
|
||||
this, [this](OakEngineNode *) { viewport()->update(); });
|
||||
}
|
||||
|
||||
void AudioWaveformView::set_viewer(ViewerOutput *playback)
|
||||
@@ -54,9 +65,8 @@ void AudioWaveformView::set_viewer(ViewerOutput *playback)
|
||||
pool_.clear();
|
||||
pool_.waitForDone();
|
||||
|
||||
disconnect(playback_, &ViewerOutput::connected_waveform_changed,
|
||||
viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
waveform_bridge_->unsubscribe(waveform_subscription_);
|
||||
waveform_subscription_ = 0;
|
||||
|
||||
set_timebase(0);
|
||||
}
|
||||
@@ -64,10 +74,12 @@ void AudioWaveformView::set_viewer(ViewerOutput *playback)
|
||||
playback_ = playback;
|
||||
|
||||
if (playback_) {
|
||||
connect(playback_, &ViewerOutput::connected_waveform_changed, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
waveform_subscription_ = waveform_bridge_->subscribe(
|
||||
reinterpret_cast<OakEngineNode *>(playback_),
|
||||
OAKENGINE_EVENT_VIEWER_CONNECTED_WAVEFORM_CHANGED);
|
||||
|
||||
Rational tb = playback_->get_video_params().frame_rate_as_time_base();
|
||||
Rational tb =
|
||||
viewer_output_video_params(playback_).frame_rate_as_time_base();
|
||||
if (tb.isNull()) {
|
||||
tb = OAK_CONFIG("DefaultSequenceFrameRate")
|
||||
.value<Rational>()
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class EngineEventBridge;
|
||||
|
||||
class AudioWaveformView : public SeekableWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -45,6 +47,12 @@ private:
|
||||
QThreadPool pool_;
|
||||
|
||||
ViewerOutput *playback_;
|
||||
|
||||
// Engine event bridge for the viewer's connected-waveform notification
|
||||
// (facade C ABI); subscription is per-viewer in set_viewer().
|
||||
EngineEventBridge *waveform_bridge_;
|
||||
|
||||
int64_t waveform_subscription_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
#include <QDrag>
|
||||
#include <QMimeData>
|
||||
|
||||
#include "config/config.h"
|
||||
#include "common/configwrapper.h"
|
||||
#include "node/project.h"
|
||||
#include "oakengine/project.h"
|
||||
#include "oakengine/timeline.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -48,13 +50,26 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent)
|
||||
connect(controls_, &PlaybackControls::audio_dragged, this,
|
||||
&FootageViewerWidget::start_audio_drag);
|
||||
|
||||
override_workarea_ = new TimelineWorkArea(this);
|
||||
override_workarea_ = reinterpret_cast<TimelineWorkArea *>(
|
||||
oakengine_workarea_create());
|
||||
}
|
||||
|
||||
FootageViewerWidget::~FootageViewerWidget()
|
||||
{
|
||||
if (override_workarea_) {
|
||||
oakengine_workarea_free(
|
||||
reinterpret_cast<OakEngineWorkarea *>(override_workarea_));
|
||||
}
|
||||
}
|
||||
|
||||
void FootageViewerWidget::override_work_area(const TimeRange &r)
|
||||
{
|
||||
override_workarea_->set_enabled(true);
|
||||
override_workarea_->set_range(r);
|
||||
oakengine_workarea_set_enabled(
|
||||
reinterpret_cast<OakEngineWorkarea *>(override_workarea_), 1);
|
||||
oakengine_workarea_set_range(
|
||||
reinterpret_cast<OakEngineWorkarea *>(override_workarea_),
|
||||
r.in().numerator(), r.in().denominator(),
|
||||
r.out().numerator(), r.out().denominator());
|
||||
this->connect_work_area(override_workarea_);
|
||||
}
|
||||
|
||||
@@ -99,7 +114,7 @@ void FootageViewerWidget::start_footage_drag_internal(bool enable_video,
|
||||
data_stream << streams
|
||||
<< reinterpret_cast<quintptr>(get_connected_node());
|
||||
|
||||
mimedata->setData(Project::k_item_mime_type, encoded_data);
|
||||
mimedata->setData(QString::fromUtf8(oakengine_project_item_mime_type()), encoded_data);
|
||||
drag->setMimeData(mimedata);
|
||||
|
||||
drag->exec();
|
||||
|
||||
@@ -33,6 +33,8 @@ class FootageViewerWidget : public ViewerWidget {
|
||||
public:
|
||||
FootageViewerWidget(QWidget *parent = nullptr);
|
||||
|
||||
~FootageViewerWidget() override;
|
||||
|
||||
void override_work_area(const TimeRange &r);
|
||||
void reset_work_area();
|
||||
|
||||
|
||||
+916
-392
File diff suppressed because it is too large
Load Diff
+70
-37
@@ -29,11 +29,11 @@
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "audio/audioprocessor.h"
|
||||
#include "oakengine/audio.h"
|
||||
#include "audiowaveformview.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "oakengine/playback.h"
|
||||
#include "render/previewautocacher.h"
|
||||
#include "render/previewaudiodevice.h"
|
||||
#include "oakengine/preview.h"
|
||||
#include "viewerdisplay.h"
|
||||
#include "viewersizer.h"
|
||||
#include "viewerwindow.h"
|
||||
@@ -44,6 +44,7 @@
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class EngineEventBridge;
|
||||
class MulticamWidget;
|
||||
|
||||
/**
|
||||
@@ -91,7 +92,7 @@ public:
|
||||
*/
|
||||
void set_full_screen(QScreen *screen = nullptr);
|
||||
|
||||
ColorManager *color_manager() const
|
||||
OakEngineColorManager *color_manager() const
|
||||
{
|
||||
return display_widget_->color_manager();
|
||||
}
|
||||
@@ -122,9 +123,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void set_node_view_selections(const QVector<Node *> &n)
|
||||
void set_node_view_selections(const QVector<OakEngineNode *> &n)
|
||||
{
|
||||
node_view_selected_ = n;
|
||||
node_view_selected_.clear();
|
||||
node_view_selected_.reserve(n.size());
|
||||
foreach (OakEngineNode *handle, n) {
|
||||
node_view_selected_.append(reinterpret_cast<Node *>(handle));
|
||||
}
|
||||
|
||||
if (!is_playing()) {
|
||||
// If is playing, this will happen by the next frame automatically
|
||||
@@ -184,12 +189,12 @@ signals:
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::ColorProcessorChanged()
|
||||
*/
|
||||
void color_processor_changed(ColorProcessorPtr processor);
|
||||
void color_processor_changed(ColorProcessorHandlePtr processor);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::ColorManagerChanged()
|
||||
*/
|
||||
void color_manager_changed(ColorManager *color_manager);
|
||||
void color_manager_changed(OakEngineColorManager *color_manager);
|
||||
|
||||
protected:
|
||||
ViewerWidget(ViewerDisplayWidget *display, QWidget *parent = nullptr);
|
||||
@@ -219,7 +224,7 @@ protected:
|
||||
ignore_scrub_++;
|
||||
}
|
||||
|
||||
RenderTicketPtr get_single_frame(const Rational &t, bool dry = false);
|
||||
OakEnginePreviewRequest *get_single_frame(const Rational &t, bool dry = false);
|
||||
|
||||
void set_waveform_mode(WaveformMode wf);
|
||||
|
||||
@@ -250,16 +255,15 @@ private:
|
||||
bool viewer_might_be_a_still();
|
||||
|
||||
void set_display_image(RenderTicketPtr ticket);
|
||||
void set_display_image(OakEnginePreviewRequest *req);
|
||||
|
||||
RenderTicketPtr get_frame(const Rational &t);
|
||||
OakEnginePreviewRequest *request_next_frame_for_queue(bool increment = true);
|
||||
|
||||
static FramePtr decode_cached_image(const QString &cache_path,
|
||||
const QUuid &cache_id,
|
||||
const int64_t &time);
|
||||
OakEnginePreviewRequest *get_frame(const Rational &t);
|
||||
|
||||
static void decode_cached_image(RenderTicketPtr ticket,
|
||||
const QString &cache_path,
|
||||
const QUuid &cache_id, const int64_t &time);
|
||||
void finish_play_preprocess();
|
||||
|
||||
int determine_playback_queue_size();
|
||||
|
||||
bool should_force_waveform() const;
|
||||
|
||||
@@ -267,6 +271,8 @@ private:
|
||||
|
||||
void update_auto_cacher();
|
||||
|
||||
void decrement_prequeued_audio();
|
||||
|
||||
void arm_for_recording();
|
||||
|
||||
void disarm_recording();
|
||||
@@ -277,15 +283,6 @@ private:
|
||||
|
||||
bool is_video_visible() const;
|
||||
|
||||
void deliver_facade_frame(const FramePtr &frame, const Rational &ts);
|
||||
|
||||
// Trampolines for the facade playback engine's pull-thread
|
||||
// callbacks; both marshal onto the main thread.
|
||||
static void facade_frame_callback(const oak_playback_frame *frame,
|
||||
void *userdata);
|
||||
static void facade_audio_callback(const oak_playback_audio *audio,
|
||||
void *userdata);
|
||||
|
||||
ViewerSizer *sizer_;
|
||||
|
||||
int playback_speed_;
|
||||
@@ -306,24 +303,33 @@ private:
|
||||
|
||||
ViewerDisplayWidget *context_menu_widget_;
|
||||
|
||||
// Facade playback session (created on first play; the engine drives
|
||||
// frames/audio from its pull thread and we poll its position).
|
||||
OakEnginePlayback *playback_;
|
||||
QTimer playback_poll_timer_;
|
||||
|
||||
// Sample-rate/channel converter for the audio SCRUB path (continuous
|
||||
// playback audio is handled by the facade engine itself).
|
||||
AudioProcessor audio_processor_;
|
||||
QTimer playback_backup_timer_;
|
||||
|
||||
int64_t playback_queue_next_frame_;
|
||||
int64_t dry_run_next_frame_;
|
||||
QVector<ViewerDisplayWidget *> playback_devices_;
|
||||
|
||||
QList<RenderTicketWatcher *> nonqueue_watchers_;
|
||||
bool prequeuing_video_;
|
||||
int prequeuing_audio_;
|
||||
|
||||
QList<OakEnginePreviewRequest *> nonqueue_watchers_;
|
||||
|
||||
Rational last_length_;
|
||||
|
||||
int prequeue_length_;
|
||||
int prequeue_count_;
|
||||
|
||||
QVector<OakEnginePreviewRequest *> queue_watchers_;
|
||||
|
||||
std::list<OakEnginePreviewRequest *> audio_playback_queue_;
|
||||
Rational audio_playback_queue_time_;
|
||||
OakEngineAudioProcessor *audio_processor_;
|
||||
QByteArray prequeued_audio_;
|
||||
static const Rational k_audio_playback_interval;
|
||||
|
||||
static QVector<ViewerWidget *> instances;
|
||||
|
||||
std::list<RenderTicketWatcher *> audio_scrub_watchers_;
|
||||
std::list<OakEnginePreviewRequest *> audio_scrub_watchers_;
|
||||
|
||||
bool record_armed_;
|
||||
bool recording_;
|
||||
@@ -332,10 +338,15 @@ private:
|
||||
Track::Reference recording_track_;
|
||||
QString recording_filename_;
|
||||
|
||||
qint64 queue_starved_start_;
|
||||
OakEnginePreviewRequest *first_requeue_watcher_;
|
||||
|
||||
bool enable_audio_scrubbing_;
|
||||
|
||||
WaveformMode waveform_mode_;
|
||||
|
||||
QVector<OakEnginePreviewRequest *> dry_run_watchers_;
|
||||
|
||||
int ignore_scrub_;
|
||||
|
||||
QVector<Block *> timeline_selected_blocks_;
|
||||
@@ -343,12 +354,16 @@ private:
|
||||
|
||||
MulticamWidget *multicam_panel_;
|
||||
|
||||
EngineEventBridge *bridge_;
|
||||
|
||||
int64_t audio_notify_sub_ = 0;
|
||||
|
||||
private slots:
|
||||
void playback_poll_update();
|
||||
void playback_timer_update();
|
||||
|
||||
void length_changed_slot(const Rational &length);
|
||||
|
||||
void interlacing_changed_slot(VideoParams::Interlacing interlacing);
|
||||
void interlacing_changed_slot(int interlacing);
|
||||
|
||||
void update_renderer_video_parameters();
|
||||
|
||||
@@ -374,6 +389,8 @@ private slots:
|
||||
|
||||
void renderer_generated_frame();
|
||||
|
||||
void renderer_generated_frame_for_queue();
|
||||
|
||||
void viewer_invalidated_video_range(const olive::TimeRange &range);
|
||||
|
||||
void update_waveform_mode_from_menu(QAction *a);
|
||||
@@ -382,14 +399,30 @@ private slots:
|
||||
|
||||
void dropped(QDropEvent *event);
|
||||
|
||||
void queue_next_audio_buffer();
|
||||
|
||||
void received_audio_buffer_for_playback();
|
||||
|
||||
void received_audio_buffer_for_scrubbing();
|
||||
|
||||
void queue_starved();
|
||||
void queue_no_longer_starved();
|
||||
|
||||
void force_requeue_from_current_time();
|
||||
void force_requeue_from_current_time_internal();
|
||||
|
||||
void update_audio_processor();
|
||||
|
||||
void create_addable_at(const QRectF &f);
|
||||
|
||||
void handle_first_requeue_destroy();
|
||||
|
||||
void show_subtitle_properties();
|
||||
|
||||
void dry_run_finished();
|
||||
|
||||
void request_next_dry_run();
|
||||
|
||||
void save_frame_as_image();
|
||||
|
||||
void detect_multicam_node_now();
|
||||
|
||||
+147
-100
@@ -35,10 +35,16 @@
|
||||
#include <QScreen>
|
||||
#include <QTextEdit>
|
||||
|
||||
#include "audio/audiomanager.h"
|
||||
#include "common/define.h"
|
||||
#include "common/html.h"
|
||||
#include "oakengine/gizmo.h"
|
||||
#include "oakengine/videoparams.h"
|
||||
#include "oakengine/display.h"
|
||||
#include "oakengine/undo.h"
|
||||
#include "widget/viewer/vieweroutpututils.h"
|
||||
#include "common/qtutils.h"
|
||||
#include "config/config.h"
|
||||
#include "common/configwrapper.h"
|
||||
#include "core.h"
|
||||
#include "node/block/subtitle/subtitle.h"
|
||||
#include "codec/frame.h"
|
||||
@@ -46,6 +52,7 @@
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/gizmo/screen.h"
|
||||
#include "render/job/colortransformjob.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
namespace olive
|
||||
@@ -72,6 +79,7 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent)
|
||||
, add_band_(false)
|
||||
, queue_starved_(false)
|
||||
, text_edit_(nullptr)
|
||||
, gizmo_params_(empty_video_params())
|
||||
{
|
||||
connect(Core::instance(), &Core::tool_changed, this,
|
||||
&ViewerDisplayWidget::tool_changed);
|
||||
@@ -83,6 +91,10 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent)
|
||||
frame_rate_averages_.resize(k_frame_rate_average_count);
|
||||
|
||||
inner_widget()->setAcceptDrops(true);
|
||||
|
||||
bridge_ = new EngineEventBridge(this);
|
||||
connect(bridge_, &EngineEventBridge::sequence_subtitles_changed, this,
|
||||
[this](OakEngineSequence *, qint64, qint64) { update(); });
|
||||
}
|
||||
|
||||
ViewerDisplayWidget::~ViewerDisplayWidget()
|
||||
@@ -223,15 +235,16 @@ void ViewerDisplayWidget::set_time(const Rational &time)
|
||||
void ViewerDisplayWidget::set_subtitle_tracks(Sequence *list)
|
||||
{
|
||||
if (subtitle_tracks_) {
|
||||
disconnect(subtitle_tracks_, &Sequence::subtitles_changed, this,
|
||||
&ViewerDisplayWidget::subtitles_changed);
|
||||
bridge_->unsubscribe(subtitle_sub_);
|
||||
subtitle_sub_ = 0;
|
||||
}
|
||||
|
||||
subtitle_tracks_ = list;
|
||||
|
||||
if (subtitle_tracks_) {
|
||||
connect(subtitle_tracks_, &Sequence::subtitles_changed, this,
|
||||
&ViewerDisplayWidget::subtitles_changed);
|
||||
subtitle_sub_ = bridge_->subscribe(
|
||||
reinterpret_cast<void *>(subtitle_tracks_),
|
||||
OAKENGINE_EVENT_SEQUENCE_SUBTITLES_CHANGED);
|
||||
}
|
||||
|
||||
update();
|
||||
@@ -396,7 +409,7 @@ void ViewerDisplayWidget::on_paint()
|
||||
bg_color.greenF(), bg_color.blueF());
|
||||
}
|
||||
|
||||
VideoParams device_params;
|
||||
VideoParams device_params = empty_video_params();
|
||||
ColorTransformJob ctj;
|
||||
bool have_ctj = false;
|
||||
|
||||
@@ -420,11 +433,12 @@ void ViewerDisplayWidget::on_paint()
|
||||
texture_->height() != frame->height() ||
|
||||
texture_->format() != frame->format() ||
|
||||
texture_->channel_count() != frame->channel_count())) {
|
||||
texture_ = renderer()->create_texture(
|
||||
frame->video_params(), frame->data(),
|
||||
frame->linesize_pixels());
|
||||
oakengine_display_renderer_create_texture(
|
||||
renderer(), &frame->video_params(), frame->data(),
|
||||
frame->linesize_pixels(), &texture_);
|
||||
} else if (!drew_backend_neutral_frame) {
|
||||
texture_->upload(frame->data(), frame->linesize_pixels());
|
||||
oakengine_display_texture_upload(texture_.get(), frame->data(),
|
||||
frame->linesize_pixels());
|
||||
}
|
||||
} else if (TexturePtr texture = load_frame_.value<TexturePtr>()) {
|
||||
// This is a GPU texture, switch to it directly when possible.
|
||||
@@ -439,15 +453,17 @@ void ViewerDisplayWidget::on_paint()
|
||||
texture_ = texture;
|
||||
} else {
|
||||
// Cross-backend texture: download and re-upload
|
||||
FramePtr frame = Frame::create();
|
||||
frame->set_video_params(texture->params());
|
||||
if (frame->allocate()) {
|
||||
FramePtr frame;
|
||||
oakengine_codec_frame_create(&frame);
|
||||
oakengine_codec_frame_set_video_params(
|
||||
frame.get(), &texture->params());
|
||||
if (oakengine_codec_frame_allocate(frame.get())) {
|
||||
texture->renderer()->download_from_texture(
|
||||
texture->id(), texture->params(), frame->data(),
|
||||
frame->linesize_pixels());
|
||||
texture_ = renderer()->create_texture(
|
||||
frame->video_params(), frame->data(),
|
||||
frame->linesize_pixels());
|
||||
oakengine_display_renderer_create_texture(
|
||||
renderer(), &frame->video_params(), frame->data(),
|
||||
frame->linesize_pixels(), &texture_);
|
||||
} else {
|
||||
texture_ = texture;
|
||||
}
|
||||
@@ -488,8 +504,9 @@ void ViewerDisplayWidget::on_paint()
|
||||
deinterlace_texture_->params() !=
|
||||
texture_to_draw->params()) {
|
||||
// (Re)create texture
|
||||
deinterlace_texture_ = renderer()->create_texture(
|
||||
texture_to_draw->params());
|
||||
oakengine_display_renderer_create_texture(
|
||||
renderer(), &texture_to_draw->params(), nullptr,
|
||||
0, &deinterlace_texture_);
|
||||
}
|
||||
|
||||
ShaderJob job;
|
||||
@@ -509,7 +526,8 @@ void ViewerDisplayWidget::on_paint()
|
||||
texture_to_draw = deinterlace_texture_;
|
||||
}
|
||||
|
||||
ctj.set_color_processor(color_service());
|
||||
oakengine_color_transform_job_set_processor(
|
||||
&ctj, color_service().get());
|
||||
ctj.set_input_texture(texture_to_draw);
|
||||
ctj.set_input_alpha_association(
|
||||
OAK_CONFIG("ReassocLinToNonLin").toBool() ?
|
||||
@@ -531,7 +549,8 @@ void ViewerDisplayWidget::on_paint()
|
||||
if (backend_neutral) {
|
||||
draw_backend_neutral(ctj, &bg_painter);
|
||||
} else {
|
||||
renderer()->blit_color_managed(ctj, device_params);
|
||||
oakengine_display_renderer_blit_color_managed(
|
||||
renderer(), &ctj, nullptr, &device_params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,20 +808,25 @@ QTransform ViewerDisplayWidget::generate_display_transform()
|
||||
return gizmo_transform;
|
||||
}
|
||||
|
||||
QTransform ViewerDisplayWidget::generate_gizmo_transform(NodeTraverser >,
|
||||
QTransform ViewerDisplayWidget::generate_gizmo_transform(Node *gizmos,
|
||||
Node *target,
|
||||
const TimeRange &range)
|
||||
{
|
||||
QTransform t = generate_display_transform();
|
||||
if (get_time_target()) {
|
||||
Node *target = get_time_target();
|
||||
if (target) {
|
||||
if (ViewerOutput *v = dynamic_cast<ViewerOutput *>(target)) {
|
||||
if (Node *n = v->get_connected_texture_output()) {
|
||||
target = n;
|
||||
}
|
||||
}
|
||||
|
||||
QTransform nt;
|
||||
gt.transform(&nt, gizmos_, target, range);
|
||||
double m[6];
|
||||
oakengine_traverse_transform(
|
||||
reinterpret_cast<OakEngineNode *>(gizmos),
|
||||
reinterpret_cast<OakEngineNode *>(target),
|
||||
range.in().numerator(), range.in().denominator(),
|
||||
range.out().numerator(), range.out().denominator(), nullptr, m);
|
||||
QTransform nt(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
|
||||
t.translate(gizmo_params_.width() * 0.5, gizmo_params_.height() * 0.5);
|
||||
t.scale(gizmo_params_.width(), gizmo_params_.height());
|
||||
@@ -861,8 +885,6 @@ void ViewerDisplayWidget::open_text_gizmo(TextGizmo *text, QMouseEvent *event)
|
||||
gizmo_draw_time_, LoopMode::k_loop_mode_off));
|
||||
|
||||
active_text_gizmo_ = text;
|
||||
connect(active_text_gizmo_, &TextGizmo::rect_changed, this,
|
||||
&ViewerDisplayWidget::update_active_text_gizmo_size);
|
||||
text_transform_ = generate_gizmo_transform();
|
||||
text_transform_inverted_ = text_transform_.inverted();
|
||||
|
||||
@@ -899,16 +921,30 @@ void ViewerDisplayWidget::open_text_gizmo(TextGizmo *text, QMouseEvent *event)
|
||||
QRectF text_rect = update_active_text_gizmo_size();
|
||||
|
||||
// Emit text gizmo activation signal
|
||||
emit text->activated();
|
||||
oakengine_text_gizmo_activated(
|
||||
reinterpret_cast<OakEngineNode *>(gizmos_));
|
||||
|
||||
// Create toolbar
|
||||
text_toolbar_ = new ViewerTextEditorToolBar(text_edit_);
|
||||
text_toolbar_->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
connect(text_toolbar_, &ViewerTextEditorToolBar::vertical_alignment_changed,
|
||||
text, &TextGizmo::set_vertical_alignment);
|
||||
connect(text, &TextGizmo::vertical_alignment_changed, text_toolbar_,
|
||||
&ViewerTextEditorToolBar::set_vertical_alignment);
|
||||
text_toolbar_->set_vertical_alignment(text->get_vertical_alignment());
|
||||
this, [this](Qt::Alignment align) {
|
||||
oakengine_text_gizmo_set_vertical_alignment(
|
||||
reinterpret_cast<OakEngineNode *>(gizmos_),
|
||||
static_cast<int>(align));
|
||||
});
|
||||
{
|
||||
oakengine_text_gizmo _tg;
|
||||
if (oakengine_text_gizmo_get(
|
||||
reinterpret_cast<OakEngineNode *>(gizmos_),
|
||||
get_gizmo_time().numerator(), get_gizmo_time().denominator(),
|
||||
&_tg) == OAKENGINE_OK) {
|
||||
text_toolbar_->set_vertical_alignment(
|
||||
static_cast<Qt::AlignmentFlag>(_tg.vertical_alignment));
|
||||
} else {
|
||||
text_toolbar_->set_vertical_alignment(Qt::AlignTop);
|
||||
}
|
||||
}
|
||||
text_edit_->connect_tool_bar(text_toolbar_);
|
||||
|
||||
QPoint toolbar_pos =
|
||||
@@ -1033,40 +1069,41 @@ bool ViewerDisplayWidget::on_mouse_move(QMouseEvent *event)
|
||||
|
||||
} else if (current_gizmo_) {
|
||||
// Signal movement
|
||||
if (DraggableGizmo *draggable =
|
||||
dynamic_cast<DraggableGizmo *>(current_gizmo_)) {
|
||||
int drag_behavior = oakengine_gizmo_get_drag_value_behavior(current_gizmo_);
|
||||
if (drag_behavior >= 0) {
|
||||
if (!gizmo_drag_started_) {
|
||||
QPointF start = screen_to_scene_point(gizmo_start_drag_);
|
||||
|
||||
Rational gizmo_time = get_gizmo_time();
|
||||
NodeTraverser t;
|
||||
t.set_cache_video_params(gizmo_params_);
|
||||
t.set_cache_audio_params(gizmo_audio_params_);
|
||||
NodeValueRow row = t.generate_row(
|
||||
gizmos_,
|
||||
TimeRange(gizmo_time,
|
||||
gizmo_time +
|
||||
gizmo_params_.frame_rate_as_time_base()));
|
||||
NodeValueRow row;
|
||||
oakengine_traverse_generate_row(
|
||||
reinterpret_cast<OakEngineNode *>(gizmos_),
|
||||
gizmo_time.numerator(), gizmo_time.denominator(),
|
||||
(gizmo_time + gizmo_params_.frame_rate_as_time_base())
|
||||
.numerator(),
|
||||
(gizmo_time + gizmo_params_.frame_rate_as_time_base())
|
||||
.denominator(),
|
||||
nullptr, 0, 0, &row);
|
||||
|
||||
draggable->drag_start(row, start.x(), start.y(), gizmo_time);
|
||||
oakengine_gizmo_drag_start(current_gizmo_, &row,
|
||||
start.x(), start.y(), gizmo_time.numerator(),
|
||||
gizmo_time.denominator());
|
||||
gizmo_drag_started_ = true;
|
||||
}
|
||||
|
||||
QPointF v = screen_to_scene_point(event->pos());
|
||||
switch (draggable->get_drag_value_behavior()) {
|
||||
case DraggableGizmo::k_absolute:
|
||||
// Above value is correct
|
||||
break;
|
||||
case DraggableGizmo::k_delta_from_previous:
|
||||
switch (drag_behavior) {
|
||||
case 1:
|
||||
v -= screen_to_scene_point(gizmo_last_drag_);
|
||||
gizmo_last_drag_ = event->pos();
|
||||
break;
|
||||
case DraggableGizmo::k_delta_from_start:
|
||||
case 2:
|
||||
v -= screen_to_scene_point(gizmo_start_drag_);
|
||||
break;
|
||||
}
|
||||
|
||||
draggable->drag_move(v.x(), v.y(), event->modifiers());
|
||||
oakengine_gizmo_drag_move(current_gizmo_, v.x(), v.y(),
|
||||
static_cast<int>(event->modifiers()));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1101,12 +1138,9 @@ bool ViewerDisplayWidget::on_mouse_release(QMouseEvent *e)
|
||||
} else if (current_gizmo_) {
|
||||
// Handle gizmo
|
||||
if (gizmo_drag_started_) {
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
if (DraggableGizmo *draggable =
|
||||
dynamic_cast<DraggableGizmo *>(current_gizmo_)) {
|
||||
draggable->drag_end(command);
|
||||
}
|
||||
Core::instance()->undo_stack()->push(command, tr("Dragged Gizmo"));
|
||||
void *command = oakengine_undo_command_create_multi();
|
||||
oakengine_gizmo_drag_end(current_gizmo_, command);
|
||||
oakengine_undo_push(command, tr("Dragged Gizmo").toUtf8().constData());
|
||||
gizmo_drag_started_ = false;
|
||||
}
|
||||
current_gizmo_ = nullptr;
|
||||
@@ -1173,7 +1207,7 @@ void ViewerDisplayWidget::emit_color_at_cursor(QMouseEvent *e)
|
||||
reference =
|
||||
renderer()->get_pixel_from_texture(texture_.get(), pixel_pos);
|
||||
if (color_service()) {
|
||||
display = color_service()->convert_color(reference);
|
||||
display = oak_convert_color(color_service(), reference);
|
||||
} else {
|
||||
display = reference;
|
||||
}
|
||||
@@ -1231,8 +1265,13 @@ void ViewerDisplayWidget::draw_subtitle_tracks()
|
||||
if (SubtitleBlock *sub = dynamic_cast<SubtitleBlock *>(
|
||||
sub_track->visible_block_at_time(time_))) {
|
||||
// Split into lines
|
||||
char text_buf[4096];
|
||||
const int len = oakengine_subtitle_get_text(
|
||||
reinterpret_cast<OakEngineNode *>(sub), text_buf,
|
||||
sizeof(text_buf));
|
||||
QStringList list = QtUtils::word_wrap_string(
|
||||
sub->get_text(), fm, bounding_box.width());
|
||||
QString::fromUtf8(text_buf, len), fm,
|
||||
bounding_box.width());
|
||||
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
int w = QtUtils::q_font_metrics_width(fm, list.at(i));
|
||||
@@ -1393,24 +1432,26 @@ void ViewerDisplayWidget::close_text_editor()
|
||||
text_edit_->deleteLater();
|
||||
text_edit_ = nullptr;
|
||||
|
||||
disconnect(active_text_gizmo_, &TextGizmo::rect_changed, this,
|
||||
&ViewerDisplayWidget::update_active_text_gizmo_size);
|
||||
active_text_gizmo_ = nullptr;
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::generate_gizmo_transforms()
|
||||
{
|
||||
NodeTraverser gt;
|
||||
gt.set_cache_video_params(gizmo_params_);
|
||||
gt.set_cache_audio_params(gizmo_audio_params_);
|
||||
|
||||
gizmo_draw_time_ = generate_gizmo_time();
|
||||
|
||||
if (gizmos_) {
|
||||
gizmo_db_ = gt.generate_row(gizmos_, gizmo_draw_time_);
|
||||
NodeValueRow row;
|
||||
oakengine_traverse_generate_row(
|
||||
reinterpret_cast<OakEngineNode *>(gizmos_),
|
||||
gizmo_draw_time_.in().numerator(),
|
||||
gizmo_draw_time_.in().denominator(),
|
||||
gizmo_draw_time_.out().numerator(),
|
||||
gizmo_draw_time_.out().denominator(), nullptr, 0, 0, &row);
|
||||
gizmo_db_ = row;
|
||||
}
|
||||
|
||||
gizmo_last_draw_transform_ = generate_gizmo_transform(gt, gizmo_draw_time_);
|
||||
gizmo_last_draw_transform_ = generate_gizmo_transform(
|
||||
gizmos_, get_time_target(), gizmo_draw_time_);
|
||||
gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted();
|
||||
}
|
||||
|
||||
@@ -1437,7 +1478,9 @@ bool ViewerDisplayWidget::draw_backend_neutral_frame(const FramePtr &frame,
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString color_id = QString::fromUtf8(color_service()->id());
|
||||
const QString color_id = oak_query_string([this](char *buf, int size) {
|
||||
return oakengine_color_processor_id(color_service().get(), buf, size);
|
||||
});
|
||||
if (backend_neutral_cpu_source_frame_.get() == frame.get() &&
|
||||
backend_neutral_cpu_color_id_ == color_id &&
|
||||
!backend_neutral_cpu_image_.isNull()) {
|
||||
@@ -1457,7 +1500,7 @@ bool ViewerDisplayWidget::draw_backend_neutral_frame(const FramePtr &frame,
|
||||
|
||||
QImage source_image;
|
||||
if (display_frame->format() == PixelFormat::u8 &&
|
||||
display_frame->channel_count() == VideoParams::k_rgba_channel_count) {
|
||||
display_frame->channel_count() == 4) {
|
||||
backend_neutral_cpu_display_frame_ = display_frame;
|
||||
backend_neutral_cpu_image_ =
|
||||
QImage(reinterpret_cast<const uchar *>(display_frame->const_data()),
|
||||
@@ -1466,7 +1509,7 @@ bool ViewerDisplayWidget::draw_backend_neutral_frame(const FramePtr &frame,
|
||||
source_image = backend_neutral_cpu_image_;
|
||||
} else if (display_frame->format() == PixelFormat::u8 &&
|
||||
display_frame->channel_count() ==
|
||||
VideoParams::k_rgb_channel_count) {
|
||||
3) {
|
||||
backend_neutral_cpu_display_frame_ = display_frame;
|
||||
backend_neutral_cpu_image_ =
|
||||
QImage(reinterpret_cast<const uchar *>(display_frame->const_data()),
|
||||
@@ -1476,7 +1519,9 @@ bool ViewerDisplayWidget::draw_backend_neutral_frame(const FramePtr &frame,
|
||||
} else {
|
||||
backend_neutral_cpu_display_frame_.reset();
|
||||
const int bytes_per_pixel =
|
||||
display_frame->video_params().get_bytes_per_pixel();
|
||||
oakengine_video_params_bytes_per_pixel(
|
||||
static_cast<int>(display_frame->video_params().format()),
|
||||
display_frame->video_params().channel_count());
|
||||
if (backend_neutral_cpu_image_.size() !=
|
||||
QSize(display_frame->width(), display_frame->height()) ||
|
||||
backend_neutral_cpu_image_.format() != QImage::Format_RGBA8888) {
|
||||
@@ -1523,7 +1568,9 @@ bool ViewerDisplayWidget::draw_backend_neutral_texture(const TexturePtr &texture
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString color_id = QString::fromUtf8(color_service()->id());
|
||||
const QString color_id = oak_query_string([this](char *buf, int size) {
|
||||
return oakengine_color_processor_id(color_service().get(), buf, size);
|
||||
});
|
||||
if (backend_neutral_cpu_source_texture_.get() == texture.get() &&
|
||||
backend_neutral_cpu_color_id_ == color_id &&
|
||||
!backend_neutral_cpu_image_.isNull()) {
|
||||
@@ -1535,13 +1582,15 @@ bool ViewerDisplayWidget::draw_backend_neutral_texture(const TexturePtr &texture
|
||||
return true;
|
||||
}
|
||||
|
||||
FramePtr frame = Frame::create();
|
||||
frame->set_video_params(texture->params());
|
||||
if (!frame->allocate()) {
|
||||
FramePtr frame;
|
||||
oakengine_codec_frame_create(&frame);
|
||||
oakengine_codec_frame_set_video_params(frame.get(), &texture->params());
|
||||
if (!oakengine_codec_frame_allocate(frame.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
texture->download(frame->data(), frame->linesize_pixels());
|
||||
oakengine_display_texture_download(texture.get(), frame->data(),
|
||||
frame->linesize_pixels());
|
||||
|
||||
if (!draw_backend_neutral_frame(frame, painter)) {
|
||||
return false;
|
||||
@@ -1564,19 +1613,23 @@ void ViewerDisplayWidget::draw_backend_neutral(const ColorTransformJob &ctj,
|
||||
const int texture_width = static_cast<int>(width() * devicePixelRatioF());
|
||||
const int texture_height = static_cast<int>(height() * devicePixelRatioF());
|
||||
|
||||
const VideoParams offscreen_params(texture_width, texture_height,
|
||||
PixelFormat::u8,
|
||||
VideoParams::k_rgba_channel_count);
|
||||
oak_video_params pod = {};
|
||||
pod.width = texture_width;
|
||||
pod.height = texture_height;
|
||||
pod.format = PixelFormat::u8;
|
||||
const VideoParams offscreen_params(video_params_from_pod(pod));
|
||||
|
||||
if (!backend_neutral_texture_ ||
|
||||
backend_neutral_texture_->params() != offscreen_params) {
|
||||
// The offscreen texture is sized in device pixels so high-DPI widgets
|
||||
// draw one downloaded pixel per device pixel after setDevicePixelRatio().
|
||||
backend_neutral_texture_ = renderer()->create_texture(offscreen_params);
|
||||
oakengine_display_renderer_create_texture(renderer(), &offscreen_params,
|
||||
nullptr, 0,
|
||||
&backend_neutral_texture_);
|
||||
backend_neutral_buffer_.resize(
|
||||
texture_width * texture_height *
|
||||
VideoParams::get_bytes_per_pixel(PixelFormat::u8,
|
||||
VideoParams::k_rgba_channel_count));
|
||||
oakengine_video_params_bytes_per_pixel(0, // PixelFormat::u8
|
||||
4));
|
||||
}
|
||||
|
||||
if (!backend_neutral_texture_ || backend_neutral_texture_->is_dummy()) {
|
||||
@@ -1588,12 +1641,13 @@ void ViewerDisplayWidget::draw_backend_neutral(const ColorTransformJob &ctj,
|
||||
|
||||
// Reuse the normal color-management shader path, but render into a texture
|
||||
// instead of an OpenGL widget framebuffer.
|
||||
renderer()->blit_color_managed(local_ctj, backend_neutral_texture_.get());
|
||||
oakengine_display_renderer_blit_color_managed(
|
||||
renderer(), &local_ctj, backend_neutral_texture_.get(), nullptr);
|
||||
|
||||
backend_neutral_texture_->download(backend_neutral_buffer_.data(), 0);
|
||||
oakengine_display_texture_download(backend_neutral_texture_.get(),
|
||||
backend_neutral_buffer_.data(), 0);
|
||||
|
||||
const int bytes_per_pixel = VideoParams::get_bytes_per_pixel(
|
||||
PixelFormat::u8, VideoParams::k_rgba_channel_count);
|
||||
const int bytes_per_pixel = oakengine_video_params_bytes_per_pixel(0, 4); // u8, RGBA
|
||||
|
||||
QImage img(
|
||||
reinterpret_cast<const uchar *>(backend_neutral_buffer_.constData()),
|
||||
@@ -1632,10 +1686,7 @@ void ViewerDisplayWidget::play(const int64_t &start_timestamp,
|
||||
playback_timebase_ = timebase;
|
||||
playback_speed_ = playback_speed;
|
||||
|
||||
// The facade playback engine owns the master clock; seed the display
|
||||
// clock with the start timestamp (the ViewerWidget keeps feeding it
|
||||
// from oakengine_playback_get_position afterwards).
|
||||
external_ts_.store(start_timestamp);
|
||||
timer_.start(start_timestamp, playback_speed, timebase.to_double());
|
||||
|
||||
if (start_updating) {
|
||||
connect(this, &ViewerDisplayWidget::frame_swapped, this,
|
||||
@@ -1650,8 +1701,6 @@ void ViewerDisplayWidget::pause()
|
||||
disconnect(this, &ViewerDisplayWidget::frame_swapped, this,
|
||||
&ViewerDisplayWidget::update_from_queue);
|
||||
|
||||
external_ts_.store(-1);
|
||||
|
||||
queue_.clear();
|
||||
queue_starved_ = false;
|
||||
}
|
||||
@@ -1667,11 +1716,7 @@ QPointF ViewerDisplayWidget::screen_to_scene_point(const QPoint &p)
|
||||
|
||||
void ViewerDisplayWidget::update_from_queue()
|
||||
{
|
||||
const int64_t t = external_ts_.load();
|
||||
if (t < 0) {
|
||||
// No facade playback position fed yet.
|
||||
return;
|
||||
}
|
||||
int64_t t = timer_.get_timestamp_now();
|
||||
|
||||
Rational time = Timecode::timestamp_to_time(t, playback_timebase_);
|
||||
|
||||
@@ -1731,14 +1776,16 @@ void ViewerDisplayWidget::text_edit_changed()
|
||||
editor->property("gizmo").value<quintptr>());
|
||||
|
||||
QString html = Html::doc_to_html(editor->document());
|
||||
gizmo->update_input_html(html, get_gizmo_time());
|
||||
oakengine_text_gizmo_update_html(
|
||||
reinterpret_cast<OakEngineNode *>(gizmos_),
|
||||
html.toUtf8().constData(),
|
||||
get_gizmo_time().numerator(), get_gizmo_time().denominator());
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::text_edit_destroyed()
|
||||
{
|
||||
TextGizmo *gizmo = reinterpret_cast<TextGizmo *>(
|
||||
sender()->property("gizmo").value<quintptr>());
|
||||
emit gizmo->deactivated();
|
||||
oakengine_text_gizmo_deactivated(
|
||||
reinterpret_cast<OakEngineNode *>(gizmos_));
|
||||
text_edit_ = nullptr;
|
||||
text_toolbar_ = nullptr;
|
||||
inner_widget()->setMouseTracking(false);
|
||||
|
||||
@@ -22,19 +22,20 @@
|
||||
#ifndef OAK_VIEWERGLWIDGET_H
|
||||
#define OAK_VIEWERGLWIDGET_H
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <QImage>
|
||||
#include <QMatrix4x4>
|
||||
#include <QRubberBand>
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "engineeventbridge.h"
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "node/gizmo/text.h"
|
||||
#include "node/node.h"
|
||||
#include "node/output/track/tracklist.h"
|
||||
#include "node/traverser.h"
|
||||
#include "node/value.h"
|
||||
#include "oakengine/traverse.h"
|
||||
#include "tool/tool.h"
|
||||
#include "viewerplaybacktimer.h"
|
||||
#include "viewerqueue.h"
|
||||
#include "viewersafemargininfo.h"
|
||||
#include "viewertexteditor.h"
|
||||
@@ -140,7 +141,7 @@ public:
|
||||
return texture_;
|
||||
}
|
||||
|
||||
ColorProcessorPtr get_current_color_processor()
|
||||
ColorProcessorHandlePtr get_current_color_processor()
|
||||
{
|
||||
return color_service();
|
||||
}
|
||||
@@ -155,16 +156,9 @@ public:
|
||||
return &queue_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Feed the facade playback position as this display's clock
|
||||
*
|
||||
* The facade playback engine owns the master clock now; the
|
||||
* ViewerWidget polls it (oakengine_playback_get_position) and pushes
|
||||
* the timestamp here for update_from_queue() to pop by.
|
||||
*/
|
||||
void set_playback_timestamp(int64_t ts)
|
||||
ViewerPlaybackTimer *timer()
|
||||
{
|
||||
external_ts_.store(ts);
|
||||
return &timer_;
|
||||
}
|
||||
|
||||
QPointF screen_to_scene_point(const QPoint &p);
|
||||
@@ -261,13 +255,12 @@ protected:
|
||||
|
||||
QTransform generate_display_transform();
|
||||
|
||||
QTransform generate_gizmo_transform(NodeTraverser >,
|
||||
const TimeRange &range);
|
||||
QTransform generate_gizmo_transform(Node *gizmos, Node *target,
|
||||
const TimeRange &range);
|
||||
QTransform generate_gizmo_transform()
|
||||
{
|
||||
NodeTraverser t;
|
||||
t.set_cache_video_params(gizmo_params_);
|
||||
return generate_gizmo_transform(t, generate_gizmo_time());
|
||||
TimeRange range = generate_gizmo_time();
|
||||
return generate_gizmo_transform(gizmos_, get_time_target(), range);
|
||||
}
|
||||
|
||||
TimeRange generate_gizmo_time()
|
||||
@@ -425,6 +418,9 @@ private:
|
||||
bool show_subtitles_;
|
||||
Sequence *subtitle_tracks_;
|
||||
|
||||
EngineEventBridge *bridge_;
|
||||
int64_t subtitle_sub_ = 0;
|
||||
|
||||
Rational time_;
|
||||
|
||||
/**
|
||||
@@ -469,7 +465,7 @@ private:
|
||||
// Playback
|
||||
ViewerQueue queue_;
|
||||
|
||||
std::atomic<int64_t> external_ts_{ -1 };
|
||||
ViewerPlaybackTimer timer_;
|
||||
|
||||
Rational playback_timebase_;
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "vieweroutpututils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
VideoParams video_params_from_pod(const oak_video_params &pod)
|
||||
{
|
||||
void *vp = oakengine_video_params_create(&pod);
|
||||
VideoParams result = *static_cast<VideoParams *>(vp);
|
||||
oakengine_video_params_free(vp);
|
||||
return result;
|
||||
}
|
||||
|
||||
VideoParams empty_video_params()
|
||||
{
|
||||
oak_video_params pod = {};
|
||||
return video_params_from_pod(pod);
|
||||
}
|
||||
|
||||
Rational sequence_timebase(const void *sequence)
|
||||
{
|
||||
oak_video_params vp = {};
|
||||
if (oakengine_viewer_get_video_params(
|
||||
reinterpret_cast<const OakEngineNode *>(sequence), 0, &vp) < 0 ||
|
||||
vp.time_base_den <= 0) {
|
||||
return Rational();
|
||||
}
|
||||
return Rational(vp.time_base_num, vp.time_base_den);
|
||||
}
|
||||
|
||||
VideoParams viewer_output_video_params(const void *viewer, int index)
|
||||
{
|
||||
oak_video_params vp;
|
||||
if (oakengine_viewer_get_video_params(
|
||||
reinterpret_cast<const OakEngineNode *>(viewer), index, &vp) < 0 ||
|
||||
vp.width <= 0 || vp.height <= 0) {
|
||||
return empty_video_params();
|
||||
}
|
||||
return video_params_from_pod(vp);
|
||||
}
|
||||
|
||||
AudioParams viewer_output_audio_params(const void *viewer, int index)
|
||||
{
|
||||
int sample_rate = 0;
|
||||
int format = 0;
|
||||
uint64_t channel_layout = 0;
|
||||
if (oakengine_viewer_get_audio_params(
|
||||
reinterpret_cast<const OakEngineNode *>(viewer), index, &sample_rate,
|
||||
&channel_layout, &format) < 0 ||
|
||||
sample_rate <= 0) {
|
||||
return AudioParams();
|
||||
}
|
||||
return AudioParams(sample_rate, channel_layout,
|
||||
static_cast<SampleFormat::Format>(format));
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
@@ -0,0 +1,103 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef VIEWEROUTPUTUTILS_H
|
||||
#define VIEWEROUTPUTUTILS_H
|
||||
|
||||
#include <olive/core/render/audioparams.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "oakengine/viewer.h"
|
||||
#include "render/subtitleparams.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
/**
|
||||
* @file vieweroutpututils.h
|
||||
* @brief Facade-backed replacements for ViewerOutput's header-inline
|
||||
* parameter getters
|
||||
*
|
||||
* ViewerOutput::get_video_params()/get_audio_params()/... are defined inline
|
||||
* in the engine header and reference the ViewerOutput::k_*_params_input
|
||||
* statics, so calling them from the app would leave undefined ViewerOutput
|
||||
* symbols in oak-editor. These helpers fetch the same values through the
|
||||
* oakengine C ABI instead. `viewer` may be any viewer handle (ViewerOutput,
|
||||
* Sequence, Footage).
|
||||
*/
|
||||
namespace olive {
|
||||
|
||||
VideoParams viewer_output_video_params(const void *viewer, int index = 0);
|
||||
|
||||
AudioParams viewer_output_audio_params(const void *viewer, int index = 0);
|
||||
|
||||
/**
|
||||
* @brief Construct a VideoParams from an oak_video_params POD using the engine
|
||||
* facade. This is the single app-side construction point allowed during the R6
|
||||
* C ABI migration; all other app code must not call VideoParams constructors
|
||||
* directly.
|
||||
*/
|
||||
VideoParams video_params_from_pod(const oak_video_params &pod);
|
||||
|
||||
/** @brief Equivalent to the default-constructed VideoParams(). */
|
||||
VideoParams empty_video_params();
|
||||
|
||||
/**
|
||||
* @brief Frame-duration timebase of `sequence` as a Rational (frame rate flipped).
|
||||
*
|
||||
* Facade-backed replacement for ViewerOutput::get_video_params().frame_rate().
|
||||
* flipped(), used for Rational -> timestamp conversions without pulling in
|
||||
* ViewerOutput inline symbols.
|
||||
*/
|
||||
Rational sequence_timebase(const void *sequence);
|
||||
|
||||
/**
|
||||
* @brief 1 if `node`'s engine type id (Node::id()) equals `type_id`.
|
||||
* Facade-backed replacement for dynamic_cast-based type probes (a
|
||||
* dynamic_cast to/from ViewerOutput drags an undefined ViewerOutput
|
||||
* typeinfo reference into the app binary).
|
||||
*/
|
||||
inline bool viewer_output_node_type_is(const void *node, const char *type_id)
|
||||
{
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
char buf[128];
|
||||
const int len = oakengine_node_get_type_id(
|
||||
reinterpret_cast<const OakEngineNode *>(node), buf, sizeof(buf));
|
||||
return len >= 0 && len < int(sizeof(buf)) && strcmp(buf, type_id) == 0;
|
||||
}
|
||||
|
||||
/** @brief 1 if `node` is a Sequence (Sequence::id()). */
|
||||
inline bool viewer_output_is_sequence(const void *node)
|
||||
{
|
||||
return viewer_output_node_type_is(node,
|
||||
"org.olivevideoeditor.Olive.sequence");
|
||||
}
|
||||
|
||||
/** @brief 1 if `node` is a Footage (Footage::id()). */
|
||||
inline bool viewer_output_is_footage(const void *node)
|
||||
{
|
||||
return viewer_output_node_type_is(node,
|
||||
"org.olivevideoeditor.Olive.footage");
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // VIEWEROUTPUTUTILS_H
|
||||
@@ -0,0 +1,45 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "viewerplaybacktimer.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
void ViewerPlaybackTimer::start(const int64_t &start_timestamp, const int &playback_speed, const double &timebase)
|
||||
{
|
||||
start_msec_ = QDateTime::currentMSecsSinceEpoch();
|
||||
start_timestamp_ = start_timestamp;
|
||||
playback_speed_ = playback_speed;
|
||||
timebase_ = timebase;
|
||||
}
|
||||
|
||||
int64_t ViewerPlaybackTimer::get_timestamp_now() const
|
||||
{
|
||||
int64_t real_time = QDateTime::currentMSecsSinceEpoch() - start_msec_;
|
||||
|
||||
int64_t frames_since_start = qRound(static_cast<double>(real_time) / (timebase_ * 1000));
|
||||
|
||||
return start_timestamp_ + frames_since_start * playback_speed_;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef VIEWERPLAYBACKTIMER_H
|
||||
#define VIEWERPLAYBACKTIMER_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ViewerPlaybackTimer {
|
||||
public:
|
||||
void start(const int64_t& start_timestamp, const int& playback_speed, const double& timebase);
|
||||
|
||||
int64_t get_timestamp_now() const;
|
||||
|
||||
private:
|
||||
qint64 start_msec_;
|
||||
int64_t start_timestamp_;
|
||||
|
||||
int playback_speed_;
|
||||
|
||||
double timebase_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // VIEWERPLAYBACKTIMER_H
|
||||
Reference in New Issue
Block a user