diff --git a/app/codec/proxymanager.cpp b/app/codec/proxymanager.cpp index fd523b9cc..8ddad7bc7 100644 --- a/app/codec/proxymanager.cpp +++ b/app/codec/proxymanager.cpp @@ -66,7 +66,9 @@ QString ProxyManager::GetProxyFilename(const QString &cache_path, QString ProxyManager::GetWorkingProxyFilename(const QString &proxy_filename) { - return QStringLiteral("%1.working").arg(proxy_filename); + // Append a recognizable suffix while keeping a standard container extension + // so ffmpeg can infer the output format. + return QStringLiteral("%1.working.mp4").arg(proxy_filename); } ProxyManager::ProxyState diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index c03805191..25b518c3a 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -235,6 +235,9 @@ void Footage::set_proxy_enabled(bool enabled) if (proxy_enabled_ != enabled) { qDebug() << "Footage::set_proxy_enabled:" << filename() << enabled; proxy_enabled_ = enabled; + if (Project *p = project()) { + p->set_modified(true); + } emit ProxySettingsChanged(); } } @@ -253,6 +256,9 @@ void Footage::SetProxy(const QString &path, proxy_video_stream_index_ = video_stream_index; proxy_preset_version_ = preset_version; proxy_enabled_ = enabled; + if (Project *p = project()) { + p->set_modified(true); + } emit ProxySettingsChanged(); } diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index ae5e45ce0..9f3419c12 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -22,6 +22,7 @@ #include "previewautocacher.h" #include +#include #include #include "codec/conformmanager.h" @@ -107,31 +108,52 @@ RenderTicketPtr PreviewAutoCacher::GetRangeOfAudio(ViewerOutput *viewer, void PreviewAutoCacher::ClearSingleFrameRenders() { - QMap> copy = - video_immediate_passthroughs_; - for (auto it = copy.cbegin(); it != copy.cend(); it++) { + // Snapshot the watchers as guarded pointers before doing anything that + // might synchronously delete them (emitting Finished runs VideoRendered, + // which deletes the watcher and removes it from the map). Iterating over a + // raw-pointer copy of the map would leave dangling pointers. + QList> watchers; + for (auto it = video_immediate_passthroughs_.cbegin(); + it != video_immediate_passthroughs_.cend(); it++) { + watchers.append(it.key()); + } + + foreach (const QPointer &w, watchers) { + if (!w) { + continue; + } + // Keep already-running workers alive: cancelling an in-flight render // forces the worker process to be torn down, which defeats the process // pool. Frames that finish late are simply ignored by the viewer. - if (it.key()->IsRunning()) { + if (w->IsRunning()) { continue; } - it.key()->Cancel(); - RenderManager::instance()->RemoveTicket(it.key()->GetTicket()); - emit it.key()->GetTicket()->Finished(); + + RenderTicketPtr ticket = w->GetTicket(); + w->Cancel(); + RenderManager::instance()->RemoveTicket(ticket); + emit ticket->Finished(); } } void PreviewAutoCacher::ClearSingleFrameRendersThatArentRunning() { - QMap> copy = - video_immediate_passthroughs_; - for (auto it = copy.cbegin(); it != copy.cend(); it++) { - if (!it.key()->IsRunning()) { - it.key()->Cancel(); - RenderManager::instance()->RemoveTicket(it.key()->GetTicket()); - emit it.key()->GetTicket()->Finished(); + QList> watchers; + for (auto it = video_immediate_passthroughs_.cbegin(); + it != video_immediate_passthroughs_.cend(); it++) { + watchers.append(it.key()); + } + + foreach (const QPointer &w, watchers) { + if (!w || w->IsRunning()) { + continue; } + + RenderTicketPtr ticket = w->GetTicket(); + w->Cancel(); + RenderManager::instance()->RemoveTicket(ticket); + emit ticket->Finished(); } }