Add first test for same track compositing

parent 7620569d
......@@ -667,6 +667,103 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
return true;
}
bool TimelineModel::mixClip(int idToMove)
{
int selectedTrack = -1;
if (idToMove == -1) {
auto sel = getCurrentSelection();
if (sel.empty()) {
return false;
}
for (int s : sel) {
if (!isClip(s)) {
continue;
}
if (selectedTrack == -1) {
idToMove = s;
break;
}
}
}
if (idToMove == -1 || !isClip(idToMove)) {
pCore->displayMessage(i18n("Select a clip to apply the mix"), InformationMessage, 500);
return false;
}
selectedTrack = getClipTrackId(idToMove);
if (selectedTrack == -1 || !isTrack(selectedTrack)) {
pCore->displayMessage(i18n("Select a clip to apply the mix"), InformationMessage, 500);
return false;
}
int mixPosition = getItemPosition(idToMove);
int clipDuration = getItemPlaytime(idToMove);
std::pair<int, int> clipsToMix;
// Check if we have a clip before and/or after
int previousClip = getTrackById_const(selectedTrack)->getClipByPosition(mixPosition - 1);
int nextClip = getTrackById_const(selectedTrack)->getClipByPosition(mixPosition + clipDuration + 1);
if (previousClip > -1 && nextClip > -1) {
// We have a clip before and a clip after, check timeline cursor position to decide where to mix
int cursor = pCore->getTimelinePosition();
if (cursor < mixPosition + clipDuration / 2) {
nextClip = -1;
} else {
previousClip = -1;
}
}
if (nextClip == -1) {
if (previousClip == -1) {
// No clip to mix, abort
pCore->displayMessage(i18n("No adjacent clip to perform mix"), InformationMessage, 500);
return false;
}
// Mix at start of selected clip
clipsToMix.first = previousClip;
clipsToMix.second = idToMove;
} else {
// Mix at end of selected clip
mixPosition += clipDuration;
clipsToMix.first = idToMove;
clipsToMix.second = nextClip;
}
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
bool result = requestClipMix(clipsToMix, selectedTrack, mixPosition, true, true, true, undo,
redo, false);
if (result) {
// Check if this is an AV split group
if (m_groups->isInGroup(idToMove)) {
int parentGroup = m_groups->getRootId(idToMove);
if (parentGroup > -1 && m_groups->getType(parentGroup) == GroupType::AVSplit) {
std::unordered_set<int> sub = m_groups->getLeaves(parentGroup);
// Perform mix on split clip
for (int current_id : sub) {
if (current_id == idToMove) {
continue;
}
int splitTrack = m_allClips[current_id]->getCurrentTrackId();
int splitId;
if (previousClip == -1) {
splitId = getTrackById_const(splitTrack)->getClipByPosition(mixPosition + 1);
clipsToMix.first = current_id;
clipsToMix.second = splitId;
} else {
splitId = getTrackById_const(splitTrack)->getClipByPosition(mixPosition - 1);
clipsToMix.first = splitId;
clipsToMix.second = current_id;
}
if (splitId > -1 && clipsToMix.first != clipsToMix.second) {
result = requestClipMix(clipsToMix, splitTrack, mixPosition, true, true, true, undo, redo, false);
}
}
}
}
pCore->pushUndo(undo, redo, i18n("Create mix"));
return result;
} else {
qDebug()<<"////// MIX OPERATION FAILED";
undo();
return false;
}
}
bool TimelineModel::requestClipMix(std::pair<int, int> clipIds, int trackId, int position, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove)
{
......
......@@ -689,6 +689,8 @@ public:
void prepareClose();
/** @brief Import project's master effects */
void importMasterEffects(std::weak_ptr<Mlt::Service> service);
/** @brief Create a mix selection with currently selected clip */
bool mixClip(int idToMove = -1);
protected:
/* @brief Register a new track. This is a call-back meant to be called from TrackModel
......
......@@ -3667,93 +3667,6 @@ void TimelineController::addTracks(int videoTracks, int audioTracks)
void TimelineController::mixClip()
{
qDebug()<<"=== PROCESS SAME TRACK COMPO";
auto sel = m_model->getCurrentSelection();
if (sel.empty()) {
return;
}
int selectedTrack = -1;
int idToMove = -1;
for (int s : sel) {
if (!m_model->isClip(s)) {
continue;
}
int tid = m_model->getItemTrackId(s);
if (selectedTrack == -1) {
selectedTrack = tid;
idToMove = s;
break;
}
}
if (idToMove == -1) {
pCore->displayMessage(i18n("Select a clip to apply the mix"), InformationMessage, 500);
return;
}
int mixPosition = m_model->getItemPosition(idToMove);
int clipDuration = m_model->getItemPlaytime(idToMove);
std::pair<int, int> clipsToMix;
// Check if we have a clip before and/or after
int previousClip = m_model->getTrackById_const(selectedTrack)->getClipByPosition(mixPosition - 1);
int nextClip = m_model->getTrackById_const(selectedTrack)->getClipByPosition(mixPosition + clipDuration + 1);
if (previousClip > -1 && nextClip > -1) {
// We have a clip before and a clip after, check timeline cursor position to decide where to mix
int cursor = pCore->getTimelinePosition();
if (cursor < mixPosition + clipDuration / 2) {
nextClip = -1;
} else {
previousClip = -1;
}
}
if (nextClip == -1) {
if (previousClip == -1) {
// No clip to mix, abort
pCore->displayMessage(i18n("No adjacent clip to perform mix"), InformationMessage, 500);
return;
}
// Mix at start of selected clip
clipsToMix.first = previousClip;
clipsToMix.second = idToMove;
} else {
// Mix at end of selected clip
mixPosition += clipDuration;
clipsToMix.first = idToMove;
clipsToMix.second = nextClip;
}
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
bool result = m_model->requestClipMix(clipsToMix, selectedTrack, mixPosition, true, true, true, undo, redo, false);
if (result) {
// Check if this is an AV split group
if (m_model->m_groups->isInGroup(idToMove)) {
int parentGroup = m_model->m_groups->getRootId(idToMove);
if (parentGroup > -1 && m_model->m_groups->getType(parentGroup) == GroupType::AVSplit) {
std::unordered_set<int> sub = m_model->m_groups->getLeaves(parentGroup);
// Perform mix on split clip
for (int current_id : sub) {
if (current_id == idToMove) {
continue;
}
int splitTrack = m_model->m_allClips[current_id]->getCurrentTrackId();
int splitId;
if (previousClip == -1) {
splitId = m_model->getTrackById_const(splitTrack)->getClipByPosition(mixPosition + 1);
clipsToMix.first = current_id;
clipsToMix.second = splitId;
} else {
splitId = m_model->getTrackById_const(splitTrack)->getClipByPosition(mixPosition - 1);
clipsToMix.first = splitId;
clipsToMix.second = current_id;
}
if (splitId > -1 && clipsToMix.first != clipsToMix.second) {
result = m_model->requestClipMix(clipsToMix, splitTrack, mixPosition, true, true, true, undo, redo, false);
}
}
}
}
pCore->pushUndo(undo, redo, i18n("Create mix"));
} else {
qDebug()<<"////// MIX OPERATION FAILED";
undo();
}
m_model->mixClip();
}
......@@ -5,6 +5,7 @@ add_executable(runTests
abortutil.cpp
compositiontest.cpp
effectstest.cpp
mixtest.cpp
groupstest.cpp
keyframetest.cpp
markertest.cpp
......
#include "catch.hpp"
#include "doc/docundostack.hpp"
#include "test_utils.hpp"
#include <QString>
#include <cmath>
#include <iostream>
#include <tuple>
#include <unordered_set>
#include "definitions.h"
#define private public
#define protected public
#include "core.h"
using namespace fakeit;
Mlt::Profile profile_mix;
TEST_CASE("Simple Mix", "[SameTrackMix]")
{
Logger::clear();
// Create timeline
auto binModel = pCore->projectItemModel();
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
std::shared_ptr<MarkerListModel> guideModel = std::make_shared<MarkerListModel>(undoStack);
// Here we do some trickery to enable testing.
// We mock the project class so that the undoStack function returns our undoStack
Mock<ProjectManager> pmMock;
When(Method(pmMock, undoStack)).AlwaysReturn(undoStack);
ProjectManager &mocked = pmMock.get();
pCore->m_projectManager = &mocked;
// We also mock timeline object to spy few functions and mock others
TimelineItemModel tim(&profile_mix, undoStack);
Mock<TimelineItemModel> timMock(tim);
auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {});
TimelineItemModel::finishConstruct(timeline, guideModel);
// Create a request
int tid1 = TrackModel::construct(timeline, -1, -1, QString(), true);
int tid2 = TrackModel::construct(timeline);
// Create clip with audio
QString binId = createProducerWithSound(profile_mix, binModel, 50);
// Setup insert stream data
QMap <int, QString>audioInfo;
audioInfo.insert(1,QStringLiteral("stream1"));
timeline->m_binAudioTargets = audioInfo;
// Create clip
int cid1;
int cid2;
REQUIRE(timeline->requestClipInsertion(binId, tid2, 100, cid1));
// Resize clip
REQUIRE(timeline->requestItemResize(cid1, 10, true, true));
REQUIRE(timeline->getClipPlaytime(cid1) == 10);
REQUIRE(timeline->getClipPosition(cid1) == 100);
REQUIRE(timeline->requestClipInsertion(binId, tid2, 110, cid2));
// Resize clip
REQUIRE(timeline->requestItemResize(cid2, 10, false, true));
REQUIRE(timeline->getClipPlaytime(cid2) == 10);
REQUIRE(timeline->requestClipMove(cid2, tid2, 110));
SECTION("Create and delete mix")
{
REQUIRE(timeline->getClipPosition(cid2) == 110);
REQUIRE(timeline->mixClip(cid2));
REQUIRE(timeline->getClipPlaytime(cid1) > 10);
REQUIRE(timeline->getClipPosition(cid2) != 110);
}
Logger::print_trace();
}
......@@ -22,7 +22,7 @@ QString createProducer(Mlt::Profile &prof, std::string color, std::shared_ptr<Pr
return binId;
}
QString createProducerWithSound(Mlt::Profile &prof, std::shared_ptr<ProjectItemModel> binModel)
QString createProducerWithSound(Mlt::Profile &prof, std::shared_ptr<ProjectItemModel> binModel, int length)
{
Logger::log_create_producer("test_producer_sound", {binModel});
// std::shared_ptr<Mlt::Producer> producer = std::make_shared<Mlt::Producer>(prof,
......@@ -30,14 +30,15 @@ QString createProducerWithSound(Mlt::Profile &prof, std::shared_ptr<ProjectItemM
// In case the test system does not have avformat support, we can switch to the integrated blipflash producer
std::shared_ptr<Mlt::Producer> producer = std::make_shared<Mlt::Producer>(prof, "blipflash");
producer->set("length", 10);
producer->set_in_and_out(0, 9);
producer->set("kdenlive:duration", 10);
producer->set("length", length);
producer->set_in_and_out(0, length - 1);
producer->set("kdenlive:duration", length);
REQUIRE(producer->is_valid());
QString binId = QString::number(binModel->getFreeClipId());
auto binClip = ProjectClip::construct(binId, QIcon(), binModel, producer);
binClip->forceLimitedDuration();
Fun undo = []() { return true; };
Fun redo = []() { return true; };
REQUIRE(binModel->addItem(binClip, binModel->getRootFolder()->clipId(), undo, redo));
......
......@@ -84,4 +84,4 @@ using namespace fakeit;
QString createProducer(Mlt::Profile &prof, std::string color, std::shared_ptr<ProjectItemModel> binModel, int length = 20, bool limited = true);
QString createProducerWithSound(Mlt::Profile &prof, std::shared_ptr<ProjectItemModel> binModel);
QString createProducerWithSound(Mlt::Profile &prof, std::shared_ptr<ProjectItemModel> binModel, int length = 10);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment