nicer effects panel, improved split selection
This commit is contained in:
+20
-20
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>557</width>
|
||||
<height>659</height>
|
||||
<width>358</width>
|
||||
<height>442</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -75,13 +75,6 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="vcodecCombobox"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Width:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="widthSpinbox">
|
||||
<property name="maximum">
|
||||
@@ -89,6 +82,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Width:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
@@ -120,14 +120,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Bitrate (Mbps/CBR):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QDoubleSpinBox" name="videobitrateSpinbox">
|
||||
<property name="maximum">
|
||||
<double>100.000000000000000</double>
|
||||
@@ -159,13 +159,6 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="acodecCombobox"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Sampling Rate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="samplingRateSpinbox">
|
||||
<property name="maximum">
|
||||
@@ -176,14 +169,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Sampling Rate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Bitrate (Kbps/CBR):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="audiobitrateSpinbox">
|
||||
<property name="maximum">
|
||||
<number>320</number>
|
||||
|
||||
@@ -10,23 +10,6 @@ void CrossDissolveTransition::process_transition(double progress) {
|
||||
glColor4f(1.0, 1.0, 1.0, color[3]*progress);
|
||||
}
|
||||
|
||||
void CrossDissolveTransition::process_audio(double range_start, double range_end, quint8* samples, int nb_samples, bool reverse) {
|
||||
double interval = (range_end - range_start) / nb_samples;
|
||||
|
||||
for (int i=0;i<nb_samples;i+=2) {
|
||||
qint16 samp = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
|
||||
if (reverse) {
|
||||
samp *= 1 - (range_start + (interval * i));
|
||||
} else {
|
||||
samp *= range_start + (interval * i);
|
||||
}
|
||||
|
||||
samples[i+1] = (quint8) (samp >> 8);
|
||||
samples[i] = (quint8) samp;
|
||||
}
|
||||
}
|
||||
|
||||
Transition* CrossDissolveTransition::copy() {
|
||||
Transition* t = new CrossDissolveTransition();
|
||||
t->length = length;
|
||||
|
||||
@@ -16,6 +16,7 @@ void init_effects() {
|
||||
video_effect_names[VIDEO_SHAKE_EFFECT] = "Shake";
|
||||
video_effect_names[VIDEO_TEXT_EFFECT] = "Text";
|
||||
video_effect_names[VIDEO_SOLID_EFFECT] = "Solid";
|
||||
video_effect_names[VIDEO_INVERT_EFFECT] = "Invert";
|
||||
|
||||
audio_effect_names[AUDIO_VOLUME_EFFECT] = "Volume";
|
||||
audio_effect_names[AUDIO_PAN_EFFECT] = "Pan";
|
||||
@@ -28,6 +29,7 @@ Effect* create_effect(int effect_id, Clip* c) {
|
||||
case VIDEO_SHAKE_EFFECT: return new ShakeEffect(c); break;
|
||||
case VIDEO_TEXT_EFFECT: return new TextEffect(c); break;
|
||||
case VIDEO_SOLID_EFFECT: return new SolidEffect(c); break;
|
||||
case VIDEO_INVERT_EFFECT: return new InvertEffect(c); break;
|
||||
}
|
||||
} else {
|
||||
switch (effect_id) {
|
||||
|
||||
@@ -21,6 +21,7 @@ enum VideoEffects {
|
||||
VIDEO_SHAKE_EFFECT,
|
||||
VIDEO_TEXT_EFFECT,
|
||||
VIDEO_SOLID_EFFECT,
|
||||
VIDEO_INVERT_EFFECT,
|
||||
VIDEO_EFFECT_COUNT
|
||||
};
|
||||
|
||||
@@ -130,6 +131,19 @@ private:
|
||||
QOpenGLTexture* texture;
|
||||
};
|
||||
|
||||
class InvertEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InvertEffect(Clip* c);
|
||||
void init();
|
||||
void process_gl(int *anchor_x, int *anchor_y);
|
||||
Effect* copy(Clip *c);
|
||||
void load(QXmlStreamReader* stream);
|
||||
void save(QXmlStreamWriter* stream);
|
||||
|
||||
LabelSlider* amount_val;
|
||||
};
|
||||
|
||||
// audio effects
|
||||
class VolumeEffect : public Effect {
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "effects/effects.h"
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QGridLayout>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QXmlStreamAttributes>
|
||||
|
||||
InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT) {
|
||||
ui_layout->addWidget(new QLabel("Amount:"), 0, 0);
|
||||
amount_val = new LabelSlider();
|
||||
ui_layout->addWidget(amount_val, 0, 1);
|
||||
|
||||
// set defaults
|
||||
amount_val->set_minimum_value(0);
|
||||
amount_val->set_maximum_value(100);
|
||||
init();
|
||||
|
||||
connect(amount_val, SIGNAL(valueChanged()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void InvertEffect::init() {
|
||||
amount_val->set_default_value(100);
|
||||
}
|
||||
|
||||
Effect* InvertEffect::copy(Clip* c) {
|
||||
InvertEffect* i = new InvertEffect(c);
|
||||
i->amount_val->set_value(amount_val->value());
|
||||
return i;
|
||||
}
|
||||
|
||||
void InvertEffect::load(QXmlStreamReader *stream) {
|
||||
const QXmlStreamAttributes& attr = stream->attributes();
|
||||
for (int i=0;i<attr.size();i++) {
|
||||
const QXmlStreamAttribute& a = attr.at(i);
|
||||
if (a.name() == "amount") {
|
||||
amount_val->set_value(a.value().toDouble());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InvertEffect::save(QXmlStreamWriter *stream) {
|
||||
stream->writeAttribute("amount", QString::number(amount_val->value()));
|
||||
}
|
||||
|
||||
void InvertEffect::process_gl(int* anchor_x, int* anchor_y) {
|
||||
|
||||
// gl_FragColor = vec4(1.0 - textureColor.r, 1.0 -textureColor.g, 1.0 -textureColor.b, 1);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "transition.h"
|
||||
|
||||
LinearFadeTransition::LinearFadeTransition() : Transition(AUDIO_LINEAR_FADE_TRANSITION) {}
|
||||
|
||||
void LinearFadeTransition::process_audio(double range_start, double range_end, quint8* samples, int nb_samples, bool reverse) {
|
||||
double interval = (range_end - range_start) / nb_samples;
|
||||
|
||||
for (int i=0;i<nb_samples;i+=2) {
|
||||
qint16 samp = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
|
||||
if (reverse) {
|
||||
samp *= 1 - (range_start + (interval * i));
|
||||
} else {
|
||||
samp *= range_start + (interval * i);
|
||||
}
|
||||
|
||||
samples[i+1] = (quint8) (samp >> 8);
|
||||
samples[i] = (quint8) samp;
|
||||
}
|
||||
}
|
||||
|
||||
Transition* LinearFadeTransition::copy() {
|
||||
Transition* t = new LinearFadeTransition();
|
||||
t->length = length;
|
||||
return t;
|
||||
}
|
||||
+15
-9
@@ -15,11 +15,6 @@ enum AudioTransitions {
|
||||
AUDIO_TRANSITION_COUNT
|
||||
};
|
||||
|
||||
void init_transitions();
|
||||
|
||||
extern QVector<QString> video_transition_names;
|
||||
extern QVector<QString> audio_transition_names;
|
||||
|
||||
class Transition
|
||||
{
|
||||
public:
|
||||
@@ -28,20 +23,31 @@ public:
|
||||
int id;
|
||||
QString name;
|
||||
int length;
|
||||
Transition* link;
|
||||
Transition* link;
|
||||
virtual void process_transition(double);
|
||||
virtual void process_audio(double, double, quint8*, int, bool);
|
||||
virtual Transition* copy();
|
||||
virtual Transition* copy();
|
||||
};
|
||||
|
||||
void init_transitions();
|
||||
Transition* create_transition(int transition_id, Clip* c);
|
||||
extern QVector<QString> video_transition_names;
|
||||
extern QVector<QString> audio_transition_names;
|
||||
|
||||
// Video Transitions
|
||||
class CrossDissolveTransition : public Transition {
|
||||
public:
|
||||
CrossDissolveTransition();
|
||||
void process_transition(double);
|
||||
void process_audio(double, double, quint8*, int, bool);
|
||||
Transition* copy();
|
||||
};
|
||||
|
||||
Transition* create_transition(int transition_id, Clip* c);
|
||||
// Audio Transitions
|
||||
class LinearFadeTransition : public Transition {
|
||||
public:
|
||||
LinearFadeTransition();
|
||||
void process_audio(double, double, quint8*, int, bool);
|
||||
Transition* copy();
|
||||
};
|
||||
|
||||
#endif // TRANSITION_H
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowTitle("Olive (August 2018 | Pre-Alpha)");
|
||||
setWindowTitle("Olive (August 2018 | Alpha)");
|
||||
statusBar()->showMessage("Welcome to Olive");
|
||||
|
||||
ui->centralWidget->setMaximumSize(0, 0);
|
||||
|
||||
@@ -66,7 +66,9 @@ SOURCES += \
|
||||
ui/comboboxex.cpp \
|
||||
ui/colorbutton.cpp \
|
||||
effects/solideffect.cpp \
|
||||
dialogs/replaceclipmediadialog.cpp
|
||||
dialogs/replaceclipmediadialog.cpp \
|
||||
effects/inverteffect.cpp \
|
||||
effects/linearfadetransition.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
|
||||
+49
-13
@@ -14,6 +14,7 @@
|
||||
#include "project/sequence.h"
|
||||
#include "project/undo.h"
|
||||
#include "panels/project.h"
|
||||
#include "panels/timeline.h"
|
||||
|
||||
EffectControls::EffectControls(QWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
@@ -33,26 +34,51 @@ EffectControls::~EffectControls()
|
||||
void EffectControls::menu_select(QAction* q) {
|
||||
TimelineAction* ta = new TimelineAction();
|
||||
for (int i=0;i<selected_clips.size();i++) {
|
||||
if ((sequence->get_clip(selected_clips.at(i))->track < 0) == video_menu) {
|
||||
ta->add_effect(sequence, selected_clips.at(i), q->data().toInt());
|
||||
Clip* c = sequence->get_clip(selected_clips.at(i));
|
||||
if (c->track < 0 == video_menu) {
|
||||
if (transition_menu) {
|
||||
if (c->opening_transition == NULL) {
|
||||
ta->add_transition(sequence, selected_clips.at(i), q->data().toInt(), TA_OPENING_TRANSITION);
|
||||
}
|
||||
if (c->closing_transition == NULL) {
|
||||
ta->add_transition(sequence, selected_clips.at(i), q->data().toInt(), TA_CLOSING_TRANSITION);
|
||||
}
|
||||
} else {
|
||||
ta->add_effect(sequence, selected_clips.at(i), q->data().toInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
undo_stack.push(ta);
|
||||
reload_clips();
|
||||
if (transition_menu) {
|
||||
panel_timeline->redraw_all_clips(true);
|
||||
} else {
|
||||
reload_clips();
|
||||
}
|
||||
}
|
||||
|
||||
void EffectControls::show_menu(bool video) {
|
||||
void EffectControls::show_effect_menu(bool video, bool transitions) {
|
||||
video_menu = video;
|
||||
transition_menu = transitions;
|
||||
|
||||
int lim;
|
||||
QVector<QString>* effect_names;
|
||||
if (video) {
|
||||
lim = VIDEO_EFFECT_COUNT;
|
||||
effect_names = &video_effect_names;
|
||||
} else {
|
||||
lim = AUDIO_EFFECT_COUNT;
|
||||
effect_names = &audio_effect_names;
|
||||
}
|
||||
if (transitions) {
|
||||
if (video) {
|
||||
lim = VIDEO_TRANSITION_COUNT;
|
||||
effect_names = &video_transition_names;
|
||||
} else {
|
||||
lim = AUDIO_TRANSITION_COUNT;
|
||||
effect_names = &audio_transition_names;
|
||||
}
|
||||
} else {
|
||||
if (video) {
|
||||
lim = VIDEO_EFFECT_COUNT;
|
||||
effect_names = &video_effect_names;
|
||||
} else {
|
||||
lim = AUDIO_EFFECT_COUNT;
|
||||
effect_names = &audio_effect_names;
|
||||
}
|
||||
}
|
||||
|
||||
QMenu effects_menu(this);
|
||||
|
||||
@@ -168,12 +194,22 @@ void EffectControls::set_clips(QVector<int>& clips) {
|
||||
|
||||
void EffectControls::on_add_video_effect_button_clicked()
|
||||
{
|
||||
show_menu(true);
|
||||
show_effect_menu(true, false);
|
||||
}
|
||||
|
||||
void EffectControls::on_add_audio_effect_button_clicked()
|
||||
{
|
||||
show_menu(false);
|
||||
show_effect_menu(false, false);
|
||||
}
|
||||
|
||||
void EffectControls::on_add_video_transition_button_clicked()
|
||||
{
|
||||
show_effect_menu(true, true);
|
||||
}
|
||||
|
||||
void EffectControls::on_add_audio_transition_button_clicked()
|
||||
{
|
||||
show_effect_menu(false, true);
|
||||
}
|
||||
|
||||
bool EffectControls::is_focused() {
|
||||
|
||||
@@ -19,7 +19,6 @@ class EffectControls : public QDockWidget
|
||||
public:
|
||||
explicit EffectControls(QWidget *parent = 0);
|
||||
~EffectControls();
|
||||
// void set_clip(Clip* c);
|
||||
void set_clips(QVector<int>& clips);
|
||||
void clear_effects(bool clear_cache);
|
||||
void delete_effects();
|
||||
@@ -28,20 +27,22 @@ public:
|
||||
|
||||
private slots:
|
||||
void menu_select(QAction* q);
|
||||
|
||||
void on_add_video_effect_button_clicked();
|
||||
|
||||
void on_add_audio_effect_button_clicked();
|
||||
void deselect_all_effects(QWidget*);
|
||||
|
||||
void deselect_all_effects(QWidget*);
|
||||
void on_add_video_transition_button_clicked();
|
||||
|
||||
void on_add_audio_transition_button_clicked();
|
||||
|
||||
private:
|
||||
Ui::EffectControls *ui;
|
||||
QVector<int> selected_clips;
|
||||
void show_menu(bool video);
|
||||
void show_effect_menu(bool video, bool transitions);
|
||||
void load_effects();
|
||||
|
||||
bool video_menu;
|
||||
bool transition_menu;
|
||||
};
|
||||
|
||||
/*class EffectAddCommand : public QUndoCommand {
|
||||
|
||||
@@ -173,7 +173,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_2">
|
||||
<widget class="QPushButton" name="add_video_transition_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Video Transition</string>
|
||||
</property>
|
||||
@@ -318,7 +318,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_3">
|
||||
<widget class="QPushButton" name="add_audio_transition_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Audio Transition</string>
|
||||
</property>
|
||||
|
||||
+19
-4
@@ -50,7 +50,7 @@ Timeline::Timeline(QWidget *parent) :
|
||||
ui(new Ui::Timeline),
|
||||
last_frame(0)
|
||||
{
|
||||
default_track_height = (QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96) * default_track_height;
|
||||
default_track_height = (QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96) * TRACK_DEFAULT_HEIGHT;
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
@@ -215,7 +215,7 @@ int Timeline::calculate_track_height(int track, int value) {
|
||||
int index = (track < 0) ? qAbs(track + 1) : track;
|
||||
QVector<int>& vector = (track < 0) ? video_track_heights : audio_track_heights;
|
||||
while (vector.size() < index+1) {
|
||||
vector.append(TRACK_DEFAULT_HEIGHT);
|
||||
vector.append(default_track_height);
|
||||
}
|
||||
if (value > -1) {
|
||||
vector[index] = value;
|
||||
@@ -837,19 +837,34 @@ bool Timeline::split_selection(TimelineAction* ta) {
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
if (s.track == clip->track) {
|
||||
if (clip->timeline_in < s.in && clip->timeline_out > s.out) {
|
||||
if (clip->timeline_in < s.in && clip->timeline_out > s.out) {
|
||||
Clip* split_A = clip->copy(sequence);
|
||||
split_A->clip_in += (s.in - clip->timeline_in);
|
||||
split_A->timeline_in = s.in;
|
||||
split_A->timeline_out = s.out;
|
||||
pre_splits.append(j);
|
||||
post_splits.append(split_A);
|
||||
post_splits.append(split_A);
|
||||
|
||||
Clip* split_B = clip->copy(sequence);
|
||||
split_B->clip_in += (s.out - clip->timeline_in);
|
||||
split_B->timeline_in = s.out;
|
||||
secondary_post_splits.append(split_B);
|
||||
|
||||
if (clip->opening_transition != NULL) {
|
||||
delete split_B->opening_transition;
|
||||
split_B->opening_transition = NULL;
|
||||
|
||||
delete split_A->opening_transition;
|
||||
split_A->opening_transition = NULL;
|
||||
}
|
||||
|
||||
if (clip->closing_transition != NULL) {
|
||||
ta->delete_transition(sequence, j, TA_CLOSING_TRANSITION);
|
||||
|
||||
delete split_A->closing_transition;
|
||||
split_A->closing_transition = NULL;
|
||||
}
|
||||
|
||||
ta->set_timeline_out(sequence, j, s.in);
|
||||
split = true;
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -2,6 +2,7 @@
|
||||
#define TIMELINE_H
|
||||
|
||||
#include "ui/timelinetools.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QVector>
|
||||
#include <QTime>
|
||||
@@ -68,7 +69,6 @@ class Timeline;
|
||||
class Timeline : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Timeline(QWidget *parent = 0);
|
||||
~Timeline();
|
||||
|
||||
+12
-6
@@ -33,7 +33,7 @@
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -272,7 +272,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>819</width>
|
||||
<height>551</height>
|
||||
<height>557</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
@@ -309,6 +309,12 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="opaqueResize">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QScrollArea" name="videoScrollArea">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
@@ -327,8 +333,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>802</width>
|
||||
<height>266</height>
|
||||
<width>805</width>
|
||||
<height>269</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@@ -378,8 +384,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>802</width>
|
||||
<height>265</height>
|
||||
<width>805</width>
|
||||
<height>269</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
|
||||
+2
-2
@@ -38,8 +38,8 @@ void init_audio() {
|
||||
if (!info.isFormatSupported(audio_format)) {
|
||||
qWarning() << "[WARNING] Couldn't initialize audio. Audio format is not supported by backend";
|
||||
} else {
|
||||
audio_output = new QAudioOutput(audio_format);
|
||||
audio_output->setBufferSize(20480);
|
||||
audio_output = new QAudioOutput(audio_format);
|
||||
audio_output->setBufferSize(20480);
|
||||
|
||||
// connect
|
||||
audio_io_device = audio_output->start();
|
||||
|
||||
@@ -131,6 +131,7 @@ void Clip::run_video_pre_effect_stack(long playhead, int* anchor_x, int* anchor_
|
||||
opening_transition->process_transition((double)transition_progress/(double)opening_transition->length);
|
||||
}
|
||||
}
|
||||
|
||||
if (closing_transition != NULL) {
|
||||
int transition_progress = closing_transition->length - (playhead - timeline_in - getLength() + closing_transition->length);
|
||||
if (transition_progress < closing_transition->length) {
|
||||
|
||||
@@ -1579,7 +1579,7 @@ void TimelineWidget::redraw_clips() {
|
||||
}
|
||||
}
|
||||
|
||||
if (minimumWidth() != panel_width || /*clip_pixmap.height() != height() || */panel_height != minimumHeight()) {
|
||||
if (minimumWidth() != panel_width || clip_pixmap.height() != height() || panel_height != minimumHeight()) {
|
||||
setMinimumWidth(panel_width);
|
||||
container->setMinimumWidth(panel_width);
|
||||
setMinimumHeight(panel_height);
|
||||
|
||||
+68
-64
@@ -32,7 +32,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
setFormat(format);
|
||||
|
||||
// start audio sending thread
|
||||
audio_sender_thread.start();
|
||||
audio_sender_thread.start();
|
||||
|
||||
// error handler - retries after 250ms if we couldn't get the entire image
|
||||
retry_timer.setInterval(250);
|
||||
@@ -40,9 +40,9 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
}
|
||||
|
||||
ViewerWidget::~ViewerWidget() {
|
||||
audio_sender_thread.close = true;
|
||||
audio_sender_thread.close = true;
|
||||
audio_sender_thread.cond.wakeAll();
|
||||
audio_sender_thread.wait();
|
||||
audio_sender_thread.wait();
|
||||
}
|
||||
|
||||
void ViewerWidget::deleteFunction() {
|
||||
@@ -87,12 +87,14 @@ void ViewerWidget::paintEvent(QPaintEvent *e) {
|
||||
if (enable_paint) QOpenGLWidget::paintEvent(e);
|
||||
}
|
||||
|
||||
void ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
|
||||
void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
|
||||
Sequence* s = sequence;
|
||||
long playhead = panel_timeline->playhead;
|
||||
|
||||
if (nest != NULL) {
|
||||
s = static_cast<Sequence*>(nest->media);
|
||||
Clip* nest = NULL;
|
||||
if (nests.size() > 0) {
|
||||
nest = nests.last();
|
||||
s = static_cast<Sequence*>(nest->media);
|
||||
playhead += nest->clip_in - nest->timeline_in;
|
||||
playhead = refactor_frame_number(playhead, sequence->frame_rate, s->frame_rate);
|
||||
}
|
||||
@@ -157,8 +159,7 @@ void ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
|
||||
texture_failed = true;
|
||||
} else {
|
||||
switch (c->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
qDebug() << "clip";
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// start preparing cache
|
||||
get_clip_frame(c, playhead);
|
||||
@@ -183,9 +184,9 @@ void ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
|
||||
// perform all transform effects
|
||||
c->run_video_pre_effect_stack(playhead, &anchor_x, &anchor_y);
|
||||
|
||||
if (nest != NULL) {
|
||||
nest->run_video_pre_effect_stack(playhead, &anchor_x, &anchor_y);
|
||||
}
|
||||
for (int i=nests.size()-1;i>=0;i--) {
|
||||
nests.at(i)->run_video_pre_effect_stack(playhead, &anchor_x, &anchor_y);
|
||||
}
|
||||
|
||||
int anchor_right = ms->video_width - anchor_x;
|
||||
int anchor_bottom = ms->video_height - anchor_y;
|
||||
@@ -217,8 +218,8 @@ void ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
qDebug() << "nest";
|
||||
compose_sequence(c, render_audio);
|
||||
nests.append(c);
|
||||
compose_sequence(nests, render_audio);
|
||||
c->run_video_post_effect_stack();
|
||||
break;
|
||||
}
|
||||
@@ -237,12 +238,13 @@ void ViewerWidget::paintGL() {
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// compose video preview
|
||||
compose_sequence(NULL, (panel_timeline->playing || force_audio));
|
||||
QVector<Clip*> nests;
|
||||
compose_sequence(nests, (panel_timeline->playing || force_audio));
|
||||
|
||||
// send audio to IO device
|
||||
if (panel_timeline->playing) {
|
||||
audio_sender_thread.cond.wakeAll();
|
||||
}
|
||||
// send audio to IO device
|
||||
if (panel_timeline->playing) {
|
||||
audio_sender_thread.cond.wakeAll();
|
||||
}
|
||||
|
||||
if (texture_failed) {
|
||||
if (multithreaded) {
|
||||
@@ -256,60 +258,62 @@ void ViewerWidget::paintGL() {
|
||||
}
|
||||
|
||||
AudioSenderThread::AudioSenderThread() : close(false) {
|
||||
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
|
||||
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
|
||||
}
|
||||
|
||||
void AudioSenderThread::run() {
|
||||
lock.lock();
|
||||
while (true) {
|
||||
cond.wait(&lock);
|
||||
if (close) {
|
||||
break;
|
||||
} else {
|
||||
int written_bytes = 0;
|
||||
lock.lock();
|
||||
while (true) {
|
||||
cond.wait(&lock);
|
||||
if (close) {
|
||||
break;
|
||||
} else {
|
||||
int written_bytes = 0;
|
||||
|
||||
int adjusted_read_index = audio_ibuffer_read%audio_ibuffer_size;
|
||||
int max_write = audio_ibuffer_size - adjusted_read_index;
|
||||
int actual_write = send_audio_to_output(adjusted_read_index, max_write);
|
||||
written_bytes += actual_write;
|
||||
if (actual_write == max_write) {
|
||||
// got all the bytes, write again
|
||||
written_bytes += send_audio_to_output(0, audio_ibuffer_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
int adjusted_read_index = audio_ibuffer_read%audio_ibuffer_size;
|
||||
int max_write = audio_ibuffer_size - adjusted_read_index;
|
||||
int actual_write = send_audio_to_output(adjusted_read_index, max_write);
|
||||
written_bytes += actual_write;
|
||||
if (actual_write == max_write) {
|
||||
// got all the bytes, write again
|
||||
written_bytes += send_audio_to_output(0, audio_ibuffer_size);
|
||||
}
|
||||
|
||||
qDebug() << "WB:" << written_bytes;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
int AudioSenderThread::send_audio_to_output(int offset, int max) {
|
||||
// send audio to device
|
||||
int actual_write = audio_io_device->write((const char*) audio_ibuffer+offset, max);
|
||||
audio_ibuffer_read += actual_write;
|
||||
// send audio to device
|
||||
int actual_write = audio_io_device->write((const char*) audio_ibuffer+offset, max);
|
||||
audio_ibuffer_read += actual_write;
|
||||
|
||||
// send samples to audio monitor cache
|
||||
if (panel_timeline->ui->audio_monitor->sample_cache_offset == -1) {
|
||||
panel_timeline->ui->audio_monitor->sample_cache_offset = panel_timeline->playhead;
|
||||
}
|
||||
long sample_cache_playhead = panel_timeline->ui->audio_monitor->sample_cache_offset + panel_timeline->ui->audio_monitor->sample_cache.size();
|
||||
int next_buffer_offset, buffer_offset_adjusted, i;
|
||||
int buffer_offset = get_buffer_offset_from_frame(sample_cache_playhead);
|
||||
samples.resize(av_get_channel_layout_nb_channels(sequence->audio_layout));
|
||||
samples.fill(0);
|
||||
while (buffer_offset < audio_ibuffer_read) {
|
||||
sample_cache_playhead++;
|
||||
next_buffer_offset = qMin(get_buffer_offset_from_frame(sample_cache_playhead), audio_ibuffer_read);
|
||||
while (buffer_offset < next_buffer_offset) {
|
||||
for (i=0;i<samples.size();i++) {
|
||||
buffer_offset_adjusted = buffer_offset%audio_ibuffer_size;
|
||||
samples[i] = qMax(qAbs((qint16) (((audio_ibuffer[buffer_offset_adjusted+1] & 0xFF) << 8) | (audio_ibuffer[buffer_offset_adjusted] & 0xFF))), samples[i]);
|
||||
buffer_offset += 2;
|
||||
}
|
||||
}
|
||||
panel_timeline->ui->audio_monitor->sample_cache.append(samples);
|
||||
buffer_offset = next_buffer_offset;
|
||||
}
|
||||
// send samples to audio monitor cache
|
||||
if (panel_timeline->ui->audio_monitor->sample_cache_offset == -1) {
|
||||
panel_timeline->ui->audio_monitor->sample_cache_offset = panel_timeline->playhead;
|
||||
}
|
||||
long sample_cache_playhead = panel_timeline->ui->audio_monitor->sample_cache_offset + panel_timeline->ui->audio_monitor->sample_cache.size();
|
||||
int next_buffer_offset, buffer_offset_adjusted, i;
|
||||
int buffer_offset = get_buffer_offset_from_frame(sample_cache_playhead);
|
||||
samples.resize(av_get_channel_layout_nb_channels(sequence->audio_layout));
|
||||
samples.fill(0);
|
||||
while (buffer_offset < audio_ibuffer_read) {
|
||||
sample_cache_playhead++;
|
||||
next_buffer_offset = qMin(get_buffer_offset_from_frame(sample_cache_playhead), audio_ibuffer_read);
|
||||
while (buffer_offset < next_buffer_offset) {
|
||||
for (i=0;i<samples.size();i++) {
|
||||
buffer_offset_adjusted = buffer_offset%audio_ibuffer_size;
|
||||
samples[i] = qMax(qAbs((qint16) (((audio_ibuffer[buffer_offset_adjusted+1] & 0xFF) << 8) | (audio_ibuffer[buffer_offset_adjusted] & 0xFF))), samples[i]);
|
||||
buffer_offset += 2;
|
||||
}
|
||||
}
|
||||
panel_timeline->ui->audio_monitor->sample_cache.append(samples);
|
||||
buffer_offset = next_buffer_offset;
|
||||
}
|
||||
|
||||
memset(audio_ibuffer+offset, 0, actual_write);
|
||||
memset(audio_ibuffer+offset, 0, actual_write);
|
||||
|
||||
return actual_write;
|
||||
return actual_write;
|
||||
}
|
||||
|
||||
+9
-9
@@ -15,14 +15,14 @@ struct Sequence;
|
||||
|
||||
class AudioSenderThread : public QThread {
|
||||
public:
|
||||
AudioSenderThread();
|
||||
void run();
|
||||
QWaitCondition cond;
|
||||
bool close;
|
||||
QMutex lock;
|
||||
AudioSenderThread();
|
||||
void run();
|
||||
QWaitCondition cond;
|
||||
bool close;
|
||||
QMutex lock;
|
||||
private:
|
||||
QVector<qint16> samples;
|
||||
int send_audio_to_output(int offset, int max);
|
||||
QVector<qint16> samples;
|
||||
int send_audio_to_output(int offset, int max);
|
||||
};
|
||||
|
||||
class ViewerWidget : public QOpenGLWidget, public QOpenGLFunctions
|
||||
@@ -43,11 +43,11 @@ protected:
|
||||
// void resizeGL(int w, int h);
|
||||
private:
|
||||
QTimer retry_timer;
|
||||
AudioSenderThread audio_sender_thread;
|
||||
AudioSenderThread audio_sender_thread;
|
||||
private slots:
|
||||
void retry();
|
||||
void deleteFunction();
|
||||
void compose_sequence(Clip *nest, bool render_audio);
|
||||
void compose_sequence(QVector<Clip*>& nests, bool render_audio);
|
||||
};
|
||||
|
||||
#endif // VIEWERWIDGET_H
|
||||
|
||||
Reference in New Issue
Block a user