diff --git a/effects/video/texteffect.cpp b/effects/video/texteffect.cpp index 492306f8c..ed0d8ffe8 100644 --- a/effects/video/texteffect.cpp +++ b/effects/video/texteffect.cpp @@ -261,7 +261,7 @@ void TextEffect::process_image(double timecode, uint8_t* data, int w, int h) { // word wrap function if (word_wrap_field->get_bool_value(timecode)) { for (int i=0;i width) { int last_space_index = 0; for (int j=0;jmedia_type) { case MEDIA_TYPE_FOOTAGE: { - MediaStream* ms = static_cast(parent_clip->media)->get_stream_from_file_index(parent_clip->media_stream); + MediaStream* ms = static_cast(parent_clip->media)->get_stream_from_file_index(parent_clip->track < 0, parent_clip->media_stream); if (ms != NULL) { default_anchor_x = ms->video_width/2; default_anchor_y = ms->video_height/2; diff --git a/io/media.cpp b/io/media.cpp index b9c0e9e95..0d1876197 100644 --- a/io/media.cpp +++ b/io/media.cpp @@ -30,15 +30,18 @@ long Media::get_length_in_frames(double frame_rate) { return ceil(((double) length / (double) AV_TIME_BASE) * frame_rate); } -MediaStream* Media::get_stream_from_file_index(int index) { - for (int i=0;ifile_index == index) { - return video_tracks.at(i); +MediaStream* Media::get_stream_from_file_index(bool video, int index) { + if (video) { + for (int i=0;ifile_index == index) { + return video_tracks.at(i); + } } - } - for (int i=0;ifile_index == index) { - return audio_tracks.at(i); + } else { + for (int i=0;ifile_index == index) { + return audio_tracks.at(i); + } } } return NULL; diff --git a/io/media.h b/io/media.h index 42272a675..869215769 100644 --- a/io/media.h +++ b/io/media.h @@ -40,7 +40,7 @@ struct Media { bool ready; long get_length_in_frames(double frame_rate); - MediaStream* get_stream_from_file_index(int index); + MediaStream* get_stream_from_file_index(bool video, int index); void reset(); }; diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp index a81d2cec0..2f17d9063 100644 --- a/io/previewgenerator.cpp +++ b/io/previewgenerator.cpp @@ -37,7 +37,7 @@ void PreviewGenerator::parse_media() { if (avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id) == NULL) { qDebug() << "[ERROR] Unsupported codec in stream %d.\n"; } else { - MediaStream* ms = media->get_stream_from_file_index(i); + MediaStream* ms = media->get_stream_from_file_index(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, i); bool append = false; if (ms == NULL) { ms = new MediaStream(); @@ -139,7 +139,7 @@ void PreviewGenerator::generate_waveform() { } } if (!end_of_file) { - MediaStream* s = media->get_stream_from_file_index(packet.stream_index); + MediaStream* s = media->get_stream_from_file_index(fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, packet.stream_index); if (s != NULL) { if (fmt_ctx->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (!s->preview_done) { diff --git a/playback/cacher.cpp b/playback/cacher.cpp index fcf66f447..c3479acb5 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -206,7 +206,7 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) { void reset_cache(Clip* c, long target_frame) { // if we seek to a whole other place in the timeline, we'll need to reset the cache with new values - MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->media_stream); + MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream); if (!ms->infinite_length) { // flush ffmpeg codecs avcodec_flush_buffers(c->codecCtx); @@ -249,7 +249,7 @@ void open_clip_worker(Clip* clip) { Media* m = static_cast(clip->media); QByteArray ba = m->url.toUtf8(); const char* filename = ba.constData(); - MediaStream* ms = m->get_stream_from_file_index(clip->media_stream); + MediaStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream); int errCode = avformat_open_input( &clip->formatCtx, @@ -410,7 +410,7 @@ void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bo void close_clip_worker(Clip* clip) { // closes ffmpeg file handle and frees any memory used for caching - MediaStream* ms = static_cast(clip->media)->get_stream_from_file_index(clip->media_stream); + MediaStream* ms = static_cast(clip->media)->get_stream_from_file_index(clip->track < 0, clip->media_stream); if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { // sws_freeContext(clip->sws_ctx); diff --git a/playback/playback.cpp b/playback/playback.cpp index cec3e3935..c0685da19 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -80,7 +80,7 @@ bool get_clip_frame(Clip* c, long playhead) { long clip_time = refactor_frame_number(sequence_clip_time, c->sequence->frame_rate, av_q2d(av_guess_frame_rate(c->formatCtx, c->stream, c->frame))); // do we need to update the texture? - MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->media_stream); + MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream); AVFrame* current_frame = NULL; bool no_frame = false; @@ -263,7 +263,10 @@ void retrieve_next_frame_raw_data(Clip* c, AVFrame* output) { } bool is_clip_active(Clip* c, long playhead) { - return c->timeline_in < playhead + ceil(c->sequence->frame_rate) && c->timeline_out > playhead && c->enabled; + return c->enabled + && c->timeline_in < playhead + ceil(c->sequence->frame_rate) + && c->timeline_out > playhead + && playhead - c->timeline_in + c->clip_in < c->getMediaLength(c->sequence->frame_rate); } void set_sequence(Sequence* s) { diff --git a/project/clip.cpp b/project/clip.cpp index 1d47d92f1..0bcdb1505 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -7,6 +7,7 @@ #include "playback/cacher.h" #include "panels/project.h" #include "project/sequence.h" +#include "panels/timeline.h" #include @@ -158,5 +159,27 @@ long Clip::get_timeline_out_with_transition() { // timeline functions long Clip::getLength() { - return timeline_out - timeline_in; + return timeline_out - timeline_in; +} + +long Clip::getMediaLength(double framerate) { + switch (media_type) { + case MEDIA_TYPE_FOOTAGE: + { + Media* m = static_cast(media); + if (m->get_stream_from_file_index(track < 0, media_stream)->infinite_length) { + return LONG_MAX; + } else { + return m->get_length_in_frames(framerate); + } + } + break; + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = static_cast(media); + return refactor_frame_number(s->getEndFrame(), s->frame_rate, framerate); + } + break; + } + return 0; } diff --git a/project/clip.h b/project/clip.h index 162b046e2..a29b7e782 100644 --- a/project/clip.h +++ b/project/clip.h @@ -60,6 +60,7 @@ struct Clip quint8 color_g; quint8 color_b; long getLength(); + long getMediaLength(double framerate); void* media; // attached media int media_type; int media_stream; diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index f4fe68882..15f69bf92 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -894,13 +894,13 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { MediaStream* ms = NULL; if (g.clip != -1 && c->media_type == MEDIA_TYPE_FOOTAGE) { - ms = static_cast(c->media)->get_stream_from_file_index(c->media_stream); + ms = static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream); } // validate ghosts for trimming if (panel_timeline->tool == TIMELINE_TOOL_SLIP) { if (c->media_type == MEDIA_TYPE_SEQUENCE - || (c->media_type == MEDIA_TYPE_FOOTAGE && !static_cast(c->media)->get_stream_from_file_index(c->media_stream)->infinite_length)) { + || (c->media_type == MEDIA_TYPE_FOOTAGE && !static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream)->infinite_length)) { // prevent slip moving a clip below 0 clip_in validator = g.old_clip_in - frame_diff; if (validator < 0) frame_diff += validator; @@ -1599,9 +1599,9 @@ void TimelineWidget::redraw_clips() { if (clip->media_type == MEDIA_TYPE_FOOTAGE) { bool draw_checkerboard = false; - QRect checkerboard_rect = clip_rect; + QRect checkerboard_rect(clip_rect); Media* m = static_cast(clip->media); - MediaStream* ms = m->get_stream_from_file_index(clip->media_stream); + MediaStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream); if (ms == NULL) { draw_checkerboard = true; } else if (ms->preview_done) { @@ -1609,9 +1609,9 @@ void TimelineWidget::redraw_clips() { long media_length = m->get_length_in_frames(clip->sequence->frame_rate); int waveform_limit = qMin(clip_rect.width(), panel_timeline->getTimelineScreenPointFromFrame(media_length - clip->clip_in)); - if (waveform_limit < clip_rect.width()) { - draw_checkerboard = true; - checkerboard_rect.setLeft(clip_rect.left() + waveform_limit); + if (waveform_limit < clip_rect.width() && !ms->infinite_length) { + draw_checkerboard = true; + if (waveform_limit > 0) checkerboard_rect.setLeft(checkerboard_rect.left() + waveform_limit); } if (clip->track < 0) { @@ -1666,7 +1666,7 @@ void TimelineWidget::redraw_clips() { // draw "error lines" if media stream is missing clip_painter.setPen(QPen(QColor(64, 64, 64), 2)); int limit = checkerboard_rect.width(); - int clip_height = checkerboard_rect.bottom()-checkerboard_rect.top(); + int clip_height = checkerboard_rect.height(); for (int j=-clip_height;j& nests, bool render_audio) { { Media* m = static_cast(c->media); if (m->ready) { - if (m->get_stream_from_file_index(c->media_stream) != NULL + if (m->get_stream_from_file_index(c->track < 0, c->media_stream) != NULL && is_clip_active(c, playhead)) { // if thread is already working, we don't want to touch this, // but we also don't want to hang the UI thread @@ -161,7 +161,7 @@ void ViewerWidget::compose_sequence(QVector& nests, bool render_audio) { qDebug() << "[WARNING] Texture hasn't been created yet"; texture_failed = true; } else if (playhead >= c->timeline_in) { - MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->media_stream); + MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(1.0, 1.0, 1.0, 1.0);