fixed various audio issues
This commit is contained in:
@@ -247,6 +247,22 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>formatCombobox</tabstop>
|
||||||
|
<tabstop>videoGroupbox</tabstop>
|
||||||
|
<tabstop>vcodecCombobox</tabstop>
|
||||||
|
<tabstop>widthSpinbox</tabstop>
|
||||||
|
<tabstop>heightSpinbox</tabstop>
|
||||||
|
<tabstop>framerateSpinbox</tabstop>
|
||||||
|
<tabstop>videobitrateSpinbox</tabstop>
|
||||||
|
<tabstop>audioGroupbox</tabstop>
|
||||||
|
<tabstop>acodecCombobox</tabstop>
|
||||||
|
<tabstop>samplingRateSpinbox</tabstop>
|
||||||
|
<tabstop>audiobitrateSpinbox</tabstop>
|
||||||
|
<tabstop>renderCancel</tabstop>
|
||||||
|
<tabstop>pushButton</tabstop>
|
||||||
|
<tabstop>pushButton_2</tabstop>
|
||||||
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|||||||
@@ -322,6 +322,16 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>comboBox</tabstop>
|
||||||
|
<tabstop>width_numeric</tabstop>
|
||||||
|
<tabstop>height_numeric</tabstop>
|
||||||
|
<tabstop>frame_rate_combobox</tabstop>
|
||||||
|
<tabstop>par_combobox</tabstop>
|
||||||
|
<tabstop>interlacing_combobox</tabstop>
|
||||||
|
<tabstop>audio_frequency_combobox</tabstop>
|
||||||
|
<tabstop>lineEdit</tabstop>
|
||||||
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ VolumeEffect::VolumeEffect(Clip* c) : Effect(c) {
|
|||||||
ui_layout->addWidget(new QLabel("Volume:"), 0, 0);
|
ui_layout->addWidget(new QLabel("Volume:"), 0, 0);
|
||||||
volume_val = new QSpinBox();
|
volume_val = new QSpinBox();
|
||||||
volume_val->setMinimum(0);
|
volume_val->setMinimum(0);
|
||||||
volume_val->setMaximum(100);
|
volume_val->setMaximum(400);
|
||||||
ui_layout->addWidget(volume_val, 0, 1);
|
ui_layout->addWidget(volume_val, 0, 1);
|
||||||
|
|
||||||
ui->setLayout(ui_layout);
|
ui->setLayout(ui_layout);
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ void load_config() {
|
|||||||
#endif
|
#endif
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
padding *= scale;
|
// padding *= scale;
|
||||||
|
|
||||||
/*DIR* dir = opendir("conf");
|
/*DIR* dir = opendir("conf");
|
||||||
if (dir) {
|
if (dir) {
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ extern const char* version;
|
|||||||
extern float scale;
|
extern float scale;
|
||||||
extern bool vsync;
|
extern bool vsync;
|
||||||
extern bool hwaccel;
|
extern bool hwaccel;
|
||||||
extern int padding;
|
//extern int padding;
|
||||||
extern bool custom_scale;
|
extern bool custom_scale;
|
||||||
extern bool saved_layout;
|
extern bool saved_layout;
|
||||||
extern bool show_track_lines;
|
extern bool show_track_lines;
|
||||||
|
|||||||
+29
-31
@@ -110,11 +110,11 @@ void ExportThread::run() {
|
|||||||
qDebug() << "[ERROR] Could not find allocate video encoding context";
|
qDebug() << "[ERROR] Could not find allocate video encoding context";
|
||||||
fail = true;
|
fail = true;
|
||||||
} else {
|
} else {
|
||||||
vcodec_ctx->width = sequence->width;
|
vcodec_ctx->width = video_width;
|
||||||
vcodec_ctx->height = sequence->height;
|
vcodec_ctx->height = video_height;
|
||||||
vcodec_ctx->sample_aspect_ratio = av_d2q(sequence->width/sequence->height, INT_MAX);
|
vcodec_ctx->sample_aspect_ratio = av_d2q(video_width/video_height, INT_MAX);
|
||||||
vcodec_ctx->pix_fmt = vcodec->pix_fmts[0]; // maybe be breakable code
|
vcodec_ctx->pix_fmt = vcodec->pix_fmts[0]; // maybe be breakable code
|
||||||
vcodec_ctx->framerate = av_d2q(sequence->frame_rate, INT_MAX);
|
vcodec_ctx->framerate = av_d2q(video_frame_rate, INT_MAX);
|
||||||
vcodec_ctx->bit_rate = video_bitrate * 1000000;
|
vcodec_ctx->bit_rate = video_bitrate * 1000000;
|
||||||
vcodec_ctx->time_base = av_inv_q(vcodec_ctx->framerate);
|
vcodec_ctx->time_base = av_inv_q(vcodec_ctx->framerate);
|
||||||
|
|
||||||
@@ -266,15 +266,7 @@ void ExportThread::run() {
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qDebug() << "[ERROR] Could not write output file header." << ret;
|
qDebug() << "[ERROR] Could not write output file header." << ret;
|
||||||
} else {
|
} else {
|
||||||
panel_timeline->seek(0);
|
panel_timeline->seek(start);
|
||||||
|
|
||||||
// clean up - close all open clips
|
|
||||||
for (int i=0;i<sequence->clip_count();i++) {
|
|
||||||
Clip& c = sequence->get_clip(i);
|
|
||||||
if (c.open) {
|
|
||||||
close_clip(&c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QOpenGLFramebufferObject fbo(video_width, video_height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
|
QOpenGLFramebufferObject fbo(video_width, video_height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
|
||||||
fbo.bind();
|
fbo.bind();
|
||||||
@@ -282,7 +274,6 @@ void ExportThread::run() {
|
|||||||
QPainter painter(&fbo_dev);
|
QPainter painter(&fbo_dev);
|
||||||
painter.beginNativePainting();
|
painter.beginNativePainting();
|
||||||
|
|
||||||
int written_frame_audio_bytes = 0;
|
|
||||||
long file_audio_samples = 0;
|
long file_audio_samples = 0;
|
||||||
|
|
||||||
while (panel_timeline->playhead < end && !fail) {
|
while (panel_timeline->playhead < end && !fail) {
|
||||||
@@ -301,8 +292,9 @@ void ExportThread::run() {
|
|||||||
memcpy(video_frame->data[0]+dst_index, flip_buffer+src_index, linesize);
|
memcpy(video_frame->data[0]+dst_index, flip_buffer+src_index, linesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// change pixel format
|
// change pixel format
|
||||||
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
|
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
|
||||||
|
// qDebug() << video_frame->linesize[0] << sws_frame->linesize[0];
|
||||||
sws_frame->pts = round(timecode_secs/av_q2d(video_stream->time_base));
|
sws_frame->pts = round(timecode_secs/av_q2d(video_stream->time_base));
|
||||||
|
|
||||||
// send to encoder
|
// send to encoder
|
||||||
@@ -310,23 +302,29 @@ void ExportThread::run() {
|
|||||||
}
|
}
|
||||||
if (audio_enabled && !fail) {
|
if (audio_enabled && !fail) {
|
||||||
// do we need to encode more audio samples?
|
// do we need to encode more audio samples?
|
||||||
if (file_audio_samples <= (timecode_secs*audio_sampling_rate)) {
|
while (file_audio_samples <= (timecode_secs*audio_sampling_rate)) {
|
||||||
uint8_t* cache = (reading_audio_cache_A) ? audio_cache_A : audio_cache_B;
|
int adjusted_read = audio_ibuffer_read%audio_ibuffer_size;
|
||||||
int copylen = qMin(aframe_bytes-written_frame_audio_bytes, audio_cache_size-audio_bytes_written);
|
int copylen = qMin(aframe_bytes, audio_ibuffer_size-adjusted_read);
|
||||||
memcpy(audio_frame->data[0]+written_frame_audio_bytes, cache+audio_bytes_written, copylen);
|
memcpy(audio_frame->data[0], audio_ibuffer+adjusted_read, copylen);
|
||||||
written_frame_audio_bytes += copylen;
|
memset(audio_ibuffer+adjusted_read, 0, copylen);
|
||||||
audio_bytes_written += copylen;
|
audio_ibuffer_read += 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 (copylen < aframe_bytes) {
|
||||||
if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream)) fail = true;
|
// copy remainder
|
||||||
|
int remainder_len = aframe_bytes-copylen;
|
||||||
file_audio_samples += swr_frame->nb_samples;
|
memcpy(audio_frame->data[0]+copylen, audio_ibuffer, remainder_len);
|
||||||
written_frame_audio_bytes = 0;
|
memset(audio_ibuffer, 0, remainder_len);
|
||||||
|
audio_ibuffer_read += remainder_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit progress_changed(((float) panel_timeline->playhead / (float) end) * 100);
|
emit progress_changed(((float) panel_timeline->playhead / (float) end) * 100);
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ HEADERS += \
|
|||||||
playback/audio.h \
|
playback/audio.h \
|
||||||
effects/effects.h \
|
effects/effects.h \
|
||||||
io/config.h \
|
io/config.h \
|
||||||
ui/timeline-tools.h \
|
|
||||||
dialogs/newsequencedialog.h \
|
dialogs/newsequencedialog.h \
|
||||||
ui/viewerwidget.h \
|
ui/viewerwidget.h \
|
||||||
ui/viewercontainer.h \
|
ui/viewercontainer.h \
|
||||||
@@ -78,7 +77,8 @@ HEADERS += \
|
|||||||
project/effect.h \
|
project/effect.h \
|
||||||
panels/panels.h \
|
panels/panels.h \
|
||||||
playback/cacher.h \
|
playback/cacher.h \
|
||||||
io/exportthread.h
|
io/exportthread.h \
|
||||||
|
ui/timelinetools.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
mainwindow.ui \
|
mainwindow.ui \
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProject>
|
<!DOCTYPE QtCreatorProject>
|
||||||
<!-- Written by QtCreator 4.6.1, 2018-06-06T05:46:13. -->
|
<!-- Written by QtCreator 4.6.1, 2018-06-06T18:33:45. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>EnvironmentId</variable>
|
<variable>EnvironmentId</variable>
|
||||||
|
|||||||
+4
-9
@@ -69,11 +69,10 @@ void Timeline::seek(long p) {
|
|||||||
Clip& c = sequence->get_clip(i);
|
Clip& c = sequence->get_clip(i);
|
||||||
c.reset_audio = true;
|
c.reset_audio = true;
|
||||||
c.frame_sample_index = 0;
|
c.frame_sample_index = 0;
|
||||||
|
c.audio_buffer_write = 0;
|
||||||
}
|
}
|
||||||
clear_cache(true, true);
|
clear_audio_ibuffer();
|
||||||
audio_bytes_written = 0;
|
audio_ibuffer_read = 0;
|
||||||
switch_audio_cache = true;
|
|
||||||
reading_audio_cache_A = false;
|
|
||||||
|
|
||||||
playhead = p;
|
playhead = p;
|
||||||
}
|
}
|
||||||
@@ -92,11 +91,7 @@ void Timeline::play() {
|
|||||||
playback_timer.start();
|
playback_timer.start();
|
||||||
start_msecs = QDateTime::currentMSecsSinceEpoch();
|
start_msecs = QDateTime::currentMSecsSinceEpoch();
|
||||||
playback_updater.start();
|
playback_updater.start();
|
||||||
playing = true;
|
playing = true;
|
||||||
|
|
||||||
if (switch_audio_cache) {
|
|
||||||
// add something to pre-cache audio
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::pause() {
|
void Timeline::pause() {
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
#ifndef TIMELINE_H
|
#ifndef TIMELINE_H
|
||||||
#define TIMELINE_H
|
#define TIMELINE_H
|
||||||
|
|
||||||
#include "ui/timeline-tools.h"
|
#include "ui/timelinetools.h"
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
|||||||
+3
-8
@@ -29,10 +29,6 @@ Viewer::~Viewer()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::receiver() {
|
|
||||||
qDebug() << "callback?";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::set_sequence(Sequence* s) {
|
void Viewer::set_sequence(Sequence* s) {
|
||||||
bool null_sequence = (s == NULL);
|
bool null_sequence = (s == NULL);
|
||||||
sequence = s;
|
sequence = s;
|
||||||
@@ -45,14 +41,13 @@ void Viewer::set_sequence(Sequence* s) {
|
|||||||
ui->pushButton_4->setEnabled(!null_sequence);
|
ui->pushButton_4->setEnabled(!null_sequence);
|
||||||
ui->pushButton_5->setEnabled(!null_sequence);
|
ui->pushButton_5->setEnabled(!null_sequence);
|
||||||
|
|
||||||
|
init_audio(s);
|
||||||
|
|
||||||
if (!null_sequence) {
|
if (!null_sequence) {
|
||||||
ui->glViewerPane->aspect_ratio = (float) sequence->width / (float) sequence->height;
|
ui->glViewerPane->aspect_ratio = (float) sequence->width / (float) sequence->height;
|
||||||
ui->glViewerPane->adjust();
|
ui->glViewerPane->adjust();
|
||||||
}
|
}
|
||||||
|
|
||||||
init_audio(s);
|
|
||||||
connect(audio_output, SIGNAL(notify()), this, SLOT(receiver()));
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -33,9 +33,7 @@ private slots:
|
|||||||
|
|
||||||
void on_pushButton_4_clicked();
|
void on_pushButton_4_clicked();
|
||||||
|
|
||||||
void on_pushButton_3_clicked();
|
void on_pushButton_3_clicked();
|
||||||
|
|
||||||
void receiver();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::Viewer *ui;
|
Ui::Viewer *ui;
|
||||||
|
|||||||
+6
-27
@@ -13,24 +13,11 @@ QAudioOutput* audio_output;
|
|||||||
QIODevice* audio_io_device;
|
QIODevice* audio_io_device;
|
||||||
bool audio_device_set = false;
|
bool audio_device_set = false;
|
||||||
|
|
||||||
uint8_t* audio_cache_A = NULL;
|
qint8 audio_ibuffer[audio_ibuffer_size];
|
||||||
uint8_t* audio_cache_B = NULL;
|
int audio_ibuffer_read = 0;
|
||||||
int audio_cache_size = 0;
|
QVector<int> audio_ibuffer_write;
|
||||||
bool switch_audio_cache = true;
|
|
||||||
bool reading_audio_cache_A = false;
|
|
||||||
int audio_bytes_written = 0;
|
|
||||||
|
|
||||||
void init_audio(Sequence* s) {
|
void init_audio(Sequence* s) {
|
||||||
if (audio_cache_A != NULL) {
|
|
||||||
delete [] audio_cache_A;
|
|
||||||
audio_cache_A = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audio_cache_B != NULL) {
|
|
||||||
delete [] audio_cache_B;
|
|
||||||
audio_cache_B = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audio_device_set) {
|
if (audio_device_set) {
|
||||||
audio_output->stop();
|
audio_output->stop();
|
||||||
delete audio_output;
|
delete audio_output;
|
||||||
@@ -55,19 +42,11 @@ void init_audio(Sequence* s) {
|
|||||||
audio_io_device = audio_output->start();
|
audio_io_device = audio_output->start();
|
||||||
audio_device_set = true;
|
audio_device_set = true;
|
||||||
|
|
||||||
audio_cache_size = av_samples_get_buffer_size(NULL, av_get_channel_layout_nb_channels(s->audio_layout), s->audio_frequency/8, AV_SAMPLE_FMT_S16, 1);
|
clear_audio_ibuffer();
|
||||||
audio_cache_A = new uint8_t[audio_cache_size];
|
|
||||||
audio_cache_B = new uint8_t[audio_cache_size];
|
|
||||||
clear_cache(true, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_cache(bool clear_A, bool clear_B) {
|
void clear_audio_ibuffer() {
|
||||||
if (clear_A) {
|
memset(audio_ibuffer, 0, audio_ibuffer_size);
|
||||||
memset(audio_cache_A, 0, audio_cache_size);
|
|
||||||
}
|
|
||||||
if (clear_B) {
|
|
||||||
memset(audio_cache_B, 0, audio_cache_size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-8
@@ -1,7 +1,7 @@
|
|||||||
#ifndef AUDIO_H
|
#ifndef AUDIO_H
|
||||||
#define AUDIO_H
|
#define AUDIO_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <QVector>
|
||||||
|
|
||||||
class QAudioOutput;
|
class QAudioOutput;
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
@@ -11,13 +11,12 @@ struct Sequence;
|
|||||||
extern QAudioOutput* audio_output;
|
extern QAudioOutput* audio_output;
|
||||||
extern QIODevice* audio_io_device;
|
extern QIODevice* audio_io_device;
|
||||||
|
|
||||||
extern uint8_t* audio_cache_A;
|
#define audio_ibuffer_size 192000
|
||||||
extern uint8_t* audio_cache_B;
|
extern qint8 audio_ibuffer[audio_ibuffer_size];
|
||||||
extern int audio_cache_size;
|
extern int audio_ibuffer_read;
|
||||||
extern int audio_bytes_written;
|
//extern QVector<int> audio_ibuffer_write;
|
||||||
extern bool switch_audio_cache;
|
void clear_audio_ibuffer();
|
||||||
extern bool reading_audio_cache_A;
|
|
||||||
void init_audio(Sequence* s);
|
void init_audio(Sequence* s);
|
||||||
void clear_cache(bool clear_A, bool clear_B);
|
|
||||||
|
|
||||||
#endif // AUDIO_H
|
#endif // AUDIO_H
|
||||||
|
|||||||
+61
-45
@@ -16,46 +16,67 @@ extern "C" {
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
void cache_audio_worker(Clip* c, bool write_A) {
|
void cache_audio_worker(Clip* c) {
|
||||||
// gets one frame worth of audio and sends it to the audio buffer
|
int written = 0;
|
||||||
AVFrame* frame = c->cache_A.frames[0];
|
int max_write = 16384;
|
||||||
uint8_t* cache = (write_A) ? audio_cache_A : audio_cache_B;
|
while (written < max_write) {
|
||||||
|
// gets one frame worth of audio and sends it to the audio buffer
|
||||||
|
AVFrame* frame = c->cache_A.frames[0];
|
||||||
|
|
||||||
int bytes_written = 0;
|
if (c->frame_sample_index == 0) {
|
||||||
int j = 0;
|
// no more audio left in frame, get a new one
|
||||||
while (bytes_written < audio_cache_size && !c->reached_end) {
|
if (!c->reached_end) {
|
||||||
// is there audio left in the frame
|
retrieve_next_frame_raw_data(c, frame);
|
||||||
if (c->frame_sample_index == 0) {
|
} else {
|
||||||
// no more audio left in frame, get a new one
|
// set by retrieve_next_frame_raw_data indicating no more frames in file,
|
||||||
retrieve_next_frame_raw_data(c, frame);
|
// but there still may be samples in swresample
|
||||||
}
|
swr_convert_frame(c->swr_ctx, frame, NULL);
|
||||||
if (!c->reached_end) {
|
}
|
||||||
int nb_bytes = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, static_cast<AVSampleFormat>(frame->format), 1);
|
}
|
||||||
int limit = std::min(c->frame_sample_index + audio_cache_size - bytes_written, nb_bytes);
|
|
||||||
// perform all audio effects
|
if (frame->nb_samples == 0) {
|
||||||
|
written = max_write;
|
||||||
|
} else {
|
||||||
|
int nb_bytes = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, static_cast<AVSampleFormat>(frame->format), 1);
|
||||||
|
|
||||||
|
// perform all audio effects
|
||||||
for (int j=0;j<c->effects.size();j++) {
|
for (int j=0;j<c->effects.size();j++) {
|
||||||
c->effects.at(j)->process_audio(frame->data[0], limit);
|
c->effects.at(j)->process_audio(frame->data[0], nb_bytes);
|
||||||
}
|
}
|
||||||
// mix audio into cache
|
|
||||||
for (int i=c->frame_sample_index;i<limit;i++) {
|
if (c->audio_buffer_write == 0) c->audio_buffer_write = audio_ibuffer_read + 1024;
|
||||||
cache[j] += frame->data[0][i];
|
while (c->frame_sample_index < nb_bytes) {
|
||||||
j++;
|
if (c->audio_buffer_write >= audio_ibuffer_read+(audio_ibuffer_size/2)) {
|
||||||
}
|
written = max_write;
|
||||||
bytes_written += limit - c->frame_sample_index;
|
break;
|
||||||
c->frame_sample_index = (limit == nb_bytes) ? 0 : limit;
|
} else {
|
||||||
}
|
audio_ibuffer[c->audio_buffer_write%audio_ibuffer_size] += frame->data[0][c->frame_sample_index];
|
||||||
}
|
c->audio_buffer_write++;
|
||||||
|
c->frame_sample_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
written += c->frame_sample_index;
|
||||||
|
if (c->frame_sample_index == nb_bytes) {
|
||||||
|
c->frame_sample_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||||
cache->mutex.lock();
|
cache->mutex.lock();
|
||||||
|
|
||||||
cache->offset = playhead;
|
cache->offset = playhead;
|
||||||
for (size_t i=0;i<c->cache_size;i++) {
|
if (!c->reached_end) {
|
||||||
retrieve_next_frame_raw_data(c, cache->frames[i]);
|
for (size_t i=0;i<c->cache_size;i++) {
|
||||||
}
|
retrieve_next_frame_raw_data(c, cache->frames[i]);
|
||||||
cache->written = true;
|
if (c->reached_end) break;
|
||||||
cache->unread = true;
|
}
|
||||||
|
}
|
||||||
|
cache->written = true;
|
||||||
|
cache->unread = true;
|
||||||
|
// setting the cache to written even if it hasn't reached_end prevents playback from
|
||||||
|
// signaling a seek/reset because it wasn't able to find the frame
|
||||||
|
|
||||||
cache->mutex.unlock();
|
cache->mutex.unlock();
|
||||||
}
|
}
|
||||||
@@ -81,7 +102,7 @@ void reset_cache(Clip* c, long target_frame) {
|
|||||||
// play up to the frame we actually want
|
// play up to the frame we actually want
|
||||||
long retrieved_frame = 0;
|
long retrieved_frame = 0;
|
||||||
AVFrame* temp = av_frame_alloc();
|
AVFrame* temp = av_frame_alloc();
|
||||||
do {
|
do {
|
||||||
retrieve_next_frame(c, temp);
|
retrieve_next_frame(c, temp);
|
||||||
if (retrieved_frame == 0) {
|
if (retrieved_frame == 0) {
|
||||||
if (target_frame != 0) {
|
if (target_frame != 0) {
|
||||||
@@ -223,7 +244,7 @@ void open_clip_worker(Clip* clip) {
|
|||||||
clip->sequence->audio_layout,
|
clip->sequence->audio_layout,
|
||||||
static_cast<AVSampleFormat>(sample_format),
|
static_cast<AVSampleFormat>(sample_format),
|
||||||
clip->sequence->audio_frequency,
|
clip->sequence->audio_frequency,
|
||||||
clip->stream->codecpar->channel_layout,
|
clip->codecCtx->channel_layout,
|
||||||
static_cast<AVSampleFormat>(clip->stream->codecpar->format),
|
static_cast<AVSampleFormat>(clip->stream->codecpar->format),
|
||||||
clip->stream->codecpar->sample_rate,
|
clip->stream->codecpar->sample_rate,
|
||||||
0,
|
0,
|
||||||
@@ -266,13 +287,8 @@ void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bo
|
|||||||
if (write_B) {
|
if (write_B) {
|
||||||
cache_video_worker(clip, playhead, &clip->cache_B);
|
cache_video_worker(clip, playhead, &clip->cache_B);
|
||||||
}
|
}
|
||||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||||
if (write_A) {
|
cache_audio_worker(clip);
|
||||||
cache_audio_worker(clip, true);
|
|
||||||
}
|
|
||||||
if (write_B) {
|
|
||||||
cache_audio_worker(clip, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,8 +339,8 @@ void Cacher::run() {
|
|||||||
|
|
||||||
caching = true;
|
caching = true;
|
||||||
while (true) {
|
while (true) {
|
||||||
clip->can_cache.wait(&clip->lock);
|
clip->can_cache.wait(&clip->lock);
|
||||||
if (!caching) {
|
if (!caching) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
cache_clip_worker(clip, playhead, write_A, write_B, reset);
|
cache_clip_worker(clip, playhead, write_A, write_B, reset);
|
||||||
|
|||||||
+25
-24
@@ -34,7 +34,7 @@ void handle_media(Sequence* sequence, long playhead, bool multithreaded) {
|
|||||||
// if thread is already working, we don't want to touch this,
|
// if thread is already working, we don't want to touch this,
|
||||||
// but we also don't want to hang the UI thread
|
// but we also don't want to hang the UI thread
|
||||||
if (!c.open) {
|
if (!c.open) {
|
||||||
if (c.lock.tryLock()) {
|
if (c.lock.tryLock()) {
|
||||||
open_clip(&c, multithreaded);
|
open_clip(&c, multithreaded);
|
||||||
|
|
||||||
// add to current_clips, (insertion) sorted by track so composite them in order
|
// add to current_clips, (insertion) sorted by track so composite them in order
|
||||||
@@ -229,13 +229,10 @@ float clip_frame_to_seconds(Clip* c, long clip_frame) {
|
|||||||
|
|
||||||
int retrieve_next_frame(Clip* c, AVFrame* f) {
|
int retrieve_next_frame(Clip* c, AVFrame* f) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int receive_ret;
|
int receive_ret;
|
||||||
|
|
||||||
// could be optimized if "linked stream" shares timeline_in and clip_in, only use one read_frame?
|
|
||||||
// depends how much time av_read_frame needs, the decoding is probably far more consuming anyway
|
|
||||||
|
|
||||||
// do we need to retrieve a new packet for a new frame?
|
// do we need to retrieve a new packet for a new frame?
|
||||||
while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) {
|
while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) {
|
||||||
int read_ret = 0;
|
int read_ret = 0;
|
||||||
do {
|
do {
|
||||||
if (c->pkt_written) {
|
if (c->pkt_written) {
|
||||||
@@ -249,10 +246,9 @@ int retrieve_next_frame(Clip* c, AVFrame* f) {
|
|||||||
int send_ret = avcodec_send_packet(c->codecCtx, c->pkt);
|
int send_ret = avcodec_send_packet(c->codecCtx, c->pkt);
|
||||||
if (send_ret < 0) {
|
if (send_ret < 0) {
|
||||||
qDebug() << "[ERROR] Failed to send packet to decoder." << send_ret;
|
qDebug() << "[ERROR] Failed to send packet to decoder." << send_ret;
|
||||||
result = send_ret;
|
return send_ret;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (read_ret != AVERROR_EOF) qDebug() << "[ERROR] Could not read frame." << read_ret;
|
if (read_ret != AVERROR_EOF) qDebug() << "[ERROR] Could not read frame." << read_ret;
|
||||||
return read_ret; // skips trying to find a frame at all
|
return read_ret; // skips trying to find a frame at all
|
||||||
}
|
}
|
||||||
@@ -266,21 +262,26 @@ int retrieve_next_frame(Clip* c, AVFrame* f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void retrieve_next_frame_raw_data(Clip* c, AVFrame* output) {
|
void retrieve_next_frame_raw_data(Clip* c, AVFrame* output) {
|
||||||
int ret = retrieve_next_frame(c, c->frame);
|
if (c->reached_end) {
|
||||||
if (ret >= 0) {
|
qDebug() << "[WARNING] Attempted to retrieve frame of stream with no frames left";
|
||||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
} else {
|
||||||
sws_scale(c->sws_ctx, c->frame->data, c->frame->linesize, 0, c->stream->codecpar->height, output->data, output->linesize);
|
int ret = retrieve_next_frame(c, c->frame);
|
||||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
if (ret >= 0) {
|
||||||
ret = swr_convert_frame(c->swr_ctx, output, c->frame);
|
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||||
if (ret < 0) {
|
sws_scale(c->sws_ctx, c->frame->data, c->frame->linesize, 0, c->stream->codecpar->height, output->data, output->linesize);
|
||||||
qDebug() << "[ERROR] Failed to resample audio." << ret;
|
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||||
}
|
ret = swr_convert_frame(c->swr_ctx, output, c->frame);
|
||||||
}
|
if (ret < 0) {
|
||||||
} else if (ret == AVERROR_EOF) {
|
qDebug() << "[ERROR] Failed to resample audio." << ret;
|
||||||
c->reached_end = true;
|
}
|
||||||
} else {
|
}
|
||||||
qDebug() << "[WARNING] Raw frame data could not be retrieved." << ret;
|
} else if (ret == AVERROR_EOF) {
|
||||||
}
|
// qDebug() << "reached end triggered" << c->track;
|
||||||
|
c->reached_end = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "[WARNING] Raw frame data could not be retrieved." << ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_clip_active(Clip* c, long playhead) {
|
bool is_clip_active(Clip* c, long playhead) {
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ void Clip::init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Clip::reset() {
|
void Clip::reset() {
|
||||||
cache_size = cache_A.offset = cache_B.offset = open = pkt_written = cache_A.written = cache_B.written = cache_A.unread = cache_B.unread = reached_end = reset_audio = frame_sample_index = 0;
|
cache_size = cache_A.offset = cache_B.offset = open = pkt_written = cache_A.written = cache_B.written = cache_A.unread = cache_B.unread = reached_end = reset_audio = frame_sample_index = audio_buffer_write = 0;
|
||||||
texture_frame = -1;
|
texture_frame = -1;
|
||||||
formatCtx = NULL;
|
formatCtx = NULL;
|
||||||
stream = NULL;
|
stream = NULL;
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ struct Clip
|
|||||||
// audio playback variables
|
// audio playback variables
|
||||||
SwrContext* swr_ctx;
|
SwrContext* swr_ctx;
|
||||||
int frame_sample_index;
|
int frame_sample_index;
|
||||||
|
int audio_buffer_write;
|
||||||
bool reset_audio;
|
bool reset_audio;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -21,5 +21,5 @@ Effect* Effect::copy() {
|
|||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Effect::process_gl(int* anchor_x, int* anchor_y) {}
|
void Effect::process_gl(int*, int*) {}
|
||||||
void Effect::process_audio(uint8_t* samples, int nb_bytes) {}
|
void Effect::process_audio(uint8_t*, int) {}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
#ifndef TIMELINETOOLS_H
|
|
||||||
#define TIMELINETOOLS_H
|
|
||||||
|
|
||||||
enum TimelineTools {
|
|
||||||
TIMELINE_TOOL_POINTER,
|
|
||||||
TIMELINE_TOOL_EDIT,
|
|
||||||
TIMELINE_TOOL_RAZOR,
|
|
||||||
TIMELINE_TOOL_RIPPLE,
|
|
||||||
TIMELINE_TOOL_ROLLING,
|
|
||||||
TIMELINE_TOOL_SLIP,
|
|
||||||
TIMELINE_TOOL_HAND,
|
|
||||||
TIMELINE_TOOL_ZOOM
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // TIMELINETOOLS_H
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef TIMELINETOOLS_H
|
||||||
|
#define TIMELINETOOLS_H
|
||||||
|
|
||||||
|
#define TIMELINE_TOOL_POINTER 0
|
||||||
|
#define TIMELINE_TOOL_EDIT 1
|
||||||
|
#define TIMELINE_TOOL_RAZOR 2
|
||||||
|
#define TIMELINE_TOOL_RIPPLE 3
|
||||||
|
#define TIMELINE_TOOL_ROLLING 4
|
||||||
|
#define TIMELINE_TOOL_SLIP 5
|
||||||
|
#define TIMELINE_TOOL_HAND 6
|
||||||
|
#define TIMELINE_TOOL_ZOOM 7
|
||||||
|
|
||||||
|
#endif // TIMELINETOOLS_H
|
||||||
+11
-12
@@ -24,9 +24,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent)
|
|||||||
{
|
{
|
||||||
bottom_align = false;
|
bottom_align = false;
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
track_height = 40;
|
track_height = 40;
|
||||||
|
|
||||||
clip_pixmap = NULL;
|
|
||||||
|
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
}
|
}
|
||||||
@@ -35,6 +33,10 @@ bool same_sign(int a, int b) {
|
|||||||
return (a < 0) == (b < 0);
|
return (a < 0) == (b < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TimelineWidget::resizeEvent(QResizeEvent*) {
|
||||||
|
if (panel_timeline->sequence != NULL) redraw_clips();
|
||||||
|
}
|
||||||
|
|
||||||
void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||||
if (static_cast<SourceTable*>(event->source()) == panel_project->source_table) {
|
if (static_cast<SourceTable*>(event->source()) == panel_project->source_table) {
|
||||||
QPoint pos = event->pos();
|
QPoint pos = event->pos();
|
||||||
@@ -77,7 +79,7 @@ void TimelineWidget::dragMoveEvent(QDragMoveEvent *event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimelineWidget::dragLeaveEvent(QDragLeaveEvent *event) {
|
void TimelineWidget::dragLeaveEvent(QDragLeaveEvent*) {
|
||||||
if (panel_timeline->importing) {
|
if (panel_timeline->importing) {
|
||||||
panel_timeline->ghosts.clear();
|
panel_timeline->ghosts.clear();
|
||||||
panel_timeline->importing = false;
|
panel_timeline->importing = false;
|
||||||
@@ -324,7 +326,6 @@ bool subvalidate_snapping(Ghost& g, long* frame_diff, long snap_point) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void validate_snapping(Ghost& g, long* frame_diff) {
|
void validate_snapping(Ghost& g, long* frame_diff) {
|
||||||
long validator;
|
|
||||||
panel_timeline->snapped = false;
|
panel_timeline->snapped = false;
|
||||||
if (panel_timeline->snapping) {
|
if (panel_timeline->snapping) {
|
||||||
if (!subvalidate_snapping(g, frame_diff, panel_timeline->playhead)) {
|
if (!subvalidate_snapping(g, frame_diff, panel_timeline->playhead)) {
|
||||||
@@ -659,15 +660,13 @@ int color_brightness(int r, int g, int b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TimelineWidget::redraw_clips() {
|
void TimelineWidget::redraw_clips() {
|
||||||
// Draw clips
|
// Draw clips
|
||||||
if (clip_pixmap != NULL) delete clip_pixmap;
|
|
||||||
|
|
||||||
int panel_width = getScreenPointFromFrame(panel_timeline->sequence->getEndFrame()) + 100;
|
int panel_width = getScreenPointFromFrame(panel_timeline->sequence->getEndFrame()) + 100;
|
||||||
setMinimumWidth(panel_width);
|
setMinimumWidth(panel_width);
|
||||||
|
|
||||||
clip_pixmap = new QPixmap(panel_width, height());
|
clip_pixmap = QPixmap(panel_width, height());
|
||||||
clip_pixmap->fill(Qt::transparent);
|
clip_pixmap.fill(Qt::transparent);
|
||||||
QPainter clip_painter(clip_pixmap);
|
QPainter clip_painter(&clip_pixmap);
|
||||||
int video_track_limit = 0;
|
int video_track_limit = 0;
|
||||||
int audio_track_limit = 0;
|
int audio_track_limit = 0;
|
||||||
for (int i=0;i<panel_timeline->sequence->clip_count();i++) {
|
for (int i=0;i<panel_timeline->sequence->clip_count();i++) {
|
||||||
@@ -726,7 +725,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
|||||||
if (panel_timeline->sequence != NULL) {
|
if (panel_timeline->sequence != NULL) {
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
|
|
||||||
if (clip_pixmap != NULL) p.drawPixmap(0, 0, minimumWidth(), height(), *clip_pixmap);
|
p.drawPixmap(0, 0, minimumWidth(), height(), clip_pixmap);
|
||||||
|
|
||||||
// Draw selections
|
// Draw selections
|
||||||
for (int i=0;i<panel_timeline->selections.size();i++) {
|
for (int i=0;i<panel_timeline->selections.size();i++) {
|
||||||
|
|||||||
+7
-3
@@ -2,7 +2,7 @@
|
|||||||
#define TIMELINEWIDGET_H
|
#define TIMELINEWIDGET_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include "timeline-tools.h"
|
#include "timelinetools.h"
|
||||||
|
|
||||||
#define GHOST_THICKNESS 2 // thiccccc
|
#define GHOST_THICKNESS 2 // thiccccc
|
||||||
#define CLIP_TEXT_PADDING 3
|
#define CLIP_TEXT_PADDING 3
|
||||||
@@ -19,9 +19,10 @@ public:
|
|||||||
|
|
||||||
bool bottom_align;
|
bool bottom_align;
|
||||||
|
|
||||||
void redraw_clips();
|
void redraw_clips();
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent*) override;
|
void paintEvent(QPaintEvent*) override;
|
||||||
|
void resizeEvent(QResizeEvent*) override;
|
||||||
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
@@ -45,12 +46,15 @@ private:
|
|||||||
QList<Clip*> pre_clips;
|
QList<Clip*> pre_clips;
|
||||||
QList<Clip*> post_clips;
|
QList<Clip*> post_clips;
|
||||||
|
|
||||||
QPixmap* clip_pixmap;
|
QPixmap clip_pixmap;
|
||||||
// QPixmap selection_pixmap;
|
// QPixmap selection_pixmap;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TIMELINEWIDGET_H
|
#endif // TIMELINEWIDGET_H
|
||||||
|
|||||||
+21
-42
@@ -17,12 +17,10 @@ extern "C" {
|
|||||||
|
|
||||||
ViewerWidget::ViewerWidget(QWidget *parent) : QOpenGLWidget(parent)
|
ViewerWidget::ViewerWidget(QWidget *parent) : QOpenGLWidget(parent)
|
||||||
{
|
{
|
||||||
multithreaded = true;
|
multithreaded = true;
|
||||||
enable_paint = true;
|
enable_paint = true;
|
||||||
force_audio = false;
|
force_audio = false;
|
||||||
|
|
||||||
audio_bytes_written = 0;
|
|
||||||
|
|
||||||
QSurfaceFormat format;
|
QSurfaceFormat format;
|
||||||
format.setDepthBufferSize(24);
|
format.setDepthBufferSize(24);
|
||||||
setFormat(format);
|
setFormat(format);
|
||||||
@@ -67,17 +65,6 @@ void ViewerWidget::paintGL()
|
|||||||
|
|
||||||
bool render_audio = (panel_timeline->playing || force_audio);
|
bool render_audio = (panel_timeline->playing || force_audio);
|
||||||
|
|
||||||
// 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();
|
cc_lock.lock();
|
||||||
for (int i=0;i<current_clips.size();i++) {
|
for (int i=0;i<current_clips.size();i++) {
|
||||||
Clip* c = current_clips.at(i);
|
Clip* c = current_clips.at(i);
|
||||||
@@ -124,38 +111,30 @@ void ViewerWidget::paintGL()
|
|||||||
|
|
||||||
c->texture->release();
|
c->texture->release();
|
||||||
}
|
}
|
||||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
} else if (render_audio &&
|
||||||
playhead >= c->timeline_in &&
|
c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||||
render_audio &&
|
playhead >= c->timeline_in &&
|
||||||
!c->reached_end &&
|
c->lock.tryLock()) {
|
||||||
(switch_audio_cache || c->reset_audio)) {
|
// clip is not caching, start caching audio
|
||||||
// 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)
|
c->lock.unlock();
|
||||||
|
cache_clip(c, playhead, false, false, c->reset_audio);
|
||||||
// cache audio
|
|
||||||
if (c->lock.tryLock()) {
|
|
||||||
// clip is not caching, start cache
|
|
||||||
c->lock.unlock();
|
|
||||||
|
|
||||||
cache_clip(c, playhead, !reading_audio_cache_A, reading_audio_cache_A, c->reset_audio);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (render_audio) {
|
if (panel_timeline->playing) {
|
||||||
switch_audio_cache = false;
|
int adjusted_read_index = audio_ibuffer_read%audio_ibuffer_size;
|
||||||
|
int max_write = audio_ibuffer_size - adjusted_read_index;
|
||||||
if (audio_cache_A != NULL && audio_bytes_written < audio_cache_size) {
|
int actual_write = audio_io_device->write((const char*) audio_ibuffer+adjusted_read_index, max_write);
|
||||||
// send cached/buffered audio to QIODevice/QAudioOutput
|
memset(audio_ibuffer+adjusted_read_index, 0, actual_write);
|
||||||
uint8_t* cache = reading_audio_cache_A ? audio_cache_A : audio_cache_B;
|
audio_ibuffer_read += actual_write;
|
||||||
if (panel_timeline->playing) {
|
if (actual_write == max_write) {
|
||||||
int max_w = audio_cache_size-audio_bytes_written;
|
// got all the bytes, write again
|
||||||
int w = audio_io_device->write((const char*) cache+audio_bytes_written, max_w);
|
actual_write = audio_io_device->write((const char*) audio_ibuffer, audio_ibuffer_size);
|
||||||
qDebug() << w << max_w;
|
memset(audio_ibuffer, 0, actual_write);
|
||||||
audio_bytes_written += w;
|
audio_ibuffer_read += actual_write;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_lock.unlock();
|
cc_lock.unlock();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user