diff --git a/.travis/install.sh b/.travis/install.sh index d38d4e291..217361a4f 100644 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -8,11 +8,11 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then if [ "$ARCH" == "x86_64" ]; then - sudo apt-get -y install qt59base qt59multimedia libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins fuse + sudo apt-get -y install qt59base qt59multimedia qt59svg libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins fuse fi if [ "$ARCH" == "i386" ]; then - sudo apt-get -y install gcc-multilib g++-multilib qt59base:i386 qt59multimedia:i386 libavformat-dev:i386 libavcodec-dev:i386 libavfilter-dev:i386 libavutil-dev:i386 libswscale-dev:i386 libswresample-dev:i386 frei0r-plugins-dev:i386 frei0r-plugins:i386 pkg-config:i386 libgl1-mesa-dev:i386 fuse:i386 + sudo apt-get -y install gcc-multilib g++-multilib qt59base:i386 qt59multimedia:i386 qt59svg:i386 libavformat-dev:i386 libavcodec-dev:i386 libavfilter-dev:i386 libavutil-dev:i386 libswscale-dev:i386 libswresample-dev:i386 frei0r-plugins-dev:i386 frei0r-plugins:i386 pkg-config:i386 libgl1-mesa-dev:i386 fuse:i386 fi source /opt/qt*/bin/qt*-env.sh diff --git a/.travis/script.sh b/.travis/script.sh index b15b61eee..8f172df7e 100644 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -58,7 +58,7 @@ elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export VERSION=$(git rev-parse --short HEAD) # use linuxdeployqt to set up dependencies - ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -appimage + ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -extra-plugins=imageformats/libqsvg.so -appimage # 64-bit linuxdeployqt can only generate a 64-bit AppImage # to generate a 32-bit one, we need to download and run 32-bit AppImageTool diff --git a/mainwindow.cpp b/mainwindow.cpp index 03ce31903..bd2b25bc2 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -73,9 +73,22 @@ MainWindow* olive::MainWindow; void MainWindow::setup_layout(bool reset) { // load panels from file if (!reset) { - QFile panel_config(get_config_path() + "/layout"); + QFile panel_config(get_config_dir().filePath("layout")); if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { - restoreState(panel_config.readAll(), 0); + + // default to resetting unless we find the tag in the XML file + reset = true; + + // read XML layout file + QXmlStreamReader stream(&panel_config); + while (!stream.atEnd()) { + stream.readNextStartElement(); + if (stream.name() == "panels") { + restoreState(QByteArray::fromBase64(stream.readElementText().toUtf8()), 0); + reset = false; + } + } + panel_config.close(); } else { reset = true; @@ -888,9 +901,14 @@ void MainWindow::closeEvent(QCloseEvent *e) { olive::CurrentConfig.save(config_fn); // save panel layout - QFile panel_config(config_path + "/layout"); + QFile panel_config(get_config_dir().filePath("layout")); if (panel_config.open(QFile::WriteOnly)) { - panel_config.write(saveState(0)); + QXmlStreamWriter stream(&panel_config); + stream.writeStartDocument(); + + stream.writeTextElement("panels", saveState(0).toBase64()); + + stream.writeEndDocument(); panel_config.close(); } else { qCritical() << "Failed to save layout"; diff --git a/olive.pro b/olive.pro index 95066c9b0..0a38ee49a 100644 --- a/olive.pro +++ b/olive.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui multimedia opengl +QT += core gui multimedia opengl svg greaterThan(QT_MAJOR_VERSION, 4): QT += widgets diff --git a/project/clip.cpp b/project/clip.cpp index 9e5b1b8b1..0a0902b5f 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -126,9 +126,11 @@ Media *Clip::media() FootageStream *Clip::media_stream() { - if (media()->get_type() == MEDIA_TYPE_FOOTAGE) { + if (media() != nullptr + && media()->get_type() == MEDIA_TYPE_FOOTAGE) { return media()->to_footage()->get_stream_from_file_index(track() < 0, media_stream_index()); } + return nullptr; } diff --git a/project/footage.cpp b/project/footage.cpp index b7e7f5a28..d4654410d 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -55,7 +55,9 @@ void Footage::reset() { } long Footage::get_length_in_frames(double frame_rate) { - if (length >= 0) return qFloor(((double) length / (double) AV_TIME_BASE) * frame_rate / speed); + if (length >= 0) { + return qFloor((double(length) / double(AV_TIME_BASE)) * frame_rate / speed); + } return 0; } diff --git a/project/sequence.cpp b/project/sequence.cpp index 2e070ce00..da1bad08f 100644 --- a/project/sequence.cpp +++ b/project/sequence.cpp @@ -42,6 +42,8 @@ SequencePtr Sequence::copy() { s->frame_rate = frame_rate; s->audio_frequency = audio_frequency; s->audio_layout = audio_layout; + + // deep copy all of the sequence's clips s->clips.resize(clips.size()); for (int i=0;iclips[i] = copy; } } + + // copy all of the sequence's markers + s->markers = markers; + return s; } diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 2c4055372..6c5785bb8 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -571,13 +571,12 @@ void Cacher::CacheVideoWorker() { do { AVFrame* decoded_frame; -// qint64 time = QDateTime::currentMSecsSinceEpoch(); + // retrieve raw RGBA frame from decoder + filter stack int retrieve_code = RetrieveFrameAndProcess(&decoded_frame); - //qDebug() << "decode took:" << (QDateTime::currentMSecsSinceEpoch() - time); - // for some reason we were unable to retrieve a frame, likely a decoder error so we report it - // again an EOF, is not really an "error", and we can continue execution if we encounter it if (retrieve_code < 0 && retrieve_code != AVERROR_EOF) { + // for some reason we were unable to retrieve a frame, likely a decoder error so we report it + // again, an EOF isn't an "error" but will how we add frames (see below) qCritical() << "Failed to retrieve frame from buffersink." << retrieve_code; @@ -668,7 +667,22 @@ void Cacher::CacheVideoWorker() { qWarning() << clip->name() << "frame had no PTS value"; av_frame_free(&decoded_frame); - SetRetrievedFrame(nullptr); + + if (retrieve_code == AVERROR_EOF && retrieved_frame == nullptr) { + // if we reached the end of the file, it's not an error but there are no more frames to retrieve + // some formats EOF before the end of the duration that Olive calculates. In this event, we simply + // return the last frame we retrieved + // + // TODO: Check duration formula + + SetRetrievedFrame(queue.last()); + + } else { + + SetRetrievedFrame(nullptr); + + } + break; } @@ -735,7 +749,10 @@ void Cacher::WakeMainThread() main_thread_lock_.unlock(); } -Cacher::Cacher(Clip* c) : clip(c) {} +Cacher::Cacher(Clip* c) : clip(c) { + frame_ = nullptr; + pkt = nullptr; +} void Cacher::OpenWorker() { qint64 time_start = QDateTime::currentMSecsSinceEpoch(); @@ -988,9 +1005,15 @@ void Cacher::CloseWorker() { queue.clear(); queue.unlock(); - av_frame_free(&frame_); + if (frame_ != nullptr) { + av_frame_free(&frame_); + frame_ = nullptr; + } - av_packet_free(&pkt); + if (pkt != nullptr) { + av_packet_free(&pkt); + pkt = nullptr; + } if (clip->media() != nullptr && clip->media()->get_type() == MEDIA_TYPE_FOOTAGE) { avfilter_graph_free(&filter_graph); @@ -1050,7 +1073,9 @@ void Cacher::Open() void Cacher::Cache(long playhead, bool scrubbing, QVector& nests, int playback_speed) { - if (clip->media_stream()->infinite_length && queue.size() > 0) { + if (clip->media_stream() != nullptr + && queue.size() > 0 + && clip->media_stream()->infinite_length) { retrieved_frame = queue.at(0); return; } @@ -1063,26 +1088,33 @@ void Cacher::Cache(long playhead, bool scrubbing, QVector& nests, int pla bool wait_for_cacher_to_respond = true; - // see if we already have this frame - retrieve_lock_.lock(); - queue.lock(); - retrieved_frame = nullptr; - int64_t target_pts = seconds_to_timestamp(clip, playhead_to_clip_seconds(clip, playhead_)); - for (int i=0;ipts == target_pts) { - retrieved_frame = queue.at(i); -// qDebug() << "================> found frame at" << i; - wait_for_cacher_to_respond = false; - break; - } else if (i > 0 && queue.at(i-1)->pts < target_pts && queue.at(i)->pts > target_pts) { - retrieved_frame = queue.at(i-1); -// qDebug() << "================> found frame at" << i-1; - wait_for_cacher_to_respond = false; - break; + if (clip->media() != nullptr) { + // see if we already have this frame + retrieve_lock_.lock(); + queue.lock(); + retrieved_frame = nullptr; + int64_t target_pts = seconds_to_timestamp(clip, playhead_to_clip_seconds(clip, playhead_)); + for (int i=0;ipts == target_pts) { + + // the queue has a frame with the exact timestamp + + retrieved_frame = queue.at(i); + wait_for_cacher_to_respond = false; + break; + } else if (i > 0 && queue.at(i-1)->pts < target_pts && queue.at(i)->pts > target_pts) { + + // the queue has a frame with a close timestamp that we'll assume is different due to a rounding error + + retrieved_frame = queue.at(i-1); + wait_for_cacher_to_respond = false; + break; + } } + queue.unlock(); + retrieve_lock_.unlock(); } - queue.unlock(); - retrieve_lock_.unlock(); if (wait_for_cacher_to_respond) { main_thread_lock_.lock();