Skip to content

Commit 737d973

Browse files
authored
Add warning for time overlaps in video composition references for exporter (#3166)
1 parent 8e2c804 commit 737d973

File tree

8 files changed

+232
-31
lines changed

8 files changed

+232
-31
lines changed

exporter/assets/qml/ExportPanel.qml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ PAGWindow {
390390
acceptedButtons: Qt.LeftButton
391391
cursorShape: Qt.PointingHandCursor
392392
onPressed: {
393-
window.hide();
393+
window.close();
394394
}
395395
}
396396
}

exporter/assets/translation/Chinese.ts

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,24 @@
44
<context>
55
<name>AlertDialog</name>
66
<message>
7+
<location filename="../qml/AlertDialog.qml" line="24"/>
78
<source>Convert to BMP?</source>
8-
<translation type="vanished">确认转成BMP?</translation>
9+
<translation>确认转成BMP?</translation>
910
</message>
1011
<message>
12+
<location filename="../qml/AlertDialog.qml" line="37"/>
1113
<source>Placeholder image and text in composition cannot edit after converting to BMP</source>
12-
<translation type="vanished">转换为BMP后,合成中的占位图和文本将无法编辑</translation>
14+
<translation>转换为BMP后,合成中的占位图和文本将无法编辑</translation>
1315
</message>
1416
<message>
17+
<location filename="../qml/AlertDialog.qml" line="63"/>
1518
<source>Cancel</source>
16-
<translation type="vanished">取消</translation>
19+
<translation>取消</translation>
1720
</message>
1821
<message>
22+
<location filename="../qml/AlertDialog.qml" line="95"/>
1923
<source>Confirm</source>
20-
<translation type="vanished">确认</translation>
24+
<translation>确认</translation>
2125
</message>
2226
</context>
2327
<context>
@@ -28,12 +32,12 @@
2832
<translation>导出错误</translation>
2933
</message>
3034
<message>
31-
<location filename="../qml/AlertError.qml" line="79"/>
35+
<location filename="../qml/AlertError.qml" line="106"/>
3236
<source>Export failed due to error:</source>
3337
<translation>导出失败,原因是:</translation>
3438
</message>
3539
<message>
36-
<location filename="../qml/AlertError.qml" line="124"/>
40+
<location filename="../qml/AlertError.qml" line="151"/>
3741
<source>Cancel and Modify</source>
3842
<translation>取消并修改</translation>
3943
</message>
@@ -61,7 +65,7 @@
6165
<translation>继续导出</translation>
6266
</message>
6367
<message>
64-
<location filename="../qml/AlertWarning.qml" line="196"/>
68+
<location filename="../qml/AlertWarning.qml" line="194"/>
6569
<source>Cancel and Modify</source>
6670
<translation>取消并修改</translation>
6771
</message>
@@ -315,7 +319,7 @@
315319
<context>
316320
<name>PAGListView</name>
317321
<message>
318-
<location filename="../qml/PAGListView.qml" line="157"/>
322+
<location filename="../qml/PAGListView.qml" line="158"/>
319323
<source>Locate</source>
320324
<translation>定位</translation>
321325
</message>
@@ -382,7 +386,7 @@
382386
<message>
383387
<location filename="../../src/utils/AlertInfo.cpp" line="38"/>
384388
<source>Increase TagLevel: Go to Preferences-&gt;PAG Config-&gt;General, change &quot;Export version control&quot; to &quot;Beta&quot;. (Generated PAG files may only be supported by newer SDK versions)</source>
385-
<translation>调高TagLevel:首选项-&gt;PAG Config-&gt;通用, 将/“导出版本控制/”改为/“Beta/”. (调高后生成的pag文件可能只有较新版本SDK才能支持)</translation>
389+
<translation>调高TagLevel:首选项-&gt;PAG Config-&gt;通用, 将“导出版本控制”改为“Beta”. (调高后生成的pag文件可能只有较新版本SDK才能支持)</translation>
386390
</message>
387391
<message>
388392
<location filename="../../src/utils/AlertInfo.cpp" line="42"/>
@@ -858,62 +862,72 @@
858862
<translation>建议去掉文本路径或去掉文本动画。</translation>
859863
</message>
860864
<message>
861-
<location filename="../../src/utils/AlertInfo.cpp" line="420"/>
865+
<location filename="../../src/utils/AlertInfo.cpp" line="412"/>
866+
<source>There are overlapping time intervals for references to video composition &quot;%1&quot;.</source>
867+
<translation>对视频合成“%1”的引用存在时间重叠。</translation>
868+
</message>
869+
<message>
870+
<location filename="../../src/utils/AlertInfo.cpp" line="414"/>
871+
<source>Recommend adjusting the start time and duration of the layers referencing video composition &quot;%1&quot;.</source>
872+
<translation>建议调整视频合成“%1”的引用图层的起始时间和持续时间。</translation>
873+
</message>
874+
<message>
875+
<location filename="../../src/utils/AlertInfo.cpp" line="430"/>
862876
<source>AE export error.</source>
863877
<translation>AE导出错误。</translation>
864878
</message>
865879
<message>
866-
<location filename="../../src/utils/AlertInfo.cpp" line="425"/>
880+
<location filename="../../src/utils/AlertInfo.cpp" line="435"/>
867881
<source>Bitmap sequence export error.</source>
868882
<translation>位图序列帧导出错误。</translation>
869883
</message>
870884
<message>
871-
<location filename="../../src/utils/AlertInfo.cpp" line="430"/>
885+
<location filename="../../src/utils/AlertInfo.cpp" line="440"/>
872886
<source>Video sequence export error.</source>
873887
<translation>视频序列帧导出错误。</translation>
874888
</message>
875889
<message>
876-
<location filename="../../src/utils/AlertInfo.cpp" line="435"/>
890+
<location filename="../../src/utils/AlertInfo.cpp" line="445"/>
877891
<source>Audio export error.</source>
878892
<translation>音频导出错误。</translation>
879893
</message>
880894
<message>
881-
<location filename="../../src/utils/AlertInfo.cpp" line="440"/>
895+
<location filename="../../src/utils/AlertInfo.cpp" line="450"/>
882896
<source>WebP encoding error.</source>
883897
<translation>Webp编码错误。</translation>
884898
</message>
885899
<message>
886-
<location filename="../../src/utils/AlertInfo.cpp" line="445"/>
900+
<location filename="../../src/utils/AlertInfo.cpp" line="455"/>
887901
<source>Export rendering error.</source>
888902
<translation>导出渲染错误。</translation>
889903
</message>
890904
<message>
891-
<location filename="../../src/utils/AlertInfo.cpp" line="450"/>
905+
<location filename="../../src/utils/AlertInfo.cpp" line="460"/>
892906
<source>Composition handle not found: The AE project handle for this composition (ID: %1) is not registered. This may occur if the composition was added after export initialization or if the project structure changed.</source>
893907
<translation>找不到合成句柄:此合成(ID: %1)的AE项目句柄未注册。这可能发生在导出初始化后添加了合成,或项目结构发生了变化。</translation>
894908
</message>
895909
<message>
896-
<location filename="../../src/utils/AlertInfo.cpp" line="454"/>
910+
<location filename="../../src/utils/AlertInfo.cpp" line="464"/>
897911
<source>Try re-exporting the project. If the issue persists, restart After Effects and try again.</source>
898912
<translation>请尝试重新导出项目。如果问题持续存在,请重启 After Effects 后重试。</translation>
899913
</message>
900914
<message>
901-
<location filename="../../src/utils/AlertInfo.cpp" line="461"/>
915+
<location filename="../../src/utils/AlertInfo.cpp" line="471"/>
902916
<source>DisplacementMap does not support referencing its own layer.</source>
903917
<translation>置换图DisplacementMap暂不支持指向自身图层。</translation>
904918
</message>
905919
<message>
906-
<location filename="../../src/utils/AlertInfo.cpp" line="462"/>
920+
<location filename="../../src/utils/AlertInfo.cpp" line="472"/>
907921
<source>Recommend removing this effect or redirecting the displacement layer to another layer.</source>
908922
<translation>建议去掉该效果,或修改该置换图层指向其它图层。</translation>
909923
</message>
910924
<message>
911-
<location filename="../../src/utils/AlertInfo.cpp" line="469"/>
925+
<location filename="../../src/utils/AlertInfo.cpp" line="479"/>
912926
<source>[Text Animation - Selector] export error.</source>
913927
<translation>[文本动画-选择器]导出错误。</translation>
914928
</message>
915929
<message>
916-
<location filename="../../src/utils/AlertInfo.cpp" line="474"/>
930+
<location filename="../../src/utils/AlertInfo.cpp" line="484"/>
917931
<source>PAG file verification error.</source>
918932
<translation>PAG文件校验错误。</translation>
919933
</message>

exporter/src/export/ExportVerify.cpp

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
/////////////////////////////////////////////////////////////////////////////////////////////////
1818

1919
#include "ExportVerify.h"
20+
#include <QDebug>
2021
#include <QJsonDocument>
2122
#include <QJsonParseError>
23+
#include "utils/AEDataTypeConverter.h"
2224
#include "utils/AEHelper.h"
2325
#include "utils/LayerHelper.h"
2426
#include "utils/ScopedHelper.h"
@@ -355,6 +357,180 @@ static void CheckVideoCompositionInUIScenes(std::shared_ptr<PAGExportSession> se
355357
}
356358
}
357359

360+
static void CollectVideoCompositionReferences(const pag::Composition* composition,
361+
const pag::VideoComposition* targetVideo,
362+
float mainCompFrameRate, pag::Frame mainCompClipStart,
363+
pag::Frame localClipStart, pag::Frame localClipEnd,
364+
std::vector<pag::TimeRange>* referenceRanges) {
365+
if (composition->type() != pag::CompositionType::Vector) {
366+
return;
367+
}
368+
369+
float currentFrameRate = composition->frameRate;
370+
for (auto layer : static_cast<const pag::VectorComposition*>(composition)->layers) {
371+
if (layer->type() != pag::LayerType::PreCompose) {
372+
continue;
373+
}
374+
375+
auto preComposeLayer = static_cast<pag::PreComposeLayer*>(layer);
376+
pag::Frame offset = preComposeLayer->compositionStartTime;
377+
378+
pag::Frame visibleStart = std::max(layer->startTime, localClipStart);
379+
pag::Frame visibleEnd = std::min(layer->startTime + layer->duration, localClipEnd);
380+
if (visibleStart >= visibleEnd) {
381+
continue;
382+
}
383+
384+
// Convert frame offset from current composition's frame rate to main composition's frame rate
385+
float toMainFactor = mainCompFrameRate / currentFrameRate;
386+
pag::Frame absoluteStart =
387+
mainCompClipStart + static_cast<pag::Frame>((visibleStart - localClipStart) * toMainFactor);
388+
pag::Frame absoluteEnd =
389+
mainCompClipStart + static_cast<pag::Frame>((visibleEnd - localClipStart) * toMainFactor);
390+
391+
if (preComposeLayer->composition == targetVideo) {
392+
referenceRanges->push_back({absoluteStart, absoluteEnd});
393+
} else if (preComposeLayer->composition->type() == pag::CompositionType::Vector) {
394+
auto subComposition = preComposeLayer->composition;
395+
float toSubFactor = subComposition->frameRate / currentFrameRate;
396+
pag::Frame newLocalClipStart =
397+
static_cast<pag::Frame>((visibleStart - offset) * toSubFactor);
398+
pag::Frame newLocalClipEnd = static_cast<pag::Frame>((visibleEnd - offset) * toSubFactor);
399+
if (newLocalClipStart < newLocalClipEnd) {
400+
CollectVideoCompositionReferences(subComposition, targetVideo, mainCompFrameRate,
401+
absoluteStart, newLocalClipStart, newLocalClipEnd,
402+
referenceRanges);
403+
}
404+
}
405+
}
406+
}
407+
408+
static std::string GetOverlapFrameRanges(const std::vector<pag::TimeRange>& referenceRanges) {
409+
if (referenceRanges.size() < 2) {
410+
return "";
411+
}
412+
413+
std::vector<std::pair<pag::Frame, int>> events;
414+
for (const auto& range : referenceRanges) {
415+
events.emplace_back(range.start, 1);
416+
events.emplace_back(range.end, -1);
417+
}
418+
419+
std::sort(events.begin(), events.end(), [](const auto& a, const auto& b) {
420+
return a.first != b.first ? a.first < b.first : a.second > b.second;
421+
});
422+
423+
std::vector<pag::TimeRange> overlapRanges;
424+
int count = 0;
425+
pag::Frame overlapStart = 0;
426+
bool inOverlap = false;
427+
428+
for (const auto& event : events) {
429+
if (count > 1 && !inOverlap) {
430+
overlapStart = event.first;
431+
inOverlap = true;
432+
}
433+
count += event.second;
434+
if (count <= 1 && inOverlap) {
435+
overlapRanges.push_back({overlapStart, event.first});
436+
inOverlap = false;
437+
} else if (count > 1 && !inOverlap) {
438+
overlapStart = event.first;
439+
inOverlap = true;
440+
}
441+
}
442+
443+
std::string result;
444+
for (size_t i = 0; i < overlapRanges.size(); i++) {
445+
if (i > 0) {
446+
result += ", ";
447+
}
448+
pag::Frame start = overlapRanges[i].start;
449+
pag::Frame end = overlapRanges[i].end - 1;
450+
if (start == end) {
451+
result += std::to_string(start);
452+
} else {
453+
result += "[" + std::to_string(start) + "-" + std::to_string(end) + "]";
454+
}
455+
}
456+
return result;
457+
}
458+
459+
static size_t GetMaxOverlapCount(const std::vector<pag::TimeRange>& referenceRanges) {
460+
if (referenceRanges.empty()) {
461+
return 0;
462+
}
463+
464+
std::vector<pag::Frame> startTimes, endTimes;
465+
for (const auto& range : referenceRanges) {
466+
startTimes.push_back(range.start);
467+
endTimes.push_back(range.end);
468+
}
469+
470+
std::sort(startTimes.begin(), startTimes.end());
471+
std::sort(endTimes.begin(), endTimes.end());
472+
473+
size_t startIndex = 0, endIndex = 0, maxCount = 0, count = 0;
474+
while (startIndex < startTimes.size()) {
475+
if (startTimes[startIndex] < endTimes[endIndex]) {
476+
maxCount = std::max(maxCount, ++count);
477+
startIndex++;
478+
} else {
479+
count--;
480+
endIndex++;
481+
}
482+
}
483+
return maxCount;
484+
}
485+
486+
static void CheckVideoCompositionOverlap(std::shared_ptr<PAGExportSession> session,
487+
std::vector<pag::Composition*>& compositions) {
488+
constexpr size_t maxAllowedOverlap = 1;
489+
490+
auto mainCompId = GetItemID(session->itemHandle);
491+
pag::Composition* mainComposition = nullptr;
492+
for (auto comp : compositions) {
493+
if (comp->id == mainCompId) {
494+
mainComposition = comp;
495+
break;
496+
}
497+
}
498+
499+
if (mainComposition == nullptr || mainComposition->type() != pag::CompositionType::Vector) {
500+
return;
501+
}
502+
503+
const auto& Suites = GetSuites();
504+
AEGP_CompH compositionHandle = GetItemCompH(session->itemHandle);
505+
A_Time workAreaStart = {};
506+
A_Time workAreaDuration = {};
507+
Suites->CompSuite6()->AEGP_GetCompWorkAreaStart(compositionHandle, &workAreaStart);
508+
Suites->CompSuite6()->AEGP_GetCompWorkAreaDuration(compositionHandle, &workAreaDuration);
509+
pag::Frame clipStart = AEDurationToFrame(workAreaStart, session->frameRate);
510+
pag::Frame clipEnd = clipStart + AEDurationToFrame(workAreaDuration, session->frameRate);
511+
512+
for (auto composition : compositions) {
513+
if (composition->type() != pag::CompositionType::Video) {
514+
continue;
515+
}
516+
517+
auto videoComposition = static_cast<pag::VideoComposition*>(composition);
518+
std::vector<pag::TimeRange> referenceRanges;
519+
CollectVideoCompositionReferences(mainComposition, videoComposition, mainComposition->frameRate,
520+
clipStart, clipStart, clipEnd, &referenceRanges);
521+
522+
size_t maxOverlap = GetMaxOverlapCount(referenceRanges);
523+
if (maxOverlap > maxAllowedOverlap) {
524+
auto itemHandle = session->itemHandleMap[videoComposition->id];
525+
auto name = GetItemName(itemHandle);
526+
auto overlapFrames = GetOverlapFrameRanges(referenceRanges);
527+
qDebug() << "References to Composition" << name.c_str()
528+
<< "overlap at frames: " << overlapFrames.c_str();
529+
session->pushWarning(AlertInfoType::VideoCompositionOverlap, name);
530+
}
531+
}
532+
}
533+
358534
static bool CompareVideoComposition(std::shared_ptr<PAGExportSession> session,
359535
pag::VideoComposition* composition1,
360536
pag::VideoComposition* composition2) {
@@ -654,6 +830,7 @@ void CheckBeforeExport(std::shared_ptr<PAGExportSession> session,
654830
CheckLayerNum(session, compositions);
655831
CheckImageFillRule(session, compositions.back());
656832
CheckVideoCompositionInUIScenes(session, compositions);
833+
CheckVideoCompositionOverlap(session, compositions);
657834
CheckDuplicateVideoSequence(session, compositions);
658835
CheckLayerProperty(session, compositions);
659836
}

exporter/src/export/PAGExport.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ static void AdjustmentPreComposeLayerForVideoComposition(std::shared_ptr<PAGExpo
171171
preComposeLayer->composition->uniqueID == videoComposition->uniqueID) {
172172
if (session->videoCompositionStartTime.find(videoComposition->uniqueID) !=
173173
session->videoCompositionStartTime.end()) {
174-
preComposeLayer->compositionStartTime +=
174+
preComposeLayer->compositionStartTime =
175175
session->videoCompositionStartTime[videoComposition->uniqueID];
176176
}
177177
}

exporter/src/export/sequence/VideoSequence.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,12 @@ static void GetVideoSequence(std::shared_ptr<PAGExportSession> session,
339339

340340
if (!currentFrameIsStatic) {
341341
if (lastFrameIsStatic && firstExportFrame != frame) {
342-
staticTimeRange.end = frame - 1;
342+
staticTimeRange.end = frame - firstExportFrame - 1;
343343
sequence->staticTimeRanges.push_back(staticTimeRange);
344344
}
345-
staticTimeRange.start = frame;
345+
staticTimeRange.start = frame - firstExportFrame;
346346
} else if (frame == duration - 1) {
347-
staticTimeRange.end = frame;
347+
staticTimeRange.end = frame - firstExportFrame;
348348
sequence->staticTimeRanges.push_back(staticTimeRange);
349349
}
350350
lastFrameIsStatic = currentFrameIsStatic;

0 commit comments

Comments
 (0)