395 lines
10 KiB
C++
395 lines
10 KiB
C++
/***
|
|
|
|
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 "media.h"
|
|
|
|
extern "C" {
|
|
#include <libavformat/avformat.h>
|
|
#include <libavcodec/avcodec.h>
|
|
}
|
|
|
|
#include <QCoreApplication>
|
|
#include <QtMath>
|
|
|
|
#include "footage.h"
|
|
#include "timeline/sequence.h"
|
|
#include "undo/undo.h"
|
|
#include "undo/undostack.h"
|
|
#include "global/config.h"
|
|
#include "panels/project.h"
|
|
#include "ui/icons.h"
|
|
#include "projectmodel.h"
|
|
#include "global/debug.h"
|
|
#include "global/timing.h"
|
|
|
|
Media::Media() :
|
|
root(false),
|
|
type(-1),
|
|
disable_thumbnail_(false)
|
|
{
|
|
}
|
|
|
|
void Media::Save(QXmlStreamWriter &stream)
|
|
{
|
|
switch (type) {
|
|
case MEDIA_TYPE_FOLDER:
|
|
stream.writeStartElement("folder");
|
|
stream.writeAttribute("name", get_name());
|
|
stream.writeEndElement();
|
|
break;
|
|
case MEDIA_TYPE_FOOTAGE:
|
|
to_footage()->Save(stream);
|
|
break;
|
|
case MEDIA_TYPE_SEQUENCE:
|
|
to_sequence()->Save(stream);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Footage* Media::to_footage() {
|
|
return static_cast<Footage*>(object.get());
|
|
}
|
|
|
|
SequencePtr Media::to_sequence() {
|
|
return std::static_pointer_cast<Sequence>(object);
|
|
}
|
|
|
|
void Media::set_footage(FootagePtr f) {
|
|
type = MEDIA_TYPE_FOOTAGE;
|
|
object = VoidPtr(f);
|
|
}
|
|
|
|
void Media::set_sequence(SequencePtr s) {
|
|
set_icon(olive::icon::MediaSequence);
|
|
type = MEDIA_TYPE_SEQUENCE;
|
|
object = VoidPtr(s);
|
|
if (s != nullptr) {
|
|
update_tooltip();
|
|
}
|
|
}
|
|
|
|
void Media::set_folder() {
|
|
if (folder_name.isEmpty()) {
|
|
folder_name = QCoreApplication::translate("Media", "New Folder");
|
|
}
|
|
set_icon(olive::icon::MediaFolder);
|
|
type = MEDIA_TYPE_FOLDER;
|
|
object = nullptr;
|
|
}
|
|
|
|
void Media::set_icon(const QString &str)
|
|
{
|
|
set_icon(QIcon(str));
|
|
}
|
|
|
|
void Media::set_icon(const QIcon &ico) {
|
|
icon = ico;
|
|
}
|
|
|
|
void Media::set_parent(Media *p) {
|
|
parent = p;
|
|
}
|
|
|
|
void Media::update_tooltip(const QString& error) {
|
|
switch (type) {
|
|
case MEDIA_TYPE_FOOTAGE:
|
|
{
|
|
Footage* f = to_footage();
|
|
tooltip = QCoreApplication::translate("Media", "Name:") + " " + f->name + "\n" + QCoreApplication::translate("Media", "Filename:") + " " + f->url + "\n";
|
|
|
|
if (error.isEmpty()) {
|
|
if (f->video_tracks.size() > 0) {
|
|
tooltip += QCoreApplication::translate("Media", "Video Dimensions:") + " ";
|
|
for (int i=0;i<f->video_tracks.size();i++) {
|
|
if (i > 0) {
|
|
tooltip += ", ";
|
|
}
|
|
tooltip += QString::number(f->video_tracks.at(i).video_width) + "x" + QString::number(f->video_tracks.at(i).video_height);
|
|
}
|
|
tooltip += "\n";
|
|
|
|
if (!f->video_tracks.at(0).infinite_length) {
|
|
tooltip += QCoreApplication::translate("Media", "Frame Rate:") + " ";
|
|
for (int i=0;i<f->video_tracks.size();i++) {
|
|
if (i > 0) {
|
|
tooltip += ", ";
|
|
}
|
|
if (f->video_tracks.at(i).video_interlacing == VIDEO_PROGRESSIVE) {
|
|
tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * f->speed);
|
|
} else {
|
|
double adjusted_rate = f->video_tracks.at(i).video_frame_rate * f->speed;
|
|
|
|
tooltip += QCoreApplication::translate("Media", "%1 field(s) (%2 frame(s))").arg(
|
|
QString::number(adjusted_rate*2.0),
|
|
QString::number(adjusted_rate)
|
|
);
|
|
}
|
|
}
|
|
tooltip += "\n";
|
|
}
|
|
|
|
tooltip += QCoreApplication::translate("Media", "Interlacing:") + " ";
|
|
for (int i=0;i<f->video_tracks.size();i++) {
|
|
if (i > 0) {
|
|
tooltip += ", ";
|
|
}
|
|
tooltip += Footage::get_interlacing_name(f->video_tracks.at(i).video_interlacing);
|
|
}
|
|
}
|
|
|
|
if (f->audio_tracks.size() > 0) {
|
|
tooltip += "\n";
|
|
|
|
tooltip += QCoreApplication::translate("Media", "Audio Frequency:") + " ";
|
|
for (int i=0;i<f->audio_tracks.size();i++) {
|
|
if (i > 0) {
|
|
tooltip += ", ";
|
|
}
|
|
tooltip += QString::number(f->audio_tracks.at(i).audio_frequency * f->speed);
|
|
}
|
|
tooltip += "\n";
|
|
|
|
tooltip += QCoreApplication::translate("Media", "Audio Channels:") + " ";
|
|
for (int i=0;i<f->audio_tracks.size();i++) {
|
|
if (i > 0) {
|
|
tooltip += ", ";
|
|
}
|
|
tooltip += Footage::get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout);
|
|
}
|
|
// tooltip += "\n";
|
|
}
|
|
} else {
|
|
tooltip += error;
|
|
}
|
|
}
|
|
break;
|
|
case MEDIA_TYPE_SEQUENCE:
|
|
{
|
|
Sequence* s = to_sequence().get();
|
|
|
|
tooltip = QCoreApplication::translate("Media", "Name: %1"
|
|
"\nVideo Dimensions: %2x%3"
|
|
"\nFrame Rate: %4"
|
|
"\nAudio Frequency: %5"
|
|
"\nAudio Layout: %6").arg(
|
|
s->name,
|
|
QString::number(s->width),
|
|
QString::number(s->height),
|
|
QString::number(s->frame_rate),
|
|
QString::number(s->audio_frequency),
|
|
Footage::get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout)
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
VoidPtr Media::to_object() {
|
|
return object;
|
|
}
|
|
|
|
int Media::get_type() {
|
|
return type;
|
|
}
|
|
|
|
const QString &Media::get_name() {
|
|
switch (type) {
|
|
case MEDIA_TYPE_FOOTAGE: return to_footage()->name;
|
|
case MEDIA_TYPE_SEQUENCE: return to_sequence()->name;
|
|
default: return folder_name;
|
|
}
|
|
}
|
|
|
|
void Media::set_name(const QString &n) {
|
|
switch (type) {
|
|
case MEDIA_TYPE_FOOTAGE: to_footage()->name = n; break;
|
|
case MEDIA_TYPE_SEQUENCE: to_sequence()->name = n; break;
|
|
case MEDIA_TYPE_FOLDER: folder_name = n; break;
|
|
}
|
|
}
|
|
|
|
void Media::disable_thumbnail(bool disable)
|
|
{
|
|
disable_thumbnail_ = disable;
|
|
}
|
|
|
|
double Media::get_frame_rate(int stream) {
|
|
switch (get_type()) {
|
|
case MEDIA_TYPE_FOOTAGE:
|
|
{
|
|
Footage* f = to_footage();
|
|
if (stream < 0) return f->video_tracks.at(0).video_frame_rate * f->speed;
|
|
return f->get_stream_from_file_index(true, stream)->video_frame_rate * f->speed;
|
|
}
|
|
case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Media::get_sampling_rate(int stream) {
|
|
switch (get_type()) {
|
|
case MEDIA_TYPE_FOOTAGE:
|
|
{
|
|
Footage* f = to_footage();
|
|
if (stream < 0) return f->audio_tracks.at(0).audio_frequency * f->speed;
|
|
return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency * f->speed;
|
|
}
|
|
case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void Media::appendChild(MediaPtr child) {
|
|
child->set_parent(this);
|
|
children.append(child);
|
|
}
|
|
|
|
bool Media::setData(int col, const QVariant &value) {
|
|
if (col == 0) {
|
|
QString n = value.toString();
|
|
if (!n.isEmpty() && get_name() != n) {
|
|
olive::undo_stack.push(new MediaRename(this, value.toString()));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Media *Media::child(int row) {
|
|
return children.value(row).get();
|
|
}
|
|
|
|
int Media::childCount() const {
|
|
return children.count();
|
|
}
|
|
|
|
int Media::columnCount() const {
|
|
return 3;
|
|
}
|
|
|
|
QString Media::GetStringDuration() {
|
|
if (get_type() == MEDIA_TYPE_SEQUENCE) {
|
|
Sequence* s = to_sequence().get();
|
|
return frame_to_timecode(s->GetEndFrame(), olive::config.timecode_view, s->frame_rate);
|
|
}
|
|
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
|
Footage* f = to_footage();
|
|
double r = 30;
|
|
|
|
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate))
|
|
r = f->video_tracks.at(0).video_frame_rate * f->speed;
|
|
|
|
long len = f->get_length_in_frames(r);
|
|
if (len > 0) return frame_to_timecode(len, olive::config.timecode_view, r);
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
QVariant Media::data(int column, int role) {
|
|
switch (role) {
|
|
case Qt::DecorationRole:
|
|
if (column == 0) {
|
|
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
|
Footage* f = to_footage();
|
|
if (!disable_thumbnail_
|
|
&& f->video_tracks.size() > 0
|
|
&& f->video_tracks.at(0).preview_done) {
|
|
return QIcon(QPixmap::fromImage(f->video_tracks.at(0).video_preview));
|
|
}
|
|
}
|
|
|
|
return icon;
|
|
}
|
|
break;
|
|
case Qt::DisplayRole:
|
|
switch (column) {
|
|
case 0: return (root) ? QCoreApplication::translate("Media", "Name") : get_name();
|
|
case 1:
|
|
if (root) return QCoreApplication::translate("Media", "Duration");
|
|
return GetStringDuration();
|
|
break;
|
|
case 2:
|
|
if (root) return QCoreApplication::translate("Media", "Rate");
|
|
if (get_type() == MEDIA_TYPE_SEQUENCE) return QString::number(get_frame_rate()) + " FPS";
|
|
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
|
Footage* f = to_footage();
|
|
double r;
|
|
if (f->video_tracks.size() > 0 && !qIsNull(r = get_frame_rate())) {
|
|
return QString::number(get_frame_rate()) + " FPS";
|
|
} else if (f->audio_tracks.size() > 0) {
|
|
return QString::number(get_sampling_rate()) + " Hz";
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case Qt::ToolTipRole:
|
|
return tooltip;
|
|
|
|
case Qt::UserRole:
|
|
// User role returns the duration
|
|
return GetStringDuration();
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
int Media::row() const {
|
|
if (parent != nullptr) {
|
|
for (int i=0;i<parent->children.size();i++) {
|
|
if (parent->children.at(i).get() == this) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Media *Media::parentItem() {
|
|
return parent;
|
|
}
|
|
|
|
void Media::removeChild(int i) {
|
|
children.removeAt(i);
|
|
}
|
|
|
|
MediaPtr Media::get_shared_ptr(Media *m)
|
|
{
|
|
for (int i=0;i<children.size();i++) {
|
|
if (children.at(i).get() == m) {
|
|
return children.at(i);
|
|
}
|
|
}
|
|
return MediaPtr();
|
|
}
|
|
|
|
QVector<Marker> &Media::get_markers() {
|
|
// returns the marker array from the internal object
|
|
//
|
|
// NOTE: if this media object is not footage or a sequence, the result is
|
|
// undefined - most likely a crash
|
|
|
|
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
|
return to_footage()->markers;
|
|
} else {
|
|
return to_sequence()->markers;
|
|
}
|
|
}
|