1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-15 07:01:18 +02:00

Exception safety in: subclasses of ImportFileHandle

This commit is contained in:
Paul Licameli
2016-08-16 13:56:15 -04:00
parent ed6f2ea80f
commit 2626f6cd5b
9 changed files with 124 additions and 89 deletions

View File

@@ -847,6 +847,8 @@ void FFmpegImportFileHandle::GetMetadata(Tags *tags, const wxChar *tag, const ch
FFmpegImportFileHandle::~FFmpegImportFileHandle() FFmpegImportFileHandle::~FFmpegImportFileHandle()
{ {
av_log_set_callback(av_log_default_callback);
// Do this before unloading the libraries // Do this before unloading the libraries
mContext.reset(); mContext.reset();

View File

@@ -561,7 +561,8 @@ ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory,
FLACImportFileHandle::~FLACImportFileHandle() FLACImportFileHandle::~FLACImportFileHandle()
{ {
//don't DELETE mFile if we are using OD. //don't finish *mFile if we are using OD,
//because it was not initialized in Init().
#ifndef EXPERIMENTAL_OD_FLAC #ifndef EXPERIMENTAL_OD_FLAC
mFile->finish(); mFile->finish();
#endif #endif

View File

@@ -157,7 +157,8 @@ struct GStreamContext
}; };
// For RAII on gst objects // For RAII on gst objects
template<typename T> using GstObjHandle = std::unique_ptr < T, Deleter<void, gst_object_unref > > ; template<typename T> using GstObjHandle =
std::unique_ptr < T, Deleter<void, gst_object_unref > > ;
///! Does actual import, returned by GStreamerImportPlugin::Open ///! Does actual import, returned by GStreamerImportPlugin::Open
class GStreamerImportFileHandle final : public ImportFileHandle class GStreamerImportFileHandle final : public ImportFileHandle
@@ -273,17 +274,21 @@ GetGStreamerImportPlugin(ImportPluginList &importPluginList,
// Initialize gstreamer // Initialize gstreamer
GErrorHandle error; GErrorHandle error;
bool initError;
{
int argc = 0; int argc = 0;
char **argv = NULL; char **argv = NULL;
GError *ee; GError *ee;
if (!gst_init_check(&argc, &argv, &ee)) initError = !gst_init_check(&argc, &argv, &ee);
error.reset(ee);
}
if ( initError )
{ {
wxLogMessage(wxT("Failed to initialize GStreamer. Error %d: %s"), wxLogMessage(wxT("Failed to initialize GStreamer. Error %d: %s"),
error.get()->code, error.get()->code,
wxString::FromUTF8(error.get()->message).c_str()); wxString::FromUTF8(error.get()->message).c_str());
return; return;
} }
error.reset(ee);
guint major, minor, micro, nano; guint major, minor, micro, nano;
gst_version(&major, &minor, &micro, &nano); gst_version(&major, &minor, &micro, &nano);

View File

@@ -142,24 +142,23 @@ public:
private: private:
// Takes a line of text in lof file and interprets it and opens files // Takes a line of text in lof file and interprets it and opens files
void lofOpenFiles(wxString* ln); void lofOpenFiles(wxString* ln);
void doDuration(); void doDurationAndScrollOffset();
void doScrollOffset();
std::unique_ptr<wxTextFile> mTextFile; std::unique_ptr<wxTextFile> mTextFile;
wxFileName mLOFFileName; /**< The name of the LOF file, which is used to wxFileName mLOFFileName; /**< The name of the LOF file, which is used to
interpret relative paths in it */ interpret relative paths in it */
AudacityProject *mProject; AudacityProject *mProject{ GetActiveProject() };
// In order to know whether or not to create a NEW window // In order to know whether or not to create a NEW window
bool windowCalledOnce; bool windowCalledOnce{ false };
// In order to zoom in, it must be done after files are opened // In order to zoom in, it must be done after files are opened
bool callDurationFactor; bool callDurationFactor{ false };
double durationFactor; double durationFactor{ 1 };
// In order to offset scrollbar, it must be done after files are opened // In order to offset scrollbar, it must be done after files are opened
bool callScrollOffset; bool callScrollOffset{ false };
double scrollOffset; double scrollOffset{ 0 };
}; };
LOFImportFileHandle::LOFImportFileHandle LOFImportFileHandle::LOFImportFileHandle
@@ -168,12 +167,6 @@ LOFImportFileHandle::LOFImportFileHandle
mTextFile(std::move(file)) mTextFile(std::move(file))
, mLOFFileName{name} , mLOFFileName{name}
{ {
mProject = GetActiveProject();
windowCalledOnce = false;
callDurationFactor = false;
durationFactor = 1;
callScrollOffset = false;
scrollOffset = 0;
} }
void GetLOFImportPlugin(ImportPluginList &importPluginList, void GetLOFImportPlugin(ImportPluginList &importPluginList,
@@ -190,6 +183,7 @@ wxString LOFImportPlugin::GetPluginFormatDescription()
std::unique_ptr<ImportFileHandle> LOFImportPlugin::Open(const wxString &filename) std::unique_ptr<ImportFileHandle> LOFImportPlugin::Open(const wxString &filename)
{ {
// Check if it is a binary file // Check if it is a binary file
{
wxFile binaryFile; wxFile binaryFile;
if (!binaryFile.Open(filename)) if (!binaryFile.Open(filename))
return nullptr; // File not found return nullptr; // File not found
@@ -208,9 +202,7 @@ std::unique_ptr<ImportFileHandle> LOFImportPlugin::Open(const wxString &filename
return nullptr; return nullptr;
} }
} }
}
// Close it again so it can be opened as a text file
binaryFile.Close();
// Now open the file again as text file // Now open the file again as text file
auto file = std::make_unique<wxTextFile>(filename); auto file = std::make_unique<wxTextFile>(filename);
@@ -235,6 +227,18 @@ auto LOFImportFileHandle::GetFileUncompressedBytes() -> ByteCount
ProgressResult LOFImportFileHandle::Import(TrackFactory * WXUNUSED(trackFactory), TrackHolders &outTracks, ProgressResult LOFImportFileHandle::Import(TrackFactory * WXUNUSED(trackFactory), TrackHolders &outTracks,
Tags * WXUNUSED(tags)) Tags * WXUNUSED(tags))
{ {
// Unlike other ImportFileHandle subclasses, this one never gives any tracks
// back to the caller.
// Instead, it recursively calls AudacityProject::Import for each file listed
// in the .lof file.
// Each importation creates a new undo state.
// If there is an error or exception during one of them, only that one's
// side effects are rolled back, and the rest of the import list is skipped.
// The file may have "window" directives that cause new AudacityProjects
// to be created, and the undo states are pushed onto the latest project.
// If a project is created but the first file import into it fails, destroy
// the project.
outTracks.clear(); outTracks.clear();
wxASSERT(mTextFile->IsOpened()); wxASSERT(mTextFile->IsOpened());
@@ -256,15 +260,13 @@ ProgressResult LOFImportFileHandle::Import(TrackFactory * WXUNUSED(trackFactory)
// for last line // for last line
lofOpenFiles(&line); lofOpenFiles(&line);
// set any duration/offset factors for last window, as all files were called if(!mTextFile->Close())
doDuration();
doScrollOffset();
// exited ok
if(mTextFile->Close())
return ProgressResult::Success;
return ProgressResult::Failed; return ProgressResult::Failed;
// set any duration/offset factors for last window, as all files were called
doDurationAndScrollOffset();
return ProgressResult::Success;
} }
static int CountNumTracks(AudacityProject *proj) static int CountNumTracks(AudacityProject *proj)
@@ -302,8 +304,7 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
if (tokenholder.IsSameAs(wxT("window"), false)) if (tokenholder.IsSameAs(wxT("window"), false))
{ {
// set any duration/offset factors for last window, as all files were called // set any duration/offset factors for last window, as all files were called
doDuration(); doDurationAndScrollOffset();
doScrollOffset();
if (windowCalledOnce) if (windowCalledOnce)
{ {
@@ -380,6 +381,8 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
} }
} }
// Do recursive call to import
#ifdef USE_MIDI #ifdef USE_MIDI
// If file is a midi // If file is a midi
if (targetfile.AfterLast(wxT('.')).IsSameAs(wxT("mid"), false) if (targetfile.AfterLast(wxT('.')).IsSameAs(wxT("mid"), false)
@@ -459,6 +462,9 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
t->SetOffset(offset); t->SetOffset(offset);
} }
} }
// Amend the undo transaction made by import
mProject->TP_ModifyState(false);
} // end of converting "offset" argument } // end of converting "offset" argument
else else
{ {
@@ -481,23 +487,25 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
} }
} }
void LOFImportFileHandle::doDuration() void LOFImportFileHandle::doDurationAndScrollOffset()
{ {
bool doSomething = callDurationFactor || callScrollOffset;
if (callDurationFactor) if (callDurationFactor)
{ {
double longestDuration = mProject->GetTracks()->GetEndTime(); double longestDuration = mProject->GetTracks()->GetEndTime();
mProject->ZoomBy(longestDuration / durationFactor); mProject->ZoomBy(longestDuration / durationFactor);
callDurationFactor = false; callDurationFactor = false;
} }
}
void LOFImportFileHandle::doScrollOffset()
{
if (callScrollOffset && (scrollOffset != 0)) if (callScrollOffset && (scrollOffset != 0))
{ {
mProject->TP_ScrollWindow(scrollOffset); mProject->TP_ScrollWindow(scrollOffset);
callScrollOffset = false; callScrollOffset = false;
} }
if (doSomething)
// Amend last undo state
mProject->TP_ModifyState(false);
} }
LOFImportFileHandle::~LOFImportFileHandle() LOFImportFileHandle::~LOFImportFileHandle()

View File

@@ -269,6 +269,10 @@ void MP3ImportFileHandle::ImportID3(Tags *tags)
#ifdef USE_LIBID3TAG #ifdef USE_LIBID3TAG
wxFile f; // will be closed when it goes out of scope wxFile f; // will be closed when it goes out of scope
struct id3_file *fp = NULL; struct id3_file *fp = NULL;
auto cleanup = finally([&]{
if (fp)
id3_file_close(fp);
});
if (f.Open(mFilename)) { if (f.Open(mFilename)) {
// Use id3_file_fdopen() instead of id3_file_open since wxWidgets can open a // Use id3_file_fdopen() instead of id3_file_open since wxWidgets can open a
@@ -285,10 +289,8 @@ void MP3ImportFileHandle::ImportID3(Tags *tags)
f.Detach(); f.Detach();
struct id3_tag *tp = id3_file_tag(fp); struct id3_tag *tp = id3_file_tag(fp);
if (!tp) { if (!tp)
id3_file_close(fp);
return; return;
}
tags->Clear(); tags->Clear();
@@ -357,6 +359,7 @@ void MP3ImportFileHandle::ImportID3(Tags *tags)
else if (frame->nfields == 3) { else if (frame->nfields == 3) {
ustr = id3_field_getstring(&frame->fields[1]); ustr = id3_field_getstring(&frame->fields[1]);
if (ustr) { if (ustr) {
// Is this duplication really needed?
MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) }; MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) };
n = UTF8CTOWX(str.get()); n = UTF8CTOWX(str.get());
} }
@@ -368,6 +371,7 @@ void MP3ImportFileHandle::ImportID3(Tags *tags)
} }
if (ustr) { if (ustr) {
// Is this duplication really needed?
MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) }; MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) };
v = UTF8CTOWX(str.get()); v = UTF8CTOWX(str.get());
} }
@@ -384,8 +388,6 @@ void MP3ImportFileHandle::ImportID3(Tags *tags)
tags->SetTag(TAG_GENRE, tags->GetGenre(g)); tags->SetTag(TAG_GENRE, tags->GetGenre(g));
} }
} }
id3_file_close(fp);
#endif // ifdef USE_LIBID3TAG #endif // ifdef USE_LIBID3TAG
} }

View File

@@ -211,7 +211,6 @@ std::unique_ptr<ImportFileHandle> OggImportPlugin::Open(const wxString &filename
} }
// what to do with message? // what to do with message?
file->Close();
return nullptr; return nullptr;
} }

View File

@@ -478,6 +478,11 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory,
else else
block = SFCall<sf_count_t>(sf_readf_float, mFile.get(), (float *)srcbuffer.ptr(), block); block = SFCall<sf_count_t>(sf_readf_float, mFile.get(), (float *)srcbuffer.ptr(), block);
if(block < 0 || block > maxBlock) {
wxASSERT(false);
block = maxBlock;
}
if (block) { if (block) {
auto iter = channels.begin(); auto iter = channels.begin();
for(int c=0; c<mInfo.channels; ++iter, ++c) { for(int c=0; c<mInfo.channels; ++iter, ++c) {
@@ -670,6 +675,7 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory,
else if (frame->nfields == 3) { else if (frame->nfields == 3) {
ustr = id3_field_getstring(&frame->fields[1]); ustr = id3_field_getstring(&frame->fields[1]);
if (ustr) { if (ustr) {
// Is this duplication really needed?
MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) }; MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) };
n = UTF8CTOWX(str.get()); n = UTF8CTOWX(str.get());
} }
@@ -681,6 +687,7 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory,
} }
if (ustr) { if (ustr) {
// Is this duplication really needed?
MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) }; MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) };
v = UTF8CTOWX(str.get()); v = UTF8CTOWX(str.get());
} }
@@ -700,8 +707,6 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory,
break; break;
} }
f.Close();
} }
} }
#endif #endif

View File

@@ -155,6 +155,9 @@ public:
// do the actual import, creating whatever tracks are necessary with // do the actual import, creating whatever tracks are necessary with
// the TrackFactory and calling the progress callback every iteration // the TrackFactory and calling the progress callback every iteration
// through the importing loop // through the importing loop
// The given Tags structure may also be modified.
// In case of errors or exceptions, it is not necessary to leave outTracks
// or tags unmodified.
virtual ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks, virtual ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks,
Tags *tags) = 0; Tags *tags) = 0;

View File

@@ -245,9 +245,14 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
UInt32 quality = kQTAudioRenderQuality_Max; UInt32 quality = kQTAudioRenderQuality_Max;
AudioStreamBasicDescription desc; AudioStreamBasicDescription desc;
UInt32 maxSampleSize; UInt32 maxSampleSize;
UInt32 bufsize;
bool res = false; bool res = false;
auto cleanup = finally( [&] {
if (maer) {
MovieAudioExtractionEnd(maer);
}
} );
CreateProgress(); CreateProgress();
do do
@@ -301,7 +306,7 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
} }
auto numchan = desc.mChannelsPerFrame; auto numchan = desc.mChannelsPerFrame;
bufsize = 5 * desc.mSampleRate; const size_t bufsize = 5 * desc.mSampleRate;
// determine sample format // determine sample format
sampleFormat format; sampleFormat format;
@@ -320,32 +325,38 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
break; break;
} }
std::unique_ptr<AudioBufferList, freer> abl // Allocate an array of pointers, whose size is not known statically,
{ static_cast<AudioBufferList *> // and prefixed with the AudioBufferList structure.
(calloc(1, offsetof(AudioBufferList, mBuffers) + MallocPtr< AudioBufferList > abl{
static_cast< AudioBufferList * >(
calloc( 1, offsetof( AudioBufferList, mBuffers ) +
(sizeof(AudioBuffer) * numchan))) }; (sizeof(AudioBuffer) * numchan))) };
abl->mNumberBuffers = numchan; abl->mNumberBuffers = numchan;
TrackHolders channels{ numchan }; TrackHolders channels{ numchan };
ArraysOf<unsigned char> holders{ numchan, sizeof(float) * bufsize }; const auto size = sizeof(float) * bufsize;
int c; ArraysOf<unsigned char> holders{ numchan, size };
for (c = 0; c < numchan; c++) { for (size_t c = 0; c < numchan; c++) {
abl->mBuffers[c].mNumberChannels = 1; auto &buffer = abl->mBuffers[c];
abl->mBuffers[c].mDataByteSize = sizeof(float) * bufsize; auto &holder = holders[c];
auto &channel = channels[c];
abl->mBuffers[c].mData = holders[c].get(); buffer.mNumberChannels = 1;
buffer.mDataByteSize = size;
channels[c] = trackFactory->NewWaveTrack(format); buffer.mData = holder.get();
channels[c]->SetRate(desc.mSampleRate);
channel = trackFactory->NewWaveTrack( format );
channel->SetRate( desc.mSampleRate );
if (numchan == 2) { if (numchan == 2) {
if (c == 0) { if (c == 0) {
channels[c]->SetChannel(Track::LeftChannel); channel->SetChannel(Track::LeftChannel);
channels[c]->SetLinked(true); channel->SetLinked(true);
} }
else if (c == 1) { else if (c == 1) {
channels[c]->SetChannel(Track::RightChannel); channel->SetChannel(Track::RightChannel);
} }
} }
} }
@@ -363,7 +374,7 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
break; break;
} }
for (c = 0; c < numchan; c++) { for (size_t c = 0; c < numchan; c++) {
channels[c]->Append((char *) abl->mBuffers[c].mData, floatSample, numFrames); channels[c]->Append((char *) abl->mBuffers[c].mData, floatSample, numFrames);
} }
@@ -398,10 +409,6 @@ ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
// done: // done:
if (maer) {
MovieAudioExtractionEnd(maer);
}
return (res ? ProgressResult::Success : ProgressResult::Failed); return (res ? ProgressResult::Success : ProgressResult::Failed);
} }
@@ -436,6 +443,12 @@ names[] =
void QTImportFileHandle::AddMetadata(Tags *tags) void QTImportFileHandle::AddMetadata(Tags *tags)
{ {
QTMetaDataRef metaDataRef = NULL; QTMetaDataRef metaDataRef = NULL;
auto cleanup = finally( [&] {
// we are done so release our metadata object
if ( metaDataRef )
QTMetaDataRelease(metaDataRef);
} );
OSErr err; OSErr err;
err = QTCopyMovieMetaData(mMovie, &metaDataRef); err = QTCopyMovieMetaData(mMovie, &metaDataRef);
@@ -526,9 +539,6 @@ void QTImportFileHandle::AddMetadata(Tags *tags)
} }
} }
// we are done so release our metadata object
QTMetaDataRelease(metaDataRef);
return; return;
} }