many changes, mostly to export

This commit is contained in:
itsmattkc
2018-06-06 03:30:22 -07:00
parent 81bf111037
commit 0ea7c43e53
26 changed files with 404 additions and 206 deletions
+23 -1
View File
@@ -302,6 +302,16 @@ void ExportDialog::on_pushButton_2_clicked()
close();
}
void ExportDialog::render_thread_finished() {
prep_ui_for_render(false);
}
void ExportDialog::prep_ui_for_render(bool rendering) {
ui->pushButton->setEnabled(!rendering);
ui->pushButton_2->setEnabled(!rendering);
ui->renderCancel->setEnabled(rendering);
}
void ExportDialog::on_pushButton_clicked()
{
QString ext;
@@ -399,15 +409,20 @@ void ExportDialog::on_pushButton_clicked()
}
QString filename = QFileDialog::getSaveFileName(this, "Export Media", "", format_strings[ui->formatCombobox->currentIndex()] + " (*." + ext + ")");
if (!filename.isEmpty()) {
ExportThread* et = new ExportThread();
et = new ExportThread();
et->surface.create();
connect(et, SIGNAL(finished()), et, SLOT(deleteLater()));
connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished()));
connect(et, SIGNAL(progress_changed(int)), this, SLOT(update_progress_bar(int)));
panel_viewer->viewer_widget->enable_paint = false;
panel_viewer->viewer_widget->context()->doneCurrent();
panel_viewer->viewer_widget->context()->moveToThread(et);
panel_viewer->viewer_widget->force_audio = true;
prep_ui_for_render(true);
et->filename = filename;
et->video_enabled = ui->videoGroupbox->isChecked();
@@ -421,6 +436,8 @@ void ExportDialog::on_pushButton_clicked()
et->audio_sampling_rate = 48000;
et->audio_bitrate = ui->audiobitrateSpinbox->value();
et->ed = this;
et->start();
}
}
@@ -435,3 +452,8 @@ void ExportDialog::set_defaults(Sequence* s) {
void ExportDialog::update_progress_bar(int value) {
ui->progressBar->setValue(value);
}
void ExportDialog::on_renderCancel_clicked()
{
et->fail = true;
}
+8
View File
@@ -8,6 +8,7 @@ class ExportDialog;
}
struct Sequence;
class ExportThread;
class ExportDialog : public QDialog
{
@@ -27,12 +28,19 @@ private slots:
void update_progress_bar(int value);
void on_renderCancel_clicked();
void render_thread_finished();
private:
Ui::ExportDialog *ui;
QVector<QString> format_strings;
QVector<int> format_vcodecs;
QVector<int> format_acodecs;
ExportThread* et;
void prep_ui_for_render(bool rendering);
};
#endif // EXPORTDIALOG_H
+28 -8
View File
@@ -172,14 +172,34 @@
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="enabled">
<bool>false</bool>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QProgressBar" name="progressBar">
<property name="enabled">
<bool>false</bool>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="renderCancel">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>x</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
+18
View File
@@ -1,6 +1,9 @@
#include "effects/effects.h"
#include "project/clip.h"
#include <QVector>
#include <QDebug>
QVector<QString> video_effect_names;
QVector<QString> audio_effect_names;
@@ -14,3 +17,18 @@ void init_effects() {
audio_effect_names[AUDIO_VOLUME_EFFECT] = "Volume";
audio_effect_names[AUDIO_PAN_EFFECT] = "Pan";
}
Effect* create_effect(int effect_id, Clip* c) {
if (c->track < 0) {
switch (effect_id) {
case VIDEO_TRANSFORM_EFFECT: return new TransformEffect(c);
}
} else {
switch (effect_id) {
case AUDIO_VOLUME_EFFECT: return new VolumeEffect(c);
case AUDIO_PAN_EFFECT: return new PanEffect(c);
}
}
qDebug() << "[ERROR] Invalid effect ID";
return NULL;
}
+12 -8
View File
@@ -20,14 +20,16 @@ enum AudioEffects {
extern QVector<QString> video_effect_names;
extern QVector<QString> audio_effect_names;
void init_effects();
Effect* create_effect(int effect_id, Clip* c);
// video effects
class TransformEffect : public Effect {
Q_OBJECT
public:
TransformEffect(Clip* c);
TransformEffect(Clip* c);
void process_gl(int* anchor_x, int* anchor_y);
private:
Effect* copy();
QSpinBox* position_x;
QSpinBox* position_y;
QSpinBox* scale_x;
@@ -44,17 +46,19 @@ public slots:
// audio effects
class VolumeEffect : public Effect {
public:
VolumeEffect(Clip* c);
void process_audio(uint8_t* samples, int nb_bytes);
private:
VolumeEffect(Clip* c);
void process_audio(uint8_t* samples, int nb_bytes);
Effect* copy();
QSpinBox* volume_val;
};
class PanEffect : public Effect {
public:
PanEffect(Clip* c);
void process_audio(uint8_t* samples, int nb_bytes);
private:
PanEffect(Clip* c);
void process_audio(uint8_t* samples, int nb_bytes);
Effect* copy();
QSpinBox* pan_val;
};
+6
View File
@@ -28,6 +28,12 @@ PanEffect::PanEffect(Clip* c) : Effect(c) {
pan_val->setValue(0);
}
Effect* PanEffect::copy() {
PanEffect* p = new PanEffect(parent_clip);
p->pan_val->setValue(pan_val->value());
return p;
}
void PanEffect::process_audio(uint8_t *samples, int nb_bytes) {
for (int i=0;i<nb_bytes;i+=4) {
int16_t left_sample = (int16_t) ((samples[i+1] << 8) | samples[i]);
+16 -2
View File
@@ -72,8 +72,8 @@ TransformEffect::TransformEffect(Clip* c) : Effect(c) {
container->setContents(ui);
// set defaults
position_x->setValue(c->sequence->width/2);
position_y->setValue(c->sequence->height/2);
position_x->setValue(c->sequence->width/2);
position_y->setValue(c->sequence->height/2);
scale_x->setValue(100);
scale_y->setValue(100);
scale_y->setEnabled(false);
@@ -94,6 +94,20 @@ TransformEffect::TransformEffect(Clip* c) : Effect(c) {
connect(uniform_scale_box, SIGNAL(toggled(bool)), this, SLOT(field_changed()));
}
Effect* TransformEffect::copy() {
TransformEffect* t = new TransformEffect(parent_clip);
t->position_x->setValue(position_x->value());
t->position_y->setValue(position_y->value());
t->scale_x->setValue(scale_x->value());
t->scale_y->setValue(scale_y->value());
t->uniform_scale_box->setChecked(uniform_scale_box->isChecked());
t->rotation->setValue(rotation->value());
t->anchor_x_box->setValue(anchor_x_box->value());
t->anchor_y_box->setValue(anchor_y_box->value());
t->opacity->setValue(opacity->value());
return t;
}
void TransformEffect::toggle_uniform_scale(bool enabled) {
scale_y->setEnabled(!enabled);
}
+6
View File
@@ -28,6 +28,12 @@ VolumeEffect::VolumeEffect(Clip* c) : Effect(c) {
volume_val->setValue(100);
}
Effect* VolumeEffect::copy() {
VolumeEffect* v = new VolumeEffect(parent_clip);
v->volume_val->setValue(volume_val->value());
return v;
}
void VolumeEffect::process_audio(uint8_t *samples, int nb_bytes) {
for (int i=0;i<nb_bytes;i+=2) {
int16_t full_sample = (int16_t) ((samples[i+1] << 8) | samples[i]);
+139 -91
View File
@@ -7,6 +7,8 @@
#include "panels/viewer.h"
#include "ui/viewerwidget.h"
#include "playback/playback.h"
#include "playback/audio.h"
#include "dialogs/exportdialog.h"
extern "C" {
#include <libavcodec/avcodec.h>
@@ -72,17 +74,21 @@ void ExportThread::run() {
AVCodec* vcodec;
AVCodecContext* vcodec_ctx;
AVFrame* video_frame;
AVFrame* sws_frame;
SwsContext* sws_ctx = NULL;
// AVStream* audio_stream;
// AVCodec* acodec;
// AVFrame* audio_frame;
// AVCodecContext* acodec_ctx;
uint8_t* flip_buffer;
AVStream* audio_stream;
AVCodec* acodec;
AVFrame* audio_frame;
AVFrame* swr_frame;
AVCodecContext* acodec_ctx;
AVPacket video_pkt;
// AVPacket audio_pkt;
// SwrContext* swr_ctx = NULL;
AVPacket audio_pkt;
SwrContext* swr_ctx = NULL;
int aframe_bytes;
int ret;
bool fail = false;
fail = false;
if (video_enabled) {
// initialize array contain opengl data
@@ -150,6 +156,13 @@ void ExportThread::run() {
NULL,
NULL
);
sws_frame = av_frame_alloc();
sws_frame->format = AV_PIX_FMT_YUV420P;
sws_frame->width = video_frame->width;
sws_frame->height = video_frame->height;
av_frame_get_buffer(sws_frame, 0);
flip_buffer = new uint8_t[video_frame->width * video_frame->height * 4];
}
}
}
@@ -157,80 +170,90 @@ void ExportThread::run() {
}
}
// if (audio_enabled && !fail) {
// audio_stream = avformat_new_stream(fmt_ctx, NULL);
// if (!audio_stream) {
// qDebug() << "[ERROR] Could not allocate output streams";
// fail = true;
// } else {
// acodec = avcodec_find_encoder((enum AVCodecID) audio_codec);
// if (!acodec) {
// qDebug() << "[ERROR] Could not find audio encoder";
// fail = true;
// } else {
// acodec_ctx = avcodec_alloc_context3(acodec);
// if (!acodec_ctx) {
// qDebug() << "[ERROR] Could not find allocate audio encoding context";
// fail = true;
// } else {
// acodec_ctx->sample_rate = audio_sampling_rate;
// acodec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
// acodec_ctx->channels = av_get_channel_layout_nb_channels(acodec_ctx->channel_layout);
// acodec_ctx->sample_fmt = acodec->sample_fmts[0];
// acodec_ctx->time_base = {1, audio_sampling_rate};
// acodec_ctx->bit_rate = audio_bitrate * 1000;
if (audio_enabled && !fail) {
audio_stream = avformat_new_stream(fmt_ctx, NULL);
if (!audio_stream) {
qDebug() << "[ERROR] Could not allocate output streams";
fail = true;
} else {
acodec = avcodec_find_encoder((enum AVCodecID) audio_codec);
if (!acodec) {
qDebug() << "[ERROR] Could not find audio encoder";
fail = true;
} else {
acodec_ctx = avcodec_alloc_context3(acodec);
if (!acodec_ctx) {
qDebug() << "[ERROR] Could not find allocate audio encoding context";
fail = true;
} else {
acodec_ctx->sample_rate = audio_sampling_rate;
acodec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is what the user sets the output audio to)
acodec_ctx->channels = av_get_channel_layout_nb_channels(acodec_ctx->channel_layout);
acodec_ctx->sample_fmt = acodec->sample_fmts[0];
acodec_ctx->time_base = {1, audio_sampling_rate};
acodec_ctx->bit_rate = audio_bitrate * 1000;
// ret = avcodec_open2(acodec_ctx, acodec, NULL);
// if (ret < 0) {
// qDebug() << "[ERROR] Could not open output audio encoder." << ret;
// fail = true;
// } else {
// ret = avcodec_parameters_from_context(audio_stream->codecpar, acodec_ctx);
// if (ret < 0) {
// qDebug() << "[ERROR] Could not copy audio encoder parameters to output stream." << ret;
// fail = true;
// } else {
// if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
// acodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
// }
ret = avcodec_open2(acodec_ctx, acodec, NULL);
if (ret < 0) {
qDebug() << "[ERROR] Could not open output audio encoder." << ret;
fail = true;
} else {
ret = avcodec_parameters_from_context(audio_stream->codecpar, acodec_ctx);
if (ret < 0) {
qDebug() << "[ERROR] Could not copy audio encoder parameters to output stream." << ret;
fail = true;
} else {
if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
acodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
// audio_stream->time_base = acodec_ctx->time_base;
audio_stream->time_base = acodec_ctx->time_base;
// // init audio resampler context
// swr_ctx = swr_alloc_set_opts(
// NULL,
// acodec_ctx->channel_layout,
// acodec_ctx->sample_fmt,
// acodec_ctx->sample_rate,
// sequence->audio_layout,
// AV_SAMPLE_FMT_S16,
// sequence->audio_frequency,
// 0,
// NULL
// );
// swr_init(swr_ctx);
// init audio resampler context
swr_ctx = swr_alloc_set_opts(
NULL,
acodec_ctx->channel_layout,
acodec_ctx->sample_fmt,
acodec_ctx->sample_rate,
sequence->audio_layout,
AV_SAMPLE_FMT_S16,
sequence->audio_frequency,
0,
NULL
);
swr_init(swr_ctx);
// // initialize raw audio frame
// audio_frame = av_frame_alloc();
// audio_frame->nb_samples = acodec_ctx->frame_size;
// if (audio_frame->nb_samples == 0) audio_frame->nb_samples = 2048; // should probably be way smaller?
// audio_frame->format = acodec_ctx->sample_fmt;
// audio_frame->channels = acodec_ctx->channels;
// audio_frame->channel_layout = acodec_ctx->channel_layout;
// ret = av_frame_get_buffer(audio_frame, 0);
// if (ret < 0) {
// qDebug() << "[ERROR] Could not allocate audio buffer." << ret;
// }
// // av_samples_alloc(&audio_frame->data[0], NULL, av_get_channel_layout_nb_channels(audio_frame->channel_layout), audio_frame->nb_samples, acodec_ctx->sample_fmt, 0);
// av_frame_make_writable(audio_frame);
// initialize raw audio frame
audio_frame = av_frame_alloc();
audio_frame->sample_rate = sequence->audio_frequency;
audio_frame->nb_samples = acodec_ctx->frame_size;
if (audio_frame->nb_samples == 0) audio_frame->nb_samples = 2048; // should possibly be smaller?
audio_frame->format = AV_SAMPLE_FMT_S16;
audio_frame->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is whatever format they're held in the internal buffer)
audio_frame->channels = av_get_channel_layout_nb_channels(audio_frame->channel_layout);
ret = av_frame_get_buffer(audio_frame, 0);
if (ret < 0) {
qDebug() << "[ERROR] Could not allocate audio buffer." << ret;
}
// av_samples_alloc(&audio_frame->data[0], NULL, av_get_channel_layout_nb_channels(audio_frame->channel_layout), audio_frame->nb_samples, acodec_ctx->sample_fmt, 0);
av_frame_make_writable(audio_frame);
aframe_bytes = av_samples_get_buffer_size(NULL, audio_frame->channels, audio_frame->nb_samples, static_cast<AVSampleFormat>(audio_frame->format), 0);
// av_init_packet(&audio_pkt);
// }
// }
// }
// }
// }
// }
// init converted audio frame
swr_frame = av_frame_alloc();
swr_frame->channel_layout = acodec_ctx->channel_layout;
swr_frame->channels = acodec_ctx->channels;
swr_frame->sample_rate = acodec_ctx->sample_rate;
swr_frame->format = acodec_ctx->sample_fmt;
av_frame_make_writable(swr_frame);
av_init_packet(&audio_pkt);
}
}
}
}
}
}
if (!fail) {
av_dump_format(fmt_ctx, 0, c_filename, 1);
@@ -257,14 +280,10 @@ void ExportThread::run() {
fbo.bind();
QOpenGLPaintDevice fbo_dev(video_width, video_height);
QPainter painter(&fbo_dev);
painter.beginNativePainting();
painter.beginNativePainting();
AVFrame* sws_frame = av_frame_alloc();
sws_frame->format = AV_PIX_FMT_YUV420P;
sws_frame->width = video_frame->width;
sws_frame->height = video_frame->height;
av_frame_get_buffer(sws_frame, 0);
uint8_t* flip_buffer = new uint8_t[video_frame->width * video_frame->height * 4];
int written_frame_audio_bytes = 0;
long file_audio_samples = 0;
while (panel_timeline->playhead < end && !fail) {
panel_viewer->viewer_widget->paintGL();
@@ -289,6 +308,27 @@ void ExportThread::run() {
// send to encoder
if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream)) fail = true;
}
if (audio_enabled && !fail) {
// do we need to encode more audio samples?
if (file_audio_samples <= (timecode_secs*audio_sampling_rate)) {
uint8_t* cache = (reading_audio_cache_A) ? audio_cache_A : audio_cache_B;
int copylen = qMin(aframe_bytes-written_frame_audio_bytes, audio_cache_size-audio_bytes_written);
memcpy(audio_frame->data[0]+written_frame_audio_bytes, cache+audio_bytes_written, copylen);
written_frame_audio_bytes += copylen;
audio_bytes_written += copylen;
if (written_frame_audio_bytes == aframe_bytes) {
// convert to export sample format
swr_convert_frame(swr_ctx, swr_frame, audio_frame);
swr_frame->pts = file_audio_samples;
// send to encoder
if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream)) fail = true;
file_audio_samples += swr_frame->nb_samples;
written_frame_audio_bytes = 0;
}
}
}
emit progress_changed(((float) panel_timeline->playhead / (float) end) * 100);
panel_timeline->playhead++;
}
@@ -297,16 +337,14 @@ void ExportThread::run() {
painter.endNativePainting();
fbo.release();
av_frame_free(&sws_frame);
if (!fail) {
// flush remaining packets
if (video_enabled) {
if (!encode(fmt_ctx, vcodec_ctx, NULL, &video_pkt, video_stream)) fail = true;
}
// if (audio_enabled) {
// if (!encode(fmt_ctx, acodec_ctx, NULL, &audio_pkt, audio_stream)) return false;
// }
if (audio_enabled) {
if (!encode(fmt_ctx, acodec_ctx, NULL, &audio_pkt, audio_stream)) fail = true;
}
}
if (!fail) {
@@ -321,16 +359,24 @@ void ExportThread::run() {
}
av_packet_unref(&video_pkt);
av_packet_unref(&audio_pkt);
av_frame_free(&video_frame);
av_frame_free(&audio_frame);
avcodec_free_context(&vcodec_ctx);
// avcodec_free_context(&acodec_ctx);
avcodec_free_context(&acodec_ctx);
avio_closep(&fmt_ctx->pb);
avformat_free_context(fmt_ctx);
// qDebug() << "Render took: %ih %im %is\n", render_time/3600000, render_time/60000, render_time/1000;
if (sws_ctx != NULL) sws_freeContext(sws_ctx);
// if (swr_ctx != NULL) swr_free(&swr_ctx);
if (sws_ctx != NULL) {
sws_freeContext(sws_ctx);
av_frame_free(&sws_frame);
}
if (swr_ctx != NULL) {
swr_free(&swr_ctx);
av_frame_free(&swr_frame);
}
}
}
@@ -339,4 +385,6 @@ void ExportThread::run() {
panel_viewer->viewer_widget->context()->doneCurrent();
panel_viewer->viewer_widget->context()->moveToThread(qApp->thread());
panel_viewer->viewer_widget->multithreaded = true;
panel_viewer->viewer_widget->force_audio = false;
panel_viewer->viewer_widget->enable_paint = true;
}
+7 -1
View File
@@ -4,6 +4,8 @@
#include <QThread>
#include <QOffscreenSurface>
class ExportDialog;
class ExportThread : public QThread {
Q_OBJECT
public:
@@ -23,8 +25,12 @@ public:
int audio_bitrate;
QOffscreenSurface surface;
ExportDialog* ed;
bool fail;
signals:
void progress_changed(int value);
void progress_changed(int value);
};
#endif // EXPORTTHREAD_H
+2 -4
View File
@@ -36,7 +36,7 @@ MainWindow::MainWindow(QWidget *parent) :
{
// set up style?
qApp->setStyle(QStyleFactory::create("Fusion"));
qApp->setStyle(QStyleFactory::create("Fusion"));
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, QColor(53,53,53));
@@ -54,9 +54,7 @@ MainWindow::MainWindow(QWidget *parent) :
darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
darkPalette.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(darkPalette);
qApp->setPalette(darkPalette);
// end style
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.6.1, 2018-06-01T16:24:49. -->
<!-- Written by QtCreator 4.6.1, 2018-06-05T16:19:18. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
+24 -11
View File
@@ -21,6 +21,27 @@ EffectControls::EffectControls(QWidget *parent) :
clip = NULL;
set_clip(NULL);
effects_menu = new QMenu(this);
for (int i=0;i<VIDEO_EFFECT_COUNT;i++) {
QAction* action = new QAction();
action->setText(video_effect_names.at(i));
action->setData(i);
effects_menu->addAction(action);
}
effects_menu->addSeparator();
for (int i=0;i<AUDIO_EFFECT_COUNT;i++) {
QAction* action = new QAction();
action->setText(audio_effect_names.at(i));
action->setData(i);
effects_menu->addAction(action);
}
connect(effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_select(QAction*)));
}
void EffectControls::menu_select(QAction* q) {
clip->effects.append(create_effect(q->data().toInt(), clip));
set_clip(clip);
}
EffectControls::~EffectControls()
@@ -30,29 +51,21 @@ EffectControls::~EffectControls()
void EffectControls::on_pushButton_clicked()
{
QMenu effects_menu(this);
for (int i=0;i<video_effect_names.size();i++) {
effects_menu.addAction(video_effect_names.at(i));
}
effects_menu.addSeparator();
for (int i=0;i<audio_effect_names.size();i++) {
effects_menu.addAction(audio_effect_names.at(i));
}
effects_menu.exec(QCursor::pos());
effects_menu->exec(QCursor::pos());
}
void EffectControls::set_clip(Clip* c) {
// clear ui->scrollAreaWidgetContents
if (clip != NULL) {
for (int i=0;i<clip->effects.size();i++) {
clip->effects.at(i)->container->setParent(NULL);
clip->effects.at(i)->container->setParent(NULL);
}
}
ui->pushButton->setEnabled(c != NULL);
if (c != NULL) {
for (int i=0;i<c->effects.size();i++) {
static_cast<QVBoxLayout*>(ui->scrollAreaWidgetContents->layout())->insertWidget(i, c->effects.at(i)->container);
static_cast<QVBoxLayout*>(ui->scrollAreaWidgetContents->layout())->insertWidget(i, c->effects.at(i)->container);
}
}
clip = c;
+3
View File
@@ -4,6 +4,7 @@
#include <QDockWidget>
struct Clip;
class QMenu;
namespace Ui {
class EffectControls;
@@ -20,10 +21,12 @@ public:
private slots:
void on_pushButton_clicked();
void menu_select(QAction* q);
private:
Ui::EffectControls *ui;
Clip* clip;
QMenu* effects_menu;
};
#endif // EFFECTCONTROLS_H
+9 -1
View File
@@ -71,6 +71,7 @@ void Timeline::seek(long p) {
c.frame_sample_index = 0;
}
clear_cache(true, true);
audio_bytes_written = 0;
switch_audio_cache = true;
reading_audio_cache_A = false;
@@ -146,7 +147,7 @@ void Timeline::repaint_timeline() {
}
ui->video_area->update();
ui->audio_area->update();
if (last_frame != playhead) {
if (last_frame != playhead) {
panel_viewer->viewer_widget->update();
last_frame = playhead;
}
@@ -319,3 +320,10 @@ void Timeline::on_toolSlipButton_toggled(bool checked)
tool = TIMELINE_TOOL_SLIP;
}
}
void Timeline::on_snappingButton_toggled(bool checked)
{
snapping = checked;
qDebug() << "clips vector size:" << sizeof(Clip) * sequence->clip_count();
}
+2
View File
@@ -137,6 +137,8 @@ private slots:
void on_toolSlipButton_toggled(bool checked);
void on_snappingButton_toggled(bool checked);
private:
Ui::Timeline *ui;
QVector<QPushButton*> tool_buttons;
+1
View File
@@ -18,6 +18,7 @@ uint8_t* audio_cache_B = NULL;
int audio_cache_size = 0;
bool switch_audio_cache = true;
bool reading_audio_cache_A = false;
int audio_bytes_written = 0;
void init_audio(Sequence* s) {
if (audio_cache_A != NULL) {
+1
View File
@@ -11,6 +11,7 @@ extern QIODevice* audio_io_device;
extern uint8_t* audio_cache_A;
extern uint8_t* audio_cache_B;
extern int audio_cache_size;
extern int audio_bytes_written;
extern bool switch_audio_cache;
extern bool reading_audio_cache_A;
void init_audio(Sequence* s);
+1 -1
View File
@@ -34,7 +34,7 @@ void cache_audio_worker(Clip* c, bool write_A) {
int limit = std::min(c->frame_sample_index + audio_cache_size - bytes_written, nb_bytes);
// perform all audio effects
for (unsigned int j=0;j<c->effects.size();j++) {
c->effects[j]->process_audio(frame->data[0], limit);
c->effects.at(j)->process_audio(frame->data[0], limit);
}
// mix audio into cache
for (int i=c->frame_sample_index;i<limit;i++) {
+7 -3
View File
@@ -38,6 +38,10 @@ void Clip::copy(const Clip& c) {
sequence = c.sequence;
media = c.media;
media_stream = c.media_stream;
for (int i=0;i<c.effects.size();i++) {
effects.append(c.effects.at(i)->copy());
}
}
void Clip::init() {
@@ -68,9 +72,9 @@ Clip::~Clip() {
open_lock.lock();
open_lock.unlock();
for (int i=0;i<effects.size();i++) {
delete effects.at(i);
}
for (int i=0;i<effects.size();i++) {
delete effects.at(i);
}
av_packet_unref(pkt);
delete pkt;
}
+1 -1
View File
@@ -57,7 +57,7 @@ struct Clip
Sequence* sequence;
// other variables (should be "duplicated" in copy())
QList<Effect*> effects;
QList<Effect*> effects;
// QVector<Clip&> linkedClips;
// media handling
+7
View File
@@ -14,5 +14,12 @@ void Effect::field_changed() {
panel_viewer->viewer_widget->update();
}
Effect* Effect::copy() {
Effect* e = new Effect(parent_clip);
name = e->name;
type = e->type;
return e;
}
void Effect::process_gl(int* anchor_x, int* anchor_y) {}
void Effect::process_audio(uint8_t* samples, int nb_bytes) {}
+4 -2
View File
@@ -14,12 +14,14 @@ class Effect : public QObject
{
Q_OBJECT
public:
Effect(Clip* c);
Effect(Clip* c); // default constructor
int type;
QString name;
CollapsibleWidget* container;
QWidget* ui;
Clip* parent_clip;
Clip* parent_clip;
virtual Effect* copy();
virtual void process_gl(int* anchor_x, int* anchor_y);
virtual void process_audio(uint8_t* samples, int nb_bytes);
+52 -55
View File
@@ -109,16 +109,17 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
if (c.track < 0) {
// add default video effects
c.effects.append(new TransformEffect(&c));
c.effects.append(create_effect(VIDEO_TRANSFORM_EFFECT, &c));
} else {
// add default audio effects
c.effects.append(new VolumeEffect(&c));
c.effects.append(new PanEffect(&c));
c.effects.append(create_effect(AUDIO_VOLUME_EFFECT, &c));
c.effects.append(create_effect(AUDIO_PAN_EFFECT, &c));
}
}
panel_timeline->ghosts.clear();
panel_timeline->importing = false;
panel_timeline->snapped = false;
panel_timeline->redraw_all_clips();
}
@@ -303,13 +304,46 @@ void TimelineWidget::init_ghosts() {
}
}
bool subvalidate_snapping(Ghost& g, long* frame_diff, long snap_point) {
int snap_range = 15;
long in_validator = g.old_in + *frame_diff - snap_point;
long out_validator = g.old_out + *frame_diff - snap_point;
if (in_validator > -snap_range && in_validator < snap_range) {
*frame_diff -= in_validator;
panel_timeline->snap_point = snap_point;
panel_timeline->snapped = true;
return true;
} else if (out_validator > -snap_range && out_validator < snap_range) {
*frame_diff -= out_validator;
panel_timeline->snap_point = snap_point;
panel_timeline->snapped = true;
return true;
}
return false;
}
void validate_snapping(Ghost& g, long* frame_diff) {
long validator;
panel_timeline->snapped = false;
if (panel_timeline->snapping) {
if (!subvalidate_snapping(g, frame_diff, panel_timeline->playhead)) {
for (int j=0;j<panel_timeline->sequence->clip_count();j++) {
Clip& c = panel_timeline->sequence->get_clip(j);
if (!subvalidate_snapping(g, frame_diff, c.timeline_in)) {
subvalidate_snapping(g, frame_diff, c.timeline_out);
}
if (panel_timeline->snapped) break;
}
}
}
}
void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
int mouse_track = getTrackFromScreenPoint(mouse_pos.y());
long frame_diff = getFrameFromScreenPoint(mouse_pos.x(), false) - panel_timeline->drag_frame_start;
int track_diff = mouse_track - panel_timeline->drag_track_start;
int snap_range = 15;
long validator;
if (panel_timeline->trim_target > -1) {
// trim ops
@@ -378,6 +412,8 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
}
}
}
validate_snapping(g, &frame_diff);
}
// resize ghosts
@@ -422,46 +458,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
}
}
// shitty hacky code but works wonders
panel_timeline->snapped = false;
if (panel_timeline->snapping) {
for (int j=0;j<panel_timeline->sequence->clip_count();j++) {
Clip& c = panel_timeline->sequence->get_clip(j);
panel_timeline->snapped = false;
for (int k=0;k<4;k++) {
switch (k) {
case 0:
// snap in to timeline_in
validator = g.old_in + frame_diff - c.timeline_in;
panel_timeline->snap_point = c.timeline_in;
break;
case 1:
// snap in to timeline out
validator = g.old_in + frame_diff - c.timeline_out;
panel_timeline->snap_point = c.timeline_out;
break;
case 2:
// snap out to timeline_in
validator = g.old_out + frame_diff - c.timeline_in;
panel_timeline->snap_point = c.timeline_in;
break;
case 3:
// snap out to timeline_out
validator = g.old_out + frame_diff - c.timeline_out;
panel_timeline->snap_point = c.timeline_out;
break;
}
if (validator > -snap_range && validator < snap_range) {
frame_diff -= validator;
panel_timeline->snapped = true;
break;
}
}
if (panel_timeline->snapped) break;
}
}
validate_snapping(g, &frame_diff);
}
// move ghosts
@@ -756,6 +753,15 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
}
}
// Draw playhead
p.setPen(Qt::red);
int playhead_x = getScreenPointFromFrame(panel_timeline->playhead);
p.drawLine(playhead_x, rect().top(), playhead_x, rect().bottom());
p.setPen(QColor(0, 0, 0, 64));
int edge_y = (bottom_align) ? rect().height()-1 : 0;
p.drawLine(0, edge_y, rect().width(), edge_y);
// draw snap point
if (panel_timeline->snapping && panel_timeline->snapped) {
p.setPen(Qt::white);
@@ -774,16 +780,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
p.setPen(Qt::gray);
p.drawLine(cursor_x, cursor_y, cursor_x, cursor_y + track_height);
}
}
// Draw playhead
p.setPen(Qt::red);
int playhead_x = getScreenPointFromFrame(panel_timeline->playhead);
p.drawLine(playhead_x, rect().top(), playhead_x, rect().bottom());
p.setPen(QColor(0, 0, 0, 64));
int edge_y = (bottom_align) ? rect().height()-1 : 0;
p.drawLine(0, edge_y, rect().width(), edge_y);
}
}
}
+21 -15
View File
@@ -18,6 +18,10 @@ extern "C" {
ViewerWidget::ViewerWidget(QWidget *parent) : QOpenGLWidget(parent)
{
multithreaded = true;
enable_paint = true;
force_audio = false;
audio_bytes_written = 0;
QSurfaceFormat format;
format.setDepthBufferSize(24);
@@ -46,7 +50,9 @@ void ViewerWidget::initializeGL() {
//{
//}
int audio_bytes_written = 0;
void ViewerWidget::paintEvent(QPaintEvent *e) {
if (enable_paint) QOpenGLWidget::paintEvent(e);
}
void ViewerWidget::paintGL()
{
@@ -59,14 +65,19 @@ void ViewerWidget::paintGL()
handle_media(panel_viewer->sequence, playhead, multithreaded);
texture_failed = false;
bool render_audio = (panel_timeline->playing);
bool render_audio = (panel_timeline->playing || force_audio);
if (render_audio && switch_audio_cache) {
reading_audio_cache_A = !reading_audio_cache_A;
audio_bytes_written = 0;
clear_cache(!reading_audio_cache_A, reading_audio_cache_A);
// switch_cache
if (render_audio && audio_bytes_written == audio_cache_size) {
switch_audio_cache = true;
reading_audio_cache_A = !reading_audio_cache_A;
audio_bytes_written = 0;
clear_cache(!reading_audio_cache_A, reading_audio_cache_A);
}
qDebug() << audio_bytes_written << "/" << audio_cache_size;
cc_lock.lock();
for (int i=0;i<current_clips.size();i++) {
Clip* c = current_clips.at(i);
@@ -92,7 +103,7 @@ void ViewerWidget::paintGL()
// perform all transform effects
for (unsigned int j=0;j<c->effects.size();j++) {
c->effects.at(j)->process_gl(&anchor_x, &anchor_y);
c->effects.at(j)->process_gl(&anchor_x, &anchor_y);
}
int anchor_right = c->media_stream->video_width - anchor_x;
@@ -118,7 +129,7 @@ void ViewerWidget::paintGL()
render_audio &&
!c->reached_end &&
(switch_audio_cache || c->reset_audio)) {
// TODO doesn't new clips appearing in the timeline (they'll ALWAYS end up in the other cache with a 1/8 sec delay)
// TODO doesn't affect new clips appearing in the timeline (they'll ALWAYS end up in the other cache with a 1/8 sec delay)
// cache audio
if (c->lock.tryLock()) {
@@ -136,13 +147,8 @@ void ViewerWidget::paintGL()
if (audio_cache_A != NULL && audio_bytes_written < audio_cache_size) {
// send cached/buffered audio to QIODevice/QAudioOutput
uint8_t* cache = reading_audio_cache_A ? audio_cache_A : audio_cache_B;
audio_bytes_written += audio_io_device->write((const char*) cache+audio_bytes_written, audio_cache_size-audio_bytes_written);
}
if (audio_bytes_written == audio_cache_size) {
// switch_cache
switch_audio_cache = true;
}
if (panel_timeline->playing) audio_bytes_written += audio_io_device->write((const char*) cache+audio_bytes_written, audio_cache_size-audio_bytes_written);
}
}
cc_lock.unlock();
+5 -1
View File
@@ -18,7 +18,11 @@ public:
// void resizeGL(int w, int h);
void paintGL();
bool multithreaded;
bool multithreaded;
bool force_audio;
bool enable_paint;
protected:
void paintEvent(QPaintEvent *e) override;
private:
QTimer retry_timer;
private slots: