many changes, mostly to export
This commit is contained in:
@@ -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,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
@@ -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">
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user