停止录制后,QAudioRecorder没有释放该文件

编程语言 2026-07-12

我在我的QML应用中有一个简单的AudioRecorder。由于在Qt 5中没有直接可用的QML控件来录制音频,我自定义了一个从 QAudioRecorder 派生的类:

#include <QObject>
#include <QAudioRecorder>
#include <QStandardPaths>
#include <QUrl>
#include <QDir>
#include <qqml.h>

class AudioRecorder : public QAudioRecorder
{
    Q_OBJECT
    QML_ELEMENT

public:
    AudioRecorder(QObject* parent = Q_NULLPTR) : QAudioRecorder(parent)
    {
        QString d = QStandardPaths::writableLocation(QStandardPaths::StandardLocation::MusicLocation);
        if (!d.isEmpty())
            setOutputLocation(QUrl::fromLocalFile(d));
    }
};

在我的应用中,我有一个简单的按钮,用于启动和停止录音:

// My own QML element
AudioRecorder {
    id: audioRecorder
}

Button {
    id: recButton

    onClicked: {
        if (audioRecorder.state === MediaState.StoppedState)
            audioRecorder.record()
        else if (audioRecorder.state === MediaState.RecordingState)
            audioRecorder.stop()
    }
}

录音结束后,会出现另一个按钮,可以用来删除创建的音频文件。我使用一个辅助类来删除文件,该类提供了一些从QML直接访问不到的函数:

Button {
    id: remButton
    visible: audioRecorder.state === MediaState.StoppedState
    onClicked: QMLHelper.removeFile(audioRecorder.actualLocation)
}
class QMLHelper : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    QML_SINGLETON

public:
    QMLHelper(QObject* parent = Q_NULLPTR) : QObject(parent) { Q_UNUSED(parent) };

    Q_INVOKABLE bool removeFile(const QString& path)
    {
        if (!path.isEmpty())
        {
            QFile f(path);
            bool r = f.remove();
            qDebug() << r << ": " << r.errorString();
            return r;
        }
        return false;
    };
}

当我尝试删除文件时,输出是:

false: 该进程无法访问该文件,因为它正在被另一个进程使用。

为什么在调用 stop() 之后,QAudioRecorder 没有释放该文件?我该如何释放该文件以便删除它?

备注:我在QML的 videoRecorder上也尝试过,这里可以正常工作:

Camera {
    id: camera
    videoRecorder {
        id: vr
    }
}

Button {
    id: vidButton
    visible: camera.videoRecorder.recorderStatus !== CameraRecorder.UnavailableStatus
    onClicked: {
        if (camera.videoRecorder.recorderState === CameraRecorder.StoppedState)
            camera.videoRecorder.record()
        else if (camera.videoRecorder.recorderState === CameraRecorder.RecordingState)
            camera.videoRecorder.stop() 
    }
}

Button {
    id: vidRemButton
    visible: camera.videoRecorder.recorderState === CameraRecorder.StoppedState
    onClicked: QMLHelper.removeFile(camera.videoRecorder.actualLocation)
}

输出:

true

解决方案

我通过把 AudioRecorder-项放到 Loader 里面解决了这个问题。删除时,我释放当前的 sourceComponent,并重新实例化一个新的,以释放当前对象的所有资源,正如 文档 中所述。

Loader {
    id: audioLoader
    sourceComponent: audioComponent
}

Component {
    id: audioComponent
    AudioRecorder { }
}

Button {
    id: remButton
    visible: audioLoader.item ? audioLoader.item.state === MediaState.StoppedState : false
    onClicked: removeFile()
}

function removeFile() {
    var p = audioLoader.item
    if (p) {
        var prevLoc = p.actualLocation
        audioLoader.sourceComponent = undefined
        audioLoader.sourceComponent = audioComponent
        QMLHelper.removeFile(prevLoc)
    }
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章