otio: began early functionality to export to OTIO
This commit is contained in:
+5
-1
@@ -69,7 +69,11 @@ find_package(FFMPEG 3.0 REQUIRED
|
||||
swresample
|
||||
)
|
||||
|
||||
find_package(OpenTimelineIO REQUIRED)
|
||||
find_package(OpenTimelineIO)
|
||||
|
||||
if (NOT OpenTimelineIO_FOUND)
|
||||
message(" OpenTimelineIO interchange will be disabled.")
|
||||
endif()
|
||||
|
||||
find_package(GoogleCrashpad)
|
||||
|
||||
|
||||
+17
-2
@@ -123,7 +123,6 @@ target_include_directories(
|
||||
${FFMPEG_INCLUDE_DIRS}
|
||||
${OCIO_INCLUDE_DIRS}
|
||||
${OIIO_INCLUDE_DIRS}
|
||||
${OTIO_INCLUDE_DIRS}
|
||||
${OPENEXR_INCLUDES}
|
||||
|
||||
# HACK: Brew on macOS separates OpenEXR and IlmBase into two folders even though they seem to
|
||||
@@ -152,7 +151,6 @@ target_link_libraries(
|
||||
FFMPEG::swresample
|
||||
${OCIO_LIBRARIES}
|
||||
${OIIO_LIBRARIES}
|
||||
${OTIO_LIBRARIES}
|
||||
${OPENEXR_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -170,6 +168,23 @@ elseif (APPLE)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Enable OTIO if found
|
||||
if (OpenTimelineIO_FOUND)
|
||||
set(OLIVE_DEFINITIONS ${OLIVE_DEFINITIONS} USE_OTIO)
|
||||
|
||||
target_include_directories(
|
||||
${OLIVE_TARGET}
|
||||
PRIVATE
|
||||
${OTIO_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
${OLIVE_TARGET}
|
||||
PRIVATE
|
||||
${OTIO_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Enable Crashpad if found
|
||||
if (GoogleCrashpad_FOUND)
|
||||
set(OLIVE_DEFINITIONS ${OLIVE_DEFINITIONS} USE_CRASHPAD)
|
||||
|
||||
@@ -170,4 +170,21 @@ bool FileFunctions::DirectoryIsValid(const QString &dir, bool try_to_create)
|
||||
return false;
|
||||
}
|
||||
|
||||
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension)
|
||||
{
|
||||
// No-op if either input is empty
|
||||
if (!fn.isEmpty() && !extension.isEmpty()) {
|
||||
QString extension_with_dot;
|
||||
|
||||
extension_with_dot.append('.');
|
||||
extension_with_dot.append(extension);
|
||||
|
||||
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
||||
fn.append(extension_with_dot);
|
||||
}
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -54,6 +54,17 @@ public:
|
||||
|
||||
static bool DirectoryIsValid(const QString& dir, bool try_to_create);
|
||||
|
||||
/**
|
||||
* @brief Ensures a given filename has a certain extension
|
||||
*
|
||||
* Checks if the filename has the extension provided and appends it if not. The extension is
|
||||
* checked case-insensitive. The extension should be provided with no dot (e.g. "ove" rather than
|
||||
* ".ove").
|
||||
|
||||
* @return The filename provided either untouched or with the extension appended to it.
|
||||
*/
|
||||
static QString EnsureFilenameExtension(QString fn, const QString& extension);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,14 @@ AVRational rational::toAVRational() const
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef USE_OTIO
|
||||
opentime::RationalTime rational::toRationalTime() const
|
||||
{
|
||||
// Is this the best way of doing this?
|
||||
return opentime::RationalTime::from_seconds(toDouble());
|
||||
}
|
||||
#endif
|
||||
|
||||
rational rational::flipped() const
|
||||
{
|
||||
return rational(denom_, numer_);
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#define RATIONAL_H
|
||||
#include <iostream>
|
||||
|
||||
#ifdef USE_OTIO
|
||||
#include <opentime/rationalTime.h>
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMetaType>
|
||||
|
||||
@@ -96,6 +100,10 @@ public:
|
||||
|
||||
AVRational toAVRational() const;
|
||||
|
||||
#ifdef USE_OTIO
|
||||
opentime::RationalTime toRationalTime() const;
|
||||
#endif
|
||||
|
||||
// Produce "flipped" version
|
||||
rational flipped() const;
|
||||
|
||||
|
||||
+20
-4
@@ -54,6 +54,7 @@
|
||||
#include "task/project/import/importerrordialog.h"
|
||||
#include "task/project/load/load.h"
|
||||
#include "task/project/save/save.h"
|
||||
#include "task/project/saveotio/projectsaveasotiotask.h"
|
||||
#include "task/taskmanager.h"
|
||||
#include "ui/style/style.h"
|
||||
#include "undo/undostack.h"
|
||||
@@ -265,6 +266,24 @@ void Core::CreateNewProject()
|
||||
AddOpenProject(std::make_shared<Project>());
|
||||
}
|
||||
|
||||
#ifdef USE_OTIO
|
||||
void Core::ExportActiveSequenceAsOTIO()
|
||||
{
|
||||
QString fn = QFileDialog::getSaveFileName(main_window_,
|
||||
tr("Export as OpenTimelineIO"),
|
||||
QString(),
|
||||
tr("OpenTimelineIO (*.otio)"));
|
||||
|
||||
if (!fn.isEmpty()) {
|
||||
fn = FileFunctions::EnsureFilenameExtension(fn, QStringLiteral("otio"));
|
||||
|
||||
ProjectSaveAsOTIOTask* task = new ProjectSaveAsOTIOTask();
|
||||
TaskDialog* dialog = new TaskDialog(task, tr("Export Sequence"), main_window_);
|
||||
dialog->open();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const bool &Core::snapping() const
|
||||
{
|
||||
return snapping_;
|
||||
@@ -858,10 +877,7 @@ bool Core::SaveProjectAs(ProjectPtr p)
|
||||
GetProjectFilter());
|
||||
|
||||
if (!fn.isEmpty()) {
|
||||
QString extension(QStringLiteral(".ove"));
|
||||
if (!fn.endsWith(extension, Qt::CaseInsensitive)) {
|
||||
fn.append(extension);
|
||||
}
|
||||
fn = FileFunctions::EnsureFilenameExtension(fn, QStringLiteral("ove"));
|
||||
|
||||
p->set_filename(fn);
|
||||
|
||||
|
||||
@@ -378,6 +378,13 @@ public slots:
|
||||
*/
|
||||
void CreateNewProject();
|
||||
|
||||
#ifdef USE_OTIO
|
||||
/**
|
||||
* @brief Exports the active sequence to OpenTimelineIO
|
||||
*/
|
||||
void ExportActiveSequenceAsOTIO();
|
||||
#endif
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when a project is opened
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
add_subdirectory(import)
|
||||
add_subdirectory(load)
|
||||
add_subdirectory(save)
|
||||
add_subdirectory(saveotio)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
task/project/saveotio/projectsaveasotiotask.h
|
||||
task/project/saveotio/projectsaveasotiotask.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,111 @@
|
||||
/***
|
||||
|
||||
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 "projectsaveasotiotask.h"
|
||||
|
||||
#include <opentimelineio/clip.h>
|
||||
#include <opentimelineio/gap.h>
|
||||
#include <opentimelineio/timeline.h>
|
||||
#include <opentimelineio/transition.h>
|
||||
|
||||
#include "node/block/transition/transition.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
ProjectSaveAsOTIOTask::ProjectSaveAsOTIOTask(Sequence *sequence) :
|
||||
sequence_(sequence)
|
||||
{
|
||||
SetTitle(tr("Saving '%1' as OpenTimelineIO").arg(sequence->name()));
|
||||
}
|
||||
|
||||
bool ProjectSaveAsOTIOTask::Run()
|
||||
{
|
||||
auto otio_timeline = new opentimelineio::v1_0::Timeline();
|
||||
|
||||
ViewerOutput* viewer = sequence_->viewer_output();
|
||||
|
||||
opentimelineio::v1_0::ErrorStatus es;
|
||||
|
||||
foreach (TrackOutput* track, viewer->track_list(Timeline::kTrackTypeVideo)->GetTracks()) {
|
||||
auto otio_track = new opentimelineio::v1_0::Track();
|
||||
|
||||
otio_track->set_kind("Video");
|
||||
|
||||
foreach (Block* block, track->Blocks()) {
|
||||
opentimelineio::v1_0::Composable* otio_block = nullptr;
|
||||
|
||||
switch (block->type()) {
|
||||
case Block::kClip:
|
||||
{
|
||||
auto otio_clip = new opentimelineio::v1_0::Clip();
|
||||
|
||||
otio_clip->set_source_range(opentimelineio::v1_0::TimeRange(block->in().toRationalTime(),
|
||||
block->length().toRationalTime()));
|
||||
|
||||
otio_block = otio_clip;
|
||||
break;
|
||||
}
|
||||
case Block::kGap:
|
||||
{
|
||||
auto otio_gap = new opentimelineio::v1_0::Gap();
|
||||
|
||||
otio_gap->set_source_range(opentimelineio::v1_0::TimeRange(block->in().toRationalTime(),
|
||||
block->length().toRationalTime()));
|
||||
|
||||
otio_block = otio_gap;
|
||||
break;
|
||||
}
|
||||
case Block::kTransition:
|
||||
{
|
||||
auto otio_transition = new opentimelineio::v1_0::Transition();
|
||||
|
||||
TransitionBlock* our_transition = static_cast<TransitionBlock*>(block);
|
||||
|
||||
otio_transition->set_in_offset(our_transition->in_offset().toRationalTime());
|
||||
otio_transition->set_out_offset(our_transition->out_offset().toRationalTime());
|
||||
|
||||
otio_block = new opentimelineio::v1_0::Transition();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!otio_block) {
|
||||
// We shouldn't ever get here, but catch without crashing if we ever do
|
||||
return false;
|
||||
}
|
||||
|
||||
otio_track->append_child(otio_block, &es);
|
||||
|
||||
if (es != opentimelineio::v1_0::ErrorStatus::OK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
otio_timeline->tracks()->append_child(otio_track, &es);
|
||||
|
||||
if (es != opentimelineio::v1_0::ErrorStatus::OK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -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/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef PROJECTSAVEASOTIOTASK_H
|
||||
#define PROJECTSAVEASOTIOTASK_H
|
||||
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class ProjectSaveAsOTIOTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProjectSaveAsOTIOTask(Sequence* sequence);
|
||||
|
||||
protected:
|
||||
virtual bool Run() override;
|
||||
|
||||
private:
|
||||
Sequence* sequence_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // PROJECTSAVEASOTIOTASK_H
|
||||
@@ -60,7 +60,12 @@ MainMenu::MainMenu(MainWindow *parent) :
|
||||
file_menu_->addSeparator();
|
||||
file_import_item_ = file_menu_->AddItem("import", Core::instance(), &Core::DialogImportShow, "Ctrl+I");
|
||||
file_menu_->addSeparator();
|
||||
file_export_item_ = file_menu_->AddItem("export", Core::instance(), &Core::DialogExportShow, "Ctrl+M");
|
||||
file_export_menu_ = new Menu(file_menu_);
|
||||
file_export_media_item_ = file_export_menu_->AddItem("export", Core::instance(), &Core::DialogExportShow, "Ctrl+M");
|
||||
#ifdef USE_OTIO
|
||||
file_export_menu_->addSeparator();
|
||||
file_export_otio_item_ = file_export_menu_->AddItem("exportotio", Core::instance(), &Core::DialogExportShow);
|
||||
#endif
|
||||
file_menu_->addSeparator();
|
||||
file_project_properties_item_ = file_menu_->AddItem("projectproperties", Core::instance(), &Core::DialogProjectPropertiesShow, "Shift+F10");
|
||||
file_menu_->addSeparator();
|
||||
@@ -642,7 +647,11 @@ void MainMenu::Retranslate()
|
||||
file_open_recent_clear_item_->setText(tr("&Clear Recent List"));
|
||||
file_save_all_item_->setText(tr("Sa&ve All Projects"));
|
||||
file_import_item_->setText(tr("&Import..."));
|
||||
file_export_item_->setText(tr("&Export..."));
|
||||
file_export_menu_->setTitle(tr("&Export"));
|
||||
file_export_media_item_->setText(tr("&Media..."));
|
||||
#ifdef USE_OTIO
|
||||
file_export_otio_item_->setText(tr("&OpenTimelineIO..."));
|
||||
#endif
|
||||
file_project_properties_item_->setText(tr("&Project Properties..."));
|
||||
file_close_all_projects_item_->setText(tr("Close All Projects"));
|
||||
file_exit_item_->setText(tr("E&xit"));
|
||||
|
||||
@@ -198,7 +198,11 @@ private:
|
||||
QAction* file_save_as_item_;
|
||||
QAction* file_save_all_item_;
|
||||
QAction* file_import_item_;
|
||||
QAction* file_export_item_;
|
||||
Menu* file_export_menu_;
|
||||
QAction* file_export_media_item_;
|
||||
#ifdef USE_OTIO
|
||||
QAction* file_export_otio_item_;
|
||||
#endif
|
||||
QAction* file_project_properties_item_;
|
||||
QAction* file_close_project_item_;
|
||||
QAction* file_close_all_projects_item_;
|
||||
|
||||
Reference in New Issue
Block a user