Enable clip jobs and convert/extract audio on subclips.

BUG: 416616
parent 0c92179e
Pipeline #14268 passed with stage
in 15 minutes and 33 seconds
......@@ -1790,15 +1790,18 @@ void Bin::selectProxyModel(const QModelIndex &id)
}
}
std::vector<QString> Bin::selectedClipsIds()
std::vector<QString> Bin::selectedClipsIds(bool allowSubClips)
{
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
std::vector<QString> ids;
// We define the lambda that will be executed on each item of the subset of nodes of the tree that are selected
auto itemAdder = [&ids](std::vector<QString> &ids_vec, std::shared_ptr<TreeItem> item) {
auto itemAdder = [&ids, allowSubClips](std::vector<QString> &ids_vec, std::shared_ptr<TreeItem> item) {
auto binItem = std::static_pointer_cast<AbstractProjectItem>(item);
if (binItem->itemType() == AbstractProjectItem::ClipItem) {
ids.push_back(binItem->clipId());
} else if (allowSubClips && binItem->itemType() == AbstractProjectItem::SubClipItem) {
auto subClipItem = std::static_pointer_cast<ProjectSubClip>(item);
ids.push_back(subClipItem->cutClipId());
}
return ids_vec;
};
......@@ -1999,47 +2002,53 @@ void Bin::contextMenuEvent(QContextMenuEvent *event)
itemType = currentItem->itemType();
if (currentItem) {
enableClipActions = true;
std::shared_ptr<ProjectClip> clip = nullptr;
if (itemType == AbstractProjectItem::ClipItem) {
auto clip = std::static_pointer_cast<ProjectClip>(currentItem);
if (clip) {
m_proxyAction->blockSignals(true);
clip = std::static_pointer_cast<ProjectClip>(currentItem);
} else if (itemType == AbstractProjectItem::SubClipItem) {
auto subClip = std::static_pointer_cast<ProjectSubClip>(currentItem);
clip = subClip->getMasterClip();
}
if (clip) {
m_proxyAction->blockSignals(true);
if (itemType == AbstractProjectItem::ClipItem) {
emit findInTimeline(clip->clipId(), clip->timelineInstances());
clipService = clip->getProducerProperty(QStringLiteral("mlt_service"));
m_proxyAction->setChecked(clip->hasProxy());
QList<QAction *> transcodeActions;
if (m_transcodeAction) {
transcodeActions = m_transcodeAction->actions();
}
QStringList dataList;
QString condition;
audioCodec = clip->codec(true);
QString videoCodec = clip->codec(false);
type = clip->clipType();
if (clip->hasUrl()) {
isImported = true;
}
bool noCodecInfo = false;
if (audioCodec.isEmpty() && videoCodec.isEmpty()) {
noCodecInfo = true;
}
for (int i = 0; i < transcodeActions.count(); ++i) {
dataList = transcodeActions.at(i)->data().toStringList();
if (dataList.count() > 4) {
condition = dataList.at(4);
if (condition.isEmpty()) {
transcodeActions.at(i)->setEnabled(true);
continue;
}
if (noCodecInfo) {
// No audio / video codec, this is an MLT clip, disable conditionnal transcoding
transcodeActions.at(i)->setEnabled(false);
continue;
}
if (condition.startsWith(QLatin1String("vcodec"))) {
transcodeActions.at(i)->setEnabled(condition.section(QLatin1Char('='), 1, 1) == videoCodec);
} else if (condition.startsWith(QLatin1String("acodec"))) {
transcodeActions.at(i)->setEnabled(condition.section(QLatin1Char('='), 1, 1) == audioCodec);
}
}
clipService = clip->getProducerProperty(QStringLiteral("mlt_service"));
m_proxyAction->setChecked(clip->hasProxy());
QList<QAction *> transcodeActions;
if (m_transcodeAction) {
transcodeActions = m_transcodeAction->actions();
}
QStringList dataList;
QString condition;
audioCodec = clip->codec(true);
QString videoCodec = clip->codec(false);
type = clip->clipType();
if (clip->hasUrl()) {
isImported = true;
}
bool noCodecInfo = false;
if (audioCodec.isEmpty() && videoCodec.isEmpty()) {
noCodecInfo = true;
}
for (int i = 0; i < transcodeActions.count(); ++i) {
dataList = transcodeActions.at(i)->data().toStringList();
if (dataList.count() > 4) {
condition = dataList.at(4);
if (condition.isEmpty()) {
transcodeActions.at(i)->setEnabled(true);
continue;
}
if (noCodecInfo) {
// No audio / video codec, this is an MLT clip, disable conditionnal transcoding
transcodeActions.at(i)->setEnabled(false);
continue;
}
if (condition.startsWith(QLatin1String("vcodec"))) {
transcodeActions.at(i)->setEnabled(condition.section(QLatin1Char('='), 1, 1) == videoCodec);
} else if (condition.startsWith(QLatin1String("acodec"))) {
transcodeActions.at(i)->setEnabled(condition.section(QLatin1Char('='), 1, 1) == audioCodec);
}
}
}
......@@ -2068,7 +2077,7 @@ void Bin::contextMenuEvent(QContextMenuEvent *event)
m_openAction->setVisible(itemType != AbstractProjectItem::FolderItem);
m_reloadAction->setVisible(itemType != AbstractProjectItem::FolderItem);
m_duplicateAction->setVisible(itemType != AbstractProjectItem::FolderItem);
m_inTimelineAction->setVisible(itemType != AbstractProjectItem::FolderItem);
m_inTimelineAction->setVisible(itemType == AbstractProjectItem::ClipItem);
if (m_transcodeAction) {
m_transcodeAction->setEnabled(enableClipActions);
......@@ -2078,7 +2087,7 @@ void Bin::contextMenuEvent(QContextMenuEvent *event)
itemType != AbstractProjectItem::FolderItem &&
(clipService.contains(QStringLiteral("avformat")) || clipService.contains(QStringLiteral("xml")) || clipService.contains(QStringLiteral("consumer"))));
m_extractAudioAction->menuAction()->setVisible(!isFolder && !audioCodec.isEmpty());
m_locateAction->setVisible(itemType != AbstractProjectItem::FolderItem && (isImported));
m_locateAction->setVisible(itemType == AbstractProjectItem::ClipItem && (isImported));
// Show menu
event->setAccepted(true);
......
......@@ -194,9 +194,10 @@ public:
/** @brief Get a clip from it's id */
std::shared_ptr<ProjectClip> getBinClip(const QString &id);
/** @brief Returns a list of selected clip ids
/** @brief Returns a list of selected clip ids.
* @param allowSubClips: if true, will include subclip ids in the form: "master clip id/in/out"
*/
std::vector<QString> selectedClipsIds();
std::vector<QString> selectedClipsIds(bool allowSubClips = false);
// Returns the selected clips
QList<std::shared_ptr<ProjectClip>> selectedClips();
......
......@@ -77,6 +77,11 @@ ProjectSubClip::~ProjectSubClip()
// controller is deleted in bincontroller
}
const QString ProjectSubClip::cutClipId() const
{
return QString("%1/%2/%3").arg(m_parentClipId).arg(m_inPoint).arg(m_outPoint);
}
void ProjectSubClip::gotThumb(int pos, const QImage &img)
{
if (pos == m_inPoint) {
......
......@@ -70,6 +70,9 @@ public:
/** @brief Returns the clip's duration. */
GenTime duration() const;
/** @brief A string composed of: Clip id / in / out. */
const QString cutClipId() const;
/** @brief Sets thumbnail for this clip. */
void setThumbnail(const QImage &);
QPixmap thumbnail(int width, int height);
......@@ -84,13 +87,13 @@ public:
/** @brief returns a pointer to the parent clip */
std::shared_ptr<ProjectClip> getMasterClip() const;
/** @brief Returns the clip type as defined in definitions.h */
ClipType::ProducerType clipType() const override;
/** @brief Set properties on this clip zone */
void setProperties(const QMap<QString, QString> &properties);
/** @brief Set rating on item */
void setRating(uint rating) override;
......
......@@ -26,7 +26,14 @@ AbstractClipJob::AbstractClipJob(JOBTYPE type, QString id, QObject *parent)
: QObject(parent)
, m_clipId(std::move(id))
, m_jobType(type)
, m_inPoint(-1)
, m_outPoint(-1)
{
if (m_clipId.count(QStringLiteral("/")) == 2) {
m_inPoint = m_clipId.section(QLatin1Char('/'), 1, 1).toInt();
m_outPoint = m_clipId.section(QLatin1Char('/'), 2).toInt();
m_clipId = m_clipId.section(QLatin1Char('/'), 0, 0);
}
}
AbstractClipJob::~AbstractClipJob() = default;
......
......@@ -88,7 +88,8 @@ protected:
QString m_logDetails;
int m_addClipToProject;
JOBTYPE m_jobType;
int m_inPoint;
int m_outPoint;
bool m_resultConsumed{false};
signals:
......
......@@ -106,7 +106,7 @@ bool AudioThumbJob::computeWithMlt()
for (double &v : mltLevels) {
m_audioLevels << 255 * v / maxLevel;
}
m_done = true;
return true;
}
......@@ -331,7 +331,7 @@ bool AudioThumbJob::startJob()
if (!m_audioLevels.isEmpty()) {
m_dataInCache = true;
}
// Check audio thumbnail image
if (ThumbnailCache::get()->hasThumbnail(m_clipId, -1, false)) {
m_thumbInCache = true;
......@@ -341,7 +341,7 @@ bool AudioThumbJob::startJob()
m_successful = true;
return true;
}
bool ok = m_binClip->clipType() == ClipType::Playlist ? false : computeWithFFMPEG();
ok = ok ? ok : computeWithMlt();
Q_ASSERT(ok == m_done);
......
......@@ -44,6 +44,12 @@ MeltJob::MeltJob(const QString &binId, JOBTYPE type, bool useProducerProfile, in
, m_out(out)
, m_requiresFilter(true)
{
if (m_in == -1) {
m_in = m_inPoint;
}
if (m_out == -1) {
m_out = m_outPoint;
}
}
bool MeltJob::startJob()
......@@ -145,6 +151,14 @@ bool MeltJob::startJob()
}
*/
configureProducer();
if ((m_producer == nullptr) || !m_producer->is_valid()) {
// Clip was removed or something went wrong, Notify user?
m_errorMessage.append(i18n("Invalid clip"));
m_successful = false;
m_done = true;
return false;
}
if (m_out == -1) {
m_out = m_producer->get_playtime() - 1;
}
......@@ -155,14 +169,6 @@ bool MeltJob::startJob()
std::swap(m_wholeProducer, m_producer);
m_producer.reset(m_wholeProducer->cut(m_in, m_out));
}
configureProducer();
if ((m_producer == nullptr) || !m_producer->is_valid()) {
// Clip was removed or something went wrong, Notify user?
m_errorMessage.append(i18n("Invalid clip"));
m_successful = false;
m_done = true;
return false;
}
// Build consumer
configureConsumer();
......
......@@ -69,6 +69,8 @@ void SpeedJob::configureProducer()
if (!qFuzzyCompare(m_speed, 1.0)) {
QString resource = m_producer->get("resource");
m_producer = std::make_unique<Mlt::Producer>(*m_profile.get(), "timewarp", QStringLiteral("%1:%2").arg(QString::fromStdString(std::to_string(m_speed))).arg(resource).toUtf8().constData());
m_in /= m_speed;
m_out /= m_speed;
}
}
......@@ -85,7 +87,7 @@ int SpeedJob::prepareJob(const std::shared_ptr<JobManager> &ptr, const std::vect
d.setLayout(l);
QLabel labUrl(&d);
KUrlRequester fileUrl(&d);
auto binClip = pCore->projectItemModel()->getClipByBinID(binIds.front());
auto binClip = pCore->projectItemModel()->getClipByBinID(binIds.front().section(QLatin1Char('/'), 0, 0));
QDir folder = QFileInfo(binClip->url()).absoluteDir();
folder.mkpath(i18n("Speed Change"));
folder.cd(i18n("Speed Change"));
......@@ -129,7 +131,7 @@ int SpeedJob::prepareJob(const std::shared_ptr<JobManager> &ptr, const std::vect
mltfile = fileUrl.url().toLocalFile();
} else {
QDir dir(fileUrl.url().toLocalFile());
binClip = pCore->projectItemModel()->getClipByBinID(binId);
binClip = pCore->projectItemModel()->getClipByBinID(binId.section(QLatin1Char('/'), 0, 0));
mltfile = QFileInfo(binClip->url()).fileName().section(QLatin1Char('.'), 0, -2);
mltfile.append(QString("-%1.mlt").arg(QString::number((int)speed)));
mltfile = dir.absoluteFilePath(mltfile);
......
......@@ -92,7 +92,7 @@ int StabilizeJob::prepareJob(const std::shared_ptr<JobManager> &ptr, const std::
QString destination = d->destination();
std::unordered_map<QString, QString> destinations; // keys are binIds, values are path to target files
for (const auto &binId : binIds) {
auto binClip = pCore->projectItemModel()->getClipByBinID(binId);
auto binClip = pCore->projectItemModel()->getClipByBinID(binId.section(QLatin1Char('/'), 0, 0));
if (binIds.size() == 1) {
// We only have one clip, destination points to the final url
destinations[binId] = destination;
......
......@@ -118,6 +118,10 @@ bool TranscodeJob::startJob()
// Ask for progress reporting
mltParameters << QStringLiteral("progress=1");
if (m_outPoint > 0) {
mltParameters.prepend(QString("out=%1").arg(m_outPoint));
mltParameters.prepend(QString("in=%1").arg(m_inPoint));
}
mltParameters.prepend(source);
m_jobProcess = new QProcess;
// m_jobProcess->setProcessChannelMode(QProcess::MergedChannels);
......@@ -136,8 +140,14 @@ bool TranscodeJob::startJob()
return false;
}
m_jobDuration = (int)binClip->duration().seconds();
//parameters << QStringLiteral("-y");
parameters << QStringLiteral("-y");
if (m_inPoint > -1) {
parameters << QStringLiteral("-ss") << QString::number(GenTime(m_inPoint, pCore->getCurrentFps()).seconds());
}
parameters << QStringLiteral("-stats") << QStringLiteral("-i") << source;
if (m_outPoint > -1) {
parameters << QStringLiteral("-to") << QString::number(GenTime(m_outPoint - m_inPoint, pCore->getCurrentFps()).seconds());
}
// Only output error data
parameters << QStringLiteral("-v") << QStringLiteral("error");
QStringList params = m_transcodeParams.split(QLatin1Char(' '));
......
......@@ -3224,7 +3224,7 @@ void MainWindow::buildDynamicActions()
QAction *action = new QAction(i18n("Stabilize (%1)", stab), m_extraFactory->actionCollection());
ts->addAction(action->text(), action);
connect(action, &QAction::triggered, [stab]() {
pCore->jobManager()->startJob<StabilizeJob>(pCore->bin()->selectedClipsIds(), {},
pCore->jobManager()->startJob<StabilizeJob>(pCore->bin()->selectedClipsIds(true), {},
i18np("Stabilize clip", "Stabilize clips", pCore->bin()->selectedClipsIds().size()), stab);
});
break;
......@@ -3236,14 +3236,14 @@ void MainWindow::buildDynamicActions()
QAction *action = new QAction(i18n("Automatic scene split"), m_extraFactory->actionCollection());
ts->addAction(action->text(), action);
connect(action, &QAction::triggered,
[&]() { pCore->jobManager()->startJob<SceneSplitJob>(pCore->bin()->selectedClipsIds(), {}, i18n("Scene detection")); });
[&]() { pCore->jobManager()->startJob<SceneSplitJob>(pCore->bin()->selectedClipsIds(true), {}, i18n("Scene detection")); });
}
}
if (true /* TODO: check if timewarp producer is available */) {
QAction *action = new QAction(i18n("Duplicate clip with speed change"), m_extraFactory->actionCollection());
ts->addAction(action->text(), action);
connect(action, &QAction::triggered,
[&]() { pCore->jobManager()->startJob<SpeedJob>(pCore->bin()->selectedClipsIds(), {}, i18n("Change clip speed")); });
[&]() { pCore->jobManager()->startJob<SpeedJob>(pCore->bin()->selectedClipsIds(true), {}, i18n("Change clip speed")); });
}
// TODO refac reimplement analyseclipjob
......@@ -3284,7 +3284,7 @@ void MainWindow::buildDynamicActions()
}
connect(a, &QAction::triggered, [&, a]() {
QStringList transcodeData = a->data().toStringList();
pCore->jobManager()->startJob<TranscodeJob>(pCore->bin()->selectedClipsIds(), -1, QString(), transcodeData.first());
pCore->jobManager()->startJob<TranscodeJob>(pCore->bin()->selectedClipsIds(true), -1, QString(), transcodeData.first());
});
if (transList.count() > 2 && transList.at(2) == QLatin1String("audio")) {
// This is an audio transcoding action
......
......@@ -1173,6 +1173,7 @@ int GLWidget::reconfigure()
m_consumer->set("prefill", qMax(1, fps / 25));
m_consumer->set("drop_max", fps / 4);
m_consumer->set("scrub_audio", 1);
m_consumer->set("channels", 2);
if (KdenliveSettings::monitor_gamma() == 0) {
m_consumer->set("color_trc", "iec61966_2_1");
} else {
......
......@@ -143,8 +143,8 @@ Monitor::Monitor(Kdenlive::MonitorId id, MonitorManager *manager, QWidget *paren
, m_loopClipTransition(true)
, m_editMarker(nullptr)
, m_forceSizeFactor(0)
, m_lastMonitorSceneType(MonitorSceneDefault)
, m_offset(id == Kdenlive::ClipMonitor ? 0 : TimelineModel::seekDuration)
, m_lastMonitorSceneType(MonitorSceneDefault)
{
auto *layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
......
......@@ -50,7 +50,7 @@ ClipStabilize::ClipStabilize(const std::vector<QString> &binIds, QString filterN
// setStyleSheet(stylesheet);
Q_ASSERT(binIds.size() > 0);
auto firstBinClip = pCore->projectItemModel()->getClipByBinID(m_binIds.front());
auto firstBinClip = pCore->projectItemModel()->getClipByBinID(m_binIds.front().section(QLatin1Char('/'), 0, 0));
auto firstUrl = firstBinClip->url();
if (m_binIds.size() == 1) {
QString newFile = firstUrl;
......@@ -150,7 +150,7 @@ void ClipStabilize::slotValidate()
QDir folder(dest_url->url().toLocalFile());
QStringList existingFiles;
for (const QString &binId : m_binIds) {
auto binClip = pCore->projectItemModel()->getClipByBinID(binId);
auto binClip = pCore->projectItemModel()->getClipByBinID(binId.section(QLatin1Char('/'), 0, 0));
auto url = binClip->url();
if (folder.exists(url + QStringLiteral(".mlt"))) {
existingFiles.append(folder.absoluteFilePath(url + QStringLiteral(".mlt")));
......
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