1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-06 23:02:42 +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()
{
av_log_set_callback(av_log_default_callback);
// Do this before unloading the libraries
mContext.reset();

View File

@ -561,7 +561,8 @@ ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory,
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
mFile->finish();
#endif

View File

@ -157,7 +157,8 @@ struct GStreamContext
};
// 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
class GStreamerImportFileHandle final : public ImportFileHandle
@ -225,8 +226,8 @@ private:
TrackFactory *mTrackFactory; //!< Factory to create tracks when samples arrive
GstString mUri; //!< URI of file
GstObjHandle<GstElement> mPipeline; //!< GStreamer pipeline
GstObjHandle<GstBus> mBus; //!< Message bus
GstObjHandle<GstElement> mPipeline; //!< GStreamer pipeline
GstObjHandle<GstBus> mBus; //!< Message bus
GstElement *mDec; //!< uridecodebin element
bool mAsyncDone; //!< true = 1st async-done message received
@ -273,17 +274,21 @@ GetGStreamerImportPlugin(ImportPluginList &importPluginList,
// Initialize gstreamer
GErrorHandle error;
int argc = 0;
char **argv = NULL;
GError *ee;
if (!gst_init_check(&argc, &argv, &ee))
bool initError;
{
int argc = 0;
char **argv = NULL;
GError *ee;
initError = !gst_init_check(&argc, &argv, &ee);
error.reset(ee);
}
if ( initError )
{
wxLogMessage(wxT("Failed to initialize GStreamer. Error %d: %s"),
error.get()->code,
wxString::FromUTF8(error.get()->message).c_str());
return;
}
error.reset(ee);
guint major, minor, micro, nano;
gst_version(&major, &minor, &micro, &nano);

View File

@ -142,24 +142,23 @@ public:
private:
// Takes a line of text in lof file and interprets it and opens files
void lofOpenFiles(wxString* ln);
void doDuration();
void doScrollOffset();
void doDurationAndScrollOffset();
std::unique_ptr<wxTextFile> mTextFile;
wxFileName mLOFFileName; /**< The name of the LOF file, which is used to
interpret relative paths in it */
AudacityProject *mProject;
AudacityProject *mProject{ GetActiveProject() };
// 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
bool callDurationFactor;
double durationFactor;
bool callDurationFactor{ false };
double durationFactor{ 1 };
// In order to offset scrollbar, it must be done after files are opened
bool callScrollOffset;
double scrollOffset;
bool callScrollOffset{ false };
double scrollOffset{ 0 };
};
LOFImportFileHandle::LOFImportFileHandle
@ -168,12 +167,6 @@ LOFImportFileHandle::LOFImportFileHandle
mTextFile(std::move(file))
, mLOFFileName{name}
{
mProject = GetActiveProject();
windowCalledOnce = false;
callDurationFactor = false;
durationFactor = 1;
callScrollOffset = false;
scrollOffset = 0;
}
void GetLOFImportPlugin(ImportPluginList &importPluginList,
@ -190,28 +183,27 @@ wxString LOFImportPlugin::GetPluginFormatDescription()
std::unique_ptr<ImportFileHandle> LOFImportPlugin::Open(const wxString &filename)
{
// Check if it is a binary file
wxFile binaryFile;
if (!binaryFile.Open(filename))
return nullptr; // File not found
char buf[BINARY_FILE_CHECK_BUFFER_SIZE];
int count = binaryFile.Read(buf, BINARY_FILE_CHECK_BUFFER_SIZE);
for (int i = 0; i < count; i++)
{
// Check if this char is below the space character, but not a
// line feed or carriage return
if (buf[i] < 32 && buf[i] != 10 && buf[i] != 13)
wxFile binaryFile;
if (!binaryFile.Open(filename))
return nullptr; // File not found
char buf[BINARY_FILE_CHECK_BUFFER_SIZE];
int count = binaryFile.Read(buf, BINARY_FILE_CHECK_BUFFER_SIZE);
for (int i = 0; i < count; i++)
{
// Assume it is a binary file
binaryFile.Close();
return nullptr;
// Check if this char is below the space character, but not a
// line feed or carriage return
if (buf[i] < 32 && buf[i] != 10 && buf[i] != 13)
{
// Assume it is a binary file
binaryFile.Close();
return nullptr;
}
}
}
// Close it again so it can be opened as a text file
binaryFile.Close();
// Now open the file again as text file
auto file = std::make_unique<wxTextFile>(filename);
file->Open();
@ -235,6 +227,18 @@ auto LOFImportFileHandle::GetFileUncompressedBytes() -> ByteCount
ProgressResult LOFImportFileHandle::Import(TrackFactory * WXUNUSED(trackFactory), TrackHolders &outTracks,
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();
wxASSERT(mTextFile->IsOpened());
@ -256,15 +260,13 @@ ProgressResult LOFImportFileHandle::Import(TrackFactory * WXUNUSED(trackFactory)
// for last line
lofOpenFiles(&line);
if(!mTextFile->Close())
return ProgressResult::Failed;
// set any duration/offset factors for last window, as all files were called
doDuration();
doScrollOffset();
doDurationAndScrollOffset();
// exited ok
if(mTextFile->Close())
return ProgressResult::Success;
return ProgressResult::Failed;
return ProgressResult::Success;
}
static int CountNumTracks(AudacityProject *proj)
@ -302,8 +304,7 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
if (tokenholder.IsSameAs(wxT("window"), false))
{
// set any duration/offset factors for last window, as all files were called
doDuration();
doScrollOffset();
doDurationAndScrollOffset();
if (windowCalledOnce)
{
@ -380,6 +381,8 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
}
}
// Do recursive call to import
#ifdef USE_MIDI
// If file is a midi
if (targetfile.AfterLast(wxT('.')).IsSameAs(wxT("mid"), false)
@ -459,6 +462,9 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
t->SetOffset(offset);
}
}
// Amend the undo transaction made by import
mProject->TP_ModifyState(false);
} // end of converting "offset" argument
else
{
@ -481,23 +487,25 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
}
}
void LOFImportFileHandle::doDuration()
void LOFImportFileHandle::doDurationAndScrollOffset()
{
bool doSomething = callDurationFactor || callScrollOffset;
if (callDurationFactor)
{
double longestDuration = mProject->GetTracks()->GetEndTime();
mProject->ZoomBy(longestDuration / durationFactor);
callDurationFactor = false;
}
}
void LOFImportFileHandle::doScrollOffset()
{
if (callScrollOffset && (scrollOffset != 0))
{
mProject->TP_ScrollWindow(scrollOffset);
callScrollOffset = false;
}
if (doSomething)
// Amend last undo state
mProject->TP_ModifyState(false);
}
LOFImportFileHandle::~LOFImportFileHandle()

View File

@ -269,6 +269,10 @@ void MP3ImportFileHandle::ImportID3(Tags *tags)
#ifdef USE_LIBID3TAG
wxFile f; // will be closed when it goes out of scope
struct id3_file *fp = NULL;
auto cleanup = finally([&]{
if (fp)
id3_file_close(fp);
});
if (f.Open(mFilename)) {
// 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();
struct id3_tag *tp = id3_file_tag(fp);
if (!tp) {
id3_file_close(fp);
if (!tp)
return;
}
tags->Clear();
@ -357,6 +359,7 @@ void MP3ImportFileHandle::ImportID3(Tags *tags)
else if (frame->nfields == 3) {
ustr = id3_field_getstring(&frame->fields[1]);
if (ustr) {
// Is this duplication really needed?
MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) };
n = UTF8CTOWX(str.get());
}
@ -368,24 +371,23 @@ void MP3ImportFileHandle::ImportID3(Tags *tags)
}
if (ustr) {
// Is this duplication really needed?
MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) };
v = UTF8CTOWX(str.get());
}
if (!n.IsEmpty() && !v.IsEmpty()) {
tags->SetTag(n, v);
}
}
}
// Convert v1 genre to name
if (tags->HasTag(TAG_GENRE)) {
long g = -1;
if (tags->GetTag(TAG_GENRE).ToLong(&g)) {
tags->SetTag(TAG_GENRE, tags->GetGenre(g));
}
}
}
id3_file_close(fp);
#endif // ifdef USE_LIBID3TAG
}

View File

@ -211,7 +211,6 @@ std::unique_ptr<ImportFileHandle> OggImportPlugin::Open(const wxString &filename
}
// what to do with message?
file->Close();
return nullptr;
}
@ -230,7 +229,7 @@ auto OggImportFileHandle::GetFileUncompressedBytes() -> ByteCount
}
ProgressResult OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks,
Tags *tags)
Tags *tags)
{
outTracks.clear();

View File

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

View File

@ -155,6 +155,9 @@ public:
// do the actual import, creating whatever tracks are necessary with
// the TrackFactory and calling the progress callback every iteration
// 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,
Tags *tags) = 0;

View File

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