Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion debian/control.in
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ Section: libdevel
Multi-Arch: same
Depends:
${shlibs:Depends},
${misc:Depends}
${misc:Depends},
dvd+rw-tools
Recommends:
libudfburn
Description: A library about burning CD.
Expand Down
31 changes: 31 additions & 0 deletions src/dfm-burn/dfm-burn-lib/dopticaldiscmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
#include "private/dopticaldiscmanager_p.h"
#include "private/dxorrisoengine.h"
#include "private/dudfburnengine.h"
#include "private/dvdrwformatengine.h"
#include "private/dsm3hash.h"

#include <QDebug>

Check warning on line 14 in src/dfm-burn/dfm-burn-lib/dopticaldiscmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 14 in src/dfm-burn/dfm-burn-lib/dopticaldiscmanager.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
Expand Down Expand Up @@ -144,6 +145,36 @@
bool DOpticalDiscManager::erase()
{
bool ret { false };

// 查询介质类型
MediaType mediaType { MediaType::kNoMedia };
{
QScopedPointer<DOpticalDiscInfo> info { DOpticalDiscManager::createOpticalInfo(dptr->curDev) };
if (info)
mediaType = info->mediaType();
}

// DVD±RW 且 dvd+rw-format 可用时走新引擎
bool isDvdRw = (mediaType == MediaType::kDVD_PLUS_RW || mediaType == MediaType::kDVD_RW);
QString dvdRwFormat = QStandardPaths::findExecutable("dvd+rw-format");

if (isDvdRw && !dvdRwFormat.isEmpty()) {
QScopedPointer<DVDRwFormatEngine> engine { new DVDRwFormatEngine };
connect(
engine.data(), &DVDRwFormatEngine::jobStatusChanged, this,
[this](JobStatus status, int progress) {
Q_EMIT jobStatusChanged(status, progress, {}, {});
},
Qt::DirectConnection);

ret = engine->doErase(dptr->curDev, mediaType);
return ret;
}

// 降级:走 xorriso 原路径
if (isDvdRw)
qWarning() << "[dfm-burn] dvd+rw-format not found, falling back to xorriso for DVD±RW erase";

QScopedPointer<DXorrisoEngine> engine { new DXorrisoEngine };
connect(
engine.data(), &DXorrisoEngine::jobStatusChanged, this,
Expand Down
165 changes: 165 additions & 0 deletions src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "dvdrwformatengine.h"

#include <QDebug>

Check warning on line 7 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 7 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QProcess>

Check warning on line 8 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QProcess> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 8 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QProcess> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QRegularExpression>

Check warning on line 9 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QRegularExpression> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 9 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QRegularExpression> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QStandardPaths>

Check warning on line 10 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QStandardPaths> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 10 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QStandardPaths> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDir>

Check warning on line 11 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 11 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.

DFM_BURN_USE_NS

DVDRwFormatEngine::DVDRwFormatEngine(QObject *parent)
: QObject(parent)
{
}

DVDRwFormatEngine::~DVDRwFormatEngine()
{
}

bool DVDRwFormatEngine::doErase(const QString &dev, MediaType type)
{
phase = 0;
lastRawProgress = 0;

// 1. 校验设备路径
QString cleanDev = QDir::cleanPath(dev);
if (!cleanDev.startsWith("/dev/sr")) {
qWarning() << "[dfm-burn] Invalid device path:" << dev;
Q_EMIT jobStatusChanged(JobStatus::kFailed, -1);
return false;
}

// 2. 查找 dvd+rw-format 二进制
QString binary = QStandardPaths::findExecutable("dvd+rw-format");
if (binary.isEmpty()) {
qWarning() << "[dfm-burn] dvd+rw-format not found";
Q_EMIT jobStatusChanged(JobStatus::kFailed, -1);
return false;
}

// 3. 根据介质类型选择命令参数
QStringList args;
if (type == MediaType::kDVD_PLUS_RW) {
args << "-force"
<< "-gui"
<< cleanDev;
} else if (type == MediaType::kDVD_RW) {
args << "-blank=full"
<< "-gui"
<< cleanDev;
} else {
qWarning() << "[dfm-burn] Unsupported media type for dvd+rw-format:" << static_cast<int>(type);
Q_EMIT jobStatusChanged(JobStatus::kFailed, -1);
return false;
}

Q_EMIT jobStatusChanged(JobStatus::kRunning, 0);

Check warning on line 61 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Local variable 'jobStatusChanged' shadows outer variable

Check warning on line 61 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Local variable 'jobStatusChanged' shadows outer variable

Check warning on line 61 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Local variable 'jobStatusChanged' shadows outer variable

Check warning on line 61 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Local variable 'jobStatusChanged' shadows outer variable

Check warning on line 61 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Local variable 'jobStatusChanged' shadows outer variable

Check warning on line 61 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Local variable 'jobStatusChanged' shadows outer variable

Check warning on line 61 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Local variable 'jobStatusChanged' shadows outer variable

Check warning on line 61 in src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Local variable 'jobStatusChanged' shadows outer variable

// dvd+rw-format 进度输出机制分析(源码 dvd+rw-format.cpp):
//
// 1. fork() 后子进程 setsid() 执行 SCSI 操作,父进程安装 SIGALRM
// 定时轮询共享内存中的 progress 值(0~65536),用退格符覆盖式
// 输出百分比到 stderr。
//
// 2. -gui 标志的 gui_action 赋值在 fork 之后的子进程中(727 行),
// 父进程的 gui_action 始终为 NULL(129 行),所以 -gui 对父进程
// 的进度输出格式无影响——父进程始终用 \b 退格覆盖方式。
//
// 3. DVD+RW -force 路径有 3 个 wait_for_unit(progress) 调用
// (FORMAT UNIT → STOP DE-ICING → CLOSE SESSION),
// 每个 SCSI 操作的进度指示器独立从 0 开始,
// 导致原始输出出现 3 个 0→100% 周期。
// DVD-RW -blank 路径只有 1 个 wait_for_unit,单周期。
//
// 4. 进度数据通过 stderr 管道传输。QProcess::readyReadStandardOutput
// 信号在 QEventLoop + MergedChannels 下可能不能及时触发(尤其在
// DVD-RW 单阶段输出稀疏时)。改用 waitForReadyRead 主动轮询
// 管道——其内部调用 processEvents 处理 Qt 信号,进度可实时传递。

int totalPhases = (type == MediaType::kDVD_PLUS_RW) ? 3 : 1;

QProcess proc;
proc.setProcessChannelMode(QProcess::MergedChannels);

QByteArray buffer;

proc.start(binary, args);
if (!proc.waitForStarted()) {
qWarning() << "[dfm-burn] Failed to start dvd+rw-format";
Q_EMIT jobStatusChanged(JobStatus::kFailed, -1);
return false;
}

// 主动轮询管道,不依赖 readyReadStandardOutput 信号。
// waitForReadyRead 内部调用 processEvents,emit 的信号能被上层接收。
const int pollInterval { 200 }; // ms

while (proc.state() == QProcess::Running) {
proc.waitForReadyRead(pollInterval);

if (proc.bytesAvailable() > 0) {
buffer += proc.readAllStandardOutput();
parseProgress(buffer, totalPhases);
if (buffer.size() > 1024)
buffer = buffer.right(512);
}
}

// 读取进程退出后管道中的剩余数据
if (proc.bytesAvailable() > 0) {
buffer += proc.readAllStandardOutput();
parseProgress(buffer, totalPhases);
}

// 检查退出结果
if (proc.exitStatus() == QProcess::CrashExit) {
qWarning() << "[dfm-burn] dvd+rw-format crashed";
Q_EMIT jobStatusChanged(JobStatus::kFailed, -1);
return false;
}

int exitCode = proc.exitCode();
if (exitCode == 0) {
Q_EMIT jobStatusChanged(JobStatus::kFinished, 100);
return true;
} else {
qWarning() << "[dfm-burn] dvd+rw-format exited with code" << exitCode;
Q_EMIT jobStatusChanged(JobStatus::kFailed, -1);
return false;
}
}

void DVDRwFormatEngine::parseProgress(const QByteArray &data, int totalPhases)
{
// dvd+rw-format 父进程用退格符覆盖式输出进度,格式如:
// 0.0%\b\b\b\b0.1%\b|\b/\b\b0.2% ...
// 也有部分行带换行符(子进程的诊断输出、父进程结束时的 \n)。
// 在整个缓冲区中搜索最后一个 XX.X% 模式即可获取当前进度。
static const QRegularExpression re("([0-9]+(?:\\.[0-9]+)?)%");
auto matches = re.globalMatch(QString::fromLocal8Bit(data));
QString lastPct;
while (matches.hasNext())
lastPct = matches.next().captured(1);
if (lastPct.isEmpty())
return;

int rawProgress = static_cast<int>(lastPct.toDouble());

// 检测相位切换:进度显著回退说明进入了下一个 SCSI 操作
if (rawProgress < lastRawProgress - 5) {
phase++;
if (phase >= totalPhases)
phase = totalPhases - 1;
}
lastRawProgress = rawProgress;

// 加权计算总体进度:(phase * 100 + rawProgress) / totalPhases
int overall = (phase * 100 + rawProgress) / totalPhases;
if (overall < 100)
Q_EMIT jobStatusChanged(JobStatus::kRunning, overall);
}
37 changes: 37 additions & 0 deletions src/dfm-burn/dfm-burn-lib/private/dvdrwformatengine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef DVDRWFORMATENGINE_H
#define DVDRWFORMATENGINE_H

#include <dfm-burn/dburn_global.h>

#include <QObject>

DFM_BURN_BEGIN_NS

class DVDRwFormatEngine : public QObject
{
Q_OBJECT

public:
explicit DVDRwFormatEngine(QObject *parent = nullptr);
~DVDRwFormatEngine() override;

bool doErase(const QString &dev, MediaType type);

Q_SIGNALS:
void jobStatusChanged(JobStatus status, int progress);

private:
void parseProgress(const QByteArray &data, int totalPhases);

private:
int phase { 0 };
int lastRawProgress { 0 };
};

DFM_BURN_END_NS

#endif // DVDRWFORMATENGINE_H
Loading