fix line endings
This commit is contained in:
+179
-179
@@ -1,179 +1,179 @@
|
||||
/***
|
||||
|
||||
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 "footage.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtMath>
|
||||
#include <QPainter>
|
||||
#include <QCoreApplication>
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace OCIO = OCIO_NAMESPACE::v1;
|
||||
|
||||
#include "project/previewgenerator.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "global/config.h"
|
||||
#include "global/global.h"
|
||||
|
||||
Footage::Footage() :
|
||||
ready(false),
|
||||
preview_gen(nullptr),
|
||||
invalid(false),
|
||||
in(0),
|
||||
out(0),
|
||||
speed(1.0),
|
||||
alpha_is_associated(true),
|
||||
proxy(false),
|
||||
start_number(0)
|
||||
{
|
||||
ready_lock.lock();
|
||||
}
|
||||
|
||||
Footage::~Footage() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Footage::Save(QXmlStreamWriter &stream)
|
||||
{
|
||||
QDir proj_dir = QFileInfo(olive::ActiveProjectFilename).absoluteDir();
|
||||
|
||||
stream.writeStartElement("footage");
|
||||
stream.writeAttribute("id", QString::number(save_id));
|
||||
stream.writeAttribute("name", name);
|
||||
stream.writeAttribute("url", proj_dir.relativeFilePath(url));
|
||||
stream.writeAttribute("duration", QString::number(length));
|
||||
stream.writeAttribute("using_inout", QString::number(using_inout));
|
||||
stream.writeAttribute("in", QString::number(in));
|
||||
stream.writeAttribute("out", QString::number(out));
|
||||
stream.writeAttribute("speed", QString::number(speed));
|
||||
stream.writeAttribute("alphapremul", QString::number(alpha_is_associated));
|
||||
stream.writeAttribute("startnumber", QString::number(start_number));
|
||||
stream.writeAttribute("colorspace", Colorspace());
|
||||
|
||||
stream.writeAttribute("proxy", QString::number(proxy));
|
||||
stream.writeAttribute("proxypath", proxy_path);
|
||||
|
||||
// save video stream metadata
|
||||
for (int j=0;j<video_tracks.size();j++) {
|
||||
const FootageStream& ms = video_tracks.at(j);
|
||||
stream.writeStartElement("video");
|
||||
stream.writeAttribute("id", QString::number(ms.file_index));
|
||||
stream.writeAttribute("width", QString::number(ms.video_width));
|
||||
stream.writeAttribute("height", QString::number(ms.video_height));
|
||||
stream.writeAttribute("framerate", QString::number(ms.video_frame_rate, 'f', 10));
|
||||
stream.writeAttribute("infinite", QString::number(ms.infinite_length));
|
||||
stream.writeEndElement(); // video
|
||||
}
|
||||
|
||||
// save audio stream metadata
|
||||
for (int j=0;j<audio_tracks.size();j++) {
|
||||
const FootageStream& ms = audio_tracks.at(j);
|
||||
stream.writeStartElement("audio");
|
||||
stream.writeAttribute("id", QString::number(ms.file_index));
|
||||
stream.writeAttribute("channels", QString::number(ms.audio_channels));
|
||||
stream.writeAttribute("layout", QString::number(ms.audio_layout));
|
||||
stream.writeAttribute("frequency", QString::number(ms.audio_frequency));
|
||||
stream.writeEndElement(); // audio
|
||||
}
|
||||
|
||||
// save footage markers
|
||||
for (int j=0;j<markers.size();j++) {
|
||||
markers.at(j).Save(stream);
|
||||
}
|
||||
|
||||
stream.writeEndElement(); // footage
|
||||
}
|
||||
|
||||
QString Footage::Colorspace()
|
||||
{
|
||||
if (!colorspace_.isEmpty()) {
|
||||
return colorspace_;
|
||||
}
|
||||
|
||||
// If this footage has no color space set, try to guess the color space from the filename
|
||||
OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
|
||||
QString guess_colorspace = config->parseColorSpaceFromString(url.toUtf8());
|
||||
|
||||
if (!guess_colorspace.isEmpty()) {
|
||||
return guess_colorspace;
|
||||
}
|
||||
|
||||
return olive::config.ocio_default_input_colorspace;
|
||||
}
|
||||
|
||||
void Footage::SetColorspace(const QString &cs)
|
||||
{
|
||||
colorspace_ = cs;
|
||||
}
|
||||
|
||||
void Footage::reset() {
|
||||
if (preview_gen != nullptr) {
|
||||
preview_gen->cancel();
|
||||
}
|
||||
video_tracks.clear();
|
||||
audio_tracks.clear();
|
||||
ready = false;
|
||||
}
|
||||
|
||||
long Footage::get_length_in_frames(double frame_rate) {
|
||||
if (length >= 0) {
|
||||
return qFloor((double(length) / double(AV_TIME_BASE)) * frame_rate / speed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FootageStream* Footage::get_stream_from_file_index(bool video, int index) {
|
||||
if (video) {
|
||||
for (int i=0;i<video_tracks.size();i++) {
|
||||
if (video_tracks.at(i).file_index == index) {
|
||||
return &video_tracks[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i=0;i<audio_tracks.size();i++) {
|
||||
if (audio_tracks.at(i).file_index == index) {
|
||||
return &audio_tracks[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString Footage::get_interlacing_name(int interlacing) {
|
||||
switch (interlacing) {
|
||||
case VIDEO_PROGRESSIVE: return QCoreApplication::translate("InterlacingName", "None (Progressive)");
|
||||
case VIDEO_TOP_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Upper Field First");
|
||||
case VIDEO_BOTTOM_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Lower Field First");
|
||||
default: return QCoreApplication::translate("InterlacingName", "Invalid");
|
||||
}
|
||||
}
|
||||
|
||||
QString Footage::get_channel_layout_name(int channels, uint64_t layout) {
|
||||
switch (channels) {
|
||||
case 0: return QCoreApplication::translate("ChannelLayoutName", "Invalid");
|
||||
case 1: return QCoreApplication::translate("ChannelLayoutName", "Mono");
|
||||
case 2: return QCoreApplication::translate("ChannelLayoutName", "Stereo");
|
||||
default: {
|
||||
char buf[50];
|
||||
av_get_channel_layout_string(buf, sizeof(buf), channels, layout);
|
||||
return QString(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
/***
|
||||
|
||||
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 "footage.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtMath>
|
||||
#include <QPainter>
|
||||
#include <QCoreApplication>
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace OCIO = OCIO_NAMESPACE::v1;
|
||||
|
||||
#include "project/previewgenerator.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "global/config.h"
|
||||
#include "global/global.h"
|
||||
|
||||
Footage::Footage() :
|
||||
ready(false),
|
||||
preview_gen(nullptr),
|
||||
invalid(false),
|
||||
in(0),
|
||||
out(0),
|
||||
speed(1.0),
|
||||
alpha_is_associated(true),
|
||||
proxy(false),
|
||||
start_number(0)
|
||||
{
|
||||
ready_lock.lock();
|
||||
}
|
||||
|
||||
Footage::~Footage() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Footage::Save(QXmlStreamWriter &stream)
|
||||
{
|
||||
QDir proj_dir = QFileInfo(olive::ActiveProjectFilename).absoluteDir();
|
||||
|
||||
stream.writeStartElement("footage");
|
||||
stream.writeAttribute("id", QString::number(save_id));
|
||||
stream.writeAttribute("name", name);
|
||||
stream.writeAttribute("url", proj_dir.relativeFilePath(url));
|
||||
stream.writeAttribute("duration", QString::number(length));
|
||||
stream.writeAttribute("using_inout", QString::number(using_inout));
|
||||
stream.writeAttribute("in", QString::number(in));
|
||||
stream.writeAttribute("out", QString::number(out));
|
||||
stream.writeAttribute("speed", QString::number(speed));
|
||||
stream.writeAttribute("alphapremul", QString::number(alpha_is_associated));
|
||||
stream.writeAttribute("startnumber", QString::number(start_number));
|
||||
stream.writeAttribute("colorspace", Colorspace());
|
||||
|
||||
stream.writeAttribute("proxy", QString::number(proxy));
|
||||
stream.writeAttribute("proxypath", proxy_path);
|
||||
|
||||
// save video stream metadata
|
||||
for (int j=0;j<video_tracks.size();j++) {
|
||||
const FootageStream& ms = video_tracks.at(j);
|
||||
stream.writeStartElement("video");
|
||||
stream.writeAttribute("id", QString::number(ms.file_index));
|
||||
stream.writeAttribute("width", QString::number(ms.video_width));
|
||||
stream.writeAttribute("height", QString::number(ms.video_height));
|
||||
stream.writeAttribute("framerate", QString::number(ms.video_frame_rate, 'f', 10));
|
||||
stream.writeAttribute("infinite", QString::number(ms.infinite_length));
|
||||
stream.writeEndElement(); // video
|
||||
}
|
||||
|
||||
// save audio stream metadata
|
||||
for (int j=0;j<audio_tracks.size();j++) {
|
||||
const FootageStream& ms = audio_tracks.at(j);
|
||||
stream.writeStartElement("audio");
|
||||
stream.writeAttribute("id", QString::number(ms.file_index));
|
||||
stream.writeAttribute("channels", QString::number(ms.audio_channels));
|
||||
stream.writeAttribute("layout", QString::number(ms.audio_layout));
|
||||
stream.writeAttribute("frequency", QString::number(ms.audio_frequency));
|
||||
stream.writeEndElement(); // audio
|
||||
}
|
||||
|
||||
// save footage markers
|
||||
for (int j=0;j<markers.size();j++) {
|
||||
markers.at(j).Save(stream);
|
||||
}
|
||||
|
||||
stream.writeEndElement(); // footage
|
||||
}
|
||||
|
||||
QString Footage::Colorspace()
|
||||
{
|
||||
if (!colorspace_.isEmpty()) {
|
||||
return colorspace_;
|
||||
}
|
||||
|
||||
// If this footage has no color space set, try to guess the color space from the filename
|
||||
OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
|
||||
QString guess_colorspace = config->parseColorSpaceFromString(url.toUtf8());
|
||||
|
||||
if (!guess_colorspace.isEmpty()) {
|
||||
return guess_colorspace;
|
||||
}
|
||||
|
||||
return olive::config.ocio_default_input_colorspace;
|
||||
}
|
||||
|
||||
void Footage::SetColorspace(const QString &cs)
|
||||
{
|
||||
colorspace_ = cs;
|
||||
}
|
||||
|
||||
void Footage::reset() {
|
||||
if (preview_gen != nullptr) {
|
||||
preview_gen->cancel();
|
||||
}
|
||||
video_tracks.clear();
|
||||
audio_tracks.clear();
|
||||
ready = false;
|
||||
}
|
||||
|
||||
long Footage::get_length_in_frames(double frame_rate) {
|
||||
if (length >= 0) {
|
||||
return qFloor((double(length) / double(AV_TIME_BASE)) * frame_rate / speed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FootageStream* Footage::get_stream_from_file_index(bool video, int index) {
|
||||
if (video) {
|
||||
for (int i=0;i<video_tracks.size();i++) {
|
||||
if (video_tracks.at(i).file_index == index) {
|
||||
return &video_tracks[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i=0;i<audio_tracks.size();i++) {
|
||||
if (audio_tracks.at(i).file_index == index) {
|
||||
return &audio_tracks[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString Footage::get_interlacing_name(int interlacing) {
|
||||
switch (interlacing) {
|
||||
case VIDEO_PROGRESSIVE: return QCoreApplication::translate("InterlacingName", "None (Progressive)");
|
||||
case VIDEO_TOP_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Upper Field First");
|
||||
case VIDEO_BOTTOM_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Lower Field First");
|
||||
default: return QCoreApplication::translate("InterlacingName", "Invalid");
|
||||
}
|
||||
}
|
||||
|
||||
QString Footage::get_channel_layout_name(int channels, uint64_t layout) {
|
||||
switch (channels) {
|
||||
case 0: return QCoreApplication::translate("ChannelLayoutName", "Invalid");
|
||||
case 1: return QCoreApplication::translate("ChannelLayoutName", "Mono");
|
||||
case 2: return QCoreApplication::translate("ChannelLayoutName", "Stereo");
|
||||
default: {
|
||||
char buf[50];
|
||||
av_get_channel_layout_string(buf, sizeof(buf), channels, layout);
|
||||
return QString(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+130
-130
@@ -1,130 +1,130 @@
|
||||
/***
|
||||
|
||||
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 FOOTAGE_H
|
||||
#define FOOTAGE_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
#include <memory>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include <QMutex>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
|
||||
#include "timeline/marker.h"
|
||||
|
||||
enum VideoInterlacingMode {
|
||||
VIDEO_PROGRESSIVE,
|
||||
VIDEO_TOP_FIELD_FIRST,
|
||||
VIDEO_BOTTOM_FIELD_FIRST
|
||||
};
|
||||
|
||||
class Sequence;
|
||||
class Clip;
|
||||
class PreviewGenerator;
|
||||
class Footage;
|
||||
|
||||
struct FootageStream {
|
||||
// Format stream index
|
||||
Footage* footage;
|
||||
int file_index;
|
||||
|
||||
// Video parameters
|
||||
int video_width;
|
||||
int video_height;
|
||||
double video_frame_rate;
|
||||
int video_interlacing;
|
||||
int video_auto_interlacing;
|
||||
bool infinite_length;
|
||||
|
||||
// Audio parameters
|
||||
int audio_channels;
|
||||
int audio_layout;
|
||||
int audio_frequency;
|
||||
|
||||
// Enabled
|
||||
bool enabled;
|
||||
|
||||
// preview thumbnail/waveform
|
||||
bool preview_done;
|
||||
QImage video_preview;
|
||||
QVector<qint8> audio_preview;
|
||||
};
|
||||
|
||||
class Footage {
|
||||
public:
|
||||
Footage();
|
||||
~Footage();
|
||||
|
||||
void Save(QXmlStreamWriter& stream);
|
||||
|
||||
// footage metadata
|
||||
QString url;
|
||||
QString name;
|
||||
int64_t length;
|
||||
QVector<FootageStream> video_tracks;
|
||||
QVector<FootageStream> audio_tracks;
|
||||
int save_id;
|
||||
bool ready;
|
||||
bool invalid;
|
||||
double speed;
|
||||
bool alpha_is_associated;
|
||||
int start_number;
|
||||
|
||||
// color management
|
||||
QString Colorspace();
|
||||
void SetColorspace(const QString& cs);
|
||||
|
||||
// proxy config
|
||||
bool proxy;
|
||||
QString proxy_path;
|
||||
|
||||
// thumbnail/waveform generation
|
||||
PreviewGenerator* preview_gen;
|
||||
QMutex ready_lock;
|
||||
|
||||
// in/out points
|
||||
bool using_inout;
|
||||
long in;
|
||||
long out;
|
||||
|
||||
// markers
|
||||
QVector<Marker> markers;
|
||||
|
||||
// functions
|
||||
long get_length_in_frames(double frame_rate);
|
||||
FootageStream *get_stream_from_file_index(bool video, int index);
|
||||
void reset();
|
||||
|
||||
static QString get_channel_layout_name(int channels, uint64_t layout);
|
||||
static QString get_interlacing_name(int interlacing);
|
||||
private:
|
||||
QString colorspace_;
|
||||
};
|
||||
|
||||
using FootagePtr = std::shared_ptr<Footage>;
|
||||
|
||||
#endif // FOOTAGE_H
|
||||
/***
|
||||
|
||||
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 FOOTAGE_H
|
||||
#define FOOTAGE_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
#include <memory>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include <QMutex>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
|
||||
#include "timeline/marker.h"
|
||||
|
||||
enum VideoInterlacingMode {
|
||||
VIDEO_PROGRESSIVE,
|
||||
VIDEO_TOP_FIELD_FIRST,
|
||||
VIDEO_BOTTOM_FIELD_FIRST
|
||||
};
|
||||
|
||||
class Sequence;
|
||||
class Clip;
|
||||
class PreviewGenerator;
|
||||
class Footage;
|
||||
|
||||
struct FootageStream {
|
||||
// Format stream index
|
||||
Footage* footage;
|
||||
int file_index;
|
||||
|
||||
// Video parameters
|
||||
int video_width;
|
||||
int video_height;
|
||||
double video_frame_rate;
|
||||
int video_interlacing;
|
||||
int video_auto_interlacing;
|
||||
bool infinite_length;
|
||||
|
||||
// Audio parameters
|
||||
int audio_channels;
|
||||
int audio_layout;
|
||||
int audio_frequency;
|
||||
|
||||
// Enabled
|
||||
bool enabled;
|
||||
|
||||
// preview thumbnail/waveform
|
||||
bool preview_done;
|
||||
QImage video_preview;
|
||||
QVector<qint8> audio_preview;
|
||||
};
|
||||
|
||||
class Footage {
|
||||
public:
|
||||
Footage();
|
||||
~Footage();
|
||||
|
||||
void Save(QXmlStreamWriter& stream);
|
||||
|
||||
// footage metadata
|
||||
QString url;
|
||||
QString name;
|
||||
int64_t length;
|
||||
QVector<FootageStream> video_tracks;
|
||||
QVector<FootageStream> audio_tracks;
|
||||
int save_id;
|
||||
bool ready;
|
||||
bool invalid;
|
||||
double speed;
|
||||
bool alpha_is_associated;
|
||||
int start_number;
|
||||
|
||||
// color management
|
||||
QString Colorspace();
|
||||
void SetColorspace(const QString& cs);
|
||||
|
||||
// proxy config
|
||||
bool proxy;
|
||||
QString proxy_path;
|
||||
|
||||
// thumbnail/waveform generation
|
||||
PreviewGenerator* preview_gen;
|
||||
QMutex ready_lock;
|
||||
|
||||
// in/out points
|
||||
bool using_inout;
|
||||
long in;
|
||||
long out;
|
||||
|
||||
// markers
|
||||
QVector<Marker> markers;
|
||||
|
||||
// functions
|
||||
long get_length_in_frames(double frame_rate);
|
||||
FootageStream *get_stream_from_file_index(bool video, int index);
|
||||
void reset();
|
||||
|
||||
static QString get_channel_layout_name(int channels, uint64_t layout);
|
||||
static QString get_interlacing_name(int interlacing);
|
||||
private:
|
||||
QString colorspace_;
|
||||
};
|
||||
|
||||
using FootagePtr = std::shared_ptr<Footage>;
|
||||
|
||||
#endif // FOOTAGE_H
|
||||
|
||||
+628
-628
File diff suppressed because it is too large
Load Diff
+65
-65
@@ -1,65 +1,65 @@
|
||||
/***
|
||||
|
||||
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 PREVIEWGENERATOR_H
|
||||
#define PREVIEWGENERATOR_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QSemaphore>
|
||||
#include <QDir>
|
||||
|
||||
#include "project/footage.h"
|
||||
#include "project/media.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
class PreviewGenerator : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PreviewGenerator(Media*);
|
||||
void run();
|
||||
void cancel();
|
||||
|
||||
static void AnalyzeMedia(Media*);
|
||||
private:
|
||||
void parse_media();
|
||||
bool retrieve_preview(const QString &hash);
|
||||
void generate_waveform();
|
||||
void finalize_media();
|
||||
void invalidate_media(const QString& error_msg);
|
||||
QString get_thumbnail_path(const QString &hash, const FootageStream &ms);
|
||||
QString get_waveform_path(const QString& hash, const FootageStream &ms);
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
Media* media_;
|
||||
Footage* footage_;
|
||||
bool retrieve_duration_;
|
||||
bool contains_still_image_;
|
||||
bool cancelled_;
|
||||
QDir data_dir_;
|
||||
};
|
||||
|
||||
#endif // PREVIEWGENERATOR_H
|
||||
/***
|
||||
|
||||
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 PREVIEWGENERATOR_H
|
||||
#define PREVIEWGENERATOR_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QSemaphore>
|
||||
#include <QDir>
|
||||
|
||||
#include "project/footage.h"
|
||||
#include "project/media.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
class PreviewGenerator : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PreviewGenerator(Media*);
|
||||
void run();
|
||||
void cancel();
|
||||
|
||||
static void AnalyzeMedia(Media*);
|
||||
private:
|
||||
void parse_media();
|
||||
bool retrieve_preview(const QString &hash);
|
||||
void generate_waveform();
|
||||
void finalize_media();
|
||||
void invalidate_media(const QString& error_msg);
|
||||
QString get_thumbnail_path(const QString &hash, const FootageStream &ms);
|
||||
QString get_waveform_path(const QString& hash, const FootageStream &ms);
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
Media* media_;
|
||||
Footage* footage_;
|
||||
bool retrieve_duration_;
|
||||
bool contains_still_image_;
|
||||
bool cancelled_;
|
||||
QDir data_dir_;
|
||||
};
|
||||
|
||||
#endif // PREVIEWGENERATOR_H
|
||||
|
||||
+34
-34
@@ -1,34 +1,34 @@
|
||||
/***
|
||||
|
||||
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 PROJECTELEMENTS_H
|
||||
#define PROJECTELEMENTS_H
|
||||
|
||||
/**
|
||||
|
||||
Simple header to include all classes used for an Olive project file
|
||||
|
||||
*/
|
||||
|
||||
// includes elements the user can use in a project
|
||||
#include "media.h"
|
||||
#include "footage.h"
|
||||
|
||||
#endif // PROJECTELEMENTS_H
|
||||
/***
|
||||
|
||||
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 PROJECTELEMENTS_H
|
||||
#define PROJECTELEMENTS_H
|
||||
|
||||
/**
|
||||
|
||||
Simple header to include all classes used for an Olive project file
|
||||
|
||||
*/
|
||||
|
||||
// includes elements the user can use in a project
|
||||
#include "media.h"
|
||||
#include "footage.h"
|
||||
|
||||
#endif // PROJECTELEMENTS_H
|
||||
|
||||
+84
-84
@@ -1,84 +1,84 @@
|
||||
/***
|
||||
|
||||
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 "projectfilter.h"
|
||||
|
||||
#include "project/media.h"
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
ProjectFilter::ProjectFilter(QObject *parent) :
|
||||
QSortFilterProxyModel(parent),
|
||||
show_sequences(true)
|
||||
{}
|
||||
|
||||
bool ProjectFilter::get_show_sequences() {
|
||||
return show_sequences;
|
||||
}
|
||||
|
||||
void ProjectFilter::set_show_sequences(bool b) {
|
||||
show_sequences = b;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void ProjectFilter::update_search_filter(const QString &s) {
|
||||
search_filter = s;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
bool ProjectFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
|
||||
// retrieve media object from index
|
||||
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
||||
Media* media = static_cast<Media*>(index.internalPointer());
|
||||
|
||||
// hide sequences if show_sequences is false
|
||||
if (!show_sequences) {
|
||||
if (media != nullptr && media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by search filter string
|
||||
if (!search_filter.isEmpty()) {
|
||||
// search markers if media is a sequene
|
||||
bool marker_contains_search = false;
|
||||
|
||||
if (media->get_type() == MEDIA_TYPE_SEQUENCE
|
||||
|| media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
QVector<Marker>& markers = media->get_markers();
|
||||
for (int i=0;i<markers.size();i++) {
|
||||
if (markers.at(i).name.contains(search_filter, Qt::CaseInsensitive)) {
|
||||
marker_contains_search = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hide any rows that don't contain the search string (unless it's a folder)
|
||||
if (!marker_contains_search
|
||||
&& media->get_type() != MEDIA_TYPE_FOLDER
|
||||
&& !media->get_name().contains(search_filter, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
||||
}
|
||||
/***
|
||||
|
||||
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 "projectfilter.h"
|
||||
|
||||
#include "project/media.h"
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
ProjectFilter::ProjectFilter(QObject *parent) :
|
||||
QSortFilterProxyModel(parent),
|
||||
show_sequences(true)
|
||||
{}
|
||||
|
||||
bool ProjectFilter::get_show_sequences() {
|
||||
return show_sequences;
|
||||
}
|
||||
|
||||
void ProjectFilter::set_show_sequences(bool b) {
|
||||
show_sequences = b;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void ProjectFilter::update_search_filter(const QString &s) {
|
||||
search_filter = s;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
bool ProjectFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
|
||||
// retrieve media object from index
|
||||
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
||||
Media* media = static_cast<Media*>(index.internalPointer());
|
||||
|
||||
// hide sequences if show_sequences is false
|
||||
if (!show_sequences) {
|
||||
if (media != nullptr && media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by search filter string
|
||||
if (!search_filter.isEmpty()) {
|
||||
// search markers if media is a sequene
|
||||
bool marker_contains_search = false;
|
||||
|
||||
if (media->get_type() == MEDIA_TYPE_SEQUENCE
|
||||
|| media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
QVector<Marker>& markers = media->get_markers();
|
||||
for (int i=0;i<markers.size();i++) {
|
||||
if (markers.at(i).name.contains(search_filter, Qt::CaseInsensitive)) {
|
||||
marker_contains_search = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hide any rows that don't contain the search string (unless it's a folder)
|
||||
if (!marker_contains_search
|
||||
&& media->get_type() != MEDIA_TYPE_FOLDER
|
||||
&& !media->get_name().contains(search_filter, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
||||
}
|
||||
|
||||
+57
-57
@@ -1,57 +1,57 @@
|
||||
/***
|
||||
|
||||
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 PROJECTFILTER_H
|
||||
#define PROJECTFILTER_H
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class ProjectFilter : public QSortFilterProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProjectFilter(QObject *parent = nullptr);
|
||||
|
||||
// are sequences visible
|
||||
bool get_show_sequences();
|
||||
|
||||
public slots:
|
||||
|
||||
// set whether sequences are visible
|
||||
void set_show_sequences(bool b);
|
||||
|
||||
// update search filter
|
||||
void update_search_filter(const QString& s);
|
||||
|
||||
protected:
|
||||
|
||||
// function that filters whether rows are displayed or not
|
||||
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
|
||||
|
||||
private:
|
||||
|
||||
// internal variable for whether to show sequences
|
||||
bool show_sequences;
|
||||
|
||||
// search filter variable
|
||||
QString search_filter;
|
||||
|
||||
};
|
||||
|
||||
#endif // PROJECTFILTER_H
|
||||
/***
|
||||
|
||||
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 PROJECTFILTER_H
|
||||
#define PROJECTFILTER_H
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class ProjectFilter : public QSortFilterProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProjectFilter(QObject *parent = nullptr);
|
||||
|
||||
// are sequences visible
|
||||
bool get_show_sequences();
|
||||
|
||||
public slots:
|
||||
|
||||
// set whether sequences are visible
|
||||
void set_show_sequences(bool b);
|
||||
|
||||
// update search filter
|
||||
void update_search_filter(const QString& s);
|
||||
|
||||
protected:
|
||||
|
||||
// function that filters whether rows are displayed or not
|
||||
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
|
||||
|
||||
private:
|
||||
|
||||
// internal variable for whether to show sequences
|
||||
bool show_sequences;
|
||||
|
||||
// search filter variable
|
||||
QString search_filter;
|
||||
|
||||
};
|
||||
|
||||
#endif // PROJECTFILTER_H
|
||||
|
||||
+427
-427
@@ -1,427 +1,427 @@
|
||||
/***
|
||||
|
||||
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 "proxygenerator.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QtMath>
|
||||
#include <QStatusBar>
|
||||
#include <QDebug>
|
||||
|
||||
#include "global/path.h"
|
||||
#include "project/previewgenerator.h"
|
||||
#include "ui/mediaiconservice.h"
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
// TODO provide more codecs than just this one
|
||||
enum AVCodecID temp_enc_codec = AV_CODEC_ID_PRORES;
|
||||
|
||||
ProxyGenerator::ProxyGenerator() : cancelled(false) {}
|
||||
|
||||
void ProxyGenerator::transcode(const ProxyInfo& info) {
|
||||
Footage* footage = info.media->to_footage();
|
||||
|
||||
// set progress to 0
|
||||
current_progress = 0.0;
|
||||
|
||||
// for image sequences that don't start at 0, set the index where it does start
|
||||
AVDictionary* format_opts = nullptr;
|
||||
if (footage->start_number > 0) {
|
||||
av_dict_set(&format_opts, "start_number", QString::number(footage->start_number).toUtf8(), 0);
|
||||
}
|
||||
|
||||
// open input file
|
||||
AVFormatContext* input_fmt_ctx = nullptr;
|
||||
avformat_open_input(&input_fmt_ctx, footage->url.toUtf8(), nullptr, &format_opts);
|
||||
|
||||
// open output file
|
||||
AVFormatContext* output_fmt_ctx = nullptr;
|
||||
avformat_alloc_output_context2(&output_fmt_ctx, nullptr, nullptr, info.path.toUtf8());
|
||||
|
||||
// open output file writing handle
|
||||
avio_open(&output_fmt_ctx->pb, info.path.toUtf8(), AVIO_FLAG_WRITE);
|
||||
|
||||
// get stream info from input file
|
||||
avformat_find_stream_info(input_fmt_ctx, nullptr);
|
||||
|
||||
// create array of input decoders
|
||||
QVector<AVCodecContext*> input_streams;
|
||||
input_streams.resize(input_fmt_ctx->nb_streams);
|
||||
input_streams.fill(nullptr);
|
||||
|
||||
// create array of output encoders
|
||||
QVector<AVCodecContext*> output_streams;
|
||||
output_streams.resize(input_fmt_ctx->nb_streams);
|
||||
output_streams.fill(nullptr);
|
||||
|
||||
// create array of swscale contexts for pixel format conversion
|
||||
QVector<SwsContext*> sws_contexts;
|
||||
sws_contexts.resize(input_fmt_ctx->nb_streams);
|
||||
sws_contexts.fill(nullptr);
|
||||
|
||||
// loop through file to find compatible video streams
|
||||
for (int i=0;i<int(input_fmt_ctx->nb_streams);i++) {
|
||||
AVStream* in_stream = input_fmt_ctx->streams[i];
|
||||
|
||||
// create new stream in output
|
||||
AVStream* out_stream = avformat_new_stream(output_fmt_ctx, nullptr);
|
||||
out_stream->id = in_stream->id;
|
||||
|
||||
// find decoder for this codec
|
||||
AVCodec* dec_codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
|
||||
|
||||
// find encoder for chosen proxy type
|
||||
AVCodec* enc_codec = avcodec_find_encoder(temp_enc_codec);
|
||||
|
||||
// we only transcode video streams, others we just passthrough
|
||||
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && dec_codec != nullptr) {
|
||||
|
||||
// allocate decoding context for this stream
|
||||
AVCodecContext* dec_ctx = avcodec_alloc_context3(dec_codec);
|
||||
|
||||
// copy parameters from stream to decoding context
|
||||
avcodec_parameters_to_context(dec_ctx, in_stream->codecpar);
|
||||
|
||||
// open decoder
|
||||
avcodec_open2(dec_ctx, dec_codec, nullptr);
|
||||
|
||||
// store decoding context in array
|
||||
input_streams[i] = dec_ctx;
|
||||
|
||||
// retrieve more information about this stream
|
||||
av_dump_format(input_fmt_ctx, i, footage->url.toUtf8(), 0);
|
||||
|
||||
// allocate encoding context for this stream
|
||||
AVCodecContext* enc_ctx = avcodec_alloc_context3(enc_codec);
|
||||
|
||||
// copy properties from decoding context to encoding context
|
||||
enc_ctx->codec_id = temp_enc_codec;
|
||||
enc_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
enc_ctx->width = qFloor(dec_ctx->width*info.size_multiplier);
|
||||
enc_ctx->height = qFloor(dec_ctx->height*info.size_multiplier);
|
||||
enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
|
||||
enc_ctx->pix_fmt = avcodec_find_best_pix_fmt_of_list(enc_codec->pix_fmts, dec_ctx->pix_fmt, 1, nullptr);
|
||||
enc_ctx->framerate = dec_ctx->framerate;
|
||||
enc_ctx->time_base = in_stream->time_base;
|
||||
out_stream->time_base = in_stream->time_base;
|
||||
|
||||
// if format uses global headers, add flag to enc_ctx
|
||||
if (output_fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
|
||||
enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
|
||||
// set encoder options (mostly just multithreading)
|
||||
AVDictionary* opts = nullptr;
|
||||
av_dict_set(&opts, "threads", "auto", 0);
|
||||
|
||||
// open encoder
|
||||
avcodec_open2(enc_ctx, enc_codec, &opts);
|
||||
|
||||
// copy parameters from encoding context to stream
|
||||
avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
|
||||
|
||||
// store encoding context in array
|
||||
output_streams[i] = enc_ctx;
|
||||
|
||||
// create swscontext for this stream
|
||||
SwsContext* sws_ctx = sws_getContext(
|
||||
in_stream->codecpar->width,
|
||||
in_stream->codecpar->height,
|
||||
static_cast<enum AVPixelFormat>(in_stream->codecpar->format),
|
||||
enc_ctx->width,
|
||||
enc_ctx->height,
|
||||
enc_ctx->pix_fmt,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
|
||||
sws_contexts[i] = sws_ctx;
|
||||
} else {
|
||||
avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
|
||||
}
|
||||
}
|
||||
|
||||
// write video header
|
||||
avformat_write_header(output_fmt_ctx, nullptr);
|
||||
|
||||
// packet that av_read_frame will dump file packets into
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
||||
// frame that decoder will decode into
|
||||
AVFrame* dec_frame = av_frame_alloc();
|
||||
|
||||
// main transcoding loop
|
||||
while (!skip) {
|
||||
|
||||
// cache stream index
|
||||
int stream_index = packet.stream_index;
|
||||
|
||||
// retrieve frame from decoder (this will clear the last frame so we don't have to do that)
|
||||
int read_ret = -1;
|
||||
int recfr_ret = -1;
|
||||
do {
|
||||
// read from input file
|
||||
read_ret = av_read_frame(input_fmt_ctx, &packet);
|
||||
|
||||
// handle errors
|
||||
if (read_ret < 0) {
|
||||
|
||||
// AVERROR_EOF means we've simply reached the end of the file, otherwise this is an error
|
||||
if (read_ret != AVERROR_EOF) {
|
||||
qWarning() << "Proxy generation for file" << footage->url << "ended prematurely";
|
||||
}
|
||||
|
||||
// either way, we shall abort reading
|
||||
break;
|
||||
}
|
||||
|
||||
stream_index = packet.stream_index;
|
||||
|
||||
// determine whether this frame is from a stream we're transcoding
|
||||
if (input_streams.at(stream_index) == nullptr) {
|
||||
// if we didn't allocate a decoder for this earlier, we just pass it through
|
||||
|
||||
av_packet_rescale_ts(&packet, input_fmt_ctx->streams[stream_index]->time_base, output_fmt_ctx->streams[stream_index]->time_base);
|
||||
|
||||
// write packet to output
|
||||
av_interleaved_write_frame(output_fmt_ctx, &packet);
|
||||
|
||||
} else {
|
||||
// we're going to transcode this packet.
|
||||
|
||||
// send packet to decoder
|
||||
avcodec_send_packet(input_streams.at(stream_index), &packet);
|
||||
|
||||
// use timestamp and stream duration to create a rough estimation of the progress through this file
|
||||
current_progress = qCeil((double(packet.pts)/double(input_fmt_ctx->streams[packet.stream_index]->duration))*100);
|
||||
|
||||
}
|
||||
|
||||
// free packet allocated by av_read_frame
|
||||
av_packet_unref(&packet);
|
||||
} while ((recfr_ret = avcodec_receive_frame(input_streams.at(packet.stream_index), dec_frame)) == AVERROR(EAGAIN) && !skip);
|
||||
|
||||
// error/eof handling - cancel while loop
|
||||
if (read_ret < 0 || skip) {
|
||||
break;
|
||||
}
|
||||
|
||||
// free packet as we're about to use it for encoding
|
||||
av_packet_unref(&packet);
|
||||
|
||||
// rescale input frame timestamp to output timestamp
|
||||
dec_frame->pts = av_rescale_q(dec_frame->pts,
|
||||
input_fmt_ctx->streams[stream_index]->time_base,
|
||||
output_fmt_ctx->streams[stream_index]->time_base);
|
||||
|
||||
// determine if the pix_fmt, width, and/or height is different, so if we need to convert
|
||||
bool convert_pix_fmt = (output_streams.at(stream_index)->pix_fmt != input_streams.at(stream_index)->pix_fmt
|
||||
|| output_streams.at(stream_index)->width != input_streams.at(stream_index)->width
|
||||
|| output_streams.at(stream_index)->height != input_streams.at(stream_index)->height);
|
||||
|
||||
// create reference to the frame to be sent to the encoder
|
||||
AVFrame* enc_frame = dec_frame;
|
||||
|
||||
if (convert_pix_fmt) {
|
||||
// create sws frame for pixel format conversion
|
||||
enc_frame = av_frame_alloc();
|
||||
enc_frame->width = output_streams.at(stream_index)->width;
|
||||
enc_frame->height = output_streams.at(stream_index)->height;
|
||||
enc_frame->format = output_streams.at(stream_index)->pix_fmt;
|
||||
av_frame_get_buffer(enc_frame, 0);
|
||||
|
||||
// convert pixel format to format expected by the encoder
|
||||
sws_scale(sws_contexts.at(stream_index), dec_frame->data, dec_frame->linesize, 0, dec_frame->height, enc_frame->data, enc_frame->linesize);
|
||||
|
||||
// set same pts as dec_frame
|
||||
enc_frame->pts = dec_frame->pts;
|
||||
}
|
||||
|
||||
// send frame to encoder
|
||||
avcodec_send_frame(output_streams.at(stream_index), enc_frame);
|
||||
|
||||
if (convert_pix_fmt) {
|
||||
// free sws frame since we made one before
|
||||
av_frame_free(&enc_frame);
|
||||
}
|
||||
|
||||
// return value for packet receiving
|
||||
int recret;
|
||||
|
||||
// loop through receiving packets
|
||||
while ((recret = avcodec_receive_packet(output_streams.at(stream_index), &packet)) >= 0 && !skip) {
|
||||
|
||||
// set packet stream index to current stream index
|
||||
packet.stream_index = stream_index;
|
||||
|
||||
// write frame to file
|
||||
av_interleaved_write_frame(output_fmt_ctx, &packet);
|
||||
|
||||
// unref old packet
|
||||
av_packet_unref(&packet);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// free dec_frame
|
||||
av_frame_free(&dec_frame);
|
||||
|
||||
// write video trailer
|
||||
av_write_trailer(output_fmt_ctx);
|
||||
|
||||
// free stream contexts
|
||||
for (int i=0;i<int(input_fmt_ctx->nb_streams);i++) {
|
||||
if (input_streams[i] != nullptr) {
|
||||
// free swscale contexts
|
||||
sws_freeContext(sws_contexts[i]);
|
||||
|
||||
// free input decoding context
|
||||
avcodec_close(input_streams[i]);
|
||||
avcodec_free_context(&input_streams[i]);
|
||||
|
||||
// free output encoding context
|
||||
avcodec_close(output_streams[i]);
|
||||
avcodec_free_context(&output_streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// close output file handle
|
||||
avio_closep(&output_fmt_ctx->pb);
|
||||
|
||||
// close output file
|
||||
avformat_free_context(output_fmt_ctx);
|
||||
|
||||
// close input file
|
||||
avformat_close_input(&input_fmt_ctx);
|
||||
|
||||
// set footage to use newly generated proxy
|
||||
footage->proxy = true;
|
||||
footage->proxy_path = info.path;
|
||||
|
||||
qInfo() << "Finished creating proxy for" << footage->url;
|
||||
QMetaObject::invokeMethod(olive::MainWindow->statusBar(),
|
||||
"showMessage",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(QString, tr("Finished generating proxy for \"%1\"").arg(footage->url)));
|
||||
}
|
||||
|
||||
// main proxy generating loop
|
||||
void ProxyGenerator::run() {
|
||||
// mutex used for thread safe signalling
|
||||
mutex.lock();
|
||||
|
||||
while (!cancelled) {
|
||||
// wait for queue() to be called
|
||||
waitCond.wait(&mutex);
|
||||
|
||||
// quit thread if cancel() was called
|
||||
if (cancelled) break;
|
||||
|
||||
// loop through queue until the queue is empty
|
||||
while (proxy_queue.size() > 0) {
|
||||
|
||||
// grab proxy info
|
||||
const ProxyInfo& info = proxy_queue.first();
|
||||
|
||||
// create directory for info
|
||||
QFileInfo(info.path).dir().mkpath(".");
|
||||
|
||||
// set skip to false
|
||||
skip = false;
|
||||
|
||||
// set media icon to animated loading icon
|
||||
olive::media_icon_service->SetMediaIcon(info.media, ICON_TYPE_LOADING);
|
||||
|
||||
// transcode proxy
|
||||
transcode(info);
|
||||
|
||||
// set media icon back to video
|
||||
olive::media_icon_service->SetMediaIcon(info.media, ICON_TYPE_VIDEO);
|
||||
|
||||
// we're finished with this proxy, remove it
|
||||
proxy_queue.removeFirst();
|
||||
|
||||
// quit loop if cancel() was called
|
||||
if (cancelled) break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
// called to add footage to generate proxies for
|
||||
void ProxyGenerator::queue(const ProxyInfo &info) {
|
||||
// remove any queued proxies with the same footage
|
||||
if (!proxy_queue.isEmpty()
|
||||
&& proxy_queue.first().media == info.media) {
|
||||
// if the thread is currently processing a proxy with the same footage, abort it
|
||||
skip = true;
|
||||
}
|
||||
|
||||
// scan through the rest of the queue for another proxy with the same footage (start with 1 since we already processed first())
|
||||
for (int i=1;i<proxy_queue.size();i++) {
|
||||
if (proxy_queue.at(i).media == info.media) {
|
||||
// found a duplicate, assume the one we're queuing now overrides and delete it
|
||||
proxy_queue.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// add proxy info to queue
|
||||
proxy_queue.append(info);
|
||||
|
||||
// wake proxy thread loop if sleeping
|
||||
waitCond.wakeAll();
|
||||
}
|
||||
|
||||
// to be called from another thread to terminate the proxy generator thread and free it
|
||||
void ProxyGenerator::cancel() {
|
||||
// signal to thread to cancel
|
||||
cancelled = true;
|
||||
skip = true;
|
||||
|
||||
// if signal is sleeping, wake it to cancel correctly
|
||||
waitCond.wakeAll();
|
||||
|
||||
// wait for thread to finish
|
||||
wait();
|
||||
}
|
||||
|
||||
double ProxyGenerator::get_proxy_progress(Media* m) {
|
||||
if (proxy_queue.first().media == m) {
|
||||
return current_progress;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// proxy generator is a global omnipotent entity
|
||||
ProxyGenerator olive::proxy_generator;
|
||||
/***
|
||||
|
||||
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 "proxygenerator.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QtMath>
|
||||
#include <QStatusBar>
|
||||
#include <QDebug>
|
||||
|
||||
#include "global/path.h"
|
||||
#include "project/previewgenerator.h"
|
||||
#include "ui/mediaiconservice.h"
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
// TODO provide more codecs than just this one
|
||||
enum AVCodecID temp_enc_codec = AV_CODEC_ID_PRORES;
|
||||
|
||||
ProxyGenerator::ProxyGenerator() : cancelled(false) {}
|
||||
|
||||
void ProxyGenerator::transcode(const ProxyInfo& info) {
|
||||
Footage* footage = info.media->to_footage();
|
||||
|
||||
// set progress to 0
|
||||
current_progress = 0.0;
|
||||
|
||||
// for image sequences that don't start at 0, set the index where it does start
|
||||
AVDictionary* format_opts = nullptr;
|
||||
if (footage->start_number > 0) {
|
||||
av_dict_set(&format_opts, "start_number", QString::number(footage->start_number).toUtf8(), 0);
|
||||
}
|
||||
|
||||
// open input file
|
||||
AVFormatContext* input_fmt_ctx = nullptr;
|
||||
avformat_open_input(&input_fmt_ctx, footage->url.toUtf8(), nullptr, &format_opts);
|
||||
|
||||
// open output file
|
||||
AVFormatContext* output_fmt_ctx = nullptr;
|
||||
avformat_alloc_output_context2(&output_fmt_ctx, nullptr, nullptr, info.path.toUtf8());
|
||||
|
||||
// open output file writing handle
|
||||
avio_open(&output_fmt_ctx->pb, info.path.toUtf8(), AVIO_FLAG_WRITE);
|
||||
|
||||
// get stream info from input file
|
||||
avformat_find_stream_info(input_fmt_ctx, nullptr);
|
||||
|
||||
// create array of input decoders
|
||||
QVector<AVCodecContext*> input_streams;
|
||||
input_streams.resize(input_fmt_ctx->nb_streams);
|
||||
input_streams.fill(nullptr);
|
||||
|
||||
// create array of output encoders
|
||||
QVector<AVCodecContext*> output_streams;
|
||||
output_streams.resize(input_fmt_ctx->nb_streams);
|
||||
output_streams.fill(nullptr);
|
||||
|
||||
// create array of swscale contexts for pixel format conversion
|
||||
QVector<SwsContext*> sws_contexts;
|
||||
sws_contexts.resize(input_fmt_ctx->nb_streams);
|
||||
sws_contexts.fill(nullptr);
|
||||
|
||||
// loop through file to find compatible video streams
|
||||
for (int i=0;i<int(input_fmt_ctx->nb_streams);i++) {
|
||||
AVStream* in_stream = input_fmt_ctx->streams[i];
|
||||
|
||||
// create new stream in output
|
||||
AVStream* out_stream = avformat_new_stream(output_fmt_ctx, nullptr);
|
||||
out_stream->id = in_stream->id;
|
||||
|
||||
// find decoder for this codec
|
||||
AVCodec* dec_codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
|
||||
|
||||
// find encoder for chosen proxy type
|
||||
AVCodec* enc_codec = avcodec_find_encoder(temp_enc_codec);
|
||||
|
||||
// we only transcode video streams, others we just passthrough
|
||||
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && dec_codec != nullptr) {
|
||||
|
||||
// allocate decoding context for this stream
|
||||
AVCodecContext* dec_ctx = avcodec_alloc_context3(dec_codec);
|
||||
|
||||
// copy parameters from stream to decoding context
|
||||
avcodec_parameters_to_context(dec_ctx, in_stream->codecpar);
|
||||
|
||||
// open decoder
|
||||
avcodec_open2(dec_ctx, dec_codec, nullptr);
|
||||
|
||||
// store decoding context in array
|
||||
input_streams[i] = dec_ctx;
|
||||
|
||||
// retrieve more information about this stream
|
||||
av_dump_format(input_fmt_ctx, i, footage->url.toUtf8(), 0);
|
||||
|
||||
// allocate encoding context for this stream
|
||||
AVCodecContext* enc_ctx = avcodec_alloc_context3(enc_codec);
|
||||
|
||||
// copy properties from decoding context to encoding context
|
||||
enc_ctx->codec_id = temp_enc_codec;
|
||||
enc_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
enc_ctx->width = qFloor(dec_ctx->width*info.size_multiplier);
|
||||
enc_ctx->height = qFloor(dec_ctx->height*info.size_multiplier);
|
||||
enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
|
||||
enc_ctx->pix_fmt = avcodec_find_best_pix_fmt_of_list(enc_codec->pix_fmts, dec_ctx->pix_fmt, 1, nullptr);
|
||||
enc_ctx->framerate = dec_ctx->framerate;
|
||||
enc_ctx->time_base = in_stream->time_base;
|
||||
out_stream->time_base = in_stream->time_base;
|
||||
|
||||
// if format uses global headers, add flag to enc_ctx
|
||||
if (output_fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
|
||||
enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
|
||||
// set encoder options (mostly just multithreading)
|
||||
AVDictionary* opts = nullptr;
|
||||
av_dict_set(&opts, "threads", "auto", 0);
|
||||
|
||||
// open encoder
|
||||
avcodec_open2(enc_ctx, enc_codec, &opts);
|
||||
|
||||
// copy parameters from encoding context to stream
|
||||
avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
|
||||
|
||||
// store encoding context in array
|
||||
output_streams[i] = enc_ctx;
|
||||
|
||||
// create swscontext for this stream
|
||||
SwsContext* sws_ctx = sws_getContext(
|
||||
in_stream->codecpar->width,
|
||||
in_stream->codecpar->height,
|
||||
static_cast<enum AVPixelFormat>(in_stream->codecpar->format),
|
||||
enc_ctx->width,
|
||||
enc_ctx->height,
|
||||
enc_ctx->pix_fmt,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
|
||||
sws_contexts[i] = sws_ctx;
|
||||
} else {
|
||||
avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
|
||||
}
|
||||
}
|
||||
|
||||
// write video header
|
||||
avformat_write_header(output_fmt_ctx, nullptr);
|
||||
|
||||
// packet that av_read_frame will dump file packets into
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
||||
// frame that decoder will decode into
|
||||
AVFrame* dec_frame = av_frame_alloc();
|
||||
|
||||
// main transcoding loop
|
||||
while (!skip) {
|
||||
|
||||
// cache stream index
|
||||
int stream_index = packet.stream_index;
|
||||
|
||||
// retrieve frame from decoder (this will clear the last frame so we don't have to do that)
|
||||
int read_ret = -1;
|
||||
int recfr_ret = -1;
|
||||
do {
|
||||
// read from input file
|
||||
read_ret = av_read_frame(input_fmt_ctx, &packet);
|
||||
|
||||
// handle errors
|
||||
if (read_ret < 0) {
|
||||
|
||||
// AVERROR_EOF means we've simply reached the end of the file, otherwise this is an error
|
||||
if (read_ret != AVERROR_EOF) {
|
||||
qWarning() << "Proxy generation for file" << footage->url << "ended prematurely";
|
||||
}
|
||||
|
||||
// either way, we shall abort reading
|
||||
break;
|
||||
}
|
||||
|
||||
stream_index = packet.stream_index;
|
||||
|
||||
// determine whether this frame is from a stream we're transcoding
|
||||
if (input_streams.at(stream_index) == nullptr) {
|
||||
// if we didn't allocate a decoder for this earlier, we just pass it through
|
||||
|
||||
av_packet_rescale_ts(&packet, input_fmt_ctx->streams[stream_index]->time_base, output_fmt_ctx->streams[stream_index]->time_base);
|
||||
|
||||
// write packet to output
|
||||
av_interleaved_write_frame(output_fmt_ctx, &packet);
|
||||
|
||||
} else {
|
||||
// we're going to transcode this packet.
|
||||
|
||||
// send packet to decoder
|
||||
avcodec_send_packet(input_streams.at(stream_index), &packet);
|
||||
|
||||
// use timestamp and stream duration to create a rough estimation of the progress through this file
|
||||
current_progress = qCeil((double(packet.pts)/double(input_fmt_ctx->streams[packet.stream_index]->duration))*100);
|
||||
|
||||
}
|
||||
|
||||
// free packet allocated by av_read_frame
|
||||
av_packet_unref(&packet);
|
||||
} while ((recfr_ret = avcodec_receive_frame(input_streams.at(packet.stream_index), dec_frame)) == AVERROR(EAGAIN) && !skip);
|
||||
|
||||
// error/eof handling - cancel while loop
|
||||
if (read_ret < 0 || skip) {
|
||||
break;
|
||||
}
|
||||
|
||||
// free packet as we're about to use it for encoding
|
||||
av_packet_unref(&packet);
|
||||
|
||||
// rescale input frame timestamp to output timestamp
|
||||
dec_frame->pts = av_rescale_q(dec_frame->pts,
|
||||
input_fmt_ctx->streams[stream_index]->time_base,
|
||||
output_fmt_ctx->streams[stream_index]->time_base);
|
||||
|
||||
// determine if the pix_fmt, width, and/or height is different, so if we need to convert
|
||||
bool convert_pix_fmt = (output_streams.at(stream_index)->pix_fmt != input_streams.at(stream_index)->pix_fmt
|
||||
|| output_streams.at(stream_index)->width != input_streams.at(stream_index)->width
|
||||
|| output_streams.at(stream_index)->height != input_streams.at(stream_index)->height);
|
||||
|
||||
// create reference to the frame to be sent to the encoder
|
||||
AVFrame* enc_frame = dec_frame;
|
||||
|
||||
if (convert_pix_fmt) {
|
||||
// create sws frame for pixel format conversion
|
||||
enc_frame = av_frame_alloc();
|
||||
enc_frame->width = output_streams.at(stream_index)->width;
|
||||
enc_frame->height = output_streams.at(stream_index)->height;
|
||||
enc_frame->format = output_streams.at(stream_index)->pix_fmt;
|
||||
av_frame_get_buffer(enc_frame, 0);
|
||||
|
||||
// convert pixel format to format expected by the encoder
|
||||
sws_scale(sws_contexts.at(stream_index), dec_frame->data, dec_frame->linesize, 0, dec_frame->height, enc_frame->data, enc_frame->linesize);
|
||||
|
||||
// set same pts as dec_frame
|
||||
enc_frame->pts = dec_frame->pts;
|
||||
}
|
||||
|
||||
// send frame to encoder
|
||||
avcodec_send_frame(output_streams.at(stream_index), enc_frame);
|
||||
|
||||
if (convert_pix_fmt) {
|
||||
// free sws frame since we made one before
|
||||
av_frame_free(&enc_frame);
|
||||
}
|
||||
|
||||
// return value for packet receiving
|
||||
int recret;
|
||||
|
||||
// loop through receiving packets
|
||||
while ((recret = avcodec_receive_packet(output_streams.at(stream_index), &packet)) >= 0 && !skip) {
|
||||
|
||||
// set packet stream index to current stream index
|
||||
packet.stream_index = stream_index;
|
||||
|
||||
// write frame to file
|
||||
av_interleaved_write_frame(output_fmt_ctx, &packet);
|
||||
|
||||
// unref old packet
|
||||
av_packet_unref(&packet);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// free dec_frame
|
||||
av_frame_free(&dec_frame);
|
||||
|
||||
// write video trailer
|
||||
av_write_trailer(output_fmt_ctx);
|
||||
|
||||
// free stream contexts
|
||||
for (int i=0;i<int(input_fmt_ctx->nb_streams);i++) {
|
||||
if (input_streams[i] != nullptr) {
|
||||
// free swscale contexts
|
||||
sws_freeContext(sws_contexts[i]);
|
||||
|
||||
// free input decoding context
|
||||
avcodec_close(input_streams[i]);
|
||||
avcodec_free_context(&input_streams[i]);
|
||||
|
||||
// free output encoding context
|
||||
avcodec_close(output_streams[i]);
|
||||
avcodec_free_context(&output_streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// close output file handle
|
||||
avio_closep(&output_fmt_ctx->pb);
|
||||
|
||||
// close output file
|
||||
avformat_free_context(output_fmt_ctx);
|
||||
|
||||
// close input file
|
||||
avformat_close_input(&input_fmt_ctx);
|
||||
|
||||
// set footage to use newly generated proxy
|
||||
footage->proxy = true;
|
||||
footage->proxy_path = info.path;
|
||||
|
||||
qInfo() << "Finished creating proxy for" << footage->url;
|
||||
QMetaObject::invokeMethod(olive::MainWindow->statusBar(),
|
||||
"showMessage",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(QString, tr("Finished generating proxy for \"%1\"").arg(footage->url)));
|
||||
}
|
||||
|
||||
// main proxy generating loop
|
||||
void ProxyGenerator::run() {
|
||||
// mutex used for thread safe signalling
|
||||
mutex.lock();
|
||||
|
||||
while (!cancelled) {
|
||||
// wait for queue() to be called
|
||||
waitCond.wait(&mutex);
|
||||
|
||||
// quit thread if cancel() was called
|
||||
if (cancelled) break;
|
||||
|
||||
// loop through queue until the queue is empty
|
||||
while (proxy_queue.size() > 0) {
|
||||
|
||||
// grab proxy info
|
||||
const ProxyInfo& info = proxy_queue.first();
|
||||
|
||||
// create directory for info
|
||||
QFileInfo(info.path).dir().mkpath(".");
|
||||
|
||||
// set skip to false
|
||||
skip = false;
|
||||
|
||||
// set media icon to animated loading icon
|
||||
olive::media_icon_service->SetMediaIcon(info.media, ICON_TYPE_LOADING);
|
||||
|
||||
// transcode proxy
|
||||
transcode(info);
|
||||
|
||||
// set media icon back to video
|
||||
olive::media_icon_service->SetMediaIcon(info.media, ICON_TYPE_VIDEO);
|
||||
|
||||
// we're finished with this proxy, remove it
|
||||
proxy_queue.removeFirst();
|
||||
|
||||
// quit loop if cancel() was called
|
||||
if (cancelled) break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
// called to add footage to generate proxies for
|
||||
void ProxyGenerator::queue(const ProxyInfo &info) {
|
||||
// remove any queued proxies with the same footage
|
||||
if (!proxy_queue.isEmpty()
|
||||
&& proxy_queue.first().media == info.media) {
|
||||
// if the thread is currently processing a proxy with the same footage, abort it
|
||||
skip = true;
|
||||
}
|
||||
|
||||
// scan through the rest of the queue for another proxy with the same footage (start with 1 since we already processed first())
|
||||
for (int i=1;i<proxy_queue.size();i++) {
|
||||
if (proxy_queue.at(i).media == info.media) {
|
||||
// found a duplicate, assume the one we're queuing now overrides and delete it
|
||||
proxy_queue.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// add proxy info to queue
|
||||
proxy_queue.append(info);
|
||||
|
||||
// wake proxy thread loop if sleeping
|
||||
waitCond.wakeAll();
|
||||
}
|
||||
|
||||
// to be called from another thread to terminate the proxy generator thread and free it
|
||||
void ProxyGenerator::cancel() {
|
||||
// signal to thread to cancel
|
||||
cancelled = true;
|
||||
skip = true;
|
||||
|
||||
// if signal is sleeping, wake it to cancel correctly
|
||||
waitCond.wakeAll();
|
||||
|
||||
// wait for thread to finish
|
||||
wait();
|
||||
}
|
||||
|
||||
double ProxyGenerator::get_proxy_progress(Media* m) {
|
||||
if (proxy_queue.first().media == m) {
|
||||
return current_progress;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// proxy generator is a global omnipotent entity
|
||||
ProxyGenerator olive::proxy_generator;
|
||||
|
||||
+72
-72
@@ -1,72 +1,72 @@
|
||||
/***
|
||||
|
||||
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 PROXYGENERATOR_H
|
||||
#define PROXYGENERATOR_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QVector>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "project/media.h"
|
||||
|
||||
struct ProxyInfo {
|
||||
Media* media;
|
||||
double size_multiplier;
|
||||
int codec_type;
|
||||
QString path;
|
||||
};
|
||||
|
||||
class ProxyGenerator : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProxyGenerator();
|
||||
void run();
|
||||
void queue(const ProxyInfo& info);
|
||||
void cancel();
|
||||
double get_proxy_progress(Media *f);
|
||||
private:
|
||||
// queue of footage to process proxies for
|
||||
QVector<ProxyInfo> proxy_queue;
|
||||
|
||||
// threading objects
|
||||
QWaitCondition waitCond;
|
||||
QMutex mutex;
|
||||
|
||||
// set to true if you want to permanently close ProxyGenerator
|
||||
bool cancelled;
|
||||
|
||||
// set to true if you want to abort the footage currently being processed
|
||||
bool skip;
|
||||
|
||||
// stores progress in percent of proxy currently being processed
|
||||
double current_progress;
|
||||
|
||||
// function that performs the actual transcode
|
||||
void transcode(const ProxyInfo& info);
|
||||
};
|
||||
|
||||
namespace olive {
|
||||
// proxy generator is a global omnipotent entity
|
||||
extern ProxyGenerator proxy_generator;
|
||||
}
|
||||
|
||||
#endif // PROXYGENERATOR_H
|
||||
/***
|
||||
|
||||
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 PROXYGENERATOR_H
|
||||
#define PROXYGENERATOR_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QVector>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "project/media.h"
|
||||
|
||||
struct ProxyInfo {
|
||||
Media* media;
|
||||
double size_multiplier;
|
||||
int codec_type;
|
||||
QString path;
|
||||
};
|
||||
|
||||
class ProxyGenerator : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProxyGenerator();
|
||||
void run();
|
||||
void queue(const ProxyInfo& info);
|
||||
void cancel();
|
||||
double get_proxy_progress(Media *f);
|
||||
private:
|
||||
// queue of footage to process proxies for
|
||||
QVector<ProxyInfo> proxy_queue;
|
||||
|
||||
// threading objects
|
||||
QWaitCondition waitCond;
|
||||
QMutex mutex;
|
||||
|
||||
// set to true if you want to permanently close ProxyGenerator
|
||||
bool cancelled;
|
||||
|
||||
// set to true if you want to abort the footage currently being processed
|
||||
bool skip;
|
||||
|
||||
// stores progress in percent of proxy currently being processed
|
||||
double current_progress;
|
||||
|
||||
// function that performs the actual transcode
|
||||
void transcode(const ProxyInfo& info);
|
||||
};
|
||||
|
||||
namespace olive {
|
||||
// proxy generator is a global omnipotent entity
|
||||
extern ProxyGenerator proxy_generator;
|
||||
}
|
||||
|
||||
#endif // PROXYGENERATOR_H
|
||||
|
||||
+466
-466
@@ -1,466 +1,466 @@
|
||||
/***
|
||||
|
||||
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 "sourcescommon.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QAbstractItemView>
|
||||
#include <QMimeData>
|
||||
#include <QMessageBox>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/menuhelper.h"
|
||||
#include "panels/panels.h"
|
||||
#include "project/media.h"
|
||||
#include "undo/undo.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/project.h"
|
||||
#include "project/footage.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "project/projectfilter.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "global/config.h"
|
||||
#include "global/global.h"
|
||||
#include "dialogs/proxydialog.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "project/proxygenerator.h"
|
||||
#include "project/projectfunctions.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "ui/menu.h"
|
||||
#include "undo/undostack.h"
|
||||
|
||||
SourcesCommon::SourcesCommon(Project* parent, ProjectFilter &sort_filter) :
|
||||
editing_item(nullptr),
|
||||
project_parent(parent),
|
||||
sort_filter_(sort_filter)
|
||||
{
|
||||
rename_timer.setInterval(1000);
|
||||
connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
|
||||
}
|
||||
|
||||
void SourcesCommon::create_seq_from_selected() {
|
||||
if (!selected_items.isEmpty()) {
|
||||
QVector<olive::timeline::MediaImportData> media_list;
|
||||
for (int i=0;i<selected_items.size();i++) {
|
||||
media_list.append(project_parent->item_to_media(selected_items.at(i)));
|
||||
}
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
SequencePtr s = olive::project::CreateSequenceFromMedia(media_list);
|
||||
|
||||
// add clips to it
|
||||
s->AddClipsFromGhosts(ca, olive::timeline::CreateGhostsFromMedia(s.get(), 0, media_list));
|
||||
|
||||
olive::project_model.CreateSequence(ca, s, true, nullptr);
|
||||
olive::undo_stack.push(ca);
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& items) {
|
||||
Menu menu(parent);
|
||||
|
||||
selected_items = items;
|
||||
|
||||
QAction* import_action = menu.addAction(tr("Import..."));
|
||||
QObject::connect(import_action, SIGNAL(triggered(bool)), olive::Global.get(), SLOT(open_import_dialog()));
|
||||
|
||||
Menu* new_menu = new Menu(tr("New"));
|
||||
menu.addMenu(new_menu);
|
||||
olive::MenuHelper.make_new_menu(new_menu);
|
||||
|
||||
Menu* view_menu = new Menu(tr("View"));
|
||||
menu.addMenu(view_menu);
|
||||
|
||||
QAction* tree_view_action = view_menu->addAction(tr("Tree View"));
|
||||
connect(tree_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_tree_view()));
|
||||
|
||||
QAction* icon_view_action = view_menu->addAction(tr("Icon View"));
|
||||
connect(icon_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_icon_view()));
|
||||
|
||||
QAction* toolbar_action = view_menu->addAction(tr("Show Toolbar"));
|
||||
toolbar_action->setCheckable(true);
|
||||
toolbar_action->setChecked(project_parent->IsToolbarVisible());
|
||||
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent, SLOT(SetToolbarVisible(bool)));
|
||||
|
||||
QAction* show_sequences = view_menu->addAction(tr("Show Sequences"));
|
||||
show_sequences->setCheckable(true);
|
||||
show_sequences->setChecked(sort_filter_.get_show_sequences());
|
||||
connect(show_sequences, SIGNAL(triggered(bool)), &sort_filter_, SLOT(set_show_sequences(bool)));
|
||||
|
||||
if (items.size() > 0) {
|
||||
if (items.size() == 1) {
|
||||
Media* first_media = project_parent->item_to_media(items.at(0));
|
||||
|
||||
// replace footage
|
||||
int type = first_media->get_type();
|
||||
if (type == MEDIA_TYPE_FOOTAGE) {
|
||||
QAction* replace_action = menu.addAction(tr("Replace/Relink Media"));
|
||||
QObject::connect(replace_action, SIGNAL(triggered(bool)), project_parent, SLOT(replace_selected_file()));
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in Explorer"));
|
||||
#elif defined(Q_OS_MAC)
|
||||
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in Finder"));
|
||||
#else
|
||||
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in File Manager"));
|
||||
#endif
|
||||
QObject::connect(reveal_in_explorer, SIGNAL(triggered(bool)), this, SLOT(reveal_in_browser()));
|
||||
}
|
||||
if (type != MEDIA_TYPE_FOLDER) {
|
||||
QAction* replace_clip_media = menu.addAction(tr("Replace Clips Using This Media"));
|
||||
QObject::connect(replace_clip_media, SIGNAL(triggered(bool)), project_parent, SLOT(replace_clip_media()));
|
||||
}
|
||||
}
|
||||
|
||||
// analyze selected footage types
|
||||
bool all_sequences = true;
|
||||
bool all_footage = true;
|
||||
|
||||
cached_selected_footage.clear();
|
||||
for (int i=0;i<items.size();i++) {
|
||||
Media* m = project_parent->item_to_media(items.at(i));
|
||||
if (m->get_type() != MEDIA_TYPE_SEQUENCE) {
|
||||
all_sequences = false;
|
||||
}
|
||||
if (m->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
cached_selected_footage.append(m);
|
||||
} else {
|
||||
all_footage = false;
|
||||
}
|
||||
}
|
||||
|
||||
// create sequence from
|
||||
QAction* create_seq_from = menu.addAction(tr("Create Sequence With This Media"));
|
||||
QObject::connect(create_seq_from, SIGNAL(triggered(bool)), this, SLOT(create_seq_from_selected()));
|
||||
|
||||
// ONLY sequences are selected
|
||||
if (all_sequences) {
|
||||
// ONLY sequences are selected
|
||||
QAction* duplicate_action = menu.addAction(tr("Duplicate"));
|
||||
QObject::connect(duplicate_action, SIGNAL(triggered(bool)), project_parent, SLOT(duplicate_selected()));
|
||||
}
|
||||
|
||||
// ONLY footage is selected
|
||||
if (all_footage) {
|
||||
QAction* delete_footage_from_sequences = menu.addAction(tr("Delete All Clips Using This Media"));
|
||||
QObject::connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), project_parent, SLOT(delete_clips_using_selected_media()));
|
||||
|
||||
Menu* proxies = new Menu(tr("Proxy"));
|
||||
menu.addMenu(proxies);
|
||||
|
||||
// special case if one footage item is selected and its proxy is currently being generated
|
||||
if (cached_selected_footage.size() == 1
|
||||
&& cached_selected_footage.at(0)->to_footage()->proxy
|
||||
&& cached_selected_footage.at(0)->to_footage()->proxy_path.isEmpty()) {
|
||||
QAction* action = proxies->addAction(tr("Generating proxy: %1% complete").arg(
|
||||
olive::proxy_generator.get_proxy_progress(cached_selected_footage.at(0))
|
||||
)
|
||||
);
|
||||
action->setEnabled(false);
|
||||
} else {
|
||||
// determine whether any selected footage has or doesn't have proxies
|
||||
bool footage_without_proxies_exists = false;
|
||||
bool footage_with_proxies_exists = false;
|
||||
|
||||
for (int i=0;i<cached_selected_footage.size();i++) {
|
||||
if (cached_selected_footage.at(i)->to_footage()->proxy) {
|
||||
footage_with_proxies_exists = true;
|
||||
} else {
|
||||
footage_without_proxies_exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if footage was selected WITHOUT proxies
|
||||
if (footage_without_proxies_exists) {
|
||||
QString create_proxy_text;
|
||||
|
||||
if (footage_with_proxies_exists) {
|
||||
// some of the footage already has proxies, so we use a different string
|
||||
create_proxy_text = tr("Create/Modify Proxy");
|
||||
} else {
|
||||
// none of the footage has proxies
|
||||
create_proxy_text = tr("Create Proxy");
|
||||
}
|
||||
|
||||
proxies->addAction(create_proxy_text, this, SLOT(open_create_proxy_dialog()));
|
||||
}
|
||||
|
||||
// if footage was selected WITH proxies
|
||||
if (footage_with_proxies_exists) {
|
||||
|
||||
if (!footage_without_proxies_exists) {
|
||||
// if all the footage has proxies, we didn't make a "Create/Modify" above, so we create one here (but only "modify")
|
||||
proxies->addAction(tr("Modify Proxy"), this, SLOT(open_create_proxy_dialog()));
|
||||
}
|
||||
|
||||
proxies->addAction(tr("Restore Original"), this, SLOT(clear_proxies_from_selected()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete media
|
||||
QAction* delete_action = menu.addAction(tr("Delete"));
|
||||
QObject::connect(delete_action, SIGNAL(triggered(bool)), project_parent, SLOT(delete_selected_media()));
|
||||
|
||||
if (items.size() == 1) {
|
||||
Media* media_item = project_parent->item_to_media(items.at(0));
|
||||
|
||||
if (media_item->get_type() != MEDIA_TYPE_FOLDER) {
|
||||
QAction* preview_in_media_viewer_action = menu.addAction(tr("Preview in Media Viewer"),
|
||||
this,
|
||||
SLOT(OpenSelectedMediaInMediaViewerFromAction()));
|
||||
preview_in_media_viewer_action->setData(reinterpret_cast<quintptr>(media_item));
|
||||
}
|
||||
|
||||
QAction* properties_action = menu.addAction(tr("Properties..."));
|
||||
QObject::connect(properties_action, SIGNAL(triggered(bool)), project_parent, SLOT(open_properties()));
|
||||
}
|
||||
}
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void SourcesCommon::replace_media(MediaPtr item, QString filename)
|
||||
{
|
||||
if (filename.isEmpty()) {
|
||||
filename = QFileDialog::getOpenFileName(
|
||||
olive::MainWindow,
|
||||
tr("Replace '%1'").arg(item->get_name()),
|
||||
"",
|
||||
tr("All Files") + " (*)");
|
||||
}
|
||||
if (!filename.isEmpty()) {
|
||||
ReplaceMediaCommand* rmc = new ReplaceMediaCommand(item, filename);
|
||||
olive::undo_stack.push(rmc);
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::mousePressEvent(QMouseEvent *) {
|
||||
stop_rename_timer();
|
||||
}
|
||||
|
||||
void SourcesCommon::item_click(Media *m, const QModelIndex& index) {
|
||||
if (editing_item == m) {
|
||||
rename_timer.start();
|
||||
} else {
|
||||
editing_item = m;
|
||||
editing_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::mouseDoubleClickEvent(const QModelIndexList& selected_items) {
|
||||
stop_rename_timer();
|
||||
if (selected_items.size() == 0) {
|
||||
olive::Global->open_import_dialog();
|
||||
} else if (selected_items.size() == 1) {
|
||||
Media* media = project_parent->item_to_media(selected_items.at(0));
|
||||
if (media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
Timeline::OpenSequence(media->to_sequence());
|
||||
} else {
|
||||
OpenSelectedMediaInMediaViewer(project_parent->item_to_media(selected_items.at(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::dropEvent(QWidget* parent,
|
||||
QDropEvent *event,
|
||||
const QModelIndex& drop_item,
|
||||
const QModelIndexList& items) {
|
||||
const QMimeData* mimeData = event->mimeData();
|
||||
MediaPtr m = project_parent->item_to_media_ptr(drop_item);
|
||||
if (mimeData->hasUrls()) {
|
||||
// drag files in from outside
|
||||
QList<QUrl> urls = mimeData->urls();
|
||||
if (!urls.isEmpty()) {
|
||||
QStringList paths;
|
||||
for (int i=0;i<urls.size();i++) {
|
||||
paths.append(urls.at(i).toLocalFile());
|
||||
}
|
||||
bool replace = false;
|
||||
if (urls.size() == 1
|
||||
&& drop_item.isValid()
|
||||
&& m->get_type() == MEDIA_TYPE_FOOTAGE
|
||||
&& !QFileInfo(paths.at(0)).isDir()
|
||||
&& olive::config.drop_on_media_to_replace
|
||||
&& QMessageBox::question(
|
||||
parent,
|
||||
tr("Replace Media"),
|
||||
tr("You dropped a file onto '%1'. Would you like to replace it with the dropped file?").arg(m->get_name()),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
|
||||
replace = true;
|
||||
replace_media(m, paths.at(0));
|
||||
}
|
||||
if (!replace) {
|
||||
QModelIndex parent;
|
||||
if (drop_item.isValid()) {
|
||||
if (m->get_type() == MEDIA_TYPE_FOLDER) {
|
||||
parent = drop_item;
|
||||
} else {
|
||||
parent = drop_item.parent();
|
||||
}
|
||||
}
|
||||
olive::project_model.process_file_list(paths, false, nullptr, project_parent->item_to_media(parent));
|
||||
}
|
||||
}
|
||||
event->acceptProposedAction();
|
||||
} else {
|
||||
event->ignore();
|
||||
|
||||
// dragging files within project
|
||||
// if we dragged to the root OR dragged to a folder
|
||||
if (!drop_item.isValid() || m->get_type() == MEDIA_TYPE_FOLDER) {
|
||||
QVector<MediaPtr> move_items;
|
||||
for (int i=0;i<items.size();i++) {
|
||||
const QModelIndex& item = items.at(i);
|
||||
const QModelIndex& parent = item.parent();
|
||||
MediaPtr s = project_parent->item_to_media_ptr(item);
|
||||
if (parent != drop_item && item != drop_item) {
|
||||
bool ignore = false;
|
||||
if (parent.isValid()) {
|
||||
// if child belongs to a selected parent, assume the user is just moving the parent and ignore the child
|
||||
QModelIndex par = parent;
|
||||
while (par.isValid() && !ignore) {
|
||||
for (int j=0;j<items.size();j++) {
|
||||
if (par == items.at(j)) {
|
||||
ignore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
par = par.parent();
|
||||
}
|
||||
}
|
||||
if (!ignore) {
|
||||
move_items.append(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (move_items.size() > 0) {
|
||||
MediaMove* mm = new MediaMove();
|
||||
mm->to = m.get();
|
||||
mm->items = move_items;
|
||||
olive::undo_stack.push(mm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::reveal_in_browser() {
|
||||
Media* media = project_parent->item_to_media(selected_items.at(0));
|
||||
Footage* m = media->to_footage();
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QStringList args;
|
||||
args << "/select," << QDir::toNativeSeparators(m->url);
|
||||
QProcess::startDetached("explorer", args);
|
||||
#elif defined(Q_OS_MAC)
|
||||
QStringList args;
|
||||
args << "-e";
|
||||
args << "tell application \"Finder\"";
|
||||
args << "-e";
|
||||
args << "activate";
|
||||
args << "-e";
|
||||
args << "select POSIX file \""+m->url+"\"";
|
||||
args << "-e";
|
||||
args << "end tell";
|
||||
QProcess::startDetached("osascript", args);
|
||||
#else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(m->url.left(m->url.lastIndexOf('/'))));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SourcesCommon::stop_rename_timer() {
|
||||
rename_timer.stop();
|
||||
}
|
||||
|
||||
void SourcesCommon::rename_interval() {
|
||||
stop_rename_timer();
|
||||
if (view->hasFocus() && editing_item != nullptr) {
|
||||
view->edit(editing_index);
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::item_renamed(Media* item) {
|
||||
if (editing_item == item) {
|
||||
MediaRename* mr = new MediaRename(item, "idk");
|
||||
olive::undo_stack.push(mr);
|
||||
editing_item = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::OpenSelectedMediaInMediaViewerFromAction()
|
||||
{
|
||||
OpenSelectedMediaInMediaViewer(reinterpret_cast<Media*>(static_cast<QAction*>(sender())->data().value<quintptr>()));
|
||||
}
|
||||
|
||||
void SourcesCommon::OpenSelectedMediaInMediaViewer(Media* item) {
|
||||
if (item->get_type() != MEDIA_TYPE_FOLDER) {
|
||||
panel_footage_viewer->set_media(item);
|
||||
panel_footage_viewer->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::open_create_proxy_dialog() {
|
||||
// open the proxy dialog and send it a list of currently selected footage
|
||||
ProxyDialog pd(olive::MainWindow, cached_selected_footage);
|
||||
pd.exec();
|
||||
}
|
||||
|
||||
void SourcesCommon::clear_proxies_from_selected() {
|
||||
QList<QString> delete_list;
|
||||
|
||||
for (int i=0;i<cached_selected_footage.size();i++) {
|
||||
Footage* f = cached_selected_footage.at(i)->to_footage();
|
||||
|
||||
if (f->proxy && !f->proxy_path.isEmpty()) {
|
||||
if (QFileInfo::exists(f->proxy_path)) {
|
||||
if (QMessageBox::question(olive::MainWindow,
|
||||
tr("Delete proxy"),
|
||||
tr("Would you like to delete the proxy file \"%1\" as well?").arg(f->proxy_path),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
delete_list.append(f->proxy_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f->proxy = false;
|
||||
f->proxy_path.clear();
|
||||
}
|
||||
|
||||
QVector<Media*> all_sequences = olive::project_model.GetAllSequences();
|
||||
for (int i=0;i<all_sequences.size();i++) {
|
||||
// close all clips so we can delete any proxies requested to be deleted
|
||||
all_sequences.at(i)->to_sequence()->Close();
|
||||
}
|
||||
|
||||
// delete proxies requested to be deleted
|
||||
for (int i=0;i<delete_list.size();i++) {
|
||||
QFile::remove(delete_list.at(i));
|
||||
}
|
||||
|
||||
if (panel_sequence_viewer->seq != nullptr) {
|
||||
// update viewer (will re-open active clips with original media)
|
||||
panel_sequence_viewer->viewer_widget()->frame_update();
|
||||
}
|
||||
|
||||
olive::Global->set_modified(true);
|
||||
}
|
||||
/***
|
||||
|
||||
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 "sourcescommon.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QAbstractItemView>
|
||||
#include <QMimeData>
|
||||
#include <QMessageBox>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/menuhelper.h"
|
||||
#include "panels/panels.h"
|
||||
#include "project/media.h"
|
||||
#include "undo/undo.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/project.h"
|
||||
#include "project/footage.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "project/projectfilter.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "global/config.h"
|
||||
#include "global/global.h"
|
||||
#include "dialogs/proxydialog.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "project/proxygenerator.h"
|
||||
#include "project/projectfunctions.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "ui/menu.h"
|
||||
#include "undo/undostack.h"
|
||||
|
||||
SourcesCommon::SourcesCommon(Project* parent, ProjectFilter &sort_filter) :
|
||||
editing_item(nullptr),
|
||||
project_parent(parent),
|
||||
sort_filter_(sort_filter)
|
||||
{
|
||||
rename_timer.setInterval(1000);
|
||||
connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
|
||||
}
|
||||
|
||||
void SourcesCommon::create_seq_from_selected() {
|
||||
if (!selected_items.isEmpty()) {
|
||||
QVector<olive::timeline::MediaImportData> media_list;
|
||||
for (int i=0;i<selected_items.size();i++) {
|
||||
media_list.append(project_parent->item_to_media(selected_items.at(i)));
|
||||
}
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
SequencePtr s = olive::project::CreateSequenceFromMedia(media_list);
|
||||
|
||||
// add clips to it
|
||||
s->AddClipsFromGhosts(ca, olive::timeline::CreateGhostsFromMedia(s.get(), 0, media_list));
|
||||
|
||||
olive::project_model.CreateSequence(ca, s, true, nullptr);
|
||||
olive::undo_stack.push(ca);
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& items) {
|
||||
Menu menu(parent);
|
||||
|
||||
selected_items = items;
|
||||
|
||||
QAction* import_action = menu.addAction(tr("Import..."));
|
||||
QObject::connect(import_action, SIGNAL(triggered(bool)), olive::Global.get(), SLOT(open_import_dialog()));
|
||||
|
||||
Menu* new_menu = new Menu(tr("New"));
|
||||
menu.addMenu(new_menu);
|
||||
olive::MenuHelper.make_new_menu(new_menu);
|
||||
|
||||
Menu* view_menu = new Menu(tr("View"));
|
||||
menu.addMenu(view_menu);
|
||||
|
||||
QAction* tree_view_action = view_menu->addAction(tr("Tree View"));
|
||||
connect(tree_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_tree_view()));
|
||||
|
||||
QAction* icon_view_action = view_menu->addAction(tr("Icon View"));
|
||||
connect(icon_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_icon_view()));
|
||||
|
||||
QAction* toolbar_action = view_menu->addAction(tr("Show Toolbar"));
|
||||
toolbar_action->setCheckable(true);
|
||||
toolbar_action->setChecked(project_parent->IsToolbarVisible());
|
||||
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent, SLOT(SetToolbarVisible(bool)));
|
||||
|
||||
QAction* show_sequences = view_menu->addAction(tr("Show Sequences"));
|
||||
show_sequences->setCheckable(true);
|
||||
show_sequences->setChecked(sort_filter_.get_show_sequences());
|
||||
connect(show_sequences, SIGNAL(triggered(bool)), &sort_filter_, SLOT(set_show_sequences(bool)));
|
||||
|
||||
if (items.size() > 0) {
|
||||
if (items.size() == 1) {
|
||||
Media* first_media = project_parent->item_to_media(items.at(0));
|
||||
|
||||
// replace footage
|
||||
int type = first_media->get_type();
|
||||
if (type == MEDIA_TYPE_FOOTAGE) {
|
||||
QAction* replace_action = menu.addAction(tr("Replace/Relink Media"));
|
||||
QObject::connect(replace_action, SIGNAL(triggered(bool)), project_parent, SLOT(replace_selected_file()));
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in Explorer"));
|
||||
#elif defined(Q_OS_MAC)
|
||||
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in Finder"));
|
||||
#else
|
||||
QAction* reveal_in_explorer = menu.addAction(tr("Reveal in File Manager"));
|
||||
#endif
|
||||
QObject::connect(reveal_in_explorer, SIGNAL(triggered(bool)), this, SLOT(reveal_in_browser()));
|
||||
}
|
||||
if (type != MEDIA_TYPE_FOLDER) {
|
||||
QAction* replace_clip_media = menu.addAction(tr("Replace Clips Using This Media"));
|
||||
QObject::connect(replace_clip_media, SIGNAL(triggered(bool)), project_parent, SLOT(replace_clip_media()));
|
||||
}
|
||||
}
|
||||
|
||||
// analyze selected footage types
|
||||
bool all_sequences = true;
|
||||
bool all_footage = true;
|
||||
|
||||
cached_selected_footage.clear();
|
||||
for (int i=0;i<items.size();i++) {
|
||||
Media* m = project_parent->item_to_media(items.at(i));
|
||||
if (m->get_type() != MEDIA_TYPE_SEQUENCE) {
|
||||
all_sequences = false;
|
||||
}
|
||||
if (m->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
cached_selected_footage.append(m);
|
||||
} else {
|
||||
all_footage = false;
|
||||
}
|
||||
}
|
||||
|
||||
// create sequence from
|
||||
QAction* create_seq_from = menu.addAction(tr("Create Sequence With This Media"));
|
||||
QObject::connect(create_seq_from, SIGNAL(triggered(bool)), this, SLOT(create_seq_from_selected()));
|
||||
|
||||
// ONLY sequences are selected
|
||||
if (all_sequences) {
|
||||
// ONLY sequences are selected
|
||||
QAction* duplicate_action = menu.addAction(tr("Duplicate"));
|
||||
QObject::connect(duplicate_action, SIGNAL(triggered(bool)), project_parent, SLOT(duplicate_selected()));
|
||||
}
|
||||
|
||||
// ONLY footage is selected
|
||||
if (all_footage) {
|
||||
QAction* delete_footage_from_sequences = menu.addAction(tr("Delete All Clips Using This Media"));
|
||||
QObject::connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), project_parent, SLOT(delete_clips_using_selected_media()));
|
||||
|
||||
Menu* proxies = new Menu(tr("Proxy"));
|
||||
menu.addMenu(proxies);
|
||||
|
||||
// special case if one footage item is selected and its proxy is currently being generated
|
||||
if (cached_selected_footage.size() == 1
|
||||
&& cached_selected_footage.at(0)->to_footage()->proxy
|
||||
&& cached_selected_footage.at(0)->to_footage()->proxy_path.isEmpty()) {
|
||||
QAction* action = proxies->addAction(tr("Generating proxy: %1% complete").arg(
|
||||
olive::proxy_generator.get_proxy_progress(cached_selected_footage.at(0))
|
||||
)
|
||||
);
|
||||
action->setEnabled(false);
|
||||
} else {
|
||||
// determine whether any selected footage has or doesn't have proxies
|
||||
bool footage_without_proxies_exists = false;
|
||||
bool footage_with_proxies_exists = false;
|
||||
|
||||
for (int i=0;i<cached_selected_footage.size();i++) {
|
||||
if (cached_selected_footage.at(i)->to_footage()->proxy) {
|
||||
footage_with_proxies_exists = true;
|
||||
} else {
|
||||
footage_without_proxies_exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if footage was selected WITHOUT proxies
|
||||
if (footage_without_proxies_exists) {
|
||||
QString create_proxy_text;
|
||||
|
||||
if (footage_with_proxies_exists) {
|
||||
// some of the footage already has proxies, so we use a different string
|
||||
create_proxy_text = tr("Create/Modify Proxy");
|
||||
} else {
|
||||
// none of the footage has proxies
|
||||
create_proxy_text = tr("Create Proxy");
|
||||
}
|
||||
|
||||
proxies->addAction(create_proxy_text, this, SLOT(open_create_proxy_dialog()));
|
||||
}
|
||||
|
||||
// if footage was selected WITH proxies
|
||||
if (footage_with_proxies_exists) {
|
||||
|
||||
if (!footage_without_proxies_exists) {
|
||||
// if all the footage has proxies, we didn't make a "Create/Modify" above, so we create one here (but only "modify")
|
||||
proxies->addAction(tr("Modify Proxy"), this, SLOT(open_create_proxy_dialog()));
|
||||
}
|
||||
|
||||
proxies->addAction(tr("Restore Original"), this, SLOT(clear_proxies_from_selected()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete media
|
||||
QAction* delete_action = menu.addAction(tr("Delete"));
|
||||
QObject::connect(delete_action, SIGNAL(triggered(bool)), project_parent, SLOT(delete_selected_media()));
|
||||
|
||||
if (items.size() == 1) {
|
||||
Media* media_item = project_parent->item_to_media(items.at(0));
|
||||
|
||||
if (media_item->get_type() != MEDIA_TYPE_FOLDER) {
|
||||
QAction* preview_in_media_viewer_action = menu.addAction(tr("Preview in Media Viewer"),
|
||||
this,
|
||||
SLOT(OpenSelectedMediaInMediaViewerFromAction()));
|
||||
preview_in_media_viewer_action->setData(reinterpret_cast<quintptr>(media_item));
|
||||
}
|
||||
|
||||
QAction* properties_action = menu.addAction(tr("Properties..."));
|
||||
QObject::connect(properties_action, SIGNAL(triggered(bool)), project_parent, SLOT(open_properties()));
|
||||
}
|
||||
}
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void SourcesCommon::replace_media(MediaPtr item, QString filename)
|
||||
{
|
||||
if (filename.isEmpty()) {
|
||||
filename = QFileDialog::getOpenFileName(
|
||||
olive::MainWindow,
|
||||
tr("Replace '%1'").arg(item->get_name()),
|
||||
"",
|
||||
tr("All Files") + " (*)");
|
||||
}
|
||||
if (!filename.isEmpty()) {
|
||||
ReplaceMediaCommand* rmc = new ReplaceMediaCommand(item, filename);
|
||||
olive::undo_stack.push(rmc);
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::mousePressEvent(QMouseEvent *) {
|
||||
stop_rename_timer();
|
||||
}
|
||||
|
||||
void SourcesCommon::item_click(Media *m, const QModelIndex& index) {
|
||||
if (editing_item == m) {
|
||||
rename_timer.start();
|
||||
} else {
|
||||
editing_item = m;
|
||||
editing_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::mouseDoubleClickEvent(const QModelIndexList& selected_items) {
|
||||
stop_rename_timer();
|
||||
if (selected_items.size() == 0) {
|
||||
olive::Global->open_import_dialog();
|
||||
} else if (selected_items.size() == 1) {
|
||||
Media* media = project_parent->item_to_media(selected_items.at(0));
|
||||
if (media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
Timeline::OpenSequence(media->to_sequence());
|
||||
} else {
|
||||
OpenSelectedMediaInMediaViewer(project_parent->item_to_media(selected_items.at(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::dropEvent(QWidget* parent,
|
||||
QDropEvent *event,
|
||||
const QModelIndex& drop_item,
|
||||
const QModelIndexList& items) {
|
||||
const QMimeData* mimeData = event->mimeData();
|
||||
MediaPtr m = project_parent->item_to_media_ptr(drop_item);
|
||||
if (mimeData->hasUrls()) {
|
||||
// drag files in from outside
|
||||
QList<QUrl> urls = mimeData->urls();
|
||||
if (!urls.isEmpty()) {
|
||||
QStringList paths;
|
||||
for (int i=0;i<urls.size();i++) {
|
||||
paths.append(urls.at(i).toLocalFile());
|
||||
}
|
||||
bool replace = false;
|
||||
if (urls.size() == 1
|
||||
&& drop_item.isValid()
|
||||
&& m->get_type() == MEDIA_TYPE_FOOTAGE
|
||||
&& !QFileInfo(paths.at(0)).isDir()
|
||||
&& olive::config.drop_on_media_to_replace
|
||||
&& QMessageBox::question(
|
||||
parent,
|
||||
tr("Replace Media"),
|
||||
tr("You dropped a file onto '%1'. Would you like to replace it with the dropped file?").arg(m->get_name()),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
|
||||
replace = true;
|
||||
replace_media(m, paths.at(0));
|
||||
}
|
||||
if (!replace) {
|
||||
QModelIndex parent;
|
||||
if (drop_item.isValid()) {
|
||||
if (m->get_type() == MEDIA_TYPE_FOLDER) {
|
||||
parent = drop_item;
|
||||
} else {
|
||||
parent = drop_item.parent();
|
||||
}
|
||||
}
|
||||
olive::project_model.process_file_list(paths, false, nullptr, project_parent->item_to_media(parent));
|
||||
}
|
||||
}
|
||||
event->acceptProposedAction();
|
||||
} else {
|
||||
event->ignore();
|
||||
|
||||
// dragging files within project
|
||||
// if we dragged to the root OR dragged to a folder
|
||||
if (!drop_item.isValid() || m->get_type() == MEDIA_TYPE_FOLDER) {
|
||||
QVector<MediaPtr> move_items;
|
||||
for (int i=0;i<items.size();i++) {
|
||||
const QModelIndex& item = items.at(i);
|
||||
const QModelIndex& parent = item.parent();
|
||||
MediaPtr s = project_parent->item_to_media_ptr(item);
|
||||
if (parent != drop_item && item != drop_item) {
|
||||
bool ignore = false;
|
||||
if (parent.isValid()) {
|
||||
// if child belongs to a selected parent, assume the user is just moving the parent and ignore the child
|
||||
QModelIndex par = parent;
|
||||
while (par.isValid() && !ignore) {
|
||||
for (int j=0;j<items.size();j++) {
|
||||
if (par == items.at(j)) {
|
||||
ignore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
par = par.parent();
|
||||
}
|
||||
}
|
||||
if (!ignore) {
|
||||
move_items.append(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (move_items.size() > 0) {
|
||||
MediaMove* mm = new MediaMove();
|
||||
mm->to = m.get();
|
||||
mm->items = move_items;
|
||||
olive::undo_stack.push(mm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::reveal_in_browser() {
|
||||
Media* media = project_parent->item_to_media(selected_items.at(0));
|
||||
Footage* m = media->to_footage();
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QStringList args;
|
||||
args << "/select," << QDir::toNativeSeparators(m->url);
|
||||
QProcess::startDetached("explorer", args);
|
||||
#elif defined(Q_OS_MAC)
|
||||
QStringList args;
|
||||
args << "-e";
|
||||
args << "tell application \"Finder\"";
|
||||
args << "-e";
|
||||
args << "activate";
|
||||
args << "-e";
|
||||
args << "select POSIX file \""+m->url+"\"";
|
||||
args << "-e";
|
||||
args << "end tell";
|
||||
QProcess::startDetached("osascript", args);
|
||||
#else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(m->url.left(m->url.lastIndexOf('/'))));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SourcesCommon::stop_rename_timer() {
|
||||
rename_timer.stop();
|
||||
}
|
||||
|
||||
void SourcesCommon::rename_interval() {
|
||||
stop_rename_timer();
|
||||
if (view->hasFocus() && editing_item != nullptr) {
|
||||
view->edit(editing_index);
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::item_renamed(Media* item) {
|
||||
if (editing_item == item) {
|
||||
MediaRename* mr = new MediaRename(item, "idk");
|
||||
olive::undo_stack.push(mr);
|
||||
editing_item = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::OpenSelectedMediaInMediaViewerFromAction()
|
||||
{
|
||||
OpenSelectedMediaInMediaViewer(reinterpret_cast<Media*>(static_cast<QAction*>(sender())->data().value<quintptr>()));
|
||||
}
|
||||
|
||||
void SourcesCommon::OpenSelectedMediaInMediaViewer(Media* item) {
|
||||
if (item->get_type() != MEDIA_TYPE_FOLDER) {
|
||||
panel_footage_viewer->set_media(item);
|
||||
panel_footage_viewer->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void SourcesCommon::open_create_proxy_dialog() {
|
||||
// open the proxy dialog and send it a list of currently selected footage
|
||||
ProxyDialog pd(olive::MainWindow, cached_selected_footage);
|
||||
pd.exec();
|
||||
}
|
||||
|
||||
void SourcesCommon::clear_proxies_from_selected() {
|
||||
QList<QString> delete_list;
|
||||
|
||||
for (int i=0;i<cached_selected_footage.size();i++) {
|
||||
Footage* f = cached_selected_footage.at(i)->to_footage();
|
||||
|
||||
if (f->proxy && !f->proxy_path.isEmpty()) {
|
||||
if (QFileInfo::exists(f->proxy_path)) {
|
||||
if (QMessageBox::question(olive::MainWindow,
|
||||
tr("Delete proxy"),
|
||||
tr("Would you like to delete the proxy file \"%1\" as well?").arg(f->proxy_path),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
delete_list.append(f->proxy_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f->proxy = false;
|
||||
f->proxy_path.clear();
|
||||
}
|
||||
|
||||
QVector<Media*> all_sequences = olive::project_model.GetAllSequences();
|
||||
for (int i=0;i<all_sequences.size();i++) {
|
||||
// close all clips so we can delete any proxies requested to be deleted
|
||||
all_sequences.at(i)->to_sequence()->Close();
|
||||
}
|
||||
|
||||
// delete proxies requested to be deleted
|
||||
for (int i=0;i<delete_list.size();i++) {
|
||||
QFile::remove(delete_list.at(i));
|
||||
}
|
||||
|
||||
if (panel_sequence_viewer->seq != nullptr) {
|
||||
// update viewer (will re-open active clips with original media)
|
||||
panel_sequence_viewer->viewer_widget()->frame_update();
|
||||
}
|
||||
|
||||
olive::Global->set_modified(true);
|
||||
}
|
||||
|
||||
+77
-77
@@ -1,77 +1,77 @@
|
||||
/***
|
||||
|
||||
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 SOURCESCOMMON_H
|
||||
#define SOURCESCOMMON_H
|
||||
|
||||
#include <QModelIndexList>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
|
||||
#include "project/footage.h"
|
||||
#include "project/projectfilter.h"
|
||||
#include "media.h"
|
||||
|
||||
class Project;
|
||||
class QMouseEvent;
|
||||
class QAbstractItemView;
|
||||
class QDropEvent;
|
||||
|
||||
class SourcesCommon : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SourcesCommon(Project *parent, ProjectFilter& sort_filter);
|
||||
QAbstractItemView* view;
|
||||
void show_context_menu(QWidget* parent, const QModelIndexList &items);
|
||||
|
||||
void replace_media(MediaPtr item, QString filename);
|
||||
|
||||
void mousePressEvent(QMouseEvent* e);
|
||||
void mouseDoubleClickEvent(const QModelIndexList& selected_items);
|
||||
void dropEvent(QWidget *parent, QDropEvent* e, const QModelIndex& drop_item, const QModelIndexList &items);
|
||||
|
||||
void item_click(Media* m, const QModelIndex &index);
|
||||
public slots:
|
||||
void stop_rename_timer();
|
||||
private slots:
|
||||
void create_seq_from_selected();
|
||||
void reveal_in_browser();
|
||||
void rename_interval();
|
||||
void item_renamed(Media *item);
|
||||
void OpenSelectedMediaInMediaViewerFromAction();
|
||||
void OpenSelectedMediaInMediaViewer(Media* item);
|
||||
|
||||
// proxy functions
|
||||
void open_create_proxy_dialog();
|
||||
void clear_proxies_from_selected();
|
||||
private:
|
||||
Media* editing_item;
|
||||
QModelIndex editing_index;
|
||||
QModelIndexList selected_items;
|
||||
Project* project_parent;
|
||||
QTimer rename_timer;
|
||||
|
||||
// we cache the selected footage items for open_create_proxy_dialog()
|
||||
QVector<Media*> cached_selected_footage;
|
||||
|
||||
ProjectFilter& sort_filter_;
|
||||
};
|
||||
|
||||
#endif // SOURCESCOMMON_H
|
||||
/***
|
||||
|
||||
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 SOURCESCOMMON_H
|
||||
#define SOURCESCOMMON_H
|
||||
|
||||
#include <QModelIndexList>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
|
||||
#include "project/footage.h"
|
||||
#include "project/projectfilter.h"
|
||||
#include "media.h"
|
||||
|
||||
class Project;
|
||||
class QMouseEvent;
|
||||
class QAbstractItemView;
|
||||
class QDropEvent;
|
||||
|
||||
class SourcesCommon : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SourcesCommon(Project *parent, ProjectFilter& sort_filter);
|
||||
QAbstractItemView* view;
|
||||
void show_context_menu(QWidget* parent, const QModelIndexList &items);
|
||||
|
||||
void replace_media(MediaPtr item, QString filename);
|
||||
|
||||
void mousePressEvent(QMouseEvent* e);
|
||||
void mouseDoubleClickEvent(const QModelIndexList& selected_items);
|
||||
void dropEvent(QWidget *parent, QDropEvent* e, const QModelIndex& drop_item, const QModelIndexList &items);
|
||||
|
||||
void item_click(Media* m, const QModelIndex &index);
|
||||
public slots:
|
||||
void stop_rename_timer();
|
||||
private slots:
|
||||
void create_seq_from_selected();
|
||||
void reveal_in_browser();
|
||||
void rename_interval();
|
||||
void item_renamed(Media *item);
|
||||
void OpenSelectedMediaInMediaViewerFromAction();
|
||||
void OpenSelectedMediaInMediaViewer(Media* item);
|
||||
|
||||
// proxy functions
|
||||
void open_create_proxy_dialog();
|
||||
void clear_proxies_from_selected();
|
||||
private:
|
||||
Media* editing_item;
|
||||
QModelIndex editing_index;
|
||||
QModelIndexList selected_items;
|
||||
Project* project_parent;
|
||||
QTimer rename_timer;
|
||||
|
||||
// we cache the selected footage items for open_create_proxy_dialog()
|
||||
QVector<Media*> cached_selected_footage;
|
||||
|
||||
ProjectFilter& sort_filter_;
|
||||
};
|
||||
|
||||
#endif // SOURCESCOMMON_H
|
||||
|
||||
Reference in New Issue
Block a user