most of the interlacing is done
This commit is contained in:
+13
-3
@@ -467,19 +467,23 @@ EffectRow::EffectRow(Effect *parent, QGridLayout *uilayout, const QString &n, in
|
||||
keyframe_enable->setCheckable(true);
|
||||
keyframe_enable->setToolTip("Enable Keyframes");
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_enabled(bool)));
|
||||
connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool)));
|
||||
key_controls->addWidget(keyframe_enable);
|
||||
|
||||
QPushButton* left_key_nav = new QPushButton("<");
|
||||
left_key_nav = new QPushButton("<");
|
||||
left_key_nav->setVisible(false);
|
||||
left_key_nav->setMaximumSize(button_size);
|
||||
key_controls->addWidget(left_key_nav);
|
||||
connect(left_key_nav, SIGNAL(clicked(bool)), this, SLOT(goto_previous_key()));
|
||||
|
||||
QPushButton* key_addremove = new QPushButton(".");
|
||||
key_addremove = new QPushButton(".");
|
||||
key_addremove->setVisible(false);
|
||||
key_addremove->setMaximumSize(button_size);
|
||||
key_controls->addWidget(key_addremove);
|
||||
connect(key_addremove, SIGNAL(clicked(bool)), this, SLOT(toggle_key()));
|
||||
|
||||
QPushButton* right_key_nav = new QPushButton(">");
|
||||
right_key_nav = new QPushButton(">");
|
||||
right_key_nav->setVisible(false);
|
||||
right_key_nav->setMaximumSize(button_size);
|
||||
key_controls->addWidget(right_key_nav);
|
||||
connect(right_key_nav, SIGNAL(clicked(bool)), this, SLOT(goto_next_key()));
|
||||
@@ -515,6 +519,12 @@ void EffectRow::set_keyframe_enabled(bool enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
void EffectRow::keyframe_ui_enabled(bool enabled) {
|
||||
left_key_nav->setVisible(enabled);
|
||||
key_addremove->setVisible(enabled);
|
||||
right_key_nav->setVisible(enabled);
|
||||
}
|
||||
|
||||
void EffectRow::goto_previous_key() {
|
||||
long key = LONG_MIN;
|
||||
Clip* c = parent_effect->parent_clip;
|
||||
|
||||
@@ -161,6 +161,7 @@ public:
|
||||
QVector<int> keyframe_types;
|
||||
private slots:
|
||||
void set_keyframe_enabled(bool);
|
||||
void keyframe_ui_enabled(bool);
|
||||
void goto_previous_key();
|
||||
void toggle_key();
|
||||
void goto_next_key();
|
||||
@@ -172,6 +173,9 @@ private:
|
||||
QVector<EffectField*> fields;
|
||||
|
||||
QPushButton* keyframe_enable;
|
||||
QPushButton* left_key_nav;
|
||||
QPushButton* key_addremove;
|
||||
QPushButton* right_key_nav;
|
||||
|
||||
bool just_made_unsafe_keyframe;
|
||||
};
|
||||
|
||||
+2
-4
@@ -9,7 +9,7 @@ Config config;
|
||||
|
||||
Config::Config()
|
||||
: saved_layout(false),
|
||||
show_track_lines(false),
|
||||
show_track_lines(true),
|
||||
scroll_zooms(false),
|
||||
img_seq_formats("jpg|jpeg|bmp|tiff|tif|psd|png|tga|jp2|gif"),
|
||||
edit_tool_selects_links(false),
|
||||
@@ -24,9 +24,7 @@ Config::Config()
|
||||
custom_title_safe_ratio(1),
|
||||
enable_drag_files_to_timeline(false),
|
||||
autoscale_by_default(false)
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
void Config::load(QString path) {
|
||||
QFile f(path);
|
||||
|
||||
@@ -60,6 +60,7 @@ void PreviewGenerator::parse_media() {
|
||||
}
|
||||
ms->video_width = fmt_ctx->streams[i]->codecpar->width;
|
||||
ms->video_height = fmt_ctx->streams[i]->codecpar->height;
|
||||
ms->video_interlacing = VIDEO_PROGRESSIVE; // default value, we get the true value later in generate_waveform()
|
||||
if (append) media->video_tracks.append(ms);
|
||||
} else if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
ms->audio_channels = fmt_ctx->streams[i]->codecpar->channels;
|
||||
@@ -170,7 +171,13 @@ void PreviewGenerator::generate_waveform() {
|
||||
|
||||
s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
|
||||
|
||||
// delete [] data;
|
||||
// is video interlaced?
|
||||
if (temp_frame->interlaced_frame) {
|
||||
s->video_interlacing = (temp_frame->top_field_first) ? VIDEO_TOP_FIELD_FIRST : VIDEO_BOTTOM_FIELD_FIRST;
|
||||
s->video_frame_rate *= 2;
|
||||
} else {
|
||||
s->video_interlacing = VIDEO_PROGRESSIVE;
|
||||
}
|
||||
|
||||
s->preview_done = true;
|
||||
|
||||
|
||||
+6
-5
@@ -293,10 +293,12 @@ bool Timeline::focused() {
|
||||
}
|
||||
|
||||
void Timeline::repaint_timeline(bool changed) {
|
||||
if (playing) {
|
||||
sequence->playhead = round(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * sequence->frame_rate));
|
||||
} else if (changed) {
|
||||
if (changed) {
|
||||
reset_all_audio();
|
||||
update_effect_controls();
|
||||
if (!playing) panel_viewer->viewer_widget->update();
|
||||
} else if (playing) {
|
||||
sequence->playhead = round(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * sequence->frame_rate));
|
||||
}
|
||||
|
||||
ui->headers->update();
|
||||
@@ -304,8 +306,6 @@ void Timeline::repaint_timeline(bool changed) {
|
||||
ui->audio_area->update();
|
||||
panel_effect_controls->update_keyframes();
|
||||
|
||||
update_effect_controls();
|
||||
|
||||
if (sequence != NULL) {
|
||||
panel_timeline->ui->horizontalScrollBar->setMaximum(qMax(0, getScreenPointFromFrame(panel_timeline->zoom, sequence->getEndFrame()) + 100 - ui->editAreas->width()));
|
||||
|
||||
@@ -318,6 +318,7 @@ void Timeline::repaint_timeline(bool changed) {
|
||||
if (zoomChanged) {
|
||||
zoomChanged = false;
|
||||
int target_scroll = getScreenPointFromFrame(zoom, sequence->playhead)-(ui->editAreas->width()>>1);
|
||||
|
||||
// TODO find a way to gradually move towards target_scroll instead of just setting it?
|
||||
ui->horizontalScrollBar->setValue(target_scroll);
|
||||
}
|
||||
|
||||
+50
-36
@@ -344,13 +344,7 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
i++;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (!error) {
|
||||
// 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->written = true;
|
||||
cache->unread = true;
|
||||
}
|
||||
cache->unread = true;
|
||||
|
||||
/* new AVFilter solution */
|
||||
if (!c->reached_end) {
|
||||
@@ -392,6 +386,7 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cache->written = true;
|
||||
i++;
|
||||
cache->write_count = i;
|
||||
}
|
||||
@@ -400,6 +395,13 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
|
||||
cache->write_count = i;
|
||||
|
||||
/*if (!error) {
|
||||
// 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->written = true;
|
||||
cache->unread = true;
|
||||
}*/
|
||||
|
||||
cache->mutex.unlock();
|
||||
}
|
||||
|
||||
@@ -415,26 +417,39 @@ void reset_cache(Clip* c, long target_frame) {
|
||||
// flush ffmpeg codecs
|
||||
avcodec_flush_buffers(c->codecCtx);
|
||||
|
||||
// flush filtergraph
|
||||
/*while (av_buffersink_get_frame(c->buffersink_ctx, temp) >= 0) {
|
||||
qDebug() << "flushed?";
|
||||
av_frame_unref(temp);
|
||||
}*/
|
||||
|
||||
double timebase = av_q2d(c->stream->time_base);
|
||||
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// seeks to nearest keyframe (target_frame represents internal clip frame)
|
||||
av_seek_frame(c->formatCtx, ms->file_index, (int64_t) qFloor(clip_frame_to_seconds(c, target_frame) / timebase), AVSEEK_FLAG_BACKWARD);
|
||||
if (target_frame > 0) {
|
||||
AVFrame* temp = av_frame_alloc();
|
||||
|
||||
// play up to the frame we actually want
|
||||
long retrieved_frame = 0;
|
||||
target_frame--;
|
||||
AVFrame* temp = av_frame_alloc();
|
||||
do {
|
||||
retrieve_next_frame(c, temp);
|
||||
if (retrieved_frame == 0) {
|
||||
if (target_frame != -1) retrieved_frame = floor(temp->pts * timebase * av_q2d(c->stream->avg_frame_rate));
|
||||
} else {
|
||||
retrieved_frame++;
|
||||
}
|
||||
} while (retrieved_frame < target_frame);
|
||||
// seeks to nearest keyframe (target_frame represents internal clip frame)
|
||||
int64_t seek_ts = qRound(clip_frame_to_seconds(c, target_frame) / timebase);
|
||||
av_seek_frame(c->formatCtx, ms->file_index, seek_ts - (av_q2d(av_inv_q(c->stream->time_base))), AVSEEK_FLAG_BACKWARD);
|
||||
|
||||
av_frame_free(&temp);
|
||||
qDebug() << "seek ts:" << seek_ts;
|
||||
|
||||
// play up to the frame we actually want
|
||||
int ret;
|
||||
do {
|
||||
ret = retrieve_next_frame(c, temp);
|
||||
if (ret < 0) {
|
||||
qDebug() << "[WARNING] Seeking terminated prematurely";
|
||||
break;
|
||||
}
|
||||
qDebug() << "received ts:" << temp->pts;
|
||||
} while (temp->pts + temp->pkt_duration + temp->pkt_duration < seek_ts);
|
||||
av_frame_unref(temp);
|
||||
av_frame_free(&temp);
|
||||
} else {
|
||||
av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);
|
||||
}
|
||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
// seek (target_frame represents timeline timecode in frames, not clip timecode)
|
||||
int64_t timestamp = qRound(playhead_to_seconds(c, target_frame) / timebase); // TODO qRound here might lead to clicking? or might fix it... who knows
|
||||
@@ -521,7 +536,7 @@ void open_clip_worker(Clip* clip) {
|
||||
// allocate filtergraph
|
||||
clip->filter_graph = avfilter_graph_alloc();
|
||||
if (clip->filter_graph == NULL) {
|
||||
qDebug() << "couldn't create filtergraph";
|
||||
qDebug() << "[ERROR] Could not create filtergraph";
|
||||
}
|
||||
char filter_args[512];
|
||||
|
||||
@@ -544,6 +559,7 @@ void open_clip_worker(Clip* clip) {
|
||||
clip->cache_size = (ms->infinite_length) ? 1 : ceil(av_q2d(clip->stream->avg_frame_rate)/4); // cache is half a second in total
|
||||
|
||||
// if (clip->skip_type == SKIP_TYPE_SEEK) clip->cache_size *= 2;
|
||||
if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->cache_size *= 2;
|
||||
|
||||
clip->cache_A.frames = new AVFrame* [clip->cache_size];
|
||||
clip->cache_B.frames = new AVFrame* [clip->cache_size];
|
||||
@@ -571,19 +587,17 @@ void open_clip_worker(Clip* clip) {
|
||||
qDebug() << "[ERROR] Could not set output pixel format";
|
||||
}
|
||||
|
||||
bool interlaced = false;
|
||||
if (interlaced) {
|
||||
// TODO make better
|
||||
AVFilterContext* yadif_filter;
|
||||
avfilter_graph_create_filter(&yadif_filter, avfilter_get_by_name("yadif"), "yadif", "mode=3", NULL, clip->filter_graph);
|
||||
|
||||
clip->speed *= 2;
|
||||
|
||||
avfilter_link(clip->buffersrc_ctx, 0, yadif_filter, 0);
|
||||
avfilter_link(yadif_filter, 0, clip->buffersink_ctx, 0);
|
||||
} else {
|
||||
if (ms->video_interlacing == VIDEO_PROGRESSIVE) {
|
||||
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
|
||||
}
|
||||
} else {
|
||||
AVFilterContext* yadif_filter;
|
||||
char yadif_args[100];
|
||||
snprintf(yadif_args, sizeof(yadif_args), "mode=3:parity=%d", ((ms->video_interlacing == VIDEO_TOP_FIELD_FIRST) ? 0 : 1)); // try mode 1
|
||||
avfilter_graph_create_filter(&yadif_filter, avfilter_get_by_name("yadif"), "yadif", yadif_args, NULL, clip->filter_graph);
|
||||
|
||||
avfilter_link(clip->buffersrc_ctx, 0, yadif_filter, 0);
|
||||
avfilter_link(yadif_filter, 0, clip->buffersink_ctx, 0);
|
||||
}
|
||||
|
||||
avfilter_graph_config(clip->filter_graph, NULL);
|
||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
@@ -765,7 +779,7 @@ void Cacher::run() {
|
||||
|
||||
open_clip_worker(clip);
|
||||
|
||||
while (caching) {
|
||||
while (caching) {
|
||||
clip->can_cache.wait(&clip->lock);
|
||||
if (!caching) {
|
||||
break;
|
||||
|
||||
@@ -117,7 +117,7 @@ bool get_clip_frame(Clip* c, long playhead) {
|
||||
|
||||
double rate = c->getMediaFrameRate();
|
||||
if (c->skip_type == SKIP_TYPE_DISCARD) rate *= c->speed;
|
||||
long clip_time = refactor_frame_number(sequence_clip_time, c->sequence->frame_rate, rate);
|
||||
long clip_time = qMax(0L, refactor_frame_number(sequence_clip_time, c->sequence->frame_rate, rate));
|
||||
|
||||
AVFrame* current_frame = NULL;
|
||||
bool no_frame = false;
|
||||
@@ -170,6 +170,8 @@ bool get_clip_frame(Clip* c, long playhead) {
|
||||
}
|
||||
} else {
|
||||
// this is technically bad, unless we just seeked
|
||||
c->cache_A.write_count = 0;
|
||||
c->cache_B.write_count = 0;
|
||||
c->cache_A.unread = false;
|
||||
c->cache_B.unread = false;
|
||||
cache_needs_reset = true;
|
||||
@@ -204,8 +206,6 @@ bool get_clip_frame(Clip* c, long playhead) {
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "current_frame is NULL:" << (current_frame == NULL);
|
||||
|
||||
if (current_frame != NULL) {
|
||||
// set up opengl texture
|
||||
if (c->texture == NULL) {
|
||||
@@ -224,7 +224,7 @@ bool get_clip_frame(Clip* c, long playhead) {
|
||||
return true;
|
||||
} else {
|
||||
texture_failed = true;
|
||||
if (!no_frame) qDebug() << "[ERROR] Failed to retrieve frame from cache (R:" << clip_time << "| A:" << c->cache_A.offset << "-" << c->cache_A.offset+c->cache_size-1 << "| B:" << c->cache_B.offset << "-" << c->cache_B.offset+c->cache_size-1 << "| WA:" << c->cache_A.written << "| WB:" << c->cache_B.written << ")";
|
||||
qDebug() << "[ERROR] Failed to retrieve frame from cache (R:" << clip_time << "| A:" << c->cache_A.offset << "-" << c->cache_A.offset+c->cache_size-1 << "| B:" << c->cache_B.offset << "-" << c->cache_B.offset+c->cache_size-1 << "| WA:" << c->cache_A.written << "| WB:" << c->cache_B.written << ")";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -242,7 +242,7 @@ double playhead_to_seconds(Clip* c, long playhead) {
|
||||
long seconds_to_clip_frame(Clip* c, double seconds) {
|
||||
// returns time as frame number (according to clip's frame rate)
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
return floor(seconds*av_q2d(c->stream->avg_frame_rate));
|
||||
return floor(seconds*c->getMediaFrameRate());
|
||||
} else {
|
||||
qDebug() << "[ERROR] seconds_to_clip_frame only works on video streams";
|
||||
return 0;
|
||||
@@ -251,7 +251,7 @@ long seconds_to_clip_frame(Clip* c, double seconds) {
|
||||
|
||||
double clip_frame_to_seconds(Clip* c, long clip_frame) {
|
||||
// returns frame number in decimal seconds
|
||||
return (double) clip_frame / av_q2d(c->stream->avg_frame_rate);
|
||||
return (double) clip_frame / c->getMediaFrameRate();
|
||||
}
|
||||
|
||||
int retrieve_next_frame(Clip* c, AVFrame* f) {
|
||||
|
||||
+7
-14
@@ -86,8 +86,7 @@ void TimelineHeader::set_out_point(long new_out) {
|
||||
}
|
||||
|
||||
void TimelineHeader::mousePressEvent(QMouseEvent* event) {
|
||||
qDebug() << "press???!?!?!??!";
|
||||
if (resizing_workarea) {
|
||||
if (resizing_workarea) {
|
||||
sequence_end = sequence->getEndFrame();
|
||||
} else {
|
||||
bool shift = (event->modifiers() & Qt::ShiftModifier);
|
||||
@@ -130,15 +129,11 @@ void TimelineHeader::mousePressEvent(QMouseEvent* event) {
|
||||
}
|
||||
set_playhead(event->pos().x());
|
||||
}
|
||||
}
|
||||
qDebug() << "3";
|
||||
dragging = true;
|
||||
qDebug() << "5" << dragging;
|
||||
event->accept();
|
||||
}
|
||||
dragging = true;
|
||||
}
|
||||
|
||||
void TimelineHeader::mouseMoveEvent(QMouseEvent* event) {
|
||||
qDebug() << "move";
|
||||
if (dragging) {
|
||||
if (resizing_workarea) {
|
||||
long frame = getHeaderFrameFromScreenPoint(event->pos().x());
|
||||
@@ -180,7 +175,7 @@ void TimelineHeader::mouseMoveEvent(QMouseEvent* event) {
|
||||
panel_timeline->repaint_timeline(false);
|
||||
} else {
|
||||
set_playhead(event->pos().x());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resizing_workarea = false;
|
||||
unsetCursor();
|
||||
@@ -200,13 +195,12 @@ void TimelineHeader::mouseMoveEvent(QMouseEvent* event) {
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineHeader::mouseReleaseEvent(QMouseEvent* event) {
|
||||
qDebug() << "release???!?!?!??!";
|
||||
if (resizing_workarea) {
|
||||
dragging = false;
|
||||
if (resizing_workarea) {
|
||||
undo_stack.push(new SetTimelineInOutCommand(sequence, true, temp_workarea_in, temp_workarea_out));
|
||||
} else if (dragging_markers && selected_markers.size() > 0) {
|
||||
bool moved = false;
|
||||
@@ -230,7 +224,6 @@ void TimelineHeader::mouseReleaseEvent(QMouseEvent* event) {
|
||||
dragging_markers = false;
|
||||
panel_timeline->snapped = false;
|
||||
panel_timeline->repaint_timeline(false);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void TimelineHeader::focusOutEvent(QFocusEvent*) {
|
||||
|
||||
Reference in New Issue
Block a user