mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-04 09:39:42 +02:00
Still better disk error detection in exports
This commit is contained in:
commit
f52f2837be
@ -1469,7 +1469,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder)
|
||||
|
||||
if(0 != encoder->private_->file) {
|
||||
if(encoder->private_->file != stdout)
|
||||
fclose(encoder->private_->file);
|
||||
error = fflush(encoder->private_->file) ||
|
||||
fclose(encoder->private_->file);
|
||||
encoder->private_->file = 0;
|
||||
}
|
||||
|
||||
|
@ -209,10 +209,17 @@ int ufile_close(AVIOContext *pb)
|
||||
{
|
||||
std::unique_ptr<wxFile> f{ (wxFile *)pb->opaque };
|
||||
|
||||
if (f)
|
||||
f->Close();
|
||||
bool success = true;
|
||||
if (f) {
|
||||
success = f->Flush() && f->Close();
|
||||
pb->opaque = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// We're not certain that a close error is for want of space, but we'll
|
||||
// guess that
|
||||
return success ? 0 : -ENOSPC;
|
||||
|
||||
// Implicitly destroy the wxFile object here
|
||||
}
|
||||
|
||||
// Open a file with a (possibly) Unicode filename
|
||||
|
26
src/FFmpeg.h
26
src/FFmpeg.h
@ -928,6 +928,18 @@ private:
|
||||
|
||||
// Deleter adaptor for functions like av_free that take a pointer
|
||||
template<typename T, typename R, R(*Fn)(T*)> struct AV_Deleter {
|
||||
inline R operator() (T* p) const
|
||||
{
|
||||
R result{};
|
||||
if (p)
|
||||
result = Fn(p);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization of previous for void return
|
||||
template<typename T, void(*Fn)(T*)>
|
||||
struct AV_Deleter<T, void, Fn> {
|
||||
inline void operator() (T* p) const
|
||||
{
|
||||
if (p)
|
||||
@ -959,9 +971,19 @@ using AVCodecContextHolder = std::unique_ptr<
|
||||
using AVDictionaryCleanup = std::unique_ptr<
|
||||
AVDictionary*, AV_Deleter<AVDictionary*, void, av_dict_free>
|
||||
>;
|
||||
using UFileHolder = std::unique_ptr<
|
||||
struct UFileHolder : public std::unique_ptr<
|
||||
AVIOContext, AV_Deleter<AVIOContext, int, ufile_close>
|
||||
>;
|
||||
>
|
||||
{
|
||||
// Close explicitly, not ignoring return values.
|
||||
int close()
|
||||
{
|
||||
auto result = get_deleter() ( get() );
|
||||
release();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> using AVMallocHolder = std::unique_ptr<
|
||||
T, AV_Deleter<void, void, av_free>
|
||||
>;
|
||||
|
@ -288,7 +288,7 @@ OSType sf_header_mactype(int format)
|
||||
|
||||
ODLock libSndFileMutex;
|
||||
|
||||
void SFFileCloser::operator() (SNDFILE *sf) const
|
||||
int SFFileCloser::operator() (SNDFILE *sf) const
|
||||
{
|
||||
auto err = SFCall<int>(sf_close, sf);
|
||||
if (err) {
|
||||
@ -299,4 +299,5 @@ void SFFileCloser::operator() (SNDFILE *sf) const
|
||||
(_("Error (file may not have been written): %s"),
|
||||
buffer));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -127,7 +127,16 @@ inline R SFCall(F fun, Args&&... args)
|
||||
}
|
||||
|
||||
//RAII for SNDFILE*
|
||||
struct SFFileCloser { void operator () (SNDFILE*) const; };
|
||||
using SFFile = std::unique_ptr<SNDFILE, SFFileCloser>;
|
||||
struct SFFileCloser { int operator () (SNDFILE*) const; };
|
||||
struct SFFile : public std::unique_ptr<SNDFILE, SFFileCloser>
|
||||
{
|
||||
// Close explicitly, not ignoring return values.
|
||||
int close()
|
||||
{
|
||||
auto result = get_deleter() ( get() );
|
||||
release();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -52,11 +52,18 @@ bool FileIO::IsOpened()
|
||||
return mOpen;
|
||||
}
|
||||
|
||||
void FileIO::Close()
|
||||
bool FileIO::Close()
|
||||
{
|
||||
mOutputStream.reset();
|
||||
bool success = true;
|
||||
if (mOutputStream) {
|
||||
// mOutputStream->Sync() returns void! Rrr!
|
||||
success = mOutputStream->GetFile()->Flush() &&
|
||||
mOutputStream->Close();
|
||||
mOutputStream.reset();
|
||||
}
|
||||
mInputStream.reset();
|
||||
mOpen = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
wxInputStream & FileIO::Read(void *buf, size_t size)
|
||||
|
@ -32,7 +32,7 @@ class FileIO
|
||||
|
||||
bool IsOpened();
|
||||
|
||||
void Close();
|
||||
bool Close();
|
||||
|
||||
wxInputStream & Read(void *buffer, size_t size);
|
||||
wxOutputStream & Write(const void *buffer, size_t size);
|
||||
@ -41,7 +41,7 @@ class FileIO
|
||||
wxString mName;
|
||||
FileIOMode mMode;
|
||||
std::unique_ptr<wxInputStream> mInputStream;
|
||||
std::unique_ptr<wxOutputStream> mOutputStream;
|
||||
std::unique_ptr<wxFFileOutputStream> mOutputStream;
|
||||
bool mOpen;
|
||||
};
|
||||
|
||||
|
@ -945,6 +945,12 @@ ProgressResult ExportFFmpeg::Export(AudacityProject *project,
|
||||
if ( !Finalize() ) // Finalize makes its own messages
|
||||
return ProgressResult::Cancelled;
|
||||
|
||||
if ( mUfileCloser.close() != 0 ) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
return ProgressResult::Cancelled;
|
||||
}
|
||||
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
|
@ -335,10 +335,13 @@ ProgressResult ExportFLAC::Export(AudacityProject *project,
|
||||
mMetadata.reset();
|
||||
|
||||
auto cleanup2 = finally( [&] {
|
||||
if (!(updateResult == ProgressResult::Success ||
|
||||
updateResult == ProgressResult::Stopped)) {
|
||||
#ifndef LEGACY_FLAC
|
||||
f.Detach(); // libflac closes the file
|
||||
f.Detach(); // libflac closes the file
|
||||
#endif
|
||||
encoder.finish();
|
||||
encoder.finish();
|
||||
}
|
||||
} );
|
||||
|
||||
const WaveTrackConstArray waveTracks =
|
||||
@ -389,6 +392,20 @@ ProgressResult ExportFLAC::Export(AudacityProject *project,
|
||||
}
|
||||
}
|
||||
|
||||
if (updateResult == ProgressResult::Success ||
|
||||
updateResult == ProgressResult::Stopped) {
|
||||
#ifndef LEGACY_FLAC
|
||||
f.Detach(); // libflac closes the file
|
||||
#endif
|
||||
if (!encoder.finish())
|
||||
// Do not reassign updateResult, see cleanup2
|
||||
return ProgressResult::Failed;
|
||||
#ifdef LEGACY_FLAC
|
||||
if (!f.Flush() || !f.Close())
|
||||
return ProgressResult::Failed;
|
||||
#endif
|
||||
}
|
||||
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
|
@ -330,7 +330,11 @@ ProgressResult ExportMP2::Export(AudacityProject *project,
|
||||
return ProgressResult::Cancelled;
|
||||
}
|
||||
|
||||
outFile.Close();
|
||||
if ( !outFile.Close() ) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
return ProgressResult::Cancelled;
|
||||
}
|
||||
|
||||
return updateResult;
|
||||
}
|
||||
|
@ -854,7 +854,7 @@ public:
|
||||
int FinishStream(unsigned char outbuffer[]);
|
||||
void CancelEncoding();
|
||||
|
||||
void PutInfoTag(wxFFile & f, wxFileOffset off);
|
||||
bool PutInfoTag(wxFFile & f, wxFileOffset off);
|
||||
|
||||
private:
|
||||
|
||||
@ -1393,17 +1393,21 @@ void MP3Exporter::CancelEncoding()
|
||||
mEncoding = false;
|
||||
}
|
||||
|
||||
void MP3Exporter::PutInfoTag(wxFFile & f, wxFileOffset off)
|
||||
bool MP3Exporter::PutInfoTag(wxFFile & f, wxFileOffset off)
|
||||
{
|
||||
if (mGF) {
|
||||
if (mInfoTagLen > 0) {
|
||||
// FIXME: TRAP_ERR Seek and writ ein MP3 exporter could fail.
|
||||
f.Seek(off, wxFromStart);
|
||||
f.Write(mInfoTagBuf, mInfoTagLen);
|
||||
if ( !f.Seek(off, wxFromStart))
|
||||
return false;
|
||||
if (mInfoTagLen > f.Write(mInfoTagBuf, mInfoTagLen))
|
||||
return false;
|
||||
}
|
||||
#if defined(__WXMSW__)
|
||||
else if (beWriteInfoTag) {
|
||||
f.Flush();
|
||||
if ( !f.Flush() )
|
||||
return false;
|
||||
// PRL: What is the correct error check on the return value?
|
||||
beWriteInfoTag(mGF, OSOUTPUT(f.GetName()));
|
||||
mGF = NULL;
|
||||
}
|
||||
@ -1413,7 +1417,10 @@ void MP3Exporter::PutInfoTag(wxFFile & f, wxFileOffset off)
|
||||
}
|
||||
}
|
||||
|
||||
f.SeekEnd();
|
||||
if ( !f.SeekEnd() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
@ -1801,7 +1808,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
|
||||
bool endOfFile;
|
||||
id3len = AddTags(project, id3buffer, &endOfFile, metadata);
|
||||
if (id3len && !endOfFile) {
|
||||
if (id3len < outFile.Write(id3buffer.get(), id3len)) {
|
||||
if (id3len > outFile.Write(id3buffer.get(), id3len)) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
return ProgressResult::Cancelled;
|
||||
@ -1887,7 +1894,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytes < outFile.Write(buffer.get(), bytes)) {
|
||||
if (bytes > outFile.Write(buffer.get(), bytes)) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
updateResult = ProgressResult::Cancelled;
|
||||
@ -1909,7 +1916,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
|
||||
}
|
||||
|
||||
if (bytes > 0) {
|
||||
if (bytes < outFile.Write(buffer.get(), bytes)) {
|
||||
if (bytes > outFile.Write(buffer.get(), bytes)) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
return ProgressResult::Cancelled;
|
||||
@ -1918,7 +1925,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
|
||||
|
||||
// Write ID3 tag if it was supposed to be at the end of the file
|
||||
if (id3len > 0 && endOfFile) {
|
||||
if (bytes < outFile.Write(id3buffer.get(), id3len)) {
|
||||
if (bytes > outFile.Write(id3buffer.get(), id3len)) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
return ProgressResult::Cancelled;
|
||||
@ -1931,9 +1938,9 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
|
||||
//
|
||||
// Also, if beWriteInfoTag() is used, mGF will no longer be valid after
|
||||
// this call, so do not use it.
|
||||
exporter.PutInfoTag(outFile, pos);
|
||||
|
||||
if (!outFile.Close()) {
|
||||
if (!exporter.PutInfoTag(outFile, pos) ||
|
||||
!outFile.Flush() ||
|
||||
!outFile.Close()) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
return ProgressResult::Cancelled;
|
||||
|
@ -359,7 +359,11 @@ ProgressResult ExportOGG::Export(AudacityProject *project,
|
||||
}
|
||||
}
|
||||
|
||||
outFile.Close();
|
||||
if ( !outFile.Close() ) {
|
||||
updateResult = ProgressResult::Cancelled;
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
}
|
||||
|
||||
return updateResult;
|
||||
}
|
||||
|
@ -530,7 +530,7 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
|
||||
|
||||
// Install the WAV metata in a "LIST" chunk at the end of the file
|
||||
if (updateResult == ProgressResult::Success ||
|
||||
updateResult == ProgressResult::Stopped)
|
||||
updateResult == ProgressResult::Stopped) {
|
||||
if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV ||
|
||||
(sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAVEX) {
|
||||
if (!AddStrings(project, sf.get(), metadata, sf_format)) {
|
||||
@ -538,6 +538,12 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
return ProgressResult::Cancelled;
|
||||
}
|
||||
}
|
||||
if (0 != sf.close()) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
return ProgressResult::Cancelled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -545,6 +551,7 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
|
||||
updateResult == ProgressResult::Stopped)
|
||||
if (((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||
|
||||
((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV))
|
||||
// Note: file has closed, and gets reopened and closed again here:
|
||||
if (!AddID3Chunk(fName, metadata, sf_format) ) {
|
||||
// TODO: more precise message
|
||||
AudacityMessageBox(_("Unable to export"));
|
||||
@ -845,6 +852,9 @@ bool ExportPCM::AddID3Chunk(wxString fName, const Tags *tags, int sf_format)
|
||||
if (4 != f.Write(&sz, 4))
|
||||
return false;
|
||||
|
||||
if (!f.Flush())
|
||||
return false;
|
||||
|
||||
if (!f.Close())
|
||||
return false;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user