improved waveform display (#76)
This commit is contained in:
+4
-3
@@ -15,12 +15,13 @@ struct MediaStream {
|
||||
int video_width;
|
||||
int video_height;
|
||||
bool infinite_length;
|
||||
int audio_channels;
|
||||
|
||||
// preview thumbnail/waveform
|
||||
bool preview_done;
|
||||
QImage preview; // TODO change to QPixmap
|
||||
QMutex preview_lock;
|
||||
int preview_audio_index;
|
||||
bool preview_done;
|
||||
QImage video_preview; // TODO change to QPixmap
|
||||
QVector<qint8> audio_preview;
|
||||
};
|
||||
|
||||
struct Media
|
||||
|
||||
+14
-33
@@ -51,16 +51,6 @@ void PreviewGenerator::run() {
|
||||
codec_ctx[i] = avcodec_alloc_context3(codec);
|
||||
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) {
|
||||
// initiate qimage
|
||||
MediaStream* ms = get_stream_from_file_index(i);
|
||||
int width = fmt_ctx->streams[i]->duration * av_q2d(fmt_ctx->streams[i]->time_base) * WAVEFORM_RESOLUTION;
|
||||
if (width > 32767) qDebug() << "[WARNING] Due to limitations in Qt's painter, this waveform may not appear correctly (see issue #76)";
|
||||
ms->preview = QImage(width, 80, QImage::Format_RGBA8888);
|
||||
ms->preview_audio_index = 0;
|
||||
ms->preview.fill(Qt::transparent);
|
||||
}
|
||||
}
|
||||
AVPacket packet;
|
||||
bool done = true;
|
||||
@@ -91,7 +81,7 @@ void PreviewGenerator::run() {
|
||||
linesize[0] = dstW*3;
|
||||
sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, temp_frame->height, &data, linesize);
|
||||
|
||||
s->preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
|
||||
s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
|
||||
|
||||
// delete [] data;
|
||||
|
||||
@@ -101,10 +91,16 @@ void PreviewGenerator::run() {
|
||||
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;
|
||||
|
||||
swr_ctx = swr_alloc_set_opts(
|
||||
NULL,
|
||||
temp_frame->channel_layout,
|
||||
AV_SAMPLE_FMT_S16,
|
||||
static_cast<AVSampleFormat>(swr_frame->format),
|
||||
temp_frame->sample_rate,
|
||||
temp_frame->channel_layout,
|
||||
static_cast<AVSampleFormat>(temp_frame->format),
|
||||
@@ -115,25 +111,14 @@ void PreviewGenerator::run() {
|
||||
|
||||
swr_init(swr_ctx);
|
||||
|
||||
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_convert_frame(swr_ctx, swr_frame, temp_frame);
|
||||
|
||||
int channel_height = s->preview.height()/swr_frame->channels;
|
||||
|
||||
QPainter p;
|
||||
p.begin(&s->preview);
|
||||
p.setPen(QColor(80, 80, 80));
|
||||
int channel_jump = swr_frame->channels * 2;
|
||||
int divider = (32768.0/(s->preview.height()/2))*swr_frame->channels;
|
||||
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_jump) {
|
||||
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;
|
||||
@@ -141,16 +126,12 @@ void PreviewGenerator::run() {
|
||||
min = sample;
|
||||
}
|
||||
}
|
||||
min /= divider;
|
||||
max /= divider;
|
||||
|
||||
int mid = channel_height*j+(channel_height/2);
|
||||
p.drawLine(s->preview_audio_index, mid+min, s->preview_audio_index, mid+max);
|
||||
i += 2;
|
||||
min /= 256;
|
||||
max /= 256;
|
||||
s->audio_preview.append(min);
|
||||
s->audio_preview.append(max);
|
||||
}
|
||||
s->preview_audio_index++;
|
||||
}
|
||||
p.end();
|
||||
|
||||
swr_free(&swr_ctx);
|
||||
av_frame_free(&swr_frame);
|
||||
|
||||
+2
-2
@@ -130,14 +130,14 @@ void MainWindow::on_actionSequence_triggered()
|
||||
void MainWindow::on_actionZoom_In_triggered()
|
||||
{
|
||||
if (panel_timeline->focused()) {
|
||||
panel_timeline->zoom_in();
|
||||
panel_timeline->set_zoom(true);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionZoom_out_triggered()
|
||||
{
|
||||
if (panel_timeline->focused()) {
|
||||
panel_timeline->zoom_out();
|
||||
panel_timeline->set_zoom(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ Media* Project::import_file(QString file) {
|
||||
ms->infinite_length = infinite_length;
|
||||
m->video_tracks.append(ms);
|
||||
} else if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
ms->audio_channels = pFormatCtx->streams[i]->codecpar->channels;
|
||||
m->audio_tracks.append(ms);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-10
@@ -14,6 +14,7 @@
|
||||
#include "playback/playback.h"
|
||||
|
||||
#include <QTime>
|
||||
#include <QScrollBar>
|
||||
#include <QtMath>
|
||||
|
||||
Timeline::Timeline(QWidget *parent) :
|
||||
@@ -224,14 +225,14 @@ void Timeline::delete_selection(bool ripple_delete) {
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::zoom_in() {
|
||||
zoom *= 2;
|
||||
redraw_all_clips();
|
||||
}
|
||||
|
||||
void Timeline::zoom_out() {
|
||||
zoom /= 2;
|
||||
redraw_all_clips();
|
||||
void Timeline::set_zoom(bool in) {
|
||||
if (in) {
|
||||
zoom *= 2;
|
||||
} else {
|
||||
zoom /= 2;
|
||||
}
|
||||
ui->timeline_area->horizontalScrollBar()->setValue(getScreenPointFromFrame(playhead) - (ui->timeline_area->width()/2));
|
||||
redraw_all_clips();
|
||||
}
|
||||
|
||||
void Timeline::ripple(long ripple_point, long ripple_length) {
|
||||
@@ -288,12 +289,12 @@ void Timeline::on_toolRazorButton_toggled(bool checked)
|
||||
|
||||
void Timeline::on_pushButton_4_clicked()
|
||||
{
|
||||
zoom_in();
|
||||
set_zoom(true);
|
||||
}
|
||||
|
||||
void Timeline::on_pushButton_5_clicked()
|
||||
{
|
||||
zoom_out();
|
||||
set_zoom(false);
|
||||
}
|
||||
|
||||
bool Timeline::is_clip_selected(Clip* clip) {
|
||||
|
||||
+2
-3
@@ -58,9 +58,8 @@ public:
|
||||
explicit Timeline(QWidget *parent = 0);
|
||||
~Timeline();
|
||||
|
||||
bool focused();
|
||||
void zoom_in();
|
||||
void zoom_out();
|
||||
bool focused();
|
||||
void set_zoom(bool in);
|
||||
void undo();
|
||||
void redo();
|
||||
void copy(bool del);
|
||||
|
||||
+31
-6
@@ -789,17 +789,42 @@ void TimelineWidget::redraw_clips() {
|
||||
if (clip->track < 0) {
|
||||
int thumb_y = clip_painter.fontMetrics().height()+CLIP_TEXT_PADDING+CLIP_TEXT_PADDING;
|
||||
int thumb_height = clip_rect.height()-thumb_y;
|
||||
int thumb_width = (thumb_height*((float)clip->media_stream->preview.width()/(float)clip->media_stream->preview.height()));
|
||||
int thumb_width = (thumb_height*((float)clip->media_stream->video_preview.width()/(float)clip->media_stream->video_preview.height()));
|
||||
if (thumb_height > thumb_y && clip_rect.width() > thumb_width) { // at small clip heights, don't even draw it
|
||||
QRect thumb_rect(clip_rect.x(), clip_rect.y()+thumb_y, thumb_width, thumb_height);
|
||||
clip_painter.drawImage(thumb_rect, clip->media_stream->preview);
|
||||
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->preview.width();
|
||||
int waveform_width = (((float)clip->getLength()/(float)length) * clip->media_stream->preview.width());
|
||||
QRect source(waveform_x, 0, waveform_width, clip->media_stream->preview.height());
|
||||
clip_painter.drawImage(clip_rect, clip->media_stream->preview, source);
|
||||
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;
|
||||
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;
|
||||
for (int j=0;j<clip->media_stream->audio_channels;j++) {
|
||||
int mid = channel_height*j+(channel_height/2);
|
||||
int offset = waveform_index+(j*2);
|
||||
qint8 min = (float)clip->media_stream->audio_preview[offset] / 128.0f * (channel_height/2);
|
||||
qint8 max = (float)clip->media_stream->audio_preview[offset+1] / 128.0f * (channel_height/2);
|
||||
clip_painter.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
|
||||
}
|
||||
}
|
||||
|
||||
// for (int i=waveform_x;i<waveform_width;i+=waveform_scaled_length) {
|
||||
// for (int j=0;j<clip->media_stream->audio_channels;j++) {
|
||||
// int mid = channel_height*j+(channel_height/2);
|
||||
// int offset = i+(j*2);
|
||||
// qint8 min = (float)clip->media_stream->audio_preview[offset] / 128.0f * (channel_height/2);
|
||||
// qint8 max = (float)clip->media_stream->audio_preview[offset+1] / 128.0f * (channel_height/2);
|
||||
// clip_painter.drawLine(draw_x, mid+min, draw_x, mid+max);
|
||||
// }
|
||||
// draw_x++;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user