Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion exporter/src/export/PAGExport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ std::shared_ptr<pag::File> PAGExport::exportAsFile() {

auto compositions = session->compositions;
if (session->exportAudio && session->configParam.isTagCodeSupport(pag::TagCode::AudioBytes)) {
GetAudioSequence(itemHandle, session->outputPath, compositions[compositions.size() - 1]);
GetAudioSequence(itemHandle, compositions[compositions.size() - 1]);
CombineAudioMarkers(compositions);
}
if (session->stopExport) {
Expand Down
22 changes: 10 additions & 12 deletions exporter/src/export/sequence/AudioSequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "AudioSequence.h"
#include <cstdio>
#include "ffmovie/movie.h"
#include "platform/PlatformHelper.h"
#include "utils/FileHelper.h"
#include "utils/PAGExportSession.h"
#include "utils/PAGExportSessionManager.h"

Expand All @@ -40,9 +42,11 @@ void CombineAudioMarkers(std::vector<pag::Composition*>& compositions) {
}
}

static std::string EncodeAudioToMP4(int16_t* samples, int numSamples, int channels, int sampleRate,
const std::string& outputPath) {
std::string mp4Path = outputPath + "_audio.mp4";
static std::string EncodeAudioToMP4(int16_t* samples, int numSamples, int channels,
int sampleRate) {
// Use a safe ASCII path in the temp folder to avoid encoding issues with non-ASCII characters
// on Windows, where FFmpeg may not correctly handle UTF-8 paths.
std::string mp4Path = JoinPaths(GetTempFolderPath(), ".pag_audio_temp.mp4");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium] 两个问题:

  1. 临时文件名固定为 .pag_audio_temp.mp4,多进程并发导出时会互相覆盖。建议使用唯一文件名(如添加 PID 或时间戳后缀)。
  2. 多个提前返回路径(encoder/muxer 创建失败等)不会删除已创建的临时文件。建议使用 RAII 或 scope guard 统一清理。

ffmovie::AudioExportConfig audioConfig = {};
audioConfig.sampleRate = sampleRate;
audioConfig.channels = channels;
Expand Down Expand Up @@ -106,16 +110,9 @@ static std::string EncodeAudioToMP4(int16_t* samples, int numSamples, int channe
return mp4Path;
}

void GetAudioSequence(const AEGP_ItemH& itemHandle, const std::string& outputPath,
pag::Composition* composition) {
void GetAudioSequence(const AEGP_ItemH& itemHandle, pag::Composition* composition) {
const auto& Suites = GetSuites();
const auto& PluginID = GetPluginID();
std::string path = outputPath;
const std::string pagExtension = ".pag";
if (path.size() > pagExtension.size() &&
path.compare(path.size() - pagExtension.size(), pagExtension.size(), pagExtension) == 0) {
path = path.substr(0, path.size() - pagExtension.size());
}

AEGP_ItemFlags flags = 0;
Suites->ItemSuite6()->AEGP_GetItemFlags(itemHandle, &flags);
Expand Down Expand Up @@ -149,12 +146,13 @@ void GetAudioSequence(const AEGP_ItemH& itemHandle, const std::string& outputPat
int offset = startSamples * soundFormat2.num_channelsL;
std::string mp4Path = EncodeAudioToMP4(static_cast<int16_t*>(samples) + offset, numSamples,
static_cast<int>(soundFormat2.num_channelsL),
static_cast<int>(soundFormat2.sample_rateF), path);
static_cast<int>(soundFormat2.sample_rateF));

if (!mp4Path.empty()) {
composition->audioBytes = pag::ByteData::FromPath(mp4Path).release();
composition->audioStartTime = static_cast<pag::Frame>(
(startSamples / soundFormat2.sample_rateF) * composition->frameRate);
DeleteFile(mp4Path);
} else {
PAGExportSessionManager::GetInstance()->recordWarning(AlertInfoType::AudioEncodeFail);
}
Expand Down
3 changes: 1 addition & 2 deletions exporter/src/export/sequence/AudioSequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ namespace exporter {

void CombineAudioMarkers(std::vector<pag::Composition*>& compositions);

void GetAudioSequence(const AEGP_ItemH& itemHandle, const std::string& outputPath,
pag::Composition* composition);
void GetAudioSequence(const AEGP_ItemH& itemHandle, pag::Composition* composition);

} // namespace exporter
1 change: 1 addition & 0 deletions viewer/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ assets/translation/Chinese.qm
build_*/
cmake-build-*/
src/editing/rttr/PAGRttr.hpp
src/editing/rttr/PAGXRttr.hpp
src/version.h
tools/*RttrAutoRegister*
tools/libclang.dll
2 changes: 2 additions & 0 deletions viewer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ endif ()
set(PAG_USE_QT ON)
set(PAG_USE_RTTR ON)
set(PAG_USE_LIBAVC OFF)
set(PAG_BUILD_PAGX ON)
set(PAG_BUILD_TESTS OFF)
set(PAG_USE_HARFBUZZ ON)
set(PAG_BUILD_SHARED OFF)
Expand All @@ -145,6 +146,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
list(APPEND PAG_OPTIONS "-DPAG_USE_QT=${PAG_USE_QT}")
list(APPEND PAG_OPTIONS "-DPAG_USE_RTTR=${PAG_USE_RTTR}")
list(APPEND PAG_OPTIONS "-DPAG_USE_LIBAVC=${PAG_USE_LIBAVC}")
list(APPEND PAG_OPTIONS "-DPAG_BUILD_PAGX=${PAG_BUILD_PAGX}")
list(APPEND PAG_OPTIONS "-DPAG_BUILD_TESTS=${PAG_BUILD_TESTS}")
list(APPEND PAG_OPTIONS "-DPAG_USE_HARFBUZZ=${PAG_USE_HARFBUZZ}")
list(APPEND PAG_OPTIONS "-DPAG_BUILD_SHARED=${PAG_BUILD_SHARED}")
Expand Down
51 changes: 33 additions & 18 deletions viewer/assets/qml/ControlForm.qml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import "components"

Item {
id: form
required property var pagView
property bool hasPAGFile: (pagView && pagView.filePath !== "")
required property var contentView
property bool hasPAGFile: (contentView && contentView.viewModel.filePath !== "")
property bool hasAnimation: (contentView && contentView.viewModel.hasAnimation)

property bool updateAvailable: false

Expand Down Expand Up @@ -49,7 +50,8 @@ Item {
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
enabled: hasPAGFile
visible: hasAnimation
enabled: hasPAGFile && hasAnimation
background: Rectangle {
x: progressSlider.leftPadding
y: progressSlider.topPadding + progressSlider.availableHeight / 2 - height / 2
Expand Down Expand Up @@ -97,41 +99,46 @@ Item {
id: playButton
width: 48
height: 48
visible: true
enabled: hasPAGFile
enabled: hasPAGFile && hasAnimation
focusPolicy: Qt.NoFocus
flat: true
display: AbstractButton.IconOnly
opacity: pressed ? 1 : hovered ? 0.9 : 0.8
opacity: enabled ? (pressed ? 1 : hovered ? 0.9 : 0.8) : 0.3
background: Image {
source: ((pagView && pagView.isPlaying) || form.lastPlayStatusIsPlaying) ? "qrc:/images/pause.png" : "qrc:/images/play.png"
source: ((contentView && contentView.viewModel.isPlaying) || form.lastPlayStatusIsPlaying) ? "qrc:/images/pause.png" : "qrc:/images/play.png"
}
onClicked: {
pagView.isPlaying = !pagView.isPlaying;
if (contentView) {
contentView.viewModel.isPlaying = !contentView.viewModel.isPlaying;
}
}
}
Button {
id: previousButton
width: 24
height: 20
enabled: hasPAGFile
enabled: hasPAGFile && hasAnimation
focusPolicy: Qt.NoFocus
display: AbstractButton.IconOnly
flat: true
anchors.verticalCenter: parent.verticalCenter
opacity: pressed ? 1 : hovered ? 0.9 : 0.8
opacity: enabled ? (pressed ? 1 : hovered ? 0.9 : 0.8) : 0.3
background: Image {
source: "qrc:/images/previous.png"
}
onPressAndHold: {
pagView.previousFrame();
if (contentView) {
contentView.viewModel.previousFrame();
}
longPressPreTimer.start();
}
onReleased: {
longPressPreTimer.stop();
}
onClicked: {
pagView.previousFrame();
if (contentView) {
contentView.viewModel.previousFrame();
}
}

Timer {
Expand All @@ -140,32 +147,38 @@ Item {
repeat: true
running: false
onTriggered: {
pagView.previousFrame();
if (contentView) {
contentView.viewModel.previousFrame();
}
}
}
}
Button {
id: nextButton
width: 24
height: 20
enabled: hasPAGFile
enabled: hasPAGFile && hasAnimation
focusPolicy: Qt.NoFocus
display: AbstractButton.IconOnly
flat: true
anchors.verticalCenter: parent.verticalCenter
opacity: pressed ? 1 : hovered ? 0.9 : 0.8
opacity: enabled ? (pressed ? 1 : hovered ? 0.9 : 0.8) : 0.3
background: Image {
source: "qrc:/images/next.png"
}
onPressAndHold: {
pagView.nextFrame();
if (contentView) {
contentView.viewModel.nextFrame();
}
longPressNextTimer.start();
}
onReleased: {
longPressNextTimer.stop();
}
onClicked: {
pagView.nextFrame();
if (contentView) {
contentView.viewModel.nextFrame();
}
}

Timer {
Expand All @@ -174,7 +187,9 @@ Item {
repeat: true
running: false
onTriggered: {
pagView.nextFrame();
if (contentView) {
contentView.viewModel.nextFrame();
}
}
}
}
Expand Down
82 changes: 76 additions & 6 deletions viewer/assets/qml/FileTreeViewDelegate.qml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Item {
id: displayComponent

Row {
id: displayRow
anchors.fill: parent
Item {
width: viewDelegate.padding + (viewDelegate.depth * viewDelegate.indentation)
Expand All @@ -74,6 +75,7 @@ Item {
}

Text {
id: displayNameText
x: viewDelegate.padding + (viewDelegate.isTreeNode ? (viewDelegate.depth * viewDelegate.indentation + font.pixelSize) : 0)
height: viewDelegate.height
text: viewDelegate.name + " : "
Expand All @@ -87,16 +89,50 @@ Item {
}
Comment thread
CodeJhF marked this conversation as resolved.

Text {
id: displayValueText
width: displayRow.width - displayNameText.width - viewDelegate.padding - (viewDelegate.depth * viewDelegate.indentation) - 20
height: viewDelegate.height
text: viewDelegate.value.length > 20 ? viewDelegate.value.substr(0, 20) : viewDelegate.value
text: viewDelegate.value
font.pixelSize: 12
font.kerning: false
font.letterSpacing: 0.2
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
clip: true
textFormat: TextEdit.RichText
elide: Text.ElideRight
textFormat: Text.PlainText
color: viewDelegate.selected ? "#FFFFFF" : "#EEEEEE"

MouseArea {
id: displayValueArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
}

ToolTip {
id: displayToolTip
parent: displayValueText
visible: displayValueArea.containsMouse && displayValueText.truncated
delay: 500
x: 0
y: displayValueText.height + 2
width: Math.min(tooltipText1.implicitWidth + 16, 280)

contentItem: Text {
id: tooltipText1
text: viewDelegate.value
font.pixelSize: 12
color: "#EEEEEE"
wrapMode: Text.Wrap
}

background: Rectangle {
color: "#2D2D37"
border.color: "#3485F6"
border.width: 1
radius: 4
}
}
}
}
}
Expand All @@ -105,6 +141,7 @@ Item {
id: editComponent

Row {
id: editRow
anchors.fill: parent
Item {
width: viewDelegate.padding + (viewDelegate.depth * viewDelegate.indentation)
Expand Down Expand Up @@ -142,16 +179,49 @@ Item {

Text {
id: valueText
width: editRow.width - nameText.width - viewDelegate.padding - (viewDelegate.depth * viewDelegate.indentation) - 40
height: viewDelegate.height
text: viewDelegate.value.length > 20 ? viewDelegate.value.substr(0, 20) : viewDelegate.value
text: viewDelegate.value
font.pixelSize: 12
font.kerning: false
font.letterSpacing: 0.2
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
clip: true
textFormat: TextEdit.RichText
elide: Text.ElideRight
textFormat: Text.PlainText
color: viewDelegate.selected ? "#FFFFFF" : "#EEEEEE"

MouseArea {
id: editValueArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
}

ToolTip {
id: editToolTip
parent: valueText
visible: editValueArea.containsMouse && valueText.truncated
delay: 500
x: 0
y: valueText.height + 2
width: Math.min(tooltipText2.implicitWidth + 16, 280)

contentItem: Text {
id: tooltipText2
text: viewDelegate.value
font.pixelSize: 12
color: "#EEEEEE"
wrapMode: Text.Wrap
}

background: Rectangle {
color: "#2D2D37"
border.color: "#3485F6"
border.width: 1
radius: 4
}
}
}

ComboBox {
Expand Down
Loading
Loading