diff --git a/include/libvideo2x/libvideo2x.h b/include/libvideo2x/libvideo2x.h index 24af4f3..03c1736 100644 --- a/include/libvideo2x/libvideo2x.h +++ b/include/libvideo2x/libvideo2x.h @@ -25,6 +25,15 @@ extern "C" { #define LIBVIDEO2X_API #endif +enum class VideoProcessorState { + Idle, + Running, + Paused, + Failed, + Aborted, + Completed +}; + class LIBVIDEO2X_API VideoProcessor { public: VideoProcessor( @@ -41,17 +50,14 @@ class LIBVIDEO2X_API VideoProcessor { [[nodiscard]] int process(const std::filesystem::path in_fname, const std::filesystem::path out_fname); - void pause() { paused_.store(true); } - void resume() { paused_.store(false); } - void abort() { aborted_.store(true); } + void pause() { state_.store(VideoProcessorState::Paused); } + void resume() { state_.store(VideoProcessorState::Running); } + void abort() { state_.store(VideoProcessorState::Aborted); } + VideoProcessorState get_state() const { return state_.load(); } int64_t get_processed_frames() const { return frame_index_.load(); } int64_t get_total_frames() const { return total_frames_.load(); } - bool is_paused() const { return paused_.load(); } - bool is_aborted() const { return aborted_.load(); } - bool is_completed() const { return completed_.load(); } - private: [[nodiscard]] int process_frames(Decoder &decoder, Encoder &encoder, std::unique_ptr &processor); @@ -86,9 +92,7 @@ class LIBVIDEO2X_API VideoProcessor { AVHWDeviceType hw_device_type_ = AV_HWDEVICE_TYPE_NONE; bool benchmark_ = false; + std::atomic state_ = VideoProcessorState::Idle; std::atomic frame_index_ = 0; std::atomic total_frames_ = 0; - std::atomic paused_ = false; - std::atomic aborted_ = false; - std::atomic completed_ = false; }; diff --git a/src/libvideo2x.cpp b/src/libvideo2x.cpp index 6754098..67dbec0 100644 --- a/src/libvideo2x.cpp +++ b/src/libvideo2x.cpp @@ -33,9 +33,23 @@ int VideoProcessor::process( const std::filesystem::path in_fname, const std::filesystem::path out_fname ) { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; int ret = 0; + // Helper lambda to handle errors: + auto handle_error = [&](int error_code, const std::string &msg) { + // Format and log the error message + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + av_strerror(error_code, errbuf, sizeof(errbuf)); + spdlog::critical("{}: {}", msg, errbuf); + + // Set the video processor state to failed and return the error code + state_.store(VideoProcessorState::Failed); + return error_code; + }; + + // Set the video processor state to running + state_.store(VideoProcessorState::Running); + // Create a smart pointer to manage the hardware device context std::unique_ptr hw_ctx( nullptr, &av_bufferref_deleter @@ -46,9 +60,7 @@ int VideoProcessor::process( AVBufferRef *tmp_hw_ctx = nullptr; ret = av_hwdevice_ctx_create(&tmp_hw_ctx, hw_device_type_, NULL, NULL, 0); if (ret < 0) { - av_strerror(ret, errbuf, sizeof(errbuf)); - spdlog::critical("Error initializing hardware device context: {}", errbuf); - return ret; + return handle_error(ret, "Error initializing hardware device context"); } hw_ctx.reset(tmp_hw_ctx); } @@ -57,9 +69,7 @@ int VideoProcessor::process( Decoder decoder; ret = decoder.init(hw_device_type_, hw_ctx.get(), in_fname); if (ret < 0) { - av_strerror(ret, errbuf, sizeof(errbuf)); - spdlog::critical("Failed to initialize decoder: {}", errbuf); - return ret; + return handle_error(ret, "Failed to initialize decoder"); } AVFormatContext *ifmt_ctx = decoder.get_format_context(); @@ -71,8 +81,7 @@ int VideoProcessor::process( ProcessorFactory::instance().create_processor(proc_cfg_, vk_device_index_) ); if (processor == nullptr) { - spdlog::critical("Failed to create filter instance"); - return -1; + return handle_error(-1, "Failed to create filter instance"); } // Initialize output dimensions based on filter configuration @@ -81,8 +90,7 @@ int VideoProcessor::process( proc_cfg_, dec_ctx->width, dec_ctx->height, output_width, output_height ); if (output_width <= 0 || output_height <= 0) { - spdlog::critical("Failed to determine the output dimensions"); - return -1; + return handle_error(-1, "Failed to determine the output dimensions"); } // Update encoder frame rate multiplier @@ -101,34 +109,34 @@ int VideoProcessor::process( in_vstream_idx ); if (ret < 0) { - av_strerror(ret, errbuf, sizeof(errbuf)); - spdlog::critical("Failed to initialize encoder: {}", errbuf); - return ret; + return handle_error(ret, "Failed to initialize encoder"); } // Initialize the filter ret = processor->init(dec_ctx, encoder.get_encoder_context(), hw_ctx.get()); if (ret < 0) { - spdlog::critical("Failed to initialize filter"); - return ret; + return handle_error(ret, "Failed to initialize filter"); } // Process frames using the encoder and decoder ret = process_frames(decoder, encoder, processor); if (ret < 0) { - av_strerror(ret, errbuf, sizeof(errbuf)); - spdlog::critical("Error processing frames: {}", errbuf); - return ret; + return handle_error(ret, "Error processing frames"); } // Write the output file trailer - av_write_trailer(encoder.get_format_context()); - - if (ret < 0 && ret != AVERROR_EOF) { - av_strerror(ret, errbuf, sizeof(errbuf)); - spdlog::critical("Error occurred: {}", errbuf); - return ret; + ret = av_write_trailer(encoder.get_format_context()); + if (ret < 0) { + return handle_error(ret, "Error writing output file trailer"); } + + // Check if an error occurred during processing + if (ret < 0 && ret != AVERROR_EOF) { + return handle_error(ret, "Error occurred"); + } + + // Processing has completed successfully + state_.store(VideoProcessorState::Completed); return 0; } @@ -187,7 +195,7 @@ int VideoProcessor::process_frames( } // Read frames from the input file - while (!aborted_.load()) { + while (state_.load() != VideoProcessorState::Aborted) { ret = av_read_frame(ifmt_ctx, packet.get()); if (ret < 0) { if (ret == AVERROR_EOF) { @@ -209,9 +217,9 @@ int VideoProcessor::process_frames( } // Process frames decoded from the packet - while (!aborted_.load()) { + while (state_.load() != VideoProcessorState::Aborted) { // Sleep for 100 ms if processing is paused - if (paused_.load()) { + if (state_.load() == VideoProcessorState::Paused) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); continue; } diff --git a/tools/video2x/src/video2x.cpp b/tools/video2x/src/video2x.cpp index 9ebf26c..5b8c135 100644 --- a/tools/video2x/src/video2x.cpp +++ b/tools/video2x/src/video2x.cpp @@ -121,14 +121,16 @@ int main(int argc, char **argv) { #endif if (ch == ' ' || ch == '\n') { - // Toggle pause state { - if (video_processor.is_paused()) { + // Toggle pause state + if (video_processor.get_state() == VideoProcessorState::Paused) { video_processor.resume(); } else { video_processor.pause(); } - if (video_processor.is_paused()) { + + // Print message based on current state and pause/resume the timer + if (video_processor.get_state() == VideoProcessorState::Paused) { std::cout << "\r\033[KProcessing paused; press [space] to resume, [q] to abort."; std::cout.flush(); @@ -146,23 +148,19 @@ int main(int argc, char **argv) { putchar('\n'); } spdlog::warn("Aborting gracefully; press Ctrl+C to terminate forcefully."); - { - video_processor.abort(); - newline_required.store(false); - } + newline_required.store(false); + video_processor.abort(); break; } // Display progress if (!arguments.no_progress) { - int64_t processed_frames, total_frames; - bool paused; - { - processed_frames = video_processor.get_processed_frames(); - total_frames = video_processor.get_total_frames(); - paused = video_processor.is_paused(); - } - if (!paused && (total_frames > 0 || processed_frames > 0)) { + int64_t processed_frames = video_processor.get_processed_frames(); + int64_t total_frames = video_processor.get_total_frames(); + + // Print the progress bar if processing is not paused + if (video_processor.get_state() != VideoProcessorState::Paused && + (total_frames > 0 || processed_frames > 0)) { double percentage = total_frames > 0 ? static_cast(processed_frames) * 100.0 / static_cast(total_frames) : 0.0; @@ -216,10 +214,10 @@ int main(int argc, char **argv) { } // Print final message based on processing result - if (video_processor.is_aborted()) { + if (video_processor.get_state() == VideoProcessorState::Aborted) { spdlog::warn("Video processing aborted"); return 2; - } else if (proc_ret != 0) { + } else if (proc_ret != 0 || video_processor.get_state() == VideoProcessorState::Failed) { spdlog::critical("Video processing failed with error code {}", proc_ret); return 1; } else {