better audio IO
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@ class ExportDialog;
|
||||
class ExportThread : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
void run() override;
|
||||
void run();
|
||||
|
||||
// export parameters
|
||||
QString filename;
|
||||
|
||||
+99
-88
@@ -56,118 +56,129 @@ void PreviewGenerator::run() {
|
||||
avcodec_parameters_to_context(codec_ctx[i], fmt_ctx->streams[i]->codecpar);
|
||||
avcodec_open2(codec_ctx[i], codec, NULL);
|
||||
|
||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
if (codec_ctx[i]->channel_layout == 0) {
|
||||
codec_ctx[i]->channel_layout = guess_layout_from_channels(fmt_ctx->streams[i]->codecpar->channels);
|
||||
}
|
||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_ctx[i]->channel_layout == 0) {
|
||||
codec_ctx[i]->channel_layout = guess_layout_from_channels(fmt_ctx->streams[i]->codecpar->channels);
|
||||
}
|
||||
}
|
||||
AVPacket packet;
|
||||
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(
|
||||
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
|
||||
);
|
||||
bool end_of_file = false;
|
||||
|
||||
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);
|
||||
// get the ball rolling
|
||||
av_read_frame(fmt_ctx, &packet);
|
||||
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;
|
||||
|
||||
s->preview_done = true;
|
||||
s->preview_lock.unlock();
|
||||
s->preview_done = true;
|
||||
s->preview_lock.unlock();
|
||||
|
||||
sws_freeContext(sws_ctx);
|
||||
} 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;
|
||||
sws_freeContext(sws_ctx);
|
||||
} 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;
|
||||
|
||||
AVFrame* swr_frame = av_frame_alloc();
|
||||
swr_frame->channel_layout = temp_frame->channel_layout;
|
||||
swr_frame->sample_rate = temp_frame->sample_rate;
|
||||
swr_frame->format = AV_SAMPLE_FMT_S16;
|
||||
AVFrame* swr_frame = av_frame_alloc();
|
||||
swr_frame->channel_layout = temp_frame->channel_layout;
|
||||
swr_frame->sample_rate = temp_frame->sample_rate;
|
||||
swr_frame->format = AV_SAMPLE_FMT_S16;
|
||||
|
||||
swr_ctx = swr_alloc_set_opts(
|
||||
NULL,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(swr_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(temp_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
swr_ctx = swr_alloc_set_opts(
|
||||
NULL,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(swr_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(temp_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
0,
|
||||
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;
|
||||
for (int i=0;i<swr_frame->nb_samples;i+=interval) {
|
||||
for (int j=0;j<swr_frame->channels;j++) {
|
||||
qint16 min = 0;
|
||||
qint16 max = 0;
|
||||
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]);
|
||||
if (sample > max) {
|
||||
max = sample;
|
||||
} else if (sample < min) {
|
||||
min = sample;
|
||||
}
|
||||
int channel_skip = swr_frame->channels * 2;
|
||||
for (int i=0;i<swr_frame->nb_samples;i+=interval) {
|
||||
for (int j=0;j<swr_frame->channels;j++) {
|
||||
qint16 min = 0;
|
||||
qint16 max = 0;
|
||||
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]);
|
||||
if (sample > max) {
|
||||
max = sample;
|
||||
} else if (sample < min) {
|
||||
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) {
|
||||
done = true;
|
||||
for (int i=0;i<media->video_tracks.size();i++) {
|
||||
if (!media->video_tracks.at(i)->preview_done) {
|
||||
done = false;
|
||||
break;
|
||||
// check if we've got all our previews
|
||||
if (media->audio_tracks.size() == 0) {
|
||||
done = true;
|
||||
for (int i=0;i<media->video_tracks.size();i++) {
|
||||
if (!media->video_tracks.at(i)->preview_done) {
|
||||
done = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
end_of_file = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
av_packet_unref(&packet);
|
||||
}
|
||||
}
|
||||
for (int i=0;i<media->audio_tracks.size();i++) {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!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>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@@ -24,7 +24,7 @@ private slots:
|
||||
void menu_select(QAction* q);
|
||||
|
||||
private:
|
||||
Ui::EffectControls *ui;
|
||||
Ui::EffectControls *ui;
|
||||
Clip* clip;
|
||||
};
|
||||
|
||||
|
||||
+33
-27
@@ -85,29 +85,33 @@ void Timeline::next_cut() {
|
||||
long n_cut = LONG_MAX;
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
if (c->timeline_out < n_cut && c->timeline_out > playhead) {
|
||||
n_cut = c->timeline_out;
|
||||
seek_enabled = true;
|
||||
} else if (c->timeline_in < n_cut && c->timeline_in > playhead) {
|
||||
if (c->timeline_in < n_cut && c->timeline_in > playhead) {
|
||||
n_cut = c->timeline_in;
|
||||
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);
|
||||
}
|
||||
|
||||
void Timeline::seek(long p) {
|
||||
pause();
|
||||
|
||||
// reset all clip audio
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
void Timeline::reset_all_audio() {
|
||||
// reset all clip audio
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
c->reset_audio = true;
|
||||
c->frame_sample_index = 0;
|
||||
c->audio_buffer_write = 0;
|
||||
}
|
||||
}
|
||||
clear_audio_ibuffer();
|
||||
audio_ibuffer_read = 0;
|
||||
}
|
||||
|
||||
void Timeline::seek(long p) {
|
||||
pause();
|
||||
|
||||
reset_all_audio();
|
||||
audio_ibuffer_frame = p;
|
||||
|
||||
playhead = p;
|
||||
|
||||
@@ -166,23 +170,25 @@ bool Timeline::focused() {
|
||||
}
|
||||
|
||||
void Timeline::undo() {
|
||||
if (sequence != NULL) {
|
||||
current_clips.clear();
|
||||
panel_effect_controls->set_clip(NULL);
|
||||
sequence->undo();
|
||||
ui->video_area->redraw_clips();
|
||||
ui->audio_area->redraw_clips();
|
||||
}
|
||||
qDebug() << "[INFO] Undo/redo was so buggy, it's not been disabled. Sorry for any inconvenience";
|
||||
// if (sequence != NULL) {
|
||||
// current_clips.clear();
|
||||
// panel_effect_controls->set_clip(NULL);
|
||||
// sequence->undo();
|
||||
// ui->video_area->redraw_clips();
|
||||
// ui->audio_area->redraw_clips();
|
||||
// }
|
||||
}
|
||||
|
||||
void Timeline::redo() {
|
||||
if (sequence != NULL) {
|
||||
current_clips.clear();
|
||||
panel_effect_controls->set_clip(NULL);
|
||||
sequence->redo();
|
||||
ui->video_area->redraw_clips();
|
||||
ui->audio_area->redraw_clips();
|
||||
}
|
||||
qDebug() << "[INFO] Undo/redo was so buggy, it's not been disabled. Sorry for any inconvenience";
|
||||
// if (sequence != NULL) {
|
||||
// current_clips.clear();
|
||||
// panel_effect_controls->set_clip(NULL);
|
||||
// sequence->redo();
|
||||
// ui->video_area->redraw_clips();
|
||||
// ui->audio_area->redraw_clips();
|
||||
// }
|
||||
}
|
||||
|
||||
QString frame_to_timecode(long f) {
|
||||
@@ -213,8 +219,6 @@ void Timeline::repaint_timeline() {
|
||||
}
|
||||
|
||||
void Timeline::redraw_all_clips() {
|
||||
// add current sequence to the undo stack
|
||||
sequence->undo_add_current();
|
||||
project_changed = true;
|
||||
|
||||
ui->video_area->redraw_clips();
|
||||
@@ -222,6 +226,8 @@ void Timeline::redraw_all_clips() {
|
||||
ui->headers->update();
|
||||
|
||||
panel_viewer->ui->endTimecode->setText(frame_to_timecode(sequence->getEndFrame()));
|
||||
|
||||
reset_all_audio();
|
||||
}
|
||||
|
||||
void Timeline::select_all() {
|
||||
|
||||
@@ -162,6 +162,7 @@ private:
|
||||
void set_tool(int tool);
|
||||
long last_frame;
|
||||
QVector<Clip*> clip_clipboard;
|
||||
void reset_all_audio();
|
||||
};
|
||||
|
||||
#endif // TIMELINE_H
|
||||
|
||||
+31
-19
@@ -70,7 +70,7 @@
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -87,8 +87,8 @@
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -100,7 +100,7 @@
|
||||
<widget class="QPushButton" name="toolEditButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -117,8 +117,8 @@
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -130,7 +130,7 @@
|
||||
<widget class="QPushButton" name="toolRippleButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -147,8 +147,8 @@
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -160,7 +160,7 @@
|
||||
<widget class="QPushButton" name="toolRollingButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -175,6 +175,12 @@
|
||||
<normaloff>:/icons/rolling.png</normaloff>
|
||||
<disabledoff>:/icons/rolling-disabled.png</disabledoff>:/icons/rolling.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -184,7 +190,7 @@
|
||||
<widget class="QPushButton" name="toolRazorButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -201,8 +207,8 @@
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -214,7 +220,7 @@
|
||||
<widget class="QPushButton" name="toolSlipButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -230,6 +236,12 @@
|
||||
<disabledoff>:/icons/slip-disabled.png</disabledoff>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -239,7 +251,7 @@
|
||||
<widget class="QPushButton" name="snappingButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -258,7 +270,7 @@
|
||||
<widget class="QPushButton" name="pushButton_4">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -274,7 +286,7 @@
|
||||
<widget class="QPushButton" name="pushButton_5">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -318,8 +330,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>468</width>
|
||||
<height>309</height>
|
||||
<width>458</width>
|
||||
<height>314</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
|
||||
+11
-1
@@ -15,7 +15,7 @@ bool audio_device_set = false;
|
||||
|
||||
qint8 audio_ibuffer[audio_ibuffer_size];
|
||||
int audio_ibuffer_read = 0;
|
||||
QVector<int> audio_ibuffer_write;
|
||||
long audio_ibuffer_frame = 0;
|
||||
|
||||
void init_audio() {
|
||||
if (audio_device_set) {
|
||||
@@ -51,4 +51,14 @@ void init_audio() {
|
||||
|
||||
void clear_audio_ibuffer() {
|
||||
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
|
||||
extern qint8 audio_ibuffer[audio_ibuffer_size];
|
||||
extern int audio_ibuffer_read;
|
||||
//extern QVector<int> audio_ibuffer_write;
|
||||
extern long audio_ibuffer_frame;
|
||||
void clear_audio_ibuffer();
|
||||
|
||||
void init_audio();
|
||||
int get_buffer_offset_from_frame(long frame);
|
||||
|
||||
#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
|
||||
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
|
||||
if (!c->reached_end) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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++) {
|
||||
@@ -46,22 +69,35 @@ void cache_audio_worker(Clip* c) {
|
||||
} else {
|
||||
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);
|
||||
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) {
|
||||
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;
|
||||
break;
|
||||
} 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++;
|
||||
}
|
||||
}
|
||||
written += c->frame_sample_index;
|
||||
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) {
|
||||
// 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));
|
||||
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) {
|
||||
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) {
|
||||
output->pts = c->frame->pts;
|
||||
ret = swr_convert_frame(c->swr_ctx, output, c->frame);
|
||||
if (ret < 0) {
|
||||
qDebug() << "[ERROR] Failed to resample audio." << 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;
|
||||
|
||||
+20
-2
@@ -39,13 +39,31 @@ Clip* Clip::copy() {
|
||||
|
||||
void Clip::init() {
|
||||
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;
|
||||
pkt = new AVPacket();
|
||||
}
|
||||
|
||||
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;
|
||||
formatCtx = NULL;
|
||||
stream = NULL;
|
||||
|
||||
@@ -86,9 +86,12 @@ struct Clip
|
||||
|
||||
// audio playback variables
|
||||
SwrContext* swr_ctx;
|
||||
bool need_new_audio_frame;
|
||||
int frame_sample_index;
|
||||
int audio_buffer_write;
|
||||
bool reset_audio;
|
||||
bool audio_just_reset;
|
||||
long audio_target_frame;
|
||||
};
|
||||
|
||||
#endif // CLIP_H
|
||||
|
||||
@@ -794,17 +794,14 @@ void TimelineWidget::redraw_clips() {
|
||||
clip_painter.drawImage(thumb_rect, clip->media_stream->video_preview);
|
||||
}
|
||||
} 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 waveform_scaled_length = qFloor((clip->media_stream->audio_preview.size() / (length*panel_timeline->zoom))/divider)*divider;
|
||||
|
||||
clip_painter.setPen(QColor(80, 80, 80));
|
||||
int draw_x = clip_rect.x();
|
||||
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++) {
|
||||
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++) {
|
||||
int mid = clip_rect.top()+channel_height*j+(channel_height/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);
|
||||
if (clip->linked.size() > 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ void ViewerWidget::paintGL()
|
||||
}
|
||||
} else if (render_audio &&
|
||||
c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||
playhead >= c->timeline_in &&
|
||||
c->lock.tryLock()) {
|
||||
// clip is not caching, start caching audio
|
||||
c->lock.unlock();
|
||||
|
||||
Reference in New Issue
Block a user