From 276aae35b3e619427e48a7ebaa1ac2f08198f20e Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 2 Feb 2016 01:09:02 -0600 Subject: [PATCH] Improvements to exception handling, including using std::exception base class, and better general purpose exception handling in SWIG with Python. Also fixed an error when thowing exceptions inside OMP regions. --- include/Exceptions.h | 2 +- include/FFmpegWriter.h | 17 ++++++++------- include/WriterBase.h | 4 ++-- src/FFmpegWriter.cpp | 34 ++++++++++++++++++++---------- src/bindings/python/CMakeLists.txt | 1 + src/bindings/python/openshot.i | 10 +++++++++ 6 files changed, 46 insertions(+), 22 deletions(-) diff --git a/include/Exceptions.h b/include/Exceptions.h index 82f627e8..1f879388 100644 --- a/include/Exceptions.h +++ b/include/Exceptions.h @@ -39,7 +39,7 @@ namespace openshot { * A custom error message field has been added to the std::exception base class. All * OpenShot exception classes inherit from this class. */ - class BaseException //: public exception + class BaseException : public std::exception //: public exception { protected: string m_message; diff --git a/include/FFmpegWriter.h b/include/FFmpegWriter.h index 0b9b33f9..f1481b70 100644 --- a/include/FFmpegWriter.h +++ b/include/FFmpegWriter.h @@ -239,10 +239,10 @@ namespace openshot void write_audio_packets(bool final); /// write video frame - void write_video_packet(tr1::shared_ptr frame, AVFrame* frame_final); + bool write_video_packet(tr1::shared_ptr frame, AVFrame* frame_final); /// write all queued frames - void write_queued_frames(); + void write_queued_frames() throw (ErrorEncodingVideo); public: @@ -260,7 +260,7 @@ namespace openshot bool IsOpen() { return is_open; }; /// Open writer - void Open() throw(InvalidFile, InvalidCodec); + void Open() throw(InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory, InvalidChannels, InvalidSampleRate); /// Output the ffmpeg info about this format, streams, and codecs (i.e. dump format) void OutputStreamInfo(); @@ -284,7 +284,8 @@ namespace openshot /// @param channels The number of audio channels needed in this file /// @param channel_layout The 'layout' of audio channels (i.e. mono, stereo, surround, etc...) /// @param bit_rate The audio bit rate used during encoding - void SetAudioOptions(bool has_audio, string codec, int sample_rate, int channels, ChannelLayout channel_layout, int bit_rate); + void SetAudioOptions(bool has_audio, string codec, int sample_rate, int channels, ChannelLayout channel_layout, int bit_rate) + throw(InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory, InvalidChannels); /// @brief Set the cache size /// @param new_size The number of frames to queue before writing to the file @@ -300,8 +301,8 @@ namespace openshot /// @param interlaced Does this video need to be interlaced? /// @param top_field_first Which frame should be used as the top field? /// @param bit_rate The video bit rate used during encoding - void SetVideoOptions(bool has_video, string codec, Fraction fps, int width, int height, - Fraction pixel_ratio, bool interlaced, bool top_field_first, int bit_rate); + void SetVideoOptions(bool has_video, string codec, Fraction fps, int width, int height,Fraction pixel_ratio, bool interlaced, bool top_field_first, int bit_rate) + throw(InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory, InvalidChannels); /// @brief Set custom options (some codecs accept additional params). This must be called after the /// PrepareStreams() method, otherwise the streams have not been initialized yet. @@ -316,13 +317,13 @@ namespace openshot /// @brief Add a frame to the stack waiting to be encoded. /// @param frame The openshot::Frame object to write to this image - void WriteFrame(tr1::shared_ptr frame) throw(WriterClosed); + void WriteFrame(tr1::shared_ptr frame) throw(ErrorEncodingVideo, WriterClosed); /// @brief Write a block of frames from a reader /// @param reader A openshot::ReaderBase object which will provide frames to be written /// @param start The starting frame number of the reader /// @param length The number of frames to write - void WriteFrame(ReaderBase* reader, long int start, long int length) throw(WriterClosed); + void WriteFrame(ReaderBase* reader, long int start, long int length) throw(ErrorEncodingVideo, WriterClosed); /// @brief Write the file trailer (after all frames are written). This is called automatically /// by the Close() method if this method has not yet been called. diff --git a/include/WriterBase.h b/include/WriterBase.h index 8516158e..44ffe0f5 100644 --- a/include/WriterBase.h +++ b/include/WriterBase.h @@ -118,10 +118,10 @@ namespace openshot virtual bool IsOpen() = 0; /// This method is required for all derived classes of WriterBase. Write a Frame to the video file. - virtual void WriteFrame(tr1::shared_ptr frame) throw(WriterClosed) = 0; + virtual void WriteFrame(tr1::shared_ptr frame) throw(ErrorEncodingVideo, WriterClosed) = 0; /// This method is required for all derived classes of WriterBase. Write a block of frames from a reader. - virtual void WriteFrame(ReaderBase* reader, long int start, long int length) throw(WriterClosed) = 0; + virtual void WriteFrame(ReaderBase* reader, long int start, long int length) throw(ErrorEncodingVideo, WriterClosed) = 0; /// Get and Set JSON methods string Json(); ///< Generate JSON string of this object diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index c9a6eca7..33745e64 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -32,7 +32,7 @@ using namespace openshot; -FFmpegWriter::FFmpegWriter(string path) throw (InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory) : +FFmpegWriter::FFmpegWriter(string path) throw (InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory): path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), audio_pts(0), video_pts(0), samples(NULL), audio_outbuf(NULL), audio_outbuf_size(0), audio_input_frame_size(0), audio_input_position(0), initial_audio_input_frame_size(0), img_convert_ctx(NULL), cache_size(8), num_of_rescalers(32), @@ -53,7 +53,7 @@ FFmpegWriter::FFmpegWriter(string path) throw (InvalidFile, InvalidFormat, Inval } // Open the writer -void FFmpegWriter::Open() throw(InvalidFile, InvalidCodec) +void FFmpegWriter::Open() throw(InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory, InvalidChannels, InvalidSampleRate) { // Open the writer is_open = true; @@ -111,8 +111,8 @@ void FFmpegWriter::initialize_streams() } // Set video export options -void FFmpegWriter::SetVideoOptions(bool has_video, string codec, Fraction fps, int width, int height, - Fraction pixel_ratio, bool interlaced, bool top_field_first, int bit_rate) +void FFmpegWriter::SetVideoOptions(bool has_video, string codec, Fraction fps, int width, int height, Fraction pixel_ratio, bool interlaced, bool top_field_first, int bit_rate) + throw(InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory, InvalidChannels) { // Set the video options if (codec.length() > 0) @@ -171,6 +171,7 @@ void FFmpegWriter::SetVideoOptions(bool has_video, string codec, Fraction fps, i // Set audio export options void FFmpegWriter::SetAudioOptions(bool has_audio, string codec, int sample_rate, int channels, ChannelLayout channel_layout, int bit_rate) + throw(InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory, InvalidChannels) { // Set audio options if (codec.length() > 0) @@ -342,7 +343,7 @@ void FFmpegWriter::WriteHeader() } // Add a frame to the queue waiting to be encoded. -void FFmpegWriter::WriteFrame(tr1::shared_ptr frame) throw(WriterClosed) +void FFmpegWriter::WriteFrame(tr1::shared_ptr frame) throw(ErrorEncodingVideo, WriterClosed) { // Check for open reader (or throw exception) if (!is_open) @@ -382,7 +383,7 @@ void FFmpegWriter::WriteFrame(tr1::shared_ptr frame) throw(WriterClosed) } // Write all frames in the queue to the video file. -void FFmpegWriter::write_queued_frames() +void FFmpegWriter::write_queued_frames() throw (ErrorEncodingVideo) { AppendDebugMethod("FFmpegWriter::write_queued_frames", "spooled_video_frames.size()", spooled_video_frames.size(), "spooled_audio_frames.size()", spooled_audio_frames.size(), "", -1, "", -1, "", -1, "", -1); @@ -402,6 +403,9 @@ void FFmpegWriter::write_queued_frames() // Allow nested OpenMP sections omp_set_nested(true); + // Create blank exception + bool has_error_encoding_video = false; + #pragma omp parallel { #pragma omp single @@ -449,7 +453,9 @@ void FFmpegWriter::write_queued_frames() AVFrame *frame_final = av_frames[frame]; // Write frame to video file - write_video_packet(frame, frame_final); + bool success = write_video_packet(frame, frame_final); + if (!success) + has_error_encoding_video = true; } } @@ -485,10 +491,13 @@ void FFmpegWriter::write_queued_frames() } // end omp single } // end omp parallel + // Raise exception from main thread + if (has_error_encoding_video) + throw ErrorEncodingVideo("Error while writing raw video frame", -1); } // Write a block of frames from a reader -void FFmpegWriter::WriteFrame(ReaderBase* reader, long int start, long int length) throw(WriterClosed) +void FFmpegWriter::WriteFrame(ReaderBase* reader, long int start, long int length) throw(ErrorEncodingVideo, WriterClosed) { AppendDebugMethod("FFmpegWriter::WriteFrame (from Reader)", "start", start, "length", length, "", -1, "", -1, "", -1, "", -1); @@ -1445,7 +1454,7 @@ void FFmpegWriter::process_video_packet(tr1::shared_ptr frame) } // write video frame -void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* frame_final) +bool FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* frame_final) { AppendDebugMethod("FFmpegWriter::write_video_packet", "frame->number", frame->number, "oc->oformat->flags & AVFMT_RAWPICTURE", oc->oformat->flags & AVFMT_RAWPICTURE, "", -1, "", -1, "", -1, "", -1); @@ -1468,7 +1477,7 @@ void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* fra if (error_code < 0) { AppendDebugMethod("FFmpegWriter::write_video_packet ERROR [" + (string)av_err2str(error_code) + "]", "error_code", error_code, "", -1, "", -1, "", -1, "", -1, "", -1); - throw ErrorEncodingVideo("Error while writing raw video frame", frame->number); + return false; } // Deallocate packet @@ -1541,7 +1550,7 @@ void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* fra if (error_code < 0) { AppendDebugMethod("FFmpegWriter::write_video_packet ERROR [" + (string)av_err2str(error_code) + "]", "error_code", error_code, "", -1, "", -1, "", -1, "", -1, "", -1); - throw ErrorEncodingVideo("Error while writing compressed video frame", frame->number); + return false; } } @@ -1552,6 +1561,9 @@ void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* fra // Deallocate packet av_free_packet(&pkt); } + + // Success + return true; } // Output the ffmpeg info about this format, streams, and codecs (i.e. dump format) diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index 34f1a432..4f83b2e7 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -40,6 +40,7 @@ IF (PYTHONLIBS_FOUND) ### Enable C++ support in SWIG SET_SOURCE_FILES_PROPERTIES(openshot.i PROPERTIES CPLUSPLUS ON) + SET(CMAKE_SWIG_FLAGS "") ### Add the SWIG interface file (which defines all the SWIG methods) SWIG_ADD_MODULE(openshot python openshot.i) diff --git a/src/bindings/python/openshot.i b/src/bindings/python/openshot.i index b5e0dd76..e755c004 100644 --- a/src/bindings/python/openshot.i +++ b/src/bindings/python/openshot.i @@ -93,6 +93,16 @@ %} #endif +/* Generic language independent exception handler. */ +%include "exception.i" +%exception { + try { + $action + } + catch (std::exception &e) { + SWIG_exception_fail(SWIG_RuntimeError, e.what()); + } +} %include "../../../include/Version.h" %include "../../../include/ReaderBase.h"