importtool: create a sequence automatically from dropped footage if one isn't
open already
This commit is contained in:
+45
-27
@@ -44,8 +44,6 @@
|
||||
#include "project/projectimportmanager.h"
|
||||
#include "project/projectloadmanager.h"
|
||||
#include "project/projectsavemanager.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "render/backend/indexmanager.h"
|
||||
#include "render/backend/opengl/opengltexturecache.h"
|
||||
#include "render/colormanager.h"
|
||||
@@ -312,34 +310,19 @@ void Core::CreateNewFolder()
|
||||
|
||||
void Core::CreateNewSequence()
|
||||
{
|
||||
// Locate the most recently focused Project panel (assume that's the panel the user wants to import into)
|
||||
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
||||
Project* active_project;
|
||||
Project* active_project = GetActiveProject();
|
||||
|
||||
if (active_project_panel == nullptr // Check that we found a Project panel
|
||||
|| (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project
|
||||
if (!active_project) {
|
||||
QMessageBox::critical(main_window_, tr("Failed to create new sequence"), tr("Failed to find active Project panel"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the selected folder in this panel
|
||||
Folder* folder = active_project_panel->GetSelectedFolder();
|
||||
|
||||
// Create new sequence
|
||||
SequencePtr new_sequence = std::make_shared<Sequence>();
|
||||
SequencePtr new_sequence = CreateNewSequenceForProject(active_project);
|
||||
|
||||
// Set all defaults for the sequence
|
||||
new_sequence->set_default_parameters();
|
||||
|
||||
// Get default name for this sequence (in the format "Sequence N", the first that doesn't exist)
|
||||
int sequence_number = 1;
|
||||
QString sequence_name;
|
||||
do {
|
||||
sequence_name = tr("Sequence %1").arg(sequence_number);
|
||||
sequence_number++;
|
||||
} while (active_project->root()->ChildExistsWithName(sequence_name));
|
||||
new_sequence->set_name(sequence_name);
|
||||
|
||||
SequenceDialog sd(new_sequence.get(), SequenceDialog::kNew, main_window_);
|
||||
|
||||
// Make sure SequenceDialog doesn't make an undo command for editing the sequence, since we make an undo command for
|
||||
@@ -348,8 +331,8 @@ void Core::CreateNewSequence()
|
||||
|
||||
if (sd.exec() == QDialog::Accepted) {
|
||||
// Create an undoable command
|
||||
ProjectViewModel::AddItemCommand* aic = new ProjectViewModel::AddItemCommand(active_project_panel->model(),
|
||||
folder,
|
||||
ProjectViewModel::AddItemCommand* aic = new ProjectViewModel::AddItemCommand(GetActiveProjectModel(),
|
||||
GetSelectedFolderInActiveProject(),
|
||||
new_sequence);
|
||||
|
||||
new_sequence->add_default_nodes();
|
||||
@@ -452,16 +435,35 @@ void Core::SaveAutorecovery()
|
||||
|
||||
Project *Core::GetActiveProject()
|
||||
{
|
||||
// Locate the most recently focused Project panel (assume that's the panel the user wants to import into)
|
||||
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
||||
|
||||
// If we couldn't find one, return nullptr
|
||||
if (active_project_panel == nullptr) {
|
||||
if (active_project_panel) {
|
||||
return active_project_panel->project();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return the project panel's project (which may be nullptr but in most cases shouldn't be)
|
||||
return active_project_panel->project();
|
||||
ProjectViewModel *Core::GetActiveProjectModel()
|
||||
{
|
||||
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
||||
|
||||
if (active_project_panel) {
|
||||
return active_project_panel->model();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Folder *Core::GetSelectedFolderInActiveProject()
|
||||
{
|
||||
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
||||
|
||||
if (active_project_panel) {
|
||||
return active_project_panel->GetSelectedFolder();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Core::SetProjectModified()
|
||||
@@ -643,6 +645,22 @@ void Core::SetPreferenceForRenderMode(RenderMode::Mode mode, const QString &pref
|
||||
Config::Current()[GetRenderModePreferencePrefix(mode, preference)] = value;
|
||||
}
|
||||
|
||||
SequencePtr Core::CreateNewSequenceForProject(Project* project) const
|
||||
{
|
||||
SequencePtr new_sequence = std::make_shared<Sequence>();
|
||||
|
||||
// Get default name for this sequence (in the format "Sequence N", the first that doesn't exist)
|
||||
int sequence_number = 1;
|
||||
QString sequence_name;
|
||||
do {
|
||||
sequence_name = tr("Sequence %1").arg(sequence_number);
|
||||
sequence_number++;
|
||||
} while (project->root()->ChildExistsWithName(sequence_name));
|
||||
new_sequence->set_name(sequence_name);
|
||||
|
||||
return new_sequence;
|
||||
}
|
||||
|
||||
void Core::OpenProject()
|
||||
{
|
||||
QString file = QFileDialog::getOpenFileName(main_window_,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "project/project.h"
|
||||
#include "project/projectviewmodel.h"
|
||||
#include "task/task.h"
|
||||
@@ -121,6 +122,8 @@ public:
|
||||
* The active Project file, or nullptr if the heuristic couldn't find one.
|
||||
*/
|
||||
Project* GetActiveProject();
|
||||
ProjectViewModel* GetActiveProjectModel();
|
||||
Folder* GetSelectedFolderInActiveProject();
|
||||
|
||||
/**
|
||||
* @brief Sets state to "modified" so that the GUI will prompt the user to save before closing
|
||||
@@ -174,6 +177,11 @@ public:
|
||||
static QVariant GetPreferenceForRenderMode(RenderMode::Mode mode, const QString& preference);
|
||||
static void SetPreferenceForRenderMode(RenderMode::Mode mode, const QString& preference, const QVariant& value);
|
||||
|
||||
/**
|
||||
* @brief Create a new sequence named appropriately for the active project
|
||||
*/
|
||||
SequencePtr CreateNewSequenceForProject(Project *project) const;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Starts an open file dialog to load a project from file
|
||||
|
||||
@@ -265,6 +265,11 @@ void Sequence::set_default_parameters()
|
||||
Config::Current()["DefaultSequenceAudioLayout"].toULongLong()));
|
||||
}
|
||||
|
||||
ViewerOutput *Sequence::viewer_output() const
|
||||
{
|
||||
return viewer_output_;
|
||||
}
|
||||
|
||||
void Sequence::NameChangedEvent(const QString &name)
|
||||
{
|
||||
viewer_output_->set_media_name(name);
|
||||
|
||||
@@ -71,6 +71,8 @@ public:
|
||||
|
||||
void set_default_parameters();
|
||||
|
||||
ViewerOutput* viewer_output() const;
|
||||
|
||||
protected:
|
||||
virtual void NameChangedEvent(const QString& name) override;
|
||||
|
||||
|
||||
@@ -616,30 +616,22 @@ void TimelineWidget::ViewMouseDoubleClicked(TimelineViewMouseEvent *event)
|
||||
|
||||
void TimelineWidget::ViewDragEntered(TimelineViewMouseEvent *event)
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
import_tool_->DragEnter(event);
|
||||
}
|
||||
import_tool_->DragEnter(event);
|
||||
}
|
||||
|
||||
void TimelineWidget::ViewDragMoved(TimelineViewMouseEvent *event)
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
import_tool_->DragMove(event);
|
||||
}
|
||||
import_tool_->DragMove(event);
|
||||
}
|
||||
|
||||
void TimelineWidget::ViewDragLeft(QDragLeaveEvent *event)
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
import_tool_->DragLeave(event);
|
||||
}
|
||||
import_tool_->DragLeave(event);
|
||||
}
|
||||
|
||||
void TimelineWidget::ViewDragDropped(TimelineViewMouseEvent *event)
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
import_tool_->DragDrop(event);
|
||||
}
|
||||
import_tool_->DragDrop(event);
|
||||
}
|
||||
|
||||
void TimelineWidget::AddBlock(Block *block, TrackReference track)
|
||||
|
||||
@@ -202,7 +202,12 @@ private:
|
||||
virtual void DragDrop(TimelineViewMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
void FootageToGhosts(rational ghost_start, const QList<Footage*>& footage, const rational &dest_tb, const int &track_start);
|
||||
|
||||
QList<Footage*> dragged_footage_;
|
||||
|
||||
int import_pre_buffer_;
|
||||
|
||||
};
|
||||
|
||||
class EditTool : public Tool
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "node/distort/transform/transform.h"
|
||||
#include "node/input/media/audio/audio.h"
|
||||
#include "node/input/media/video/video.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
Timeline::TrackType TrackTypeFromStreamType(Stream::Type stream_type)
|
||||
@@ -78,10 +79,6 @@ void TimelineWidget::ImportTool::DragEnter(TimelineViewMouseEvent *event)
|
||||
// Set drag start position
|
||||
drag_start_ = event->GetCoordinates();
|
||||
|
||||
// Set ghosts to start where the cursor entered
|
||||
|
||||
rational ghost_start = drag_start_.GetFrame() - parent()->SceneToTime(import_pre_buffer_);
|
||||
|
||||
snap_points_.clear();
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
@@ -92,62 +89,20 @@ void TimelineWidget::ImportTool::DragEnter(TimelineViewMouseEvent *event)
|
||||
|
||||
// Check if Item is Footage
|
||||
if (item->type() == Item::kFootage) {
|
||||
|
||||
// If the Item is Footage, we can create a Ghost from it
|
||||
Footage* footage = static_cast<Footage*>(item);
|
||||
dragged_footage_.append(static_cast<Footage*>(item));
|
||||
|
||||
// Each stream is offset by one track per track "type", we keep track of them in this vector
|
||||
QVector<int> track_offsets(Timeline::kTrackTypeCount);
|
||||
track_offsets.fill(drag_start_.GetTrack().index());
|
||||
|
||||
rational footage_duration;
|
||||
|
||||
// Loop through all streams in footage
|
||||
foreach (StreamPtr stream, footage->streams()) {
|
||||
Timeline::TrackType track_type = TrackTypeFromStreamType(stream->type());
|
||||
|
||||
// Check if this stream has a compatible TrackList
|
||||
if (track_type == Timeline::kTrackTypeNone || !stream->enabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TimelineViewGhostItem* ghost = new TimelineViewGhostItem();
|
||||
|
||||
if (stream->type() == Stream::kImage) {
|
||||
// Stream is essentially length-less - use config's default image length
|
||||
footage_duration = Config::Current()["DefaultStillLength"].value<rational>();
|
||||
} else {
|
||||
// Use duration from file
|
||||
int64_t stream_duration = stream->duration();
|
||||
|
||||
// Rescale to timeline timebase
|
||||
stream_duration = qCeil(static_cast<double>(stream_duration) * stream->timebase().toDouble() / parent()->timebase_dbl());
|
||||
|
||||
// Convert to rational time
|
||||
footage_duration = rational(parent()->timebase().numerator() * stream_duration,
|
||||
parent()->timebase().denominator());
|
||||
}
|
||||
|
||||
ghost->SetIn(ghost_start);
|
||||
ghost->SetOut(ghost_start + footage_duration);
|
||||
ghost->SetTrack(TrackReference(track_type, track_offsets.at(track_type)));
|
||||
|
||||
// Increment track count for this track type
|
||||
track_offsets[track_type]++;
|
||||
|
||||
snap_points_.append(ghost->In());
|
||||
snap_points_.append(ghost->Out());
|
||||
|
||||
ghost->setData(TimelineViewGhostItem::kAttachedFootage, QVariant::fromValue(stream));
|
||||
ghost->SetMode(Timeline::kMove);
|
||||
|
||||
parent()->AddGhost(ghost);
|
||||
}
|
||||
|
||||
// Stack each ghost one after the other
|
||||
ghost_start += footage_duration;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent()->GetConnectedNode()) {
|
||||
FootageToGhosts(drag_start_.GetFrame() - parent()->SceneToTime(import_pre_buffer_),
|
||||
dragged_footage_,
|
||||
parent()->timebase(),
|
||||
drag_start_.GetTrack().index());
|
||||
}
|
||||
|
||||
event->accept();
|
||||
} else {
|
||||
// FIXME: Implement dropping from file
|
||||
@@ -157,45 +112,48 @@ void TimelineWidget::ImportTool::DragEnter(TimelineViewMouseEvent *event)
|
||||
|
||||
void TimelineWidget::ImportTool::DragMove(TimelineViewMouseEvent *event)
|
||||
{
|
||||
if (parent()->HasGhosts()) {
|
||||
rational time_movement = event->GetFrame() - drag_start_.GetFrame();
|
||||
int track_movement = event->GetTrack().index() - drag_start_.GetTrack().index();
|
||||
if (!dragged_footage_.isEmpty()) {
|
||||
|
||||
// If snapping is enabled, check for snap points
|
||||
if (Core::instance()->snapping()) {
|
||||
SnapPoint(snap_points_, &time_movement);
|
||||
if (parent()->HasGhosts()) {
|
||||
rational time_movement = event->GetFrame() - drag_start_.GetFrame();
|
||||
int track_movement = event->GetTrack().index() - drag_start_.GetTrack().index();
|
||||
|
||||
// If snapping is enabled, check for snap points
|
||||
if (Core::instance()->snapping()) {
|
||||
SnapPoint(snap_points_, &time_movement);
|
||||
}
|
||||
|
||||
time_movement = ValidateFrameMovement(time_movement, parent()->ghost_items_);
|
||||
track_movement = ValidateTrackMovement(track_movement, parent()->ghost_items_);
|
||||
|
||||
rational earliest_ghost = RATIONAL_MAX;
|
||||
|
||||
// Move ghosts to the mouse cursor
|
||||
foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) {
|
||||
ghost->SetInAdjustment(time_movement);
|
||||
ghost->SetOutAdjustment(time_movement);
|
||||
ghost->SetTrackAdjustment(track_movement);
|
||||
|
||||
TrackReference adjusted_track = ghost->GetAdjustedTrack();
|
||||
ghost->SetYCoords(parent()->GetTrackY(adjusted_track), parent()->GetTrackHeight(adjusted_track));
|
||||
|
||||
earliest_ghost = qMin(earliest_ghost, ghost->GetAdjustedIn());
|
||||
}
|
||||
|
||||
// Generate tooltip (showing earliest in point of imported clip)
|
||||
int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->timebase());
|
||||
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
|
||||
parent()->timebase(),
|
||||
Timecode::CurrentDisplay());
|
||||
|
||||
// Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way
|
||||
// of the cursor)
|
||||
QToolTip::hideText();
|
||||
QToolTip::showText(QCursor::pos(),
|
||||
tooltip_text,
|
||||
parent());
|
||||
}
|
||||
|
||||
time_movement = ValidateFrameMovement(time_movement, parent()->ghost_items_);
|
||||
track_movement = ValidateTrackMovement(track_movement, parent()->ghost_items_);
|
||||
|
||||
rational earliest_ghost = RATIONAL_MAX;
|
||||
|
||||
// Move ghosts to the mouse cursor
|
||||
foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) {
|
||||
ghost->SetInAdjustment(time_movement);
|
||||
ghost->SetOutAdjustment(time_movement);
|
||||
ghost->SetTrackAdjustment(track_movement);
|
||||
|
||||
TrackReference adjusted_track = ghost->GetAdjustedTrack();
|
||||
ghost->SetYCoords(parent()->GetTrackY(adjusted_track), parent()->GetTrackHeight(adjusted_track));
|
||||
|
||||
earliest_ghost = qMin(earliest_ghost, ghost->GetAdjustedIn());
|
||||
}
|
||||
|
||||
// Generate tooltip (showing earliest in point of imported clip)
|
||||
int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->timebase());
|
||||
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
|
||||
parent()->timebase(),
|
||||
Timecode::CurrentDisplay());
|
||||
|
||||
// Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way
|
||||
// of the cursor)
|
||||
QToolTip::hideText();
|
||||
QToolTip::showText(QCursor::pos(),
|
||||
tooltip_text,
|
||||
parent());
|
||||
|
||||
event->accept();
|
||||
} else {
|
||||
event->ignore();
|
||||
@@ -204,8 +162,9 @@ void TimelineWidget::ImportTool::DragMove(TimelineViewMouseEvent *event)
|
||||
|
||||
void TimelineWidget::ImportTool::DragLeave(QDragLeaveEvent* event)
|
||||
{
|
||||
if (parent()->HasGhosts()) {
|
||||
if (!dragged_footage_.isEmpty()) {
|
||||
parent()->ClearGhosts();
|
||||
dragged_footage_.clear();
|
||||
|
||||
event->accept();
|
||||
} else {
|
||||
@@ -215,75 +174,144 @@ void TimelineWidget::ImportTool::DragLeave(QDragLeaveEvent* event)
|
||||
|
||||
void TimelineWidget::ImportTool::DragDrop(TimelineViewMouseEvent *event)
|
||||
{
|
||||
if (parent()->HasGhosts()) {
|
||||
if (!dragged_footage_.isEmpty()) {
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
NodeGraph* dst_graph = static_cast<NodeGraph*>(parent()->GetConnectedNode()->parent());
|
||||
|
||||
QVector<Block*> block_items(parent()->ghost_items_.size());
|
||||
NodeGraph* dst_graph;
|
||||
ViewerOutput* viewer_node;
|
||||
Sequence* open_sequence = nullptr;
|
||||
|
||||
for (int i=0;i<parent()->ghost_items_.size();i++) {
|
||||
TimelineViewGhostItem* ghost = parent()->ghost_items_.at(i);
|
||||
if (parent()->GetConnectedNode()) {
|
||||
viewer_node = parent()->GetConnectedNode();
|
||||
dst_graph = static_cast<NodeGraph*>(parent()->GetConnectedNode()->parent());
|
||||
} else {
|
||||
Project* active_project = Core::instance()->GetActiveProject();
|
||||
|
||||
StreamPtr footage_stream = ghost->data(TimelineViewGhostItem::kAttachedFootage).value<StreamPtr>();
|
||||
if (active_project) {
|
||||
SequencePtr new_sequence = Core::instance()->CreateNewSequenceForProject(active_project);
|
||||
|
||||
ClipBlock* clip = new ClipBlock();
|
||||
clip->set_length_and_media_out(ghost->Length());
|
||||
clip->set_block_name(footage_stream->footage()->name());
|
||||
new NodeAddCommand(dst_graph, clip, command);
|
||||
new_sequence->set_default_parameters();
|
||||
|
||||
switch (footage_stream->type()) {
|
||||
case Stream::kVideo:
|
||||
case Stream::kImage:
|
||||
{
|
||||
VideoInput* video_input = new VideoInput();
|
||||
video_input->SetFootage(footage_stream);
|
||||
new NodeAddCommand(dst_graph, video_input, command);
|
||||
new NodeEdgeAddCommand(video_input->output(), clip->texture_input(), command);
|
||||
bool found_video_params = false;
|
||||
bool found_audio_params = false;
|
||||
|
||||
TransformDistort* transform = new TransformDistort();
|
||||
new NodeAddCommand(dst_graph, transform, command);
|
||||
new NodeEdgeAddCommand(transform->output(), video_input->matrix_input(), command);
|
||||
foreach (Footage* f, dragged_footage_) {
|
||||
foreach (StreamPtr s, f->streams()) {
|
||||
if (!found_video_params && s->type() == Stream::kVideo) {
|
||||
|
||||
//OpacityNode* opacity = new OpacityNode();
|
||||
//NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input());
|
||||
//NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input());
|
||||
break;
|
||||
}
|
||||
case Stream::kAudio:
|
||||
{
|
||||
AudioInput* audio_input = new AudioInput();
|
||||
audio_input->SetFootage(footage_stream);
|
||||
new NodeAddCommand(dst_graph, audio_input, command);
|
||||
VideoStream* vs = static_cast<VideoStream*>(s.get());
|
||||
|
||||
VolumeNode* volume_node = new VolumeNode();
|
||||
new NodeAddCommand(dst_graph, volume_node, command);
|
||||
if (vs->frame_rate() != 0) {
|
||||
new_sequence->set_video_params(VideoParams(vs->width(), vs->height(), vs->frame_rate().flipped()));
|
||||
found_video_params = true;
|
||||
}
|
||||
|
||||
new NodeEdgeAddCommand(audio_input->output(), volume_node->samples_input(), command);
|
||||
new NodeEdgeAddCommand(volume_node->output(), clip->texture_input(), command);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (!found_audio_params && s->type() == Stream::kAudio) {
|
||||
|
||||
if (event->GetModifiers() & Qt::ControlModifier) {
|
||||
//emit parent()->RequestInsertBlockAtTime(clip, ghost->GetAdjustedIn());
|
||||
AudioStream* as = static_cast<AudioStream*>(s.get());
|
||||
new_sequence->set_audio_params(AudioParams(as->sample_rate(), as->channel_layout()));
|
||||
found_audio_params = true;
|
||||
|
||||
}
|
||||
|
||||
if (found_video_params && found_audio_params) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_video_params && found_audio_params) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
new_sequence->add_default_nodes();
|
||||
|
||||
new ProjectViewModel::AddItemCommand(Core::instance()->GetActiveProjectModel(),
|
||||
Core::instance()->GetSelectedFolderInActiveProject(),
|
||||
new_sequence,
|
||||
command);
|
||||
|
||||
FootageToGhosts(0, dragged_footage_, new_sequence->video_params().time_base(), 0);
|
||||
|
||||
dst_graph = new_sequence.get();
|
||||
viewer_node = new_sequence->viewer_output();
|
||||
|
||||
// Set this as the sequence to open
|
||||
open_sequence = new_sequence.get();
|
||||
} else {
|
||||
new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(ghost->GetAdjustedTrack().type()),
|
||||
ghost->GetAdjustedTrack().index(),
|
||||
clip,
|
||||
ghost->GetAdjustedIn(),
|
||||
command);
|
||||
dst_graph = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
block_items.replace(i, clip);
|
||||
if (dst_graph) {
|
||||
|
||||
// Link any clips so far that share the same Footage with this one
|
||||
for (int j=0;j<i;j++) {
|
||||
StreamPtr footage_compare = parent()->ghost_items_.at(j)->data(TimelineViewGhostItem::kAttachedFootage).value<StreamPtr>();
|
||||
QVector<Block*> block_items(parent()->ghost_items_.size());
|
||||
|
||||
if (footage_compare->footage() == footage_stream->footage()) {
|
||||
Block::Link(block_items.at(j), clip);
|
||||
for (int i=0;i<parent()->ghost_items_.size();i++) {
|
||||
TimelineViewGhostItem* ghost = parent()->ghost_items_.at(i);
|
||||
|
||||
StreamPtr footage_stream = ghost->data(TimelineViewGhostItem::kAttachedFootage).value<StreamPtr>();
|
||||
|
||||
ClipBlock* clip = new ClipBlock();
|
||||
clip->set_length_and_media_out(ghost->Length());
|
||||
clip->set_block_name(footage_stream->footage()->name());
|
||||
new NodeAddCommand(dst_graph, clip, command);
|
||||
|
||||
switch (footage_stream->type()) {
|
||||
case Stream::kVideo:
|
||||
case Stream::kImage:
|
||||
{
|
||||
VideoInput* video_input = new VideoInput();
|
||||
video_input->SetFootage(footage_stream);
|
||||
new NodeAddCommand(dst_graph, video_input, command);
|
||||
new NodeEdgeAddCommand(video_input->output(), clip->texture_input(), command);
|
||||
|
||||
TransformDistort* transform = new TransformDistort();
|
||||
new NodeAddCommand(dst_graph, transform, command);
|
||||
new NodeEdgeAddCommand(transform->output(), video_input->matrix_input(), command);
|
||||
|
||||
//OpacityNode* opacity = new OpacityNode();
|
||||
//NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input());
|
||||
//NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input());
|
||||
break;
|
||||
}
|
||||
case Stream::kAudio:
|
||||
{
|
||||
AudioInput* audio_input = new AudioInput();
|
||||
audio_input->SetFootage(footage_stream);
|
||||
new NodeAddCommand(dst_graph, audio_input, command);
|
||||
|
||||
VolumeNode* volume_node = new VolumeNode();
|
||||
new NodeAddCommand(dst_graph, volume_node, command);
|
||||
|
||||
new NodeEdgeAddCommand(audio_input->output(), volume_node->samples_input(), command);
|
||||
new NodeEdgeAddCommand(volume_node->output(), clip->texture_input(), command);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (event->GetModifiers() & Qt::ControlModifier) {
|
||||
//emit parent()->RequestInsertBlockAtTime(clip, ghost->GetAdjustedIn());
|
||||
} else {
|
||||
new TrackPlaceBlockCommand(viewer_node->track_list(ghost->GetAdjustedTrack().type()),
|
||||
ghost->GetAdjustedTrack().index(),
|
||||
clip,
|
||||
ghost->GetAdjustedIn(),
|
||||
command);
|
||||
}
|
||||
|
||||
block_items.replace(i, clip);
|
||||
|
||||
// Link any clips so far that share the same Footage with this one
|
||||
for (int j=0;j<i;j++) {
|
||||
StreamPtr footage_compare = parent()->ghost_items_.at(j)->data(TimelineViewGhostItem::kAttachedFootage).value<StreamPtr>();
|
||||
|
||||
if (footage_compare->footage() == footage_stream->footage()) {
|
||||
Block::Link(block_items.at(j), clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,9 +319,73 @@ void TimelineWidget::ImportTool::DragDrop(TimelineViewMouseEvent *event)
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
|
||||
parent()->ClearGhosts();
|
||||
dragged_footage_.clear();
|
||||
|
||||
if (open_sequence) {
|
||||
Sequence::Open(open_sequence);
|
||||
}
|
||||
|
||||
event->accept();
|
||||
} else {
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QList<Footage *> &footage_list, const rational& dest_tb, const int& track_start)
|
||||
{
|
||||
foreach (Footage* footage, footage_list) {
|
||||
|
||||
// Each stream is offset by one track per track "type", we keep track of them in this vector
|
||||
QVector<int> track_offsets(Timeline::kTrackTypeCount);
|
||||
track_offsets.fill(track_start);
|
||||
|
||||
rational footage_duration;
|
||||
|
||||
// Loop through all streams in footage
|
||||
foreach (StreamPtr stream, footage->streams()) {
|
||||
Timeline::TrackType track_type = TrackTypeFromStreamType(stream->type());
|
||||
|
||||
// Check if this stream has a compatible TrackList
|
||||
if (track_type == Timeline::kTrackTypeNone || !stream->enabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TimelineViewGhostItem* ghost = new TimelineViewGhostItem();
|
||||
|
||||
if (stream->type() == Stream::kImage) {
|
||||
// Stream is essentially length-less - use config's default image length
|
||||
footage_duration = Config::Current()["DefaultStillLength"].value<rational>();
|
||||
} else {
|
||||
// Use duration from file
|
||||
int64_t stream_duration = stream->duration();
|
||||
|
||||
// Rescale to timeline timebase
|
||||
stream_duration = qCeil(static_cast<double>(stream_duration) * stream->timebase().toDouble() / dest_tb.toDouble());
|
||||
|
||||
// Convert to rational time
|
||||
footage_duration = rational(dest_tb.numerator() * stream_duration,
|
||||
dest_tb.denominator());
|
||||
}
|
||||
|
||||
ghost->SetIn(ghost_start);
|
||||
ghost->SetOut(ghost_start + footage_duration);
|
||||
ghost->SetTrack(TrackReference(track_type, track_offsets.at(track_type)));
|
||||
|
||||
// Increment track count for this track type
|
||||
track_offsets[track_type]++;
|
||||
|
||||
snap_points_.append(ghost->In());
|
||||
snap_points_.append(ghost->Out());
|
||||
|
||||
ghost->setData(TimelineViewGhostItem::kAttachedFootage, QVariant::fromValue(stream));
|
||||
ghost->SetMode(Timeline::kMove);
|
||||
|
||||
parent()->AddGhost(ghost);
|
||||
|
||||
}
|
||||
|
||||
// Stack each ghost one after the other
|
||||
ghost_start += footage_duration;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user