better audio IO
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@ class ExportDialog;
|
|||||||
class ExportThread : public QThread {
|
class ExportThread : public QThread {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
void run() override;
|
void run();
|
||||||
|
|
||||||
// export parameters
|
// export parameters
|
||||||
QString filename;
|
QString filename;
|
||||||
|
|||||||
+99
-88
@@ -56,118 +56,129 @@ void PreviewGenerator::run() {
|
|||||||
avcodec_parameters_to_context(codec_ctx[i], fmt_ctx->streams[i]->codecpar);
|
avcodec_parameters_to_context(codec_ctx[i], fmt_ctx->streams[i]->codecpar);
|
||||||
avcodec_open2(codec_ctx[i], codec, NULL);
|
avcodec_open2(codec_ctx[i], codec, NULL);
|
||||||
|
|
||||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_ctx[i]->channel_layout == 0) {
|
||||||
if (codec_ctx[i]->channel_layout == 0) {
|
codec_ctx[i]->channel_layout = guess_layout_from_channels(fmt_ctx->streams[i]->codecpar->channels);
|
||||||
codec_ctx[i]->channel_layout = guess_layout_from_channels(fmt_ctx->streams[i]->codecpar->channels);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
bool done = true;
|
bool done = true;
|
||||||
while (av_read_frame(fmt_ctx, &packet) >= 0) {
|
|
||||||
MediaStream* s = get_stream_from_file_index(packet.stream_index);
|
|
||||||
if (s != NULL && !s->preview_done) {
|
|
||||||
if (avcodec_send_packet(codec_ctx[packet.stream_index], &packet) >= 0) {
|
|
||||||
if (avcodec_receive_frame(codec_ctx[packet.stream_index], temp_frame) >= 0) {
|
|
||||||
if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
|
||||||
int dstH = 120;
|
|
||||||
int dstW = dstH * ((float)temp_frame->width/(float)temp_frame->height);
|
|
||||||
uint8_t* data = new uint8_t[dstW*dstH*3];
|
|
||||||
|
|
||||||
sws_ctx = sws_getContext(
|
bool end_of_file = false;
|
||||||
temp_frame->width,
|
|
||||||
temp_frame->height,
|
|
||||||
static_cast<AVPixelFormat>(temp_frame->format),
|
|
||||||
dstW,
|
|
||||||
dstH,
|
|
||||||
static_cast<AVPixelFormat>(AV_PIX_FMT_RGB24),
|
|
||||||
SWS_FAST_BILINEAR,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
int linesize[AV_NUM_DATA_POINTERS];
|
// get the ball rolling
|
||||||
linesize[0] = dstW*3;
|
av_read_frame(fmt_ctx, &packet);
|
||||||
sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, temp_frame->height, &data, linesize);
|
avcodec_send_packet(codec_ctx[packet.stream_index], &packet);
|
||||||
|
|
||||||
s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
|
while (!end_of_file) {
|
||||||
|
while (avcodec_receive_frame(codec_ctx[packet.stream_index], temp_frame) == AVERROR(EAGAIN)) {
|
||||||
|
av_packet_unref(&packet);
|
||||||
|
int read_ret = av_read_frame(fmt_ctx, &packet);
|
||||||
|
if (read_ret < 0) {
|
||||||
|
end_of_file = true;
|
||||||
|
if (read_ret != AVERROR_EOF) qDebug() << "[ERROR] Failed to read packet for preview generation";
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
avcodec_send_packet(codec_ctx[packet.stream_index], &packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!end_of_file) {
|
||||||
|
MediaStream* s = get_stream_from_file_index(packet.stream_index);
|
||||||
|
if (s != NULL && !s->preview_done) {
|
||||||
|
if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||||
|
int dstH = 120;
|
||||||
|
int dstW = dstH * ((float)temp_frame->width/(float)temp_frame->height);
|
||||||
|
qDebug() << dstW;
|
||||||
|
uint8_t* data = new uint8_t[dstW*dstH*3];
|
||||||
|
|
||||||
|
sws_ctx = sws_getContext(
|
||||||
|
temp_frame->width,
|
||||||
|
temp_frame->height,
|
||||||
|
static_cast<AVPixelFormat>(temp_frame->format),
|
||||||
|
dstW,
|
||||||
|
dstH,
|
||||||
|
static_cast<AVPixelFormat>(AV_PIX_FMT_RGB24),
|
||||||
|
SWS_FAST_BILINEAR,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
int linesize[AV_NUM_DATA_POINTERS];
|
||||||
|
linesize[0] = dstW*3;
|
||||||
|
sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, temp_frame->height, &data, linesize);
|
||||||
|
|
||||||
|
s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
|
||||||
|
|
||||||
// delete [] data;
|
// delete [] data;
|
||||||
|
|
||||||
s->preview_done = true;
|
s->preview_done = true;
|
||||||
s->preview_lock.unlock();
|
s->preview_lock.unlock();
|
||||||
|
|
||||||
sws_freeContext(sws_ctx);
|
sws_freeContext(sws_ctx);
|
||||||
} else if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
} else if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||||
int interval = qFloor((temp_frame->sample_rate/WAVEFORM_RESOLUTION)/4)*4;
|
int interval = qFloor((temp_frame->sample_rate/WAVEFORM_RESOLUTION)/4)*4;
|
||||||
|
|
||||||
AVFrame* swr_frame = av_frame_alloc();
|
AVFrame* swr_frame = av_frame_alloc();
|
||||||
swr_frame->channel_layout = temp_frame->channel_layout;
|
swr_frame->channel_layout = temp_frame->channel_layout;
|
||||||
swr_frame->sample_rate = temp_frame->sample_rate;
|
swr_frame->sample_rate = temp_frame->sample_rate;
|
||||||
swr_frame->format = AV_SAMPLE_FMT_S16;
|
swr_frame->format = AV_SAMPLE_FMT_S16;
|
||||||
|
|
||||||
swr_ctx = swr_alloc_set_opts(
|
swr_ctx = swr_alloc_set_opts(
|
||||||
NULL,
|
NULL,
|
||||||
temp_frame->channel_layout,
|
temp_frame->channel_layout,
|
||||||
static_cast<AVSampleFormat>(swr_frame->format),
|
static_cast<AVSampleFormat>(swr_frame->format),
|
||||||
temp_frame->sample_rate,
|
temp_frame->sample_rate,
|
||||||
temp_frame->channel_layout,
|
temp_frame->channel_layout,
|
||||||
static_cast<AVSampleFormat>(temp_frame->format),
|
static_cast<AVSampleFormat>(temp_frame->format),
|
||||||
temp_frame->sample_rate,
|
temp_frame->sample_rate,
|
||||||
0,
|
0,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
swr_init(swr_ctx);
|
swr_init(swr_ctx);
|
||||||
|
|
||||||
swr_convert_frame(swr_ctx, swr_frame, temp_frame);
|
swr_convert_frame(swr_ctx, swr_frame, temp_frame);
|
||||||
|
|
||||||
int channel_skip = swr_frame->channels * 2;
|
int channel_skip = swr_frame->channels * 2;
|
||||||
for (int i=0;i<swr_frame->nb_samples;i+=interval) {
|
for (int i=0;i<swr_frame->nb_samples;i+=interval) {
|
||||||
for (int j=0;j<swr_frame->channels;j++) {
|
for (int j=0;j<swr_frame->channels;j++) {
|
||||||
qint16 min = 0;
|
qint16 min = 0;
|
||||||
qint16 max = 0;
|
qint16 max = 0;
|
||||||
for (int k=j*2;k<interval;k+=channel_skip) {
|
for (int k=j*2;k<interval;k+=channel_skip) {
|
||||||
qint16 sample = ((swr_frame->data[0][i+k+1] << 8) | swr_frame->data[0][i+k]);
|
qint16 sample = ((swr_frame->data[0][i+k+1] << 8) | swr_frame->data[0][i+k]);
|
||||||
if (sample > max) {
|
if (sample > max) {
|
||||||
max = sample;
|
max = sample;
|
||||||
} else if (sample < min) {
|
} else if (sample < min) {
|
||||||
min = sample;
|
min = sample;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
min /= 256;
|
|
||||||
max /= 256;
|
|
||||||
s->audio_preview.append(min);
|
|
||||||
s->audio_preview.append(max);
|
|
||||||
}
|
}
|
||||||
|
min /= 256;
|
||||||
|
max /= 256;
|
||||||
|
s->audio_preview.append(min);
|
||||||
|
s->audio_preview.append(max);
|
||||||
}
|
}
|
||||||
|
|
||||||
swr_free(&swr_ctx);
|
|
||||||
av_frame_free(&swr_frame);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qDebug() << "[ERROR] Failed to retrieve frame";
|
swr_free(&swr_ctx);
|
||||||
|
av_frame_free(&swr_frame);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qDebug() << "[ERROR] Failed to send packet";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
av_packet_unref(&packet);
|
// check if we've got all our previews
|
||||||
|
if (media->audio_tracks.size() == 0) {
|
||||||
// check if we've got all our previews
|
done = true;
|
||||||
if (media->audio_tracks.size() == 0) {
|
for (int i=0;i<media->video_tracks.size();i++) {
|
||||||
done = true;
|
if (!media->video_tracks.at(i)->preview_done) {
|
||||||
for (int i=0;i<media->video_tracks.size();i++) {
|
done = false;
|
||||||
if (!media->video_tracks.at(i)->preview_done) {
|
break;
|
||||||
done = false;
|
}
|
||||||
break;
|
}
|
||||||
|
if (done) {
|
||||||
|
end_of_file = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (done) {
|
av_packet_unref(&packet);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i=0;i<media->audio_tracks.size();i++) {
|
for (int i=0;i<media->audio_tracks.size();i++) {
|
||||||
|
|||||||
+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.2, 2018-06-21T10:36:29. -->
|
<!-- Written by QtCreator 4.6.2, 2018-06-22T02:31:47. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>EnvironmentId</variable>
|
<variable>EnvironmentId</variable>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ private slots:
|
|||||||
void menu_select(QAction* q);
|
void menu_select(QAction* q);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::EffectControls *ui;
|
Ui::EffectControls *ui;
|
||||||
Clip* clip;
|
Clip* clip;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+33
-27
@@ -85,29 +85,33 @@ void Timeline::next_cut() {
|
|||||||
long n_cut = LONG_MAX;
|
long n_cut = LONG_MAX;
|
||||||
for (int i=0;i<sequence->clip_count();i++) {
|
for (int i=0;i<sequence->clip_count();i++) {
|
||||||
Clip* c = sequence->get_clip(i);
|
Clip* c = sequence->get_clip(i);
|
||||||
if (c->timeline_out < n_cut && c->timeline_out > playhead) {
|
if (c->timeline_in < n_cut && c->timeline_in > playhead) {
|
||||||
n_cut = c->timeline_out;
|
|
||||||
seek_enabled = true;
|
|
||||||
} else if (c->timeline_in < n_cut && c->timeline_in > playhead) {
|
|
||||||
n_cut = c->timeline_in;
|
n_cut = c->timeline_in;
|
||||||
seek_enabled = true;
|
seek_enabled = true;
|
||||||
|
} else if (c->timeline_out < n_cut && c->timeline_out > playhead) {
|
||||||
|
n_cut = c->timeline_out;
|
||||||
|
seek_enabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (seek_enabled) seek(n_cut);
|
if (seek_enabled) seek(n_cut);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::seek(long p) {
|
void Timeline::reset_all_audio() {
|
||||||
pause();
|
// reset all clip audio
|
||||||
|
for (int i=0;i<sequence->clip_count();i++) {
|
||||||
// reset all clip audio
|
|
||||||
for (int i=0;i<sequence->clip_count();i++) {
|
|
||||||
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;
|
c->audio_buffer_write = 0;
|
||||||
}
|
}
|
||||||
clear_audio_ibuffer();
|
clear_audio_ibuffer();
|
||||||
audio_ibuffer_read = 0;
|
}
|
||||||
|
|
||||||
|
void Timeline::seek(long p) {
|
||||||
|
pause();
|
||||||
|
|
||||||
|
reset_all_audio();
|
||||||
|
audio_ibuffer_frame = p;
|
||||||
|
|
||||||
playhead = p;
|
playhead = p;
|
||||||
|
|
||||||
@@ -166,23 +170,25 @@ bool Timeline::focused() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::undo() {
|
void Timeline::undo() {
|
||||||
if (sequence != NULL) {
|
qDebug() << "[INFO] Undo/redo was so buggy, it's not been disabled. Sorry for any inconvenience";
|
||||||
current_clips.clear();
|
// if (sequence != NULL) {
|
||||||
panel_effect_controls->set_clip(NULL);
|
// current_clips.clear();
|
||||||
sequence->undo();
|
// panel_effect_controls->set_clip(NULL);
|
||||||
ui->video_area->redraw_clips();
|
// sequence->undo();
|
||||||
ui->audio_area->redraw_clips();
|
// ui->video_area->redraw_clips();
|
||||||
}
|
// ui->audio_area->redraw_clips();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::redo() {
|
void Timeline::redo() {
|
||||||
if (sequence != NULL) {
|
qDebug() << "[INFO] Undo/redo was so buggy, it's not been disabled. Sorry for any inconvenience";
|
||||||
current_clips.clear();
|
// if (sequence != NULL) {
|
||||||
panel_effect_controls->set_clip(NULL);
|
// current_clips.clear();
|
||||||
sequence->redo();
|
// panel_effect_controls->set_clip(NULL);
|
||||||
ui->video_area->redraw_clips();
|
// sequence->redo();
|
||||||
ui->audio_area->redraw_clips();
|
// ui->video_area->redraw_clips();
|
||||||
}
|
// ui->audio_area->redraw_clips();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
QString frame_to_timecode(long f) {
|
QString frame_to_timecode(long f) {
|
||||||
@@ -213,8 +219,6 @@ void Timeline::repaint_timeline() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::redraw_all_clips() {
|
void Timeline::redraw_all_clips() {
|
||||||
// add current sequence to the undo stack
|
|
||||||
sequence->undo_add_current();
|
|
||||||
project_changed = true;
|
project_changed = true;
|
||||||
|
|
||||||
ui->video_area->redraw_clips();
|
ui->video_area->redraw_clips();
|
||||||
@@ -222,6 +226,8 @@ void Timeline::redraw_all_clips() {
|
|||||||
ui->headers->update();
|
ui->headers->update();
|
||||||
|
|
||||||
panel_viewer->ui->endTimecode->setText(frame_to_timecode(sequence->getEndFrame()));
|
panel_viewer->ui->endTimecode->setText(frame_to_timecode(sequence->getEndFrame()));
|
||||||
|
|
||||||
|
reset_all_audio();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::select_all() {
|
void Timeline::select_all() {
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ private:
|
|||||||
void set_tool(int tool);
|
void set_tool(int tool);
|
||||||
long last_frame;
|
long last_frame;
|
||||||
QVector<Clip*> clip_clipboard;
|
QVector<Clip*> clip_clipboard;
|
||||||
|
void reset_all_audio();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TIMELINE_H
|
#endif // TIMELINE_H
|
||||||
|
|||||||
+31
-19
@@ -70,7 +70,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -87,8 +87,8 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
<size>
|
<size>
|
||||||
<width>16</width>
|
<width>20</width>
|
||||||
<height>16</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
<widget class="QPushButton" name="toolEditButton">
|
<widget class="QPushButton" name="toolEditButton">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -117,8 +117,8 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
<size>
|
<size>
|
||||||
<width>16</width>
|
<width>20</width>
|
||||||
<height>16</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
<widget class="QPushButton" name="toolRippleButton">
|
<widget class="QPushButton" name="toolRippleButton">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -147,8 +147,8 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
<size>
|
<size>
|
||||||
<width>16</width>
|
<width>20</width>
|
||||||
<height>16</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
<widget class="QPushButton" name="toolRollingButton">
|
<widget class="QPushButton" name="toolRollingButton">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -175,6 +175,12 @@
|
|||||||
<normaloff>:/icons/rolling.png</normaloff>
|
<normaloff>:/icons/rolling.png</normaloff>
|
||||||
<disabledoff>:/icons/rolling-disabled.png</disabledoff>:/icons/rolling.png</iconset>
|
<disabledoff>:/icons/rolling-disabled.png</disabledoff>:/icons/rolling.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
@@ -184,7 +190,7 @@
|
|||||||
<widget class="QPushButton" name="toolRazorButton">
|
<widget class="QPushButton" name="toolRazorButton">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -201,8 +207,8 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
<size>
|
<size>
|
||||||
<width>16</width>
|
<width>20</width>
|
||||||
<height>16</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@@ -214,7 +220,7 @@
|
|||||||
<widget class="QPushButton" name="toolSlipButton">
|
<widget class="QPushButton" name="toolSlipButton">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -230,6 +236,12 @@
|
|||||||
<disabledoff>:/icons/slip-disabled.png</disabledoff>
|
<disabledoff>:/icons/slip-disabled.png</disabledoff>
|
||||||
</iconset>
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
@@ -239,7 +251,7 @@
|
|||||||
<widget class="QPushButton" name="snappingButton">
|
<widget class="QPushButton" name="snappingButton">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -258,7 +270,7 @@
|
|||||||
<widget class="QPushButton" name="pushButton_4">
|
<widget class="QPushButton" name="pushButton_4">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -274,7 +286,7 @@
|
|||||||
<widget class="QPushButton" name="pushButton_5">
|
<widget class="QPushButton" name="pushButton_5">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>40</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -318,8 +330,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>468</width>
|
<width>458</width>
|
||||||
<height>309</height>
|
<height>314</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
|||||||
+11
-1
@@ -15,7 +15,7 @@ bool audio_device_set = false;
|
|||||||
|
|
||||||
qint8 audio_ibuffer[audio_ibuffer_size];
|
qint8 audio_ibuffer[audio_ibuffer_size];
|
||||||
int audio_ibuffer_read = 0;
|
int audio_ibuffer_read = 0;
|
||||||
QVector<int> audio_ibuffer_write;
|
long audio_ibuffer_frame = 0;
|
||||||
|
|
||||||
void init_audio() {
|
void init_audio() {
|
||||||
if (audio_device_set) {
|
if (audio_device_set) {
|
||||||
@@ -51,4 +51,14 @@ void init_audio() {
|
|||||||
|
|
||||||
void clear_audio_ibuffer() {
|
void clear_audio_ibuffer() {
|
||||||
memset(audio_ibuffer, 0, audio_ibuffer_size);
|
memset(audio_ibuffer, 0, audio_ibuffer_size);
|
||||||
|
audio_ibuffer_read = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_buffer_offset_from_frame(long frame) {
|
||||||
|
if (frame >= audio_ibuffer_frame) {
|
||||||
|
return av_samples_get_buffer_size(NULL, av_get_channel_layout_nb_channels(sequence->audio_layout), ((frame-audio_ibuffer_frame)/sequence->frame_rate)*sequence->audio_frequency, AV_SAMPLE_FMT_S16, 1);
|
||||||
|
} else {
|
||||||
|
qDebug() << "[WARNING] get_buffer_offset_from_frame called incorrectly";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -14,9 +14,10 @@ extern QIODevice* audio_io_device;
|
|||||||
#define audio_ibuffer_size 192000
|
#define audio_ibuffer_size 192000
|
||||||
extern qint8 audio_ibuffer[audio_ibuffer_size];
|
extern qint8 audio_ibuffer[audio_ibuffer_size];
|
||||||
extern int audio_ibuffer_read;
|
extern int audio_ibuffer_read;
|
||||||
//extern QVector<int> audio_ibuffer_write;
|
extern long audio_ibuffer_frame;
|
||||||
void clear_audio_ibuffer();
|
void clear_audio_ibuffer();
|
||||||
|
|
||||||
void init_audio();
|
void init_audio();
|
||||||
|
int get_buffer_offset_from_frame(long frame);
|
||||||
|
|
||||||
#endif // AUDIO_H
|
#endif // AUDIO_H
|
||||||
|
|||||||
+46
-7
@@ -23,7 +23,7 @@ void cache_audio_worker(Clip* c) {
|
|||||||
// gets one frame worth of audio and sends it to the audio buffer
|
// gets one frame worth of audio and sends it to the audio buffer
|
||||||
AVFrame* frame = c->cache_A.frames[0];
|
AVFrame* frame = c->cache_A.frames[0];
|
||||||
|
|
||||||
if (c->frame_sample_index == 0) {
|
if (c->need_new_audio_frame) {
|
||||||
// no more audio left in frame, get a new one
|
// no more audio left in frame, get a new one
|
||||||
if (!c->reached_end) {
|
if (!c->reached_end) {
|
||||||
retrieve_next_frame_raw_data(c, frame);
|
retrieve_next_frame_raw_data(c, frame);
|
||||||
@@ -33,6 +33,29 @@ void cache_audio_worker(Clip* c) {
|
|||||||
swr_convert_frame(c->swr_ctx, frame, NULL);
|
swr_convert_frame(c->swr_ctx, frame, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c->need_new_audio_frame = false;
|
||||||
|
if (c->audio_just_reset) {
|
||||||
|
// get precise sample offset for the elected clip_in from this audio frame
|
||||||
|
|
||||||
|
float target_sts = 0;
|
||||||
|
if (c->audio_target_frame < c->timeline_in) {
|
||||||
|
target_sts = playhead_to_seconds(c, c->clip_in);
|
||||||
|
} else {
|
||||||
|
target_sts = playhead_to_seconds(c, c->audio_target_frame);
|
||||||
|
}
|
||||||
|
float frame_sts = (frame->pts * av_q2d(c->stream->time_base));
|
||||||
|
int nb_samples = qRound((target_sts - frame_sts)*c->sequence->audio_frequency);
|
||||||
|
if (nb_samples == 0) {
|
||||||
|
c->frame_sample_index = 0;
|
||||||
|
} else {
|
||||||
|
c->frame_sample_index = av_samples_get_buffer_size(NULL, av_get_channel_layout_nb_channels(c->sequence->audio_layout), nb_samples, AV_SAMPLE_FMT_S16, 1);
|
||||||
|
}
|
||||||
|
qDebug() << target_sts << frame_sts << nb_samples << c->frame_sample_index;
|
||||||
|
c->audio_just_reset = false;
|
||||||
|
} else {
|
||||||
|
c->frame_sample_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// perform all audio effects
|
// perform all audio effects
|
||||||
int nb_bytes = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, static_cast<AVSampleFormat>(frame->format), 1);
|
int nb_bytes = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, static_cast<AVSampleFormat>(frame->format), 1);
|
||||||
for (int j=0;j<c->effects.size();j++) {
|
for (int j=0;j<c->effects.size();j++) {
|
||||||
@@ -46,22 +69,35 @@ void cache_audio_worker(Clip* c) {
|
|||||||
} else {
|
} else {
|
||||||
int nb_bytes = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, static_cast<AVSampleFormat>(frame->format), 1);
|
int nb_bytes = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, static_cast<AVSampleFormat>(frame->format), 1);
|
||||||
|
|
||||||
if (c->audio_buffer_write == 0) c->audio_buffer_write = (((int)(audio_ibuffer_read/2))*2) + 1024;
|
|
||||||
int half_buffer = (audio_ibuffer_size/2);
|
int half_buffer = (audio_ibuffer_size/2);
|
||||||
|
if (c->audio_buffer_write == 0) {
|
||||||
|
c->audio_buffer_write = get_buffer_offset_from_frame(qMax(c->timeline_in, c->audio_target_frame));
|
||||||
|
int offset = audio_ibuffer_read - c->audio_buffer_write;
|
||||||
|
if (offset > 0) {
|
||||||
|
c->audio_buffer_write += offset;
|
||||||
|
c->frame_sample_index += offset;
|
||||||
|
while (c->frame_sample_index > nb_bytes) {
|
||||||
|
// get new frame
|
||||||
|
retrieve_next_frame_raw_data(c, frame);
|
||||||
|
|
||||||
|
nb_bytes = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, static_cast<AVSampleFormat>(frame->format), 1);
|
||||||
|
c->frame_sample_index -= nb_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
while (c->frame_sample_index < nb_bytes) {
|
while (c->frame_sample_index < nb_bytes) {
|
||||||
if (c->audio_buffer_write >= audio_ibuffer_read+half_buffer) {
|
if (c->audio_buffer_write >= audio_ibuffer_read+half_buffer || c->audio_buffer_write >= get_buffer_offset_from_frame(c->timeline_out)) {
|
||||||
written = max_write;
|
written = max_write;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
audio_ibuffer[c->audio_buffer_write%audio_ibuffer_size] += frame->data[0][c->frame_sample_index];
|
audio_ibuffer[c->audio_buffer_write%audio_ibuffer_size] += frame->data[0][c->frame_sample_index];
|
||||||
|
|
||||||
c->audio_buffer_write++;
|
c->audio_buffer_write++;
|
||||||
c->frame_sample_index++;
|
c->frame_sample_index++;
|
||||||
|
written++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
written += c->frame_sample_index;
|
|
||||||
if (c->frame_sample_index == nb_bytes) {
|
if (c->frame_sample_index == nb_bytes) {
|
||||||
c->frame_sample_index = 0;
|
c->need_new_audio_frame = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +156,10 @@ void reset_cache(Clip* c, long target_frame) {
|
|||||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||||
// seek (target_frame represents timeline timecode in frames, not clip timecode)
|
// seek (target_frame represents timeline timecode in frames, not clip timecode)
|
||||||
swr_drop_output(c->swr_ctx, swr_get_out_samples(c->swr_ctx, 0));
|
swr_drop_output(c->swr_ctx, swr_get_out_samples(c->swr_ctx, 0));
|
||||||
av_seek_frame(c->formatCtx, c->media_stream->file_index, playhead_to_seconds(c, target_frame) / timebase, AVSEEK_FLAG_ANY);
|
av_seek_frame(c->formatCtx, c->media_stream->file_index, playhead_to_seconds(c, target_frame) / timebase, AVSEEK_FLAG_BACKWARD);
|
||||||
|
c->audio_target_frame = target_frame;
|
||||||
|
c->need_new_audio_frame = true;
|
||||||
|
c->audio_just_reset = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -272,13 +272,13 @@ void retrieve_next_frame_raw_data(Clip* c, AVFrame* output) {
|
|||||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||||
sws_scale(c->sws_ctx, c->frame->data, c->frame->linesize, 0, c->stream->codecpar->height, output->data, output->linesize);
|
sws_scale(c->sws_ctx, c->frame->data, c->frame->linesize, 0, c->stream->codecpar->height, output->data, output->linesize);
|
||||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||||
|
output->pts = c->frame->pts;
|
||||||
ret = swr_convert_frame(c->swr_ctx, output, c->frame);
|
ret = swr_convert_frame(c->swr_ctx, output, c->frame);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qDebug() << "[ERROR] Failed to resample audio." << ret;
|
qDebug() << "[ERROR] Failed to resample audio." << ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ret == AVERROR_EOF) {
|
} else if (ret == AVERROR_EOF) {
|
||||||
// qDebug() << "reached end triggered" << c->track;
|
|
||||||
c->reached_end = true;
|
c->reached_end = true;
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "[WARNING] Raw frame data could not be retrieved." << ret;
|
qDebug() << "[WARNING] Raw frame data could not be retrieved." << ret;
|
||||||
|
|||||||
+20
-2
@@ -39,13 +39,31 @@ Clip* Clip::copy() {
|
|||||||
|
|
||||||
void Clip::init() {
|
void Clip::init() {
|
||||||
reset();
|
reset();
|
||||||
clip_in = timeline_in = timeline_out = track = undeletable = 0;
|
clip_in = 0;
|
||||||
|
timeline_in = 0;
|
||||||
|
timeline_out = 0;
|
||||||
|
track = 0;
|
||||||
|
undeletable = 0;
|
||||||
texture = NULL;
|
texture = NULL;
|
||||||
pkt = new AVPacket();
|
pkt = new AVPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = audio_buffer_write = 0;
|
cache_size = false;
|
||||||
|
audio_just_reset = false;
|
||||||
|
cache_A.offset = false;
|
||||||
|
cache_B.offset = false;
|
||||||
|
open = false;
|
||||||
|
pkt_written = false;
|
||||||
|
cache_A.written = false;
|
||||||
|
cache_B.written = false;
|
||||||
|
cache_A.unread = false;
|
||||||
|
cache_B.unread = false;
|
||||||
|
reached_end = false;
|
||||||
|
reset_audio = false;
|
||||||
|
frame_sample_index = false;
|
||||||
|
audio_buffer_write = false;
|
||||||
|
need_new_audio_frame = false;
|
||||||
texture_frame = -1;
|
texture_frame = -1;
|
||||||
formatCtx = NULL;
|
formatCtx = NULL;
|
||||||
stream = NULL;
|
stream = NULL;
|
||||||
|
|||||||
@@ -86,9 +86,12 @@ struct Clip
|
|||||||
|
|
||||||
// audio playback variables
|
// audio playback variables
|
||||||
SwrContext* swr_ctx;
|
SwrContext* swr_ctx;
|
||||||
|
bool need_new_audio_frame;
|
||||||
int frame_sample_index;
|
int frame_sample_index;
|
||||||
int audio_buffer_write;
|
int audio_buffer_write;
|
||||||
bool reset_audio;
|
bool reset_audio;
|
||||||
|
bool audio_just_reset;
|
||||||
|
long audio_target_frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CLIP_H
|
#endif // CLIP_H
|
||||||
|
|||||||
@@ -794,17 +794,14 @@ void TimelineWidget::redraw_clips() {
|
|||||||
clip_painter.drawImage(thumb_rect, clip->media_stream->video_preview);
|
clip_painter.drawImage(thumb_rect, clip->media_stream->video_preview);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
long length = clip->media->get_length_in_frames(clip->sequence->frame_rate);
|
|
||||||
int waveform_x = ((float)clip->clip_in/(float)length) * clip->media_stream->audio_preview.size();
|
|
||||||
int waveform_width = (((float)clip->getLength()/(float)length) * clip->media_stream->audio_preview.size());
|
|
||||||
int divider = clip->media_stream->audio_channels*2;
|
int divider = clip->media_stream->audio_channels*2;
|
||||||
int waveform_scaled_length = qFloor((clip->media_stream->audio_preview.size() / (length*panel_timeline->zoom))/divider)*divider;
|
|
||||||
|
|
||||||
clip_painter.setPen(QColor(80, 80, 80));
|
clip_painter.setPen(QColor(80, 80, 80));
|
||||||
int draw_x = clip_rect.x();
|
|
||||||
int channel_height = clip_rect.height()/clip->media_stream->audio_channels;
|
int channel_height = clip_rect.height()/clip->media_stream->audio_channels;
|
||||||
|
long media_length = clip->media->get_length_in_frames(clip->sequence->frame_rate);
|
||||||
for (int i=0;i<clip_rect.width();i++) {
|
for (int i=0;i<clip_rect.width();i++) {
|
||||||
int waveform_index = qFloor((((float)i / (float)clip_rect.width()) * clip->media_stream->audio_preview.size())/divider)*divider;
|
int waveform_index = qFloor((((clip->clip_in + ((float) i/panel_timeline->zoom))/media_length) * clip->media_stream->audio_preview.size())/divider)*divider;
|
||||||
|
|
||||||
for (int j=0;j<clip->media_stream->audio_channels;j++) {
|
for (int j=0;j<clip->media_stream->audio_channels;j++) {
|
||||||
int mid = clip_rect.top()+channel_height*j+(channel_height/2);
|
int mid = clip_rect.top()+channel_height*j+(channel_height/2);
|
||||||
int offset = waveform_index+(j*2);
|
int offset = waveform_index+(j*2);
|
||||||
@@ -830,7 +827,7 @@ void TimelineWidget::redraw_clips() {
|
|||||||
clip_painter.drawText(text_rect, 0, clip->name, &text_rect);
|
clip_painter.drawText(text_rect, 0, clip->name, &text_rect);
|
||||||
if (clip->linked.size() > 0) {
|
if (clip->linked.size() > 0) {
|
||||||
int underline_y = CLIP_TEXT_PADDING + clip_painter.fontMetrics().height() + clip_rect.top();
|
int underline_y = CLIP_TEXT_PADDING + clip_painter.fontMetrics().height() + clip_rect.top();
|
||||||
int underline_width = qMin(clip_rect.width() - CLIP_TEXT_PADDING, clip_rect.left() + CLIP_TEXT_PADDING+clip_painter.fontMetrics().width(clip->name));
|
int underline_width = qMin(clip_rect.left() + clip_rect.width() - CLIP_TEXT_PADDING, clip_rect.left() + CLIP_TEXT_PADDING + clip_painter.fontMetrics().width(clip->name));
|
||||||
clip_painter.drawLine(clip_rect.left() + CLIP_TEXT_PADDING, underline_y, underline_width, underline_y);
|
clip_painter.drawLine(clip_rect.left() + CLIP_TEXT_PADDING, underline_y, underline_width, underline_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ void ViewerWidget::paintGL()
|
|||||||
}
|
}
|
||||||
} else if (render_audio &&
|
} else if (render_audio &&
|
||||||
c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||||
playhead >= c->timeline_in &&
|
|
||||||
c->lock.tryLock()) {
|
c->lock.tryLock()) {
|
||||||
// clip is not caching, start caching audio
|
// clip is not caching, start caching audio
|
||||||
c->lock.unlock();
|
c->lock.unlock();
|
||||||
|
|||||||
Reference in New Issue
Block a user