diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index eec88f957..8b7d60ef6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -75,14 +75,13 @@ jobs:
if: ${{ runner.os == 'linux' }}
run: |
sudo apt update
- sudo apt remove libzmq5 # See actions/virtual-environments#3317
sudo apt install \
cmake swig doxygen graphviz curl lcov \
libasound2-dev \
qtbase5-dev qtbase5-dev-tools libqt5svg5-dev \
libfdk-aac-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswscale-dev libswresample-dev \
- libzmq3-dev libbabl-dev \
+ libbabl-dev \
libopencv-dev libprotobuf-dev protobuf-compiler \
cargo libomp5 libomp-dev
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e8ad920bc..97fce6565 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -106,7 +106,7 @@ windows-builder-x64:
- Expand-Archive -Path artifacts.zip -DestinationPath .
- $env:OPENCV_ROOT = "C:\msys64\mingw64\opencv-4.13.0"
- $env:OpenCV_DIR = "$env:OPENCV_ROOT\lib\cmake\opencv4"
- - $env:Path = "$env:OPENCV_ROOT\bin;C:\msys64\mingw64\bin;C:\msys64\usr\bin;C:\msys64\usr\local\bin;" + $env:Path;
+ - $env:Path = "$env:OPENCV_ROOT\x64\mingw\bin;$env:OPENCV_ROOT\bin;C:\msys64\mingw64\bin;C:\msys64\usr\bin;C:\msys64\usr\local\bin;" + $env:Path;
- $env:MSYSTEM = "MINGW64"
- ffmpeg -hide_banner -devices | findstr /I "gdigrab"
- ffmpeg -hide_banner -devices | findstr /I "dshow"
diff --git a/INSTALL.md b/INSTALL.md
index d99f59c09..80a563be5 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -54,12 +54,6 @@ Libraries and executables have been labeled in the list below to help distinguis
apply image effects, and many other utility functions,
such as file system manipulation, high resolution timers, etc.
-#### ZeroMQ (libzmq)
-* **(Library)**
-
-* This library is used to communicate between libopenshot and other applications (publisher / subscriber).
- Primarily used to send debug data from libopenshot.
-
#### OpenMP (`-fopenmp`)
* **(Compiler Flag)**
diff --git a/README.md b/README.md
index 8fd820999..eabc32fe1 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,8 @@ Please see [`doc/HW-ACCEL.md`](doc/HW-ACCEL.md) for more information.
## Documentation
+See [Logging](doc/logging.rst) for file output, verbosity, and environment settings.
+
Beautiful HTML documentation can be generated using Doxygen.
```
make doc
diff --git a/bindings/java/openshot.i b/bindings/java/openshot.i
index 9f8d1794f..cc13f189c 100644
--- a/bindings/java/openshot.i
+++ b/bindings/java/openshot.i
@@ -130,7 +130,7 @@ typedef struct OpenShotByteBuffer {
#include "TimelineBase.h"
#include "Timeline.h"
#include "Qt/VideoCacheThread.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
%}
// Prevent SWIG from ever generating a wrapper for juce::Thread’s constructor (or run())
@@ -235,7 +235,7 @@ typedef struct OpenShotByteBuffer {
%include "TimelineBase.h"
%include "Qt/VideoCacheThread.h"
%include "Timeline.h"
-%include "ZmqLogger.h"
+%include "Logger.h"
#ifdef USE_IMAGEMAGICK
%include "ImageReader.h"
diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt
index d748f252d..ef43d543f 100644
--- a/bindings/python/CMakeLists.txt
+++ b/bindings/python/CMakeLists.txt
@@ -138,3 +138,15 @@ install(TARGETS ${_pyopenshot_target}
DESTINATION ${PYTHON_MODULE_PATH} )
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/openshot.py
DESTINATION ${PYTHON_MODULE_PATH} )
+
+if(BUILD_TESTING)
+ add_test(NAME Logger:PythonEnvironment
+ COMMAND ${CMAKE_COMMAND} -E env
+ "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}"
+ "OPENSHOT_TEST_MODULE_DIR=$"
+ "OPENSHOT_TEST_DLL_DIR=$"
+ # The Windows build copies libopenshot-audio.dll into the tests directory.
+ "OPENSHOT_TEST_AUDIO_DLL_DIR=${PROJECT_BINARY_DIR}/tests"
+ ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/test_logger.py")
+ set_tests_properties(Logger:PythonEnvironment PROPERTIES LABELS Logger)
+endif()
diff --git a/bindings/python/openshot.i b/bindings/python/openshot.i
index 73d3da46e..da0fde570 100644
--- a/bindings/python/openshot.i
+++ b/bindings/python/openshot.i
@@ -114,7 +114,7 @@ class QWidget;
#include "TimelineBase.h"
#include "Timeline.h"
#include "Qt/VideoCacheThread.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#include
static void *openshot_swig_pylong_as_ptr(PyObject *obj) {
@@ -523,7 +523,7 @@ static int openshot_swig_is_qwidget(PyObject *obj) {
%include "TimelineBase.h"
%include "Qt/VideoCacheThread.h"
%include "Timeline.h"
-%include "ZmqLogger.h"
+%include "Logger.h"
#ifdef USE_OPENCV
%include "ClipProcessingJobs.h"
@@ -566,3 +566,8 @@ static int openshot_swig_is_qwidget(PyObject *obj) {
%include "effects/ObjectDetection.h"
%include "effects/Outline.h"
#endif
+
+%pythoncode %{
+# Deprecated source compatibility alias (no networking).
+ZmqLogger = Logger
+%}
diff --git a/bindings/python/test_logger.py b/bindings/python/test_logger.py
new file mode 100644
index 000000000..a5b0ecbae
--- /dev/null
+++ b/bindings/python/test_logger.py
@@ -0,0 +1,131 @@
+# SPDX-FileCopyrightText: 2026 OpenShot Studios, LLC
+# SPDX-License-Identifier: LGPL-3.0-or-later
+
+"""Exercise native logger configuration in fresh processes through SWIG."""
+import os
+from pathlib import Path
+import subprocess
+import sys
+import tempfile
+import unittest
+
+
+class NativeLoggerTests(unittest.TestCase):
+ def run_logger(self, variables, body=''):
+ with tempfile.TemporaryDirectory() as directory:
+ path = Path(directory) / 'native-é.log'
+ env = {key: value for key, value in os.environ.items()
+ if not key.startswith(('OPENSHOT_LOG_', 'LIBOPENSHOT_LOG_'))
+ and key != 'LIBOPENSHOT_DEBUG'}
+ env.update(variables, LIBOPENSHOT_LOG_FILE=str(path))
+ module_dir = env.get('OPENSHOT_TEST_MODULE_DIR')
+ if module_dir:
+ env['PYTHONPATH'] = module_dir + os.pathsep + env.get('PYTHONPATH', '')
+ if os.name == 'nt':
+ # Older MinGW Python uses PATH and ignores add_dll_directory().
+ dll_dirs = [env[key] for key in (
+ 'OPENSHOT_TEST_DLL_DIR', 'OPENSHOT_TEST_AUDIO_DLL_DIR')
+ if env.get(key)]
+ env['PATH'] = os.pathsep.join(dll_dirs + [env.get('PATH', '')])
+ result = subprocess.run([sys.executable, '-c', '''
+import os
+_dll_handles = []
+if os.name == 'nt' and hasattr(os, 'add_dll_directory'):
+ # Modern Python does not search PATH for extension-module dependencies.
+ # Include the project DLLs and the dependency directories supplied by CI.
+ _dll_dirs = [os.environ.get('OPENSHOT_TEST_DLL_DIR', ''),
+ os.environ.get('OPENSHOT_TEST_AUDIO_DLL_DIR', '')]
+ _dll_dirs.extend(os.environ.get('PATH', '').split(os.pathsep))
+ _seen = set()
+ for _directory in _dll_dirs:
+ _directory = _directory.strip('"')
+ if not _directory or not os.path.isdir(_directory):
+ continue
+ _directory = os.path.abspath(_directory)
+ _key = os.path.normcase(_directory)
+ if _key not in _seen:
+ _dll_handles.append(os.add_dll_directory(_directory))
+ _seen.add(_key)
+import openshot
+logger = openshot.Logger.Instance()
+assert openshot.ZmqLogger is openshot.Logger
+''' + body + '''
+logger.Log("native-debug-record", openshot.Logger.LevelDebug)
+logger.Log("native-info-record", openshot.Logger.LevelInfo)
+logger.Log("native-warning-record", openshot.Logger.LevelWarning)
+logger.Log("native-error-record", openshot.Logger.LevelError)
+logger.Log("native-critical-record", openshot.Logger.LevelCritical)
+logger.Close()
+'''], env=env, text=True, capture_output=True, timeout=20)
+ self.assertEqual(result.returncode, 0, result.stderr)
+ return path.read_text(), result.stderr
+
+ def test_default_and_legacy_console(self):
+ for variables, debug_console in (({}, False), ({'LIBOPENSHOT_DEBUG': '0'}, True)):
+ with self.subTest(variables=variables):
+ file, console = self.run_logger(variables)
+ self.assertNotIn('native-debug-record', file)
+ self.assertEqual('native-debug-record' in console, debug_console)
+ self.assertIn('native-info-record', file)
+ self.assertIn('native-info-record', console)
+
+ def test_component_and_destination_precedence(self):
+ file, console = self.run_logger({
+ 'OPENSHOT_LOG_FILE_LEVEL': 'off',
+ 'LIBOPENSHOT_LOG_LEVEL': 'debug',
+ 'LIBOPENSHOT_LOG_CONSOLE_LEVEL': 'error'})
+ self.assertIn('native-debug-record', file)
+ self.assertNotIn('native-info-record', console)
+ self.assertIn('native-error-record', console)
+
+ def test_invalid_level_fallback(self):
+ file, console = self.run_logger({
+ 'OPENSHOT_LOG_LEVEL': 'error',
+ 'LIBOPENSHOT_LOG_FILE_LEVEL': 'invalid',
+ 'LIBOPENSHOT_DEBUG': '1'})
+ self.assertNotIn('native-info-record', file)
+ self.assertNotIn('native-debug-record', console)
+ self.assertIn('native-error-record', file)
+ self.assertEqual(console.count('ignoring invalid'), 1)
+
+ def test_every_environment_variable_filters_the_correct_output(self):
+ for prefix in ('OPENSHOT', 'LIBOPENSHOT'):
+ for suffix, debug_file, debug_console in (
+ ('LEVEL', True, True), ('FILE_LEVEL', True, False),
+ ('CONSOLE_LEVEL', False, True)):
+ variable = prefix + '_LOG_' + suffix
+ with self.subTest(variable=variable):
+ file, console = self.run_logger({variable: 'debug'})
+ self.assertEqual('native-debug-record' in file, debug_file)
+ self.assertEqual('native-debug-record' in console, debug_console)
+
+ def test_all_level_thresholds(self):
+ levels = ('debug', 'info', 'warning', 'error', 'critical', 'off')
+ for threshold, level in enumerate(levels):
+ with self.subTest(level=level):
+ file, console = self.run_logger({'LIBOPENSHOT_LOG_LEVEL': level})
+ for index, message_level in enumerate(levels[:-1]):
+ message = 'native-' + message_level + '-record'
+ self.assertEqual(message in file, index >= threshold)
+ self.assertEqual(message in console, index >= threshold)
+
+ def test_api_overrides_environment_and_preserves_crash_output(self):
+ file, console = self.run_logger({'LIBOPENSHOT_LOG_LEVEL': 'off'}, '''
+logger.SetFileLevel("debug")
+logger.SetConsoleLevel("error")
+logger.LogToFile("---- Unhandled Exception: Stack Trace ----\\ncrash-evidence\\n---- End of Stack Trace ----\\n")
+try:
+ logger.SetFileLevel("invalid")
+except RuntimeError:
+ pass
+else:
+ raise AssertionError("invalid level accepted")
+''')
+ self.assertIn('native-debug-record', file)
+ self.assertIn('crash-evidence', file)
+ self.assertIn('native-error-record', console)
+ self.assertNotIn('native-debug-record', console)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/bindings/ruby/openshot.i b/bindings/ruby/openshot.i
index 566a84b13..d039b66a0 100644
--- a/bindings/ruby/openshot.i
+++ b/bindings/ruby/openshot.i
@@ -136,7 +136,7 @@ typedef struct OpenShotByteBuffer {
#include "TimelineBase.h"
#include "Timeline.h"
#include "Qt/VideoCacheThread.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
/* Move FFmpeg's RSHIFT to FF_RSHIFT, if present */
#ifdef RSHIFT
@@ -272,7 +272,7 @@ typedef struct OpenShotByteBuffer {
%include "TimelineBase.h"
%include "Qt/VideoCacheThread.h"
%include "Timeline.h"
-%include "ZmqLogger.h"
+%include "Logger.h"
#ifdef USE_IMAGEMAGICK
%include "ImageReader.h"
diff --git a/cmake/Modules/FindZeroMQ.cmake b/cmake/Modules/FindZeroMQ.cmake
deleted file mode 100644
index cec8113e4..000000000
--- a/cmake/Modules/FindZeroMQ.cmake
+++ /dev/null
@@ -1,45 +0,0 @@
-# © OpenShot Studios, LLC
-#
-# SPDX-License-Identifier: LGPL-3.0-or-later
-
-set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON)
-find_package(PkgConfig)
-pkg_check_modules(PC_LIBZMQ QUIET libzmq)
-
-set(ZeroMQ_VERSION ${PC_LIBZMQ_VERSION})
-
-find_path(ZeroMQ_INCLUDE_DIR zmq.h
- PATHS
- ${ZeroMQ_DIR}/include
- ${PC_LIBZMQ_INCLUDE_DIRS})
-
-find_library(ZeroMQ_LIBRARY
- NAMES zmq
- PATHS
- ${ZeroMQ_DIR}/lib
- ${PC_LIBZMQ_LIBDIR}
- ${PC_LIBZMQ_LIBRARY_DIRS})
-
-if(ZeroMQ_LIBRARY)
- set(ZeroMQ_FOUND ON)
-endif()
-
-set ( ZeroMQ_LIBRARIES ${ZeroMQ_LIBRARY} )
-set ( ZeroMQ_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIR} )
-
-if(NOT TARGET libzmq)
- add_library(libzmq UNKNOWN IMPORTED)
- set_target_properties(libzmq PROPERTIES
- IMPORTED_LOCATION ${ZeroMQ_LIBRARIES}
- INTERFACE_INCLUDE_DIRECTORIES ${ZeroMQ_INCLUDE_DIRS})
-endif()
-
-include ( FindPackageHandleStandardArgs )
-# handle the QUIETLY and REQUIRED arguments and set ZMQ_FOUND to TRUE
-# if all listed variables are TRUE
-find_package_handle_standard_args(ZeroMQ
- REQUIRED_VARS
- ZeroMQ_LIBRARIES
- ZeroMQ_INCLUDE_DIRS
- VERSION_VAR
- ZeroMQ_VERSION)
diff --git a/doc/INSTALL-LINUX.md b/doc/INSTALL-LINUX.md
index 7633da879..669f0c52c 100644
--- a/doc/INSTALL-LINUX.md
+++ b/doc/INSTALL-LINUX.md
@@ -72,10 +72,6 @@ list below to help distinguish between them.
* https://github.com/unittest-cpp/ `(Library)`
* This library is used to execute unit tests for libopenshot. It contains many macros used to keep our unit testing code very clean and simple.
-### ZeroMQ (libzmq)
- * http://zeromq.org/ `(Library)`
- * This library is used to communicate between libopenshot and other applications (publisher / subscriber). Primarily used to send debug data from libopenshot.
-
### OpenMP (-fopenmp)
* http://openmp.org/wp/ `(Compiler Flag)`
* If your compiler supports this flag (GCC, Clang, and most other compilers), it provides libopenshot with easy methods of using parallel programming techniques to improve performance and take advantage of multi-core processors.
@@ -166,7 +162,6 @@ software packages available to download and install.
libxcursor-dev \
libxinerama-dev \
libxrandr-dev \
- libzmq3-dev \
pkg-config \
python3-dev \
qtbase5-dev \
@@ -174,7 +169,6 @@ software packages available to download and install.
swig \
xdg-desktop-portal \
xdg-desktop-portal-gtk \
- python3-zmq \
python3-pyqt5.qtwebengine
```
diff --git a/doc/INSTALL-MAC.md b/doc/INSTALL-MAC.md
index 183603207..8851c3958 100644
--- a/doc/INSTALL-MAC.md
+++ b/doc/INSTALL-MAC.md
@@ -67,10 +67,6 @@ list below to help distinguish between them.
* https://github.com/unittest-cpp/ `(Library)`
* This library is used to execute unit tests for libopenshot. It contains many macros used to keep our unit testing code very clean and simple.
-### ZeroMQ (libzmq)
- * http://zeromq.org/ `(Library)`
- * This library is used to communicate between libopenshot and other applications (publisher / subscriber). Primarily used to send debug data from libopenshot.
-
### OpenMP (-fopenmp)
* http://openmp.org/wp/ `(Compiler Flag)`
* If your compiler supports this flag (GCC, Clang, and most other compilers), it provides libopenshot with easy methods of using parallel programming techniques to improve performance and take advantage of multi-core processors.
@@ -151,7 +147,6 @@ brew install doxygen
brew install unittest-cpp --cc=gcc-4.8. You must specify the c++ compiler with the --cc flag to be 4.7 or 4.8.
brew install qt5
brew install cmake
-brew install zeromq
brew install babl
```
diff --git a/doc/INSTALL-WINDOWS.md b/doc/INSTALL-WINDOWS.md
index 2fe1e464a..7dcd3e2f8 100644
--- a/doc/INSTALL-WINDOWS.md
+++ b/doc/INSTALL-WINDOWS.md
@@ -69,10 +69,6 @@ have been labeled in the list below to help distinguish between them.
* https://github.com/unittest-cpp/ `(Library)`
* This library is used to execute unit tests for libopenshot. It contains many macros used to keep our unit testing code very clean and simple.
-### ZeroMQ (libzmq)
- * http://zeromq.org/ `(Library)`
- * This library is used to communicate between libopenshot and other applications (publisher / subscriber). Primarily used to send debug data from libopenshot.
-
### OpenMP (-fopenmp)
* http://openmp.org/wp/ `(Compiler Flag)`
* If your compiler supports this flag (GCC, Clang, and most other compilers), it provides libopenshot with easy methods of using parallel programming techniques to improve performance and take advantage of multi-core processors.
@@ -116,7 +112,6 @@ check each folder path for accuracy, as your paths will likely be different than
* QTDIR (`C:\qt5`)
* SNDFILE_DIR (`C:\Program Files\libsndfile`)
* UNITTEST_DIR (`C:\UnitTest++`)
-* ZMQDIR (`C:\msys2\usr\local\`)
* PATH (`The following paths are an example`)
* `C:\Qt5\bin; C:\Qt5\MinGW\bin\; C:\msys\1.0\local\lib; C:\Program Files\CMake 2.8\bin; C:\UnitTest++\build; C:\libopenshot\build\src; C:\Program Files\doxygen\bin; C:\ffmpeg-git-95f163b-win32-dev\lib; C:\swigwin-2.0.4; C:\Python33; C:\Program Files\Project\lib; C:\msys2\usr\local\`
@@ -196,7 +191,7 @@ pacman -S --needed --noconfirm \
mingw-w64-x86_64-rust base-devel mingw-w64-x86_64-toolchain \
mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-qt5 mingw-w64-x86_64-python3-pyqt5 \
mingw-w64-x86_64-swig mingw-w64-x86_64-cmake mingw-w64-x86_64-doxygen \
-mingw-w64-x86_64-python3-pip mingw-w64-i686-zeromq mingw-w64-x86_64-python3-pyzmq \
+mingw-w64-x86_64-python3-pip \
mingw-w64-x86_64-python3-cx_Freeze mingw-w64-x86_64-ninja mingw-w64-x86_64-catch \
mingw-w64-x86_64-python3-PyOpenGL mingw-w64-clang-x86_64-python-pyopengl-accelerate \
mingw-w64-x86_64-python-pyopengl-accelerate mingw-w64-x86_64-python-pywin32 git
@@ -212,7 +207,7 @@ pacman -S --needed --noconfirm \
mingw-w64-i686-rust mingw-w64-i686-toolchain mingw-w64-i686-ffmpeg \
mingw-w64-i686-qt5 mingw-w64-i686-python3-pyqt5 mingw-w64-i686-swig \
mingw-w64-i686-cmake mingw-w64-i686-doxygen mingw-w64-i686-python3-pip \
-mingw-w64-i686-zeromq mingw-w64-i686-python3-pyzmq mingw-w64-i686-python3-cx_Freeze \
+mingw-w64-i686-python3-cx_Freeze \
mingw-w64-i686-ninja mingw-w64-i686-catch mingw-w64-i686-python-pyopengl \
mingw-w64-i686-python-pyopengl-accelerate mingw-w64-i686-python-pywin32
@@ -288,8 +283,6 @@ mkdir -p /usr/include/resvg/
cp crates/c-api/*.h /usr/include/resvg/
```
-11) ZMQ++ Header (This might not be needed anymore)
- NOTE: Download and copy zmq.hpp into the /c/msys64/mingw64/include/ folder
## Manual Dependencies
@@ -310,12 +303,6 @@ Create an environment variable called DXSDK_DIR and set the value to `C:\Program
* Download and Install the Win32 Setup program.
* Create an environment variable called SNDFILE_DIR and set the value to `C:\Program Files\libsndfile`. This environment variable will be used by CMake to find the binary and header files.
-### libzmq
- * http://zeromq.org/intro:get-the-software
- * Download source code (zip)
- * Follow their instructions, and build with mingw
- * Create an environment variable called ZMQDIR and set the value to `C:\libzmq\build\` (the location of the compiled version). This environment variable will be used by CMake to find the binary and header files.
-
## Windows Build Instructions (libopenshot-audio)
In order to compile libopenshot-audio, launch a command prompt and enter the following commands. This does not require the MSYS2 prompt, but it should work in both the Windows command prompt and the MSYS2 prompt.
diff --git a/doc/logging.rst b/doc/logging.rst
new file mode 100644
index 000000000..bf56796d6
--- /dev/null
+++ b/doc/logging.rst
@@ -0,0 +1,83 @@
+.. SPDX-FileCopyrightText: 2026 OpenShot Studios, LLC
+.. SPDX-License-Identifier: LGPL-3.0-or-later
+
+Logging
+=========
+
+``openshot::Logger`` provides synchronous, thread-safe ordinary logging to an
+append-only file and stderr, with independent severity thresholds. It uses
+standard C++ and requires no ZeroMQ dependency or forwarding thread.
+
+Configuration
+-------------
+
+Both thresholds default to INFO. Supported levels are DEBUG, INFO, WARNING,
+ERROR, CRITICAL, and OFF (case-insensitive). Existing method traces use DEBUG.
+No file is opened until a path is supplied through the API or environment.
+
+.. code-block:: cpp
+
+ #include "Logger.h"
+
+ auto* logger = openshot::Logger::Instance();
+ logger->Path("render.log"); // UTF-8; relative to the working directory
+ logger->SetFileLevel("DEBUG");
+ logger->SetConsoleLevel("WARNING");
+ logger->Log("Starting export", openshot::Logger::LevelInfo);
+
+The same API is exposed by the Python binding. For a standalone script:
+
+.. code-block:: bash
+
+ LIBOPENSHOT_LOG_FILE=render.log LIBOPENSHOT_LOG_LEVEL=debug python3 render.py
+
+.. list-table:: Environment variables
+ :header-rows: 1
+ :widths: 50 50
+
+ * - Variable
+ - Scope
+ * - ``LIBOPENSHOT_LOG_FILE``
+ - Output file path; no standalone default
+ * - ``LIBOPENSHOT_LOG_LEVEL``
+ - File and stderr thresholds
+ * - ``LIBOPENSHOT_LOG_FILE_LEVEL``
+ - File threshold
+ * - ``LIBOPENSHOT_LOG_CONSOLE_LEVEL``
+ - Stderr threshold
+ * - ``OPENSHOT_LOG_LEVEL``
+ - Shared fallback for both thresholds
+ * - ``OPENSHOT_LOG_FILE_LEVEL`` / ``OPENSHOT_LOG_CONSOLE_LEVEL``
+ - Shared fallback for the respective threshold
+ * - ``LIBOPENSHOT_DEBUG``
+ - Legacy stderr DEBUG fallback; any present value, including ``0``
+
+Environment variables are read at singleton initialization. Precedence is
+explicit API configuration > ``LIBOPENSHOT_`` variables > ``OPENSHOT_`` variables
+> legacy debug switch > INFO. Within each variable group, destination-specific
+levels override the general level. Invalid environment levels emit a diagnostic
+and fall back; invalid API levels throw ``std::invalid_argument``.
+
+In openshot-qt, the host resolves CLI > environment > saved preferences > defaults
+and configures this API. ``--debug-engine`` enables engine DEBUG in its file and
+stderr; **Video & Audio Engine Debug Logging** affects only ``libopenshot.log``.
+``--debug-ui`` (aliases ``--debug``, ``-d``) and **User Interface Debug Logging**
+control the separate Python logger and ``openshot-qt.log``. The GUI selects its
+own file path under ``~/.openshot_qt/``.
+
+Behavior and compatibility
+--------------------------
+
+* Records include local time, severity, and thread ID. ``ShouldLog(level)`` can
+ guard expensive message construction. File writes flush per record; there is
+ no rotation or asynchronous queue. DEBUG can generate substantial I/O; avoid
+ logging from real-time audio callbacks.
+* ``Close()`` closes the file and disables ordinary output. Reopening requires
+ setting the path and desired levels again.
+* ``ZmqLogger`` is a deprecated C++/Python source alias; rebuild consumers and
+ bindings. ``Connection()`` is a no-op. ``Enable(true/false)`` selects file
+ DEBUG/OFF. ``Settings::DEBUG_TO_STDERR`` remains a legacy console switch,
+ overridden by explicit modern console configuration.
+* ``LogToFile()`` preserves the existing unfiltered raw crash-write path and
+ does not acquire the ordinary logger mutex. Crash markers and error handling
+ are unchanged. Stream flushing does not guarantee durability after power loss.
diff --git a/examples/qt-demo/main.cpp b/examples/qt-demo/main.cpp
index 2ef142248..9dfaa050e 100644
--- a/examples/qt-demo/main.cpp
+++ b/examples/qt-demo/main.cpp
@@ -11,15 +11,15 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "Qt/PlayerDemo.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#include
int main(int argc, char *argv[])
{
// Enable logging for openshot-player since this is primarily used for
// profiling and debugging video playback issues.
- openshot::ZmqLogger::Instance()->Enable(true);
- openshot::ZmqLogger::Instance()->Path("./player.log");
+ openshot::Logger::Instance()->Enable(true);
+ openshot::Logger::Instance()->Path("./player.log");
QApplication app(argc, argv);
PlayerDemo demo;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a27f2f7ac..7452807ac 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -92,7 +92,7 @@ set(OPENSHOT_SOURCES
TimelineBase.cpp
Timeline.cpp
TrackedObjectBase.cpp
- ZmqLogger.cpp
+ Logger.cpp
)
set(OPENSHOT_WAYLAND_CAPTURE FALSE)
@@ -514,26 +514,6 @@ endif()
target_link_libraries(openshot PUBLIC OpenMP::OpenMP_CXX)
-###
-### ZeroMQ
-###
-
-# Find ZeroMQ library (used for socket communication & logging)
-find_package(ZeroMQ REQUIRED) # Creates libzmq target
-
-# Some platforms package the header-only cppzmq C++ bindings separately,
-# others (Ubuntu) bundle them in with libzmq itself
-find_package(cppzmq QUIET) # Creates cppzmq target
-
-# Link ZeroMQ library
-if (TARGET libzmq)
- target_link_libraries(openshot PUBLIC libzmq)
-endif()
-# Include cppzmq headers, if not bundled into libzmq
-if (TARGET cppzmq)
- target_link_libraries(openshot PUBLIC cppzmq)
-endif()
-
###
### Babl
###
diff --git a/src/CVObjectDetection.cpp b/src/CVObjectDetection.cpp
index 7fe03387b..7f3c0c1aa 100644
--- a/src/CVObjectDetection.cpp
+++ b/src/CVObjectDetection.cpp
@@ -18,7 +18,7 @@
#include "CVObjectDetection.h"
#include "Exceptions.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#define int64 int64_t
#define uint64 uint64_t
@@ -266,7 +266,7 @@ void CVObjectDetection::setProcessingDevice(){
if (processingDevice == "CPU") {
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
- ZmqLogger::Instance()->Log("Object Detection DNN device: requested CPU, selected CPU");
+ Logger::Instance()->Log("Object Detection DNN device: requested CPU, selected CPU");
return;
}
@@ -276,7 +276,7 @@ void CVObjectDetection::setProcessingDevice(){
if (std::find(targets.begin(), targets.end(), cv::dnn::DNN_TARGET_CUDA) != targets.end()) {
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
- ZmqLogger::Instance()->Log("Object Detection DNN device: requested " + requestedDevice + ", selected CUDA");
+ Logger::Instance()->Log("Object Detection DNN device: requested " + requestedDevice + ", selected CUDA");
return;
}
} catch (const cv::Exception&) {
@@ -290,7 +290,7 @@ void CVObjectDetection::setProcessingDevice(){
cv::ocl::setUseOpenCL(true);
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(cv::dnn::DNN_TARGET_OPENCL);
- ZmqLogger::Instance()->Log("Object Detection DNN device: requested " + requestedDevice + ", selected OpenCL");
+ Logger::Instance()->Log("Object Detection DNN device: requested " + requestedDevice + ", selected OpenCL");
return;
}
} catch (const cv::Exception&) {
@@ -300,7 +300,7 @@ void CVObjectDetection::setProcessingDevice(){
processingDevice = "CPU";
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
- ZmqLogger::Instance()->Log("Object Detection DNN device: requested " + requestedDevice + ", selected CPU");
+ Logger::Instance()->Log("Object Detection DNN device: requested " + requestedDevice + ", selected CPU");
}
void CVObjectDetection::detectObjectsClip(openshot::Clip &video, size_t _start, size_t _end, bool process_interval)
diff --git a/src/CVObjectMask.cpp b/src/CVObjectMask.cpp
index d977ec4fd..e1ad218f1 100644
--- a/src/CVObjectMask.cpp
+++ b/src/CVObjectMask.cpp
@@ -13,7 +13,7 @@
#include "CVObjectMask.h"
#include "Exceptions.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#include "objdetectdata.pb.h"
#define int64 int64_t
@@ -861,7 +861,7 @@ void CVObjectMask::SetProcessingDevice()
if (processingDevice == "CPU") {
efficientSam.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
efficientSam.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
- ZmqLogger::Instance()->Log("Object Mask EfficientSAM DNN device: requested CPU, selected CPU");
+ Logger::Instance()->Log("Object Mask EfficientSAM DNN device: requested CPU, selected CPU");
return;
}
@@ -871,7 +871,7 @@ void CVObjectMask::SetProcessingDevice()
if (std::find(targets.begin(), targets.end(), cv::dnn::DNN_TARGET_CUDA) != targets.end()) {
efficientSam.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
efficientSam.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
- ZmqLogger::Instance()->Log("Object Mask EfficientSAM DNN device: requested " + requestedDevice + ", selected CUDA");
+ Logger::Instance()->Log("Object Mask EfficientSAM DNN device: requested " + requestedDevice + ", selected CUDA");
return;
}
} catch (const cv::Exception&) {
@@ -885,7 +885,7 @@ void CVObjectMask::SetProcessingDevice()
cv::ocl::setUseOpenCL(true);
efficientSam.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
efficientSam.setPreferableTarget(cv::dnn::DNN_TARGET_OPENCL);
- ZmqLogger::Instance()->Log("Object Mask EfficientSAM DNN device: requested " + requestedDevice + ", selected OpenCL");
+ Logger::Instance()->Log("Object Mask EfficientSAM DNN device: requested " + requestedDevice + ", selected OpenCL");
return;
}
} catch (const cv::Exception&) {
@@ -895,7 +895,7 @@ void CVObjectMask::SetProcessingDevice()
processingDevice = "CPU";
efficientSam.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
efficientSam.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
- ZmqLogger::Instance()->Log("Object Mask EfficientSAM DNN device: requested " + requestedDevice + ", selected CPU");
+ Logger::Instance()->Log("Object Mask EfficientSAM DNN device: requested " + requestedDevice + ", selected CPU");
}
void CVObjectMask::maskClip(openshot::Clip& video, size_t _start, size_t _end, bool process_interval)
@@ -948,7 +948,7 @@ void CVObjectMask::maskClip(openshot::Clip& video, size_t _start, size_t _end, b
try {
cutie.Load(cutieEncodeKeyModelPath, cutieEncodeValueModelPath, cutieMemoryReadoutModelPath, cutieDecodeModelPath);
const std::string cutieDevice = cutie.SetDevice(processingDevice);
- ZmqLogger::Instance()->Log("Object Mask Cutie DNN device: requested " + processingDevice + ", selected " + cutieDevice);
+ Logger::Instance()->Log("Object Mask Cutie DNN device: requested " + processingDevice + ", selected " + cutieDevice);
} catch (const cv::Exception& e) {
processingController->SetError(true, std::string("Failed to load Cutie ONNX models: ") + e.what());
error = true;
diff --git a/src/Clip.cpp b/src/Clip.cpp
index 585990a49..a03df145e 100644
--- a/src/Clip.cpp
+++ b/src/Clip.cpp
@@ -20,7 +20,7 @@
#include "ChunkReader.h"
#include "DummyReader.h"
#include "Timeline.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#include "effects/AudioVisualization.h"
#include
@@ -411,7 +411,7 @@ void Clip::Open()
void Clip::Close()
{
if (is_open && reader) {
- ZmqLogger::Instance()->AppendDebugMethod("Clip::Close");
+ Logger::Instance()->AppendDebugMethod("Clip::Close");
// Close the reader
reader->Close();
@@ -765,7 +765,7 @@ std::shared_ptr Clip::GetOrCreateFrame(int64_t number, bool enable_time)
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Clip::GetOrCreateFrame (from reader)",
"number", number, "clip_frame_number", clip_frame_number);
@@ -802,7 +802,7 @@ std::shared_ptr Clip::GetOrCreateFrame(int64_t number, bool enable_time)
int estimated_samples_in_frame = Frame::GetSamplesPerFrame(number, reader->info.fps, reader->info.sample_rate, reader->info.channels);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Clip::GetOrCreateFrame (create blank)",
"number", number,
"estimated_samples_in_frame", estimated_samples_in_frame);
@@ -1477,7 +1477,7 @@ void Clip::apply_waveform(std::shared_ptr frame, QSize timeline_size) {
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_waveform (Generate Waveform Image)",
+ Logger::Instance()->AppendDebugMethod("Clip::apply_waveform (Generate Waveform Image)",
"frame->number", frame->number,
"Waveform()", Waveform(),
"width", timeline_size.width(),
@@ -1673,7 +1673,7 @@ QTransform Clip::get_transform(std::shared_ptr frame, int width, int heig
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Clip::get_transform (Gravity)",
"frame->number", frame->number,
"source_clip->gravity", gravity,
@@ -1708,7 +1708,7 @@ QTransform Clip::get_transform(std::shared_ptr frame, int width, int heig
float origin_y_value = origin_y.GetValue(frame->number);
// Transform source image (if needed)
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Clip::get_transform (Build QTransform - if needed)",
"frame->number", frame->number,
"x", x, "y", y,
diff --git a/src/CrashHandler.cpp b/src/CrashHandler.cpp
index 26018cc56..c47b98c80 100644
--- a/src/CrashHandler.cpp
+++ b/src/CrashHandler.cpp
@@ -112,7 +112,7 @@ void CrashHandler::abortHandler( int signum, siginfo_t* si, void* unused )
void CrashHandler::printStackTrace(FILE *out, unsigned int max_frames)
{
fprintf(out, "---- Unhandled Exception: Stack Trace ----\n");
- ZmqLogger::Instance()->LogToFile("---- Unhandled Exception: Stack Trace ----\n");
+ Logger::Instance()->LogToFile("---- Unhandled Exception: Stack Trace ----\n");
stringstream stack_output;
#ifdef __MINGW32__
@@ -197,7 +197,7 @@ void CrashHandler::printStackTrace(FILE *out, unsigned int max_frames)
if ( addrlen == 0 )
{
fprintf(out, " No stack trace found (addrlen == 0)\n");
- ZmqLogger::Instance()->LogToFile(" No stack trace found (addrlen == 0)\n");
+ Logger::Instance()->LogToFile(" No stack trace found (addrlen == 0)\n");
return;
}
@@ -302,8 +302,8 @@ void CrashHandler::printStackTrace(FILE *out, unsigned int max_frames)
#endif
// Write stacktrace to file (if log path set)
- ZmqLogger::Instance()->LogToFile(stack_output.str());
+ Logger::Instance()->LogToFile(stack_output.str());
fprintf(out, "---- End of Stack Trace ----\n");
- ZmqLogger::Instance()->LogToFile("---- End of Stack Trace ----\n");
+ Logger::Instance()->LogToFile("---- End of Stack Trace ----\n");
}
diff --git a/src/CrashHandler.h b/src/CrashHandler.h
index 1fe5d8526..ca13029e5 100644
--- a/src/CrashHandler.h
+++ b/src/CrashHandler.h
@@ -25,7 +25,7 @@
#endif
#include
#include
-#include "ZmqLogger.h"
+#include "Logger.h"
namespace openshot {
@@ -33,7 +33,7 @@ namespace openshot {
* @brief This class is designed to catch exceptions thrown by libc (SIGABRT, SIGSEGV, SIGILL, SIGFPE)
*
* This class is a singleton which only needs to be instantiated 1 time, and it will register as a signal
- * handler with libc, and log errors using the ZmqLogger class.
+ * handler with libc, and log errors using the Logger class.
*/
class CrashHandler {
private:
diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp
index fc8cc0087..d3fd1c80d 100644
--- a/src/EffectBase.cpp
+++ b/src/EffectBase.cpp
@@ -24,7 +24,7 @@
#include "ChunkReader.h"
#include "FFmpegReader.h"
#include "QtImageReader.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#include
#include
#include
@@ -526,7 +526,7 @@ std::shared_ptr EffectBase::GetMaskImage(std::shared_ptr target_
source_mask = std::make_shared(*source_frame->GetImage());
}
} catch (const std::exception& e) {
- ZmqLogger::Instance()->Log(
+ Logger::Instance()->Log(
std::string("EffectBase::GetMaskImage unable to read mask frame: ") + e.what());
source_mask.reset();
}
diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp
index ad901b1f0..a7fa7c123 100644
--- a/src/FFmpegReader.cpp
+++ b/src/FFmpegReader.cpp
@@ -29,7 +29,7 @@
#include "Exceptions.h"
#include "MemoryTrim.h"
#include "Timeline.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#define ENABLE_VAAPI 0
@@ -236,7 +236,7 @@ static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx, const enum AVPi
break;
}
}
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::get_hw_dec_format (Unable to decode this file using hardware decode)");
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::get_hw_dec_format (Unable to decode this file using hardware decode)");
return AV_PIX_FMT_NONE;
}
@@ -273,7 +273,7 @@ void FFmpegReader::Open() {
hw_decode_failed = false;
hw_decode_error_count = 0;
hw_decode_succeeded = false;
- ZmqLogger::Instance()->AppendDebugMethod("Decode hardware acceleration settings", "hw_de_on", hw_de_on, "HARDWARE_DECODER", openshot::Settings::Instance()->HARDWARE_DECODER);
+ Logger::Instance()->AppendDebugMethod("Decode hardware acceleration settings", "hw_de_on", hw_de_on, "HARDWARE_DECODER", openshot::Settings::Instance()->HARDWARE_DECODER);
}
// Open video file
@@ -353,7 +353,7 @@ void FFmpegReader::Open() {
char *adapter_ptr = NULL;
int adapter_num;
adapter_num = openshot::Settings::Instance()->HW_DE_DEVICE_SET;
- ZmqLogger::Instance()->AppendDebugMethod("Hardware decoding device number", "adapter_num", adapter_num);
+ Logger::Instance()->AppendDebugMethod("Hardware decoding device number", "adapter_num", adapter_num);
// Set hardware pix format (callback)
pCodecCtx->get_format = get_hw_dec_format;
@@ -429,11 +429,11 @@ void FFmpegReader::Open() {
#elif defined(__APPLE__)
if( adapter_ptr != NULL ) {
#endif
- ZmqLogger::Instance()->AppendDebugMethod("Decode Device present using device");
+ Logger::Instance()->AppendDebugMethod("Decode Device present using device");
}
else {
adapter_ptr = NULL; // use default
- ZmqLogger::Instance()->AppendDebugMethod("Decode Device not present using default");
+ Logger::Instance()->AppendDebugMethod("Decode Device not present using default");
}
hw_device_ctx = NULL;
@@ -442,7 +442,7 @@ void FFmpegReader::Open() {
const char* hw_name = av_hwdevice_get_type_name(hw_de_av_device_type);
std::string hw_msg = "HW decode active: ";
hw_msg += (hw_name ? hw_name : "unknown");
- ZmqLogger::Instance()->Log(hw_msg);
+ Logger::Instance()->Log(hw_msg);
if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) {
throw InvalidCodec("Hardware device reference create failed.", path);
}
@@ -475,7 +475,7 @@ void FFmpegReader::Open() {
*/
}
else {
- ZmqLogger::Instance()->Log("HW decode active: no (falling back to software)");
+ Logger::Instance()->Log("HW decode active: no (falling back to software)");
throw InvalidCodec("Hardware device create failed.", path);
}
}
@@ -513,7 +513,7 @@ void FFmpegReader::Open() {
pCodecCtx->coded_height < constraints->min_height ||
pCodecCtx->coded_width > constraints->max_width ||
pCodecCtx->coded_height > constraints->max_height) {
- ZmqLogger::Instance()->AppendDebugMethod("DIMENSIONS ARE TOO LARGE for hardware acceleration\n");
+ Logger::Instance()->AppendDebugMethod("DIMENSIONS ARE TOO LARGE for hardware acceleration\n");
hw_de_supported = 0;
retry_decode_open = 1;
AV_FREE_CONTEXT(pCodecCtx);
@@ -524,7 +524,7 @@ void FFmpegReader::Open() {
}
else {
// All is just peachy
- ZmqLogger::Instance()->AppendDebugMethod("\nDecode hardware acceleration is used\n", "Min width :", constraints->min_width, "Min Height :", constraints->min_height, "MaxWidth :", constraints->max_width, "MaxHeight :", constraints->max_height, "Frame width :", pCodecCtx->coded_width, "Frame height :", pCodecCtx->coded_height);
+ Logger::Instance()->AppendDebugMethod("\nDecode hardware acceleration is used\n", "Min width :", constraints->min_width, "Min Height :", constraints->min_height, "MaxWidth :", constraints->max_width, "MaxHeight :", constraints->max_height, "Frame width :", pCodecCtx->coded_width, "Frame height :", pCodecCtx->coded_height);
retry_decode_open = 0;
}
av_hwframe_constraints_free(&constraints);
@@ -538,13 +538,13 @@ void FFmpegReader::Open() {
max_h = openshot::Settings::Instance()->DE_LIMIT_HEIGHT_MAX;
//max_w = ((getenv( "LIMIT_WIDTH_MAX" )==NULL) ? MAX_SUPPORTED_WIDTH : atoi(getenv( "LIMIT_WIDTH_MAX" )));
max_w = openshot::Settings::Instance()->DE_LIMIT_WIDTH_MAX;
- ZmqLogger::Instance()->AppendDebugMethod("Constraints could not be found using default limit\n");
+ Logger::Instance()->AppendDebugMethod("Constraints could not be found using default limit\n");
//cerr << "Constraints could not be found using default limit\n";
if (pCodecCtx->coded_width < 0 ||
pCodecCtx->coded_height < 0 ||
pCodecCtx->coded_width > max_w ||
pCodecCtx->coded_height > max_h ) {
- ZmqLogger::Instance()->AppendDebugMethod("DIMENSIONS ARE TOO LARGE for hardware acceleration\n", "Max Width :", max_w, "Max Height :", max_h, "Frame width :", pCodecCtx->coded_width, "Frame height :", pCodecCtx->coded_height);
+ Logger::Instance()->AppendDebugMethod("DIMENSIONS ARE TOO LARGE for hardware acceleration\n", "Max Width :", max_w, "Max Height :", max_h, "Frame width :", pCodecCtx->coded_width, "Frame height :", pCodecCtx->coded_height);
hw_de_supported = 0;
retry_decode_open = 1;
AV_FREE_CONTEXT(pCodecCtx);
@@ -554,13 +554,13 @@ void FFmpegReader::Open() {
}
}
else {
- ZmqLogger::Instance()->AppendDebugMethod("\nDecode hardware acceleration is used\n", "Max Width :", max_w, "Max Height :", max_h, "Frame width :", pCodecCtx->coded_width, "Frame height :", pCodecCtx->coded_height);
+ Logger::Instance()->AppendDebugMethod("\nDecode hardware acceleration is used\n", "Max Width :", max_w, "Max Height :", max_h, "Frame width :", pCodecCtx->coded_width, "Frame height :", pCodecCtx->coded_height);
retry_decode_open = 0;
}
}
} // if hw_de_on && hw_de_supported
else {
- ZmqLogger::Instance()->AppendDebugMethod("\nDecode in software is used\n");
+ Logger::Instance()->AppendDebugMethod("\nDecode in software is used\n");
}
#else
retry_decode_open = 0;
@@ -616,7 +616,7 @@ void FFmpegReader::Open() {
(info.audio_timebase.den <= 0) ||
(aCodecCtx->sample_fmt == AV_SAMPLE_FMT_NONE);
if (invalid_audio_info) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::Open (Disable invalid audio stream)",
"channels", info.channels,
"sample_rate", info.sample_rate,
@@ -638,7 +638,7 @@ void FFmpegReader::Open() {
}
} else {
// Keep decoding video, but disable bad/unsupported audio stream.
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::Open (Audio codec unavailable; disabling audio)",
"audioStream", audioStream);
info.has_audio = false;
@@ -655,7 +655,7 @@ void FFmpegReader::Open() {
// Guard invalid frame-rate / timebase values from malformed streams.
if (info.fps.num <= 0 || info.fps.den <= 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::Open (Invalid FPS detected; applying fallback)",
"fps.num", info.fps.num,
"fps.den", info.fps.den);
@@ -663,7 +663,7 @@ void FFmpegReader::Open() {
info.fps.den = 1;
}
if (info.video_timebase.num <= 0 || info.video_timebase.den <= 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::Open (Invalid video_timebase detected; applying fallback)",
"video_timebase.num", info.video_timebase.num,
"video_timebase.den", info.video_timebase.den);
@@ -1054,11 +1054,11 @@ void FFmpegReader::UpdateVideoInfo() {
info.fps.den = framerate.den;
}
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::UpdateVideoInfo", "info.fps.num", info.fps.num, "info.fps.den", info.fps.den);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::UpdateVideoInfo", "info.fps.num", info.fps.num, "info.fps.den", info.fps.den);
// TODO: remove excessive debug info in the next releases
// The debug info below is just for comparison and troubleshooting on users side during the transition period
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::UpdateVideoInfo (pStream->avg_frame_rate)", "num", pStream->avg_frame_rate.num, "den", pStream->avg_frame_rate.den);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::UpdateVideoInfo (pStream->avg_frame_rate)", "num", pStream->avg_frame_rate.num, "den", pStream->avg_frame_rate.den);
if (pStream->sample_aspect_ratio.num != 0) {
info.pixel_ratio.num = pStream->sample_aspect_ratio.num;
@@ -1260,13 +1260,13 @@ std::shared_ptr FFmpegReader::GetFrame(int64_t requested_frame) {
throw InvalidFile("Could not detect the duration of the video or audio stream.", path);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetFrame", "requested_frame", requested_frame, "last_frame", last_frame);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetFrame", "requested_frame", requested_frame, "last_frame", last_frame);
// Check the cache for this frame
std::shared_ptr frame = final_cache.GetFrame(requested_frame);
if (frame) {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetFrame", "returned cached frame", requested_frame);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetFrame", "returned cached frame", requested_frame);
// Return the cached frame
return frame;
} else {
@@ -1278,7 +1278,7 @@ std::shared_ptr FFmpegReader::GetFrame(int64_t requested_frame) {
frame = final_cache.GetFrame(requested_frame);
if (frame) {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetFrame", "returned cached frame on 2nd look", requested_frame);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetFrame", "returned cached frame on 2nd look", requested_frame);
} else {
// Frame is not in cache
// Reset seek count
@@ -1321,7 +1321,7 @@ std::shared_ptr FFmpegReader::ReadStream(int64_t requested_frame) {
double prev_video_pts_seconds = video_pts_seconds;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream", "requested_frame", requested_frame);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream", "requested_frame", requested_frame);
// Loop through the stream until the correct frame is found
while (true) {
@@ -1347,7 +1347,7 @@ std::shared_ptr FFmpegReader::ReadStream(int64_t requested_frame) {
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (GetNextPacket)", "requested_frame", requested_frame,"packets_read", packet_status.packets_read(), "packets_decoded", packet_status.packets_decoded(), "is_seeking", is_seeking);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (GetNextPacket)", "requested_frame", requested_frame,"packets_read", packet_status.packets_read(), "packets_decoded", packet_status.packets_decoded(), "is_seeking", is_seeking);
// Check the status of a seek (if any)
if (is_seeking) {
@@ -1403,7 +1403,7 @@ std::shared_ptr FFmpegReader::ReadStream(int64_t requested_frame) {
if ((packet_status.packets_eof && packet_status.packets_read() == packet_status.packets_decoded()) || packet_status.end_of_file) {
// Force EOF (end of file) variables to true, if decoder does not support EOF detection.
// If we have no more packets, and all known packets have been decoded
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (force EOF)", "packets_read", packet_status.packets_read(), "packets_decoded", packet_status.packets_decoded(), "packets_eof", packet_status.packets_eof, "video_eof", packet_status.video_eof, "audio_eof", packet_status.audio_eof, "end_of_file", packet_status.end_of_file);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (force EOF)", "packets_read", packet_status.packets_read(), "packets_decoded", packet_status.packets_decoded(), "packets_eof", packet_status.packets_eof, "video_eof", packet_status.video_eof, "audio_eof", packet_status.audio_eof, "end_of_file", packet_status.end_of_file);
if (!packet_status.video_eof) {
packet_status.video_eof = true;
}
@@ -1430,7 +1430,7 @@ std::shared_ptr FFmpegReader::ReadStream(int64_t requested_frame) {
&& packet_status.packets_eof
&& !packet
&& !hold_packet) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (force EOF after stall)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (force EOF after stall)",
"requested_frame", requested_frame,
"no_progress_count", no_progress_count,
"packets_read", packet_status.packets_read(),
@@ -1450,7 +1450,7 @@ std::shared_ptr FFmpegReader::ReadStream(int64_t requested_frame) {
} // end while
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (Completed)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (Completed)",
"packets_read", packet_status.packets_read(),
"packets_decoded", packet_status.packets_decoded(),
"end_of_file", packet_status.end_of_file,
@@ -1555,7 +1555,7 @@ bool FFmpegReader::GetAVFrame() {
}
if (err == AVERROR_INVALIDDATA && packet_status.video_decoded == 0) {
hw_decode_error_count++;
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
std::string("FFmpegReader::GetAVFrame (hardware decode failure candidate during ") + stage + ")",
"error_count", hw_decode_error_count,
"error", err);
@@ -1581,7 +1581,7 @@ bool FFmpegReader::GetAVFrame() {
if (packet && send_packet_err >= 0) {
send_packet_pts = GetPacketPTS();
hold_packet = false;
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet succeeded)", "send_packet_err", send_packet_err, "send_packet_pts", send_packet_pts);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet succeeded)", "send_packet_err", send_packet_err, "send_packet_pts", send_packet_pts);
}
}
@@ -1591,17 +1591,17 @@ bool FFmpegReader::GetAVFrame() {
hw_de_av_device_type = hw_de_av_device_type_global;
#endif // USE_HW_ACCEL
if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet: Not sent [" + av_err2string(send_packet_err) + "])", "send_packet_err", send_packet_err, "send_packet_pts", send_packet_pts);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet: Not sent [" + av_err2string(send_packet_err) + "])", "send_packet_err", send_packet_err, "send_packet_pts", send_packet_pts);
note_hw_decode_failure(send_packet_err, "send_packet");
if (send_packet_err == AVERROR(EAGAIN)) {
hold_packet = true;
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet: AVERROR(EAGAIN): user must read output with avcodec_receive_frame()", "send_packet_pts", send_packet_pts);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet: AVERROR(EAGAIN): user must read output with avcodec_receive_frame()", "send_packet_pts", send_packet_pts);
}
if (send_packet_err == AVERROR(EINVAL)) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet: AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush", "send_packet_pts", send_packet_pts);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet: AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush", "send_packet_pts", send_packet_pts);
}
if (send_packet_err == AVERROR(ENOMEM)) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet: AVERROR(ENOMEM): failed to add packet to internal queue, or legitimate decoding errors", "send_packet_pts", send_packet_pts);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (send packet: AVERROR(ENOMEM): failed to add packet to internal queue, or legitimate decoding errors", "send_packet_pts", send_packet_pts);
}
}
@@ -1625,26 +1625,26 @@ bool FFmpegReader::GetAVFrame() {
receive_frame_err = avcodec_receive_frame(pCodecCtx, next_frame2);
if (receive_frame_err != 0) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (receive frame: frame not ready yet from decoder [\" + av_err2string(receive_frame_err) + \"])", "receive_frame_err", receive_frame_err, "send_packet_pts", send_packet_pts);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (receive frame: frame not ready yet from decoder [\" + av_err2string(receive_frame_err) + \"])", "receive_frame_err", receive_frame_err, "send_packet_pts", send_packet_pts);
note_hw_decode_failure(receive_frame_err, "receive_frame");
if (receive_frame_err == AVERROR_EOF) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::GetAVFrame (receive frame: AVERROR_EOF: EOF detected from decoder, flushing buffers)", "send_packet_pts", send_packet_pts);
avcodec_flush_buffers(pCodecCtx);
packet_status.video_eof = true;
}
if (receive_frame_err == AVERROR(EINVAL)) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::GetAVFrame (receive frame: AVERROR(EINVAL): invalid frame received, flushing buffers)", "send_packet_pts", send_packet_pts);
avcodec_flush_buffers(pCodecCtx);
}
if (receive_frame_err == AVERROR(EAGAIN)) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::GetAVFrame (receive frame: AVERROR(EAGAIN): output is not available in this state - user must try to send new input)", "send_packet_pts", send_packet_pts);
}
if (receive_frame_err == AVERROR_INPUT_CHANGED) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::GetAVFrame (receive frame: AVERROR_INPUT_CHANGED: current decoded frame has changed parameters with respect to first decoded frame)", "send_packet_pts", send_packet_pts);
}
@@ -1658,7 +1658,7 @@ bool FFmpegReader::GetAVFrame() {
int err;
if (next_frame2->format == hw_de_av_pix_fmt) {
if ((err = av_hwframe_transfer_data(next_frame, next_frame2, 0)) < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::GetAVFrame (Failed to transfer data to output frame)",
"hw_de_on", hw_de_on,
"error", err);
@@ -1666,7 +1666,7 @@ bool FFmpegReader::GetAVFrame() {
break;
}
if ((err = av_frame_copy_props(next_frame, next_frame2)) < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::GetAVFrame (Failed to copy props to output frame)",
"hw_de_on", hw_de_on,
"error", err);
@@ -1695,7 +1695,7 @@ bool FFmpegReader::GetAVFrame() {
}
if (!decoded_frame->data[0]) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::GetAVFrame (Decoded frame missing image data)",
"format", decoded_frame->format,
"width", decoded_frame->width,
@@ -1748,7 +1748,7 @@ bool FFmpegReader::GetAVFrame() {
video_pts = decoded_frame->pkt_dts;
}
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::GetAVFrame (Successful frame received)", "video_pts", video_pts, "send_packet_pts", send_packet_pts);
// break out of loop after each successful image returned
@@ -1788,7 +1788,7 @@ bool FFmpegReader::ReopenWithoutHardwareDecode(int64_t requested_frame) {
return false;
}
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::ReopenWithoutHardwareDecode (falling back to software decode)",
"requested_frame", requested_frame,
"video_packets_read", packet_status.video_read,
@@ -1846,7 +1846,7 @@ bool FFmpegReader::CheckSeek() {
// determine if we are "before" the requested frame
if (max_seeked_frame >= seeking_frame) {
// SEEKED TOO FAR
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::CheckSeek (Too far, seek again)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::CheckSeek (Too far, seek again)",
"is_video_seek", is_video_seek,
"max_seeked_frame", max_seeked_frame,
"seeking_frame", seeking_frame,
@@ -1866,7 +1866,7 @@ bool FFmpegReader::CheckSeek() {
}
} else {
// SEEK WORKED
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::CheckSeek (Successful)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::CheckSeek (Successful)",
"is_video_seek", is_video_seek,
"packet->pts", GetPacketPTS(),
"seeking_pts", seeking_pts,
@@ -1914,7 +1914,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
working_cache.Add(CreateFrame(requested_frame));
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessVideoPacket (Before)", "requested_frame", requested_frame, "current_frame", current_frame);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessVideoPacket (Before)", "requested_frame", requested_frame, "current_frame", current_frame);
// Init some things local (for OpenMP)
AVPixelFormat decoded_pix_fmt = (pFrame && pFrame->format != AV_PIX_FMT_NONE)
@@ -2062,7 +2062,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
#if USE_HW_ACCEL
if (hw_de_on && hw_de_supported && !force_sw_decode) {
hw_decode_failed = true;
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::ProcessVideoPacket (Invalid source frame; forcing software fallback)",
"requested_frame", requested_frame,
"current_frame", current_frame,
@@ -2085,7 +2085,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
#if USE_HW_ACCEL
if (hw_de_on && hw_de_supported && !force_sw_decode) {
hw_decode_failed = true;
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::ProcessVideoPacket (sws_scale failed; forcing software fallback)",
"requested_frame", requested_frame,
"current_frame", current_frame,
@@ -2131,7 +2131,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
video_pts_seconds = (double(video_pts) * info.video_timebase.ToDouble()) + pts_offset_seconds;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessVideoPacket (After)", "requested_frame", requested_frame, "current_frame", current_frame, "f->number", f->number, "video_pts_seconds", video_pts_seconds);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessVideoPacket (After)", "requested_frame", requested_frame, "current_frame", current_frame, "f->number", f->number, "video_pts_seconds", video_pts_seconds);
}
// Process an audio packet
@@ -2154,7 +2154,7 @@ void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
working_cache.Add(CreateFrame(requested_frame));
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (Before)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (Before)",
"requested_frame", requested_frame,
"target_frame", location.frame,
"starting_sample", location.sample_start);
@@ -2170,7 +2170,7 @@ void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
#if IS_FFMPEG_3_2
int send_packet_err = avcodec_send_packet(aCodecCtx, packet);
if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (Packet not sent)");
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (Packet not sent)");
}
else {
int receive_frame_err = avcodec_receive_frame(aCodecCtx, audio_frame);
@@ -2178,15 +2178,15 @@ void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
frame_finished = 1;
}
if (receive_frame_err == AVERROR_EOF) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (EOF detected from decoder)");
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (EOF detected from decoder)");
packet_status.audio_eof = true;
}
if (receive_frame_err == AVERROR(EINVAL) || receive_frame_err == AVERROR_EOF) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (invalid frame received or EOF from decoder)");
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (invalid frame received or EOF from decoder)");
avcodec_flush_buffers(aCodecCtx);
}
if (receive_frame_err != 0) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (frame not ready yet from decoder)");
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (frame not ready yet from decoder)");
}
}
#else
@@ -2228,7 +2228,7 @@ void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
// Bail if no samples found
if (pts_remaining_samples == 0) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (No samples, bailing)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (No samples, bailing)",
"packet_samples", packet_samples,
"info.channels", info.channels,
"pts_remaining_samples", pts_remaining_samples);
@@ -2257,7 +2257,7 @@ void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
}
}
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (ReSample)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (ReSample)",
"packet_samples", packet_samples,
"info.channels", info.channels,
"info.sample_rate", info.sample_rate,
@@ -2341,7 +2341,7 @@ void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
f->AddAudio(true, channel_filter, start, channel_buffer, samples, 1.0f);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (f->AddAudio)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (f->AddAudio)",
"frame", starting_frame_number,
"start", start,
"samples", samples,
@@ -2375,7 +2375,7 @@ void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
audio_pts_seconds = (double(audio_pts) * info.audio_timebase.ToDouble()) + pts_offset_seconds;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (After)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::ProcessAudioPacket (After)",
"requested_frame", requested_frame,
"starting_frame", location.frame,
"end_frame", starting_frame_number - 1,
@@ -2397,7 +2397,7 @@ void FFmpegReader::Seek(int64_t requested_frame) {
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::Seek",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::Seek",
"requested_frame", requested_frame,
"seek_count", seek_count,
"last_frame", last_frame);
@@ -2457,7 +2457,7 @@ void FFmpegReader::Seek(int64_t requested_frame) {
if (!seek_worked && info.has_video && !HasAlbumArt()) {
seek_target = ConvertFrameToVideoPTS(requested_frame - buffer_amount);
if (av_seek_frame(pFormatCtx, info.video_stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0) {
- ZmqLogger::Instance()->Log(std::string(pFormatCtx->AV_FILENAME) + ": error while seeking video stream");
+ Logger::Instance()->Log(std::string(pFormatCtx->AV_FILENAME) + ": error while seeking video stream");
} else {
// VIDEO SEEK
is_video_seek = true;
@@ -2469,7 +2469,7 @@ void FFmpegReader::Seek(int64_t requested_frame) {
if (!seek_worked && info.has_audio) {
seek_target = ConvertFrameToAudioPTS(requested_frame - buffer_amount);
if (av_seek_frame(pFormatCtx, info.audio_stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0) {
- ZmqLogger::Instance()->Log(std::string(pFormatCtx->AV_FILENAME) + ": error while seeking audio stream");
+ Logger::Instance()->Log(std::string(pFormatCtx->AV_FILENAME) + ": error while seeking audio stream");
} else {
// AUDIO SEEK
is_video_seek = false;
@@ -2726,11 +2726,11 @@ AudioLocation FFmpegReader::GetAudioPTSLocation(int64_t pts) {
location.frame = previous_packet_location.frame;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAudioPTSLocation (Audio Gap Detected)", "Source Frame", orig_frame, "Source Audio Sample", orig_start, "Target Frame", location.frame, "Target Audio Sample", location.sample_start, "pts", pts);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetAudioPTSLocation (Audio Gap Detected)", "Source Frame", orig_frame, "Source Audio Sample", orig_start, "Target Frame", location.frame, "Target Audio Sample", location.sample_start, "pts", pts);
} else {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAudioPTSLocation (Audio Gap Ignored - too big)", "Previous location frame", previous_packet_location.frame, "Target Frame", location.frame, "Target Audio Sample", location.sample_start, "pts", pts);
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::GetAudioPTSLocation (Audio Gap Ignored - too big)", "Previous location frame", previous_packet_location.frame, "Target Frame", location.frame, "Target Audio Sample", location.sample_start, "pts", pts);
}
}
@@ -2820,7 +2820,7 @@ void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
// Video stream is past this frame (so it must be done)
// OR video stream is too far behind, missing, or end-of-file
is_video_ready = true;
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames (video ready)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames (video ready)",
"frame_number", f->number,
"frame_pts_seconds", frame_pts_seconds,
"video_pts_seconds", video_pts_seconds,
@@ -2852,7 +2852,7 @@ void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
// Last-resort fallback if no prior image is available.
if (!f->has_image_data) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegReader::CheckWorkingFrames (no previous image found; using black frame)",
"frame_number", f->number);
f->AddColor("#000000");
@@ -2868,7 +2868,7 @@ void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
// OR audio stream is too far behind, missing, or end-of-file
// Adding a bit of margin here, to allow for partial audio packets
is_audio_ready = true;
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames (audio ready)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames (audio ready)",
"frame_number", f->number,
"frame_pts_seconds", frame_pts_seconds,
"audio_pts_seconds", audio_pts_seconds,
@@ -2882,7 +2882,7 @@ void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
if (!info.has_audio) is_audio_ready = true;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames",
"frame_number", f->number,
"is_video_ready", is_video_ready,
"is_audio_ready", is_audio_ready,
@@ -2944,7 +2944,7 @@ void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
}
if ((!packet_status.end_of_file && is_video_ready && is_audio_ready) || packet_status.end_of_file || is_seek_trash) {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames (mark frame as final)",
+ Logger::Instance()->AppendDebugMethod("FFmpegReader::CheckWorkingFrames (mark frame as final)",
"requested_frame", requested_frame,
"f->number", f->number,
"is_seek_trash", is_seek_trash,
diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp
index 4ae40ec50..12c477d09 100644
--- a/src/FFmpegWriter.cpp
+++ b/src/FFmpegWriter.cpp
@@ -27,7 +27,7 @@
#include "Frame.h"
#include "OpenMPUtilities.h"
#include "Settings.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
using namespace openshot;
@@ -143,7 +143,7 @@ void FFmpegWriter::auto_detect_format() {
// initialize streams
void FFmpegWriter::initialize_streams() {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::initialize_streams",
"oc->oformat->video_codec", oc->oformat->video_codec,
"oc->oformat->audio_codec", oc->oformat->audio_codec,
@@ -264,7 +264,7 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f
info.display_ratio.num = size.num;
info.display_ratio.den = size.den;
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::SetVideoOptions (" + codec + ")",
"width", width, "height", height,
"size.num", size.num, "size.den", size.den,
@@ -310,7 +310,7 @@ void FFmpegWriter::SetAudioOptions(bool has_audio, std::string codec, int sample
if (original_channels == 0)
original_channels = info.channels;
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::SetAudioOptions (" + codec + ")",
"sample_rate", sample_rate,
"channels", channels,
@@ -579,7 +579,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va
AV_OPTION_SET(st, c->priv_data, name.c_str(), value.c_str(), c);
}
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::SetOption (" + (std::string)name + ")",
"stream == VIDEO_STREAM", stream == VIDEO_STREAM);
@@ -614,7 +614,7 @@ void FFmpegWriter::PrepareStreams() {
if (!info.has_audio && !info.has_video)
throw InvalidOptions("No video or audio options have been set. You must set has_video or has_audio (or both).", path);
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::PrepareStreams [" + path + "]",
"info.has_audio", info.has_audio,
"info.has_video", info.has_video);
@@ -653,7 +653,7 @@ void FFmpegWriter::WriteHeader() {
// Write the stream header
if (avformat_write_header(oc, &dict) != 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::WriteHeader (avformat_write_header)");
throw InvalidFile("Could not write header to file.", path);
};
@@ -665,7 +665,7 @@ void FFmpegWriter::WriteHeader() {
// Mark as 'written'
write_header = true;
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::WriteHeader");
+ Logger::Instance()->AppendDebugMethod("FFmpegWriter::WriteHeader");
}
// Add a frame to the queue waiting to be encoded.
@@ -674,7 +674,7 @@ void FFmpegWriter::WriteFrame(std::shared_ptr frame) {
if (!is_open)
throw WriterClosed("The FFmpegWriter is closed. Call Open() before calling this method.", path);
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::WriteFrame",
"frame->number", frame->number,
"is_writing", is_writing);
@@ -695,7 +695,7 @@ void FFmpegWriter::WriteFrameAt(std::shared_ptr frame, int64_t
frame_number = 1;
}
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::WriteFrameAt",
"frame->number", frame->number,
"output_frame_number", frame_number,
@@ -762,7 +762,7 @@ void FFmpegWriter::write_frame(std::shared_ptr frame) {
// Write a block of frames from a reader
void FFmpegWriter::WriteFrame(ReaderBase *reader, int64_t start, int64_t length) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::WriteFrame (from Reader)",
"start", start,
"length", length);
@@ -802,7 +802,7 @@ void FFmpegWriter::WriteTrailer() {
// Mark as 'written'
write_trailer = true;
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::WriteTrailer");
+ Logger::Instance()->AppendDebugMethod("FFmpegWriter::WriteTrailer");
}
// Flush encoders
@@ -865,7 +865,7 @@ void FFmpegWriter::flush_encoders() {
#endif // IS_FFMPEG_3_2
if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::flush_encoders ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -884,7 +884,7 @@ void FFmpegWriter::flush_encoders() {
// Write packet
error_code = av_interleaved_write_frame(oc, pkt);
if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::flush_encoders ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -915,7 +915,7 @@ void FFmpegWriter::flush_encoders() {
}
error_code = avcodec_send_frame(audio_codec_ctx, NULL);
if (error_code < 0 && error_code != AVERROR_EOF) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::flush_encoders ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -927,7 +927,7 @@ void FFmpegWriter::flush_encoders() {
break;
}
if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::flush_encoders ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -952,7 +952,7 @@ void FFmpegWriter::flush_encoders() {
error_code = av_interleaved_write_frame(oc, pkt);
if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::flush_encoders ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -965,7 +965,7 @@ void FFmpegWriter::flush_encoders() {
#else
error_code = avcodec_encode_audio2(audio_codec_ctx, pkt, NULL, &got_packet);
if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::flush_encoders ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -991,7 +991,7 @@ void FFmpegWriter::flush_encoders() {
// Write packet
error_code = av_interleaved_write_frame(oc, pkt);
if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::flush_encoders ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -1093,7 +1093,7 @@ void FFmpegWriter::Close() {
write_header = false;
write_trailer = false;
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::Close");
+ Logger::Instance()->AppendDebugMethod("FFmpegWriter::Close");
}
// Add an AVFrame to the cache
@@ -1233,7 +1233,7 @@ AVStream *FFmpegWriter::add_audio_stream() {
channel_layout_label = "c->channel_layout";
#endif
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::add_audio_stream",
"c->codec_id", c->codec_id,
"c->bit_rate", c->bit_rate,
@@ -1429,7 +1429,7 @@ AVStream *FFmpegWriter::add_video_stream() {
}
AV_COPY_PARAMS_FROM_CONTEXT(st, c);
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::add_video_stream ("
+ (std::string)oc->oformat->name + " : "
+ (std::string)av_get_pix_fmt_name(c->pix_fmt) + ")",
@@ -1508,7 +1508,7 @@ void FFmpegWriter::open_audio(AVFormatContext *oc, AVStream *st) {
av_dict_set(&st->metadata, iter->first.c_str(), iter->second.c_str(), 0);
}
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::open_audio",
"audio_codec_ctx->thread_count", audio_codec_ctx->thread_count,
"audio_input_frame_size", audio_input_frame_size,
@@ -1550,19 +1550,19 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
#elif defined(_WIN32) || defined(__APPLE__)
if( adapter_ptr != NULL ) {
#endif
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Encode Device present using device",
"adapter", adapter_num);
}
else {
adapter_ptr = NULL; // use default
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Encode Device not present, using default");
}
if (av_hwdevice_ctx_create(&hw_device_ctx,
hw_en_av_device_type, adapter_ptr, NULL, 0) < 0)
{
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::open_video ERROR creating hwdevice, Codec name:",
info.vcodec.c_str(), -1);
throw InvalidCodec("Could not create hwdevice", path);
@@ -1626,7 +1626,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
// tested to work with defaults
break;
default:
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"No codec-specific options defined for this codec. HW encoding may fail",
"codec_id", video_codec_ctx->codec_id);
break;
@@ -1636,7 +1636,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
int err;
if ((err = set_hwframe_ctx(video_codec_ctx, hw_device_ctx, info.width, info.height)) < 0)
{
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::open_video (set_hwframe_ctx) ERROR faled to set hwframe context",
"width", info.width,
"height", info.height,
@@ -1671,7 +1671,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
av_dict_set(&st->metadata, iter->first.c_str(), iter->second.c_str(), 0);
}
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::open_video",
"video_codec_ctx->thread_count", video_codec_ctx->thread_count);
@@ -1742,7 +1742,7 @@ void FFmpegWriter::write_audio_packets(bool is_final, std::shared_ptrAppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_audio_packets",
"is_final", is_final,
"total_frame_samples", total_frame_samples,
@@ -1764,7 +1764,7 @@ void FFmpegWriter::write_audio_packets(bool is_final, std::shared_ptrAppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_audio_packets ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -1804,7 +1804,7 @@ void FFmpegWriter::write_audio_packets(bool is_final, std::shared_ptrnb_samples = total_frame_samples / channels_in_frame;
av_samples_alloc(audio_converted->data, audio_converted->linesize, info.channels, audio_converted->nb_samples, output_sample_fmt, 0);
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_audio_packets (1st resampling)",
"in_sample_fmt", AV_SAMPLE_FMT_S16,
"out_sample_fmt", output_sample_fmt,
@@ -1873,7 +1873,7 @@ void FFmpegWriter::write_audio_packets(bool is_final, std::shared_ptrAppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_audio_packets (Successfully completed 1st resampling)",
"nb_samples", nb_samples,
"remaining_frame_samples", remaining_frame_samples);
@@ -1927,7 +1927,7 @@ void FFmpegWriter::write_audio_packets(bool is_final, std::shared_ptrsample_fmt)) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_audio_packets (2nd resampling for Planar formats)",
"in_sample_fmt", output_sample_fmt,
"out_sample_fmt", audio_codec_ctx->sample_fmt,
@@ -2018,7 +2018,7 @@ void FFmpegWriter::write_audio_packets(bool is_final, std::shared_ptrAppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_audio_packets (Successfully completed 2nd resampling for Planar formats)",
"nb_samples", nb_samples);
@@ -2130,7 +2130,7 @@ void FFmpegWriter::write_audio_packets(bool is_final, std::shared_ptrAppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_audio_packets ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -2320,7 +2320,7 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) {
bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *frame_final) {
#if (LIBAVFORMAT_VERSION_MAJOR >= 58)
// FFmpeg 4.0+
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_video_packet",
"frame->number", frame->number,
"oc->oformat->flags", oc->oformat->flags);
@@ -2330,7 +2330,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
// TODO: Should we have moved away from oc->oformat->flags / AVFMT_RAWPICTURE
// on ffmpeg < 4.0 as well?
// Does AV_CODEC_ID_RAWVIDEO not work in ffmpeg 3.x?
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_video_packet",
"frame->number", frame->number,
"oc->oformat->flags & AVFMT_RAWPICTURE", oc->oformat->flags & AVFMT_RAWPICTURE);
@@ -2359,7 +2359,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
/* write the compressed frame in the media file */
int error_code = av_interleaved_write_frame(oc, pkt);
if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_video_packet ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
@@ -2419,7 +2419,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
}
error_code = ret;
if (ret < 0 ) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_video_packet (Frame not sent)");
if (ret == AVERROR(EAGAIN) ) {
std::clog << "Frame EAGAIN\n";
@@ -2447,13 +2447,13 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
// Write video packet (older than FFmpeg 3.2)
error_code = avcodec_encode_video2(video_codec_ctx, pkt, frame_final, &got_packet_ptr);
if (error_code != 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_video_packet ERROR ["
+ av_err2string(error_code) + "]",
"error_code", error_code);
}
if (got_packet_ptr == 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_video_packet (Frame gotpacket error)");
}
#endif // IS_FFMPEG_3_2
@@ -2470,7 +2470,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
/* write the compressed frame in the media file */
int result = av_interleaved_write_frame(oc, pkt);
if (result < 0) {
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FFmpegWriter::write_video_packet ERROR ["
+ av_err2string(result) + "]",
"result", result);
diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp
index 5080924c0..5405c767f 100644
--- a/src/FrameMapper.cpp
+++ b/src/FrameMapper.cpp
@@ -19,7 +19,7 @@
#include "Exceptions.h"
#include "Clip.h"
#include "MemoryTrim.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
using namespace std;
using namespace openshot;
@@ -114,7 +114,7 @@ void FrameMapper::Clear() {
// whether the frame rate is increasing or decreasing.
void FrameMapper::Init()
{
- ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Init (Calculate frame mappings)");
+ Logger::Instance()->AppendDebugMethod("FrameMapper::Init (Calculate frame mappings)");
// Do not initialize anything if just a picture with no audio
if (info.has_video and !info.has_audio and info.has_single_image)
@@ -378,7 +378,7 @@ MappedFrame FrameMapper::GetMappedFrame(int64_t TargetFrameNumber)
TargetFrameNumber = frames.size();
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::GetMappedFrame",
"TargetFrameNumber", TargetFrameNumber,
"frames.size()", frames.size(),
@@ -399,7 +399,7 @@ std::shared_ptr FrameMapper::GetOrCreateFrame(int64_t number)
try {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::GetOrCreateFrame (from reader)",
"number", number,
"samples_in_frame", samples_in_frame);
@@ -417,7 +417,7 @@ std::shared_ptr FrameMapper::GetOrCreateFrame(int64_t number)
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::GetOrCreateFrame (create blank)",
"number", number,
"samples_in_frame", samples_in_frame);
@@ -494,7 +494,7 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame)
int minimum_frames = 1;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::GetFrame (Loop through frames)",
"requested_frame", requested_frame,
"minimum_frames", minimum_frames);
@@ -503,7 +503,7 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame)
for (int64_t frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
{
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::GetFrame (inside omp for loop)",
"frame_number", frame_number,
"minimum_frames", minimum_frames,
@@ -727,7 +727,7 @@ void FrameMapper::Open()
{
if (reader)
{
- ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Open");
+ Logger::Instance()->AppendDebugMethod("FrameMapper::Open");
// Open the reader
reader->Open();
@@ -742,7 +742,7 @@ void FrameMapper::Close()
// Create a scoped lock, allowing only a single thread to run the following code at one time
const std::lock_guard lock(getFrameMutex);
- ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Close");
+ Logger::Instance()->AppendDebugMethod("FrameMapper::Close");
// Close internal reader
reader->Close();
@@ -824,7 +824,7 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow
// the resampler while this mapping update is in progress.
const std::lock_guard lock(getFrameMutex);
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::ChangeMapping",
"target_fps.num", target_fps.num,
"target_fps.den", target_fps.den,
@@ -889,7 +889,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig
int samples_in_frame = frame->GetAudioSamplesCount();
ChannelLayout channel_layout_in_frame = frame->ChannelsLayout();
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::ResampleMappedAudio",
"frame->number", frame->number,
"original_frame_number", original_frame_number,
@@ -942,7 +942,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig
if (error_code < 0)
{
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::ResampleMappedAudio ERROR [" + av_err2string(error_code) + "]",
"error_code", error_code);
throw ErrorEncodingVideo("Error while resampling audio in frame mapper", frame->number);
@@ -1029,7 +1029,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr frame, int64_t orig
int channel_buffer_size = nb_samples;
frame->ResizeAudio(info.channels, channel_buffer_size, info.sample_rate, info.channel_layout);
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"FrameMapper::ResampleMappedAudio (Audio successfully resampled)",
"nb_samples", nb_samples,
"total_frame_samples", total_frame_samples,
diff --git a/src/ImageWriter.cpp b/src/ImageWriter.cpp
index c56aab72f..883e663e1 100644
--- a/src/ImageWriter.cpp
+++ b/src/ImageWriter.cpp
@@ -20,7 +20,7 @@
#include "Exceptions.h"
#include "Frame.h"
#include "ReaderBase.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
using namespace openshot;
@@ -65,7 +65,7 @@ void ImageWriter::SetVideoOptions(
// Set the ratio based on the reduced fraction
info.display_ratio = size;
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"ImageWriter::SetVideoOptions (" + format + ")",
"width", width,
"height", height,
@@ -120,7 +120,7 @@ void ImageWriter::WriteFrame(std::shared_ptr frame)
// Write a block of frames from a reader
void ImageWriter::WriteFrame(ReaderBase* reader, int64_t start, int64_t length)
{
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"ImageWriter::WriteFrame (from Reader)",
"start", start,
"length", length);
@@ -147,7 +147,7 @@ void ImageWriter::Close()
write_video_count = 0;
is_open = false;
- ZmqLogger::Instance()->AppendDebugMethod("ImageWriter::Close");
+ Logger::Instance()->AppendDebugMethod("ImageWriter::Close");
}
#endif //USE_IMAGEMAGICK
diff --git a/src/Logger.cpp b/src/Logger.cpp
new file mode 100644
index 000000000..6a51112f8
--- /dev/null
+++ b/src/Logger.cpp
@@ -0,0 +1,212 @@
+// Copyright (c) 2008-2026 OpenShot Studios, LLC
+// SPDX-License-Identifier: LGPL-3.0-or-later
+#include "Logger.h"
+#include "Settings.h"
+#if USE_RESVG == 1
+#include "ResvgQt.h"
+#endif
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+
+using namespace openshot;
+namespace {
+int ParseLevel(std::string value) {
+ std::transform(value.begin(), value.end(), value.begin(),
+ [](unsigned char c) { return std::toupper(c); });
+ if (value == "DEBUG") return Logger::LevelDebug;
+ if (value == "INFO") return Logger::LevelInfo;
+ if (value == "WARNING") return Logger::LevelWarning;
+ if (value == "ERROR") return Logger::LevelError;
+ if (value == "CRITICAL") return Logger::LevelCritical;
+ if (value == "OFF") return Logger::LevelOff;
+ throw std::invalid_argument("Invalid logging level: " + value);
+}
+const char* LevelName(Logger::Level level) {
+ switch (level) {
+ case Logger::LevelDebug: return "DEBUG";
+ case Logger::LevelInfo: return "INFO";
+ case Logger::LevelWarning: return "WARNING";
+ case Logger::LevelError: return "ERROR";
+ case Logger::LevelCritical: return "CRITICAL";
+ default: return "OFF";
+ }
+}
+std::tm LocalTime(std::time_t now) {
+ std::tm result{};
+#ifdef _WIN32
+ localtime_s(&result, &now);
+#else
+ localtime_r(&now, &result);
+#endif
+ return result;
+}
+}
+
+Logger* Logger::Instance() {
+ // Retain the logger through static destruction, including crash reporting.
+ static Logger* instance = new Logger;
+ return instance;
+}
+
+Logger::Logger() : legacy_settings(Settings::Instance()) {
+ // Parse each variable once; malformed values do not hide valid fallbacks.
+ for (const char* key : {"OPENSHOT_LOG_LEVEL", "OPENSHOT_LOG_FILE_LEVEL",
+ "OPENSHOT_LOG_CONSOLE_LEVEL", "LIBOPENSHOT_LOG_LEVEL",
+ "LIBOPENSHOT_LOG_FILE_LEVEL", "LIBOPENSHOT_LOG_CONSOLE_LEVEL"}) {
+ const char* value = std::getenv(key);
+ if (!value) continue;
+ try {
+ int level = ParseLevel(value);
+ std::string name(key);
+ if (name.find("CONSOLE") == std::string::npos) file_level = level;
+ if (name.find("FILE") == std::string::npos) {
+ console_level = level;
+ console_configured = true;
+ }
+ } catch (const std::invalid_argument&) {
+ std::cerr << "libopenshot: ignoring invalid " << key << "=" << value << '\n';
+ }
+ }
+#ifdef _WIN32
+ // Windows environment paths are UTF-16; avoid the locale-dependent narrow getenv.
+ if (const wchar_t* path = _wgetenv(L"LIBOPENSHOT_LOG_FILE")) {
+ try { Path(std::filesystem::path(path).u8string()); }
+ catch (const std::exception&) {
+ std::cerr << "libopenshot: invalid LIBOPENSHOT_LOG_FILE path\n";
+ }
+ }
+#else
+ if (const char* path = std::getenv("LIBOPENSHOT_LOG_FILE")) Path(path);
+#endif
+#if USE_RESVG == 1
+ ResvgRenderer::initLog();
+#endif
+}
+
+int Logger::ConsoleLevel() const {
+ // Preserve the old explicit Settings API and presence-based environment alias.
+ if (!console_configured && legacy_settings->DEBUG_TO_STDERR) return LevelDebug;
+ return console_level;
+}
+bool Logger::ShouldLog(Level level) const {
+ return level != LevelOff && ((file_open && level >= file_level) || level >= ConsoleLevel());
+}
+void Logger::SetFileLevel(std::string level) { file_level = ParseLevel(level); }
+void Logger::SetConsoleLevel(std::string level) {
+ console_level = ParseLevel(level);
+ console_configured = true;
+}
+void Logger::Enable(bool enabled) { file_level = enabled ? LevelDebug : LevelOff; }
+
+void Logger::Path(std::string path) {
+ const std::lock_guard lock(loggerMutex);
+ if (path == file_path && file_open) return;
+ file_open = false;
+ if (log_file.is_open()) log_file.close();
+ log_file.clear();
+ file_path = path;
+ if (path.empty()) return;
+ try {
+#ifdef _WIN32
+ log_file.open(std::filesystem::u8path(path), std::ios::out | std::ios::app);
+#else
+ // POSIX paths already use UTF-8 bytes. Avoid std::filesystem, which
+ // requires macOS 10.15 even when compiling with C++17 enabled.
+ log_file.open(path, std::ios::out | std::ios::app);
+#endif
+ } catch (const std::exception&) {
+ std::cerr << "libopenshot: invalid log file path: " << path << '\n';
+ return;
+ }
+ file_open = log_file.is_open();
+ if (!file_open) {
+ std::cerr << "libopenshot: unable to open log file: " << path << '\n';
+ return;
+ }
+ auto now = LocalTime(std::time(nullptr));
+ // Keep these markers intact for openshot-qt's existing crash recovery scanner.
+ log_file << "------------------------------------------\n"
+ << "libopenshot logging: " << std::put_time(&now, "%a %b %d %H:%M:%S %Y") << '\n'
+ << "------------------------------------------" << std::endl;
+}
+void Logger::LogToFile(std::string message) {
+ // Deliberately retain the existing crash-write path (no level filter or new lock).
+ if (log_file.is_open()) log_file << message << std::flush;
+}
+void Logger::Log(std::string message, Level level) {
+ if (!ShouldLog(level)) return;
+ const std::lock_guard lock(loggerMutex);
+ auto now = LocalTime(std::time(nullptr));
+ std::ostringstream record;
+ record << std::put_time(&now, "%Y-%m-%d %H:%M:%S") << ' '
+ << LevelName(level) << " [" << std::this_thread::get_id() << "] " << message;
+ if (message.empty() || message.back() != '\n') record << '\n';
+ const auto text = record.str();
+ if (level >= file_level) LogToFile(text);
+ if (level >= ConsoleLevel()) std::clog << text << std::flush;
+}
+void Logger::Close() {
+ const std::lock_guard lock(loggerMutex);
+ file_level = LevelOff;
+ console_level = LevelOff;
+ console_configured = true;
+ file_open = false;
+ if (log_file.is_open()) log_file.close();
+}
+
+// Append debug information
+void Logger::AppendDebugMethod(std::string method_name,
+ std::string arg1_name, float arg1_value,
+ std::string arg2_name, float arg2_value,
+ std::string arg3_name, float arg3_value,
+ std::string arg4_name, float arg4_value,
+ std::string arg5_name, float arg5_value,
+ std::string arg6_name, float arg6_value)
+{
+ if (!ShouldLog(LevelDebug))
+ // Don't do anything
+ return;
+
+ {
+ // Create a scoped lock, allowing only a single thread to run the following code at one time
+ const std::lock_guard lock(loggerMutex);
+
+ std::stringstream message;
+ message << std::fixed << std::setprecision(4);
+
+ // Construct message
+ message << method_name << " (";
+
+ if (arg1_name.length() > 0)
+ message << arg1_name << "=" << arg1_value;
+
+ if (arg2_name.length() > 0)
+ message << ", " << arg2_name << "=" << arg2_value;
+
+ if (arg3_name.length() > 0)
+ message << ", " << arg3_name << "=" << arg3_value;
+
+ if (arg4_name.length() > 0)
+ message << ", " << arg4_name << "=" << arg4_value;
+
+ if (arg5_name.length() > 0)
+ message << ", " << arg5_name << "=" << arg5_value;
+
+ if (arg6_name.length() > 0)
+ message << ", " << arg6_name << "=" << arg6_value;
+
+ message << ")" << std::endl;
+
+ Log(message.str(), LevelDebug);
+ }
+}
diff --git a/src/Logger.h b/src/Logger.h
new file mode 100644
index 000000000..b93de8975
--- /dev/null
+++ b/src/Logger.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2008-2026 OpenShot Studios, LLC
+// SPDX-License-Identifier: LGPL-3.0-or-later
+#ifndef OPENSHOT_LOGGER_H
+#define OPENSHOT_LOGGER_H
+
+#include
+#include
+#include
+#include
+
+namespace openshot {
+class Settings;
+/// Independent file and stderr logging. No networking or worker thread.
+class Logger {
+public:
+ enum Level { LevelDebug = 10, LevelInfo = 20, LevelWarning = 30, LevelError = 40, LevelCritical = 50, LevelOff = 100 };
+ static Logger* Instance();
+ /// Explicit configuration overrides environment defaults. Invalid levels throw.
+ void SetFileLevel(std::string level);
+ void SetConsoleLevel(std::string level);
+ bool ShouldLog(Level level = LevelDebug) const;
+ void Path(std::string path);
+ void Close();
+ void Log(std::string message, Level level = LevelDebug);
+ /// Unfiltered raw output, retained for the existing crash handler.
+ void LogToFile(std::string message);
+ /// Compatibility: enable debug file output, or disable ordinary file output.
+ void Enable(bool enabled);
+ /// Deprecated compatibility no-op; logging no longer uses connections.
+ void Connection(std::string connection) {}
+ void AppendDebugMethod(std::string method_name,
+ std::string arg1_name="", float arg1_value=-1.0,
+ std::string arg2_name="", float arg2_value=-1.0,
+ std::string arg3_name="", float arg3_value=-1.0,
+ std::string arg4_name="", float arg4_value=-1.0,
+ std::string arg5_name="", float arg5_value=-1.0,
+ std::string arg6_name="", float arg6_value=-1.0);
+private:
+ Logger();
+ Settings* legacy_settings;
+ Logger(const Logger&) = delete;
+ Logger& operator=(const Logger&) = delete;
+ std::recursive_mutex loggerMutex;
+ std::ofstream log_file;
+ std::string file_path;
+ std::atomic file_level{LevelInfo};
+ std::atomic console_level{LevelInfo};
+ std::atomic file_open{false};
+ std::atomic console_configured{false};
+ int ConsoleLevel() const;
+};
+}
+#endif
diff --git a/src/OpenShot.h b/src/OpenShot.h
index 1a753377e..e7f8935b1 100644
--- a/src/OpenShot.h
+++ b/src/OpenShot.h
@@ -101,6 +101,8 @@
// Include the version number of OpenShot Library
#include "OpenShotVersion.h"
+#include "Logger.h"
+#include "ZmqLogger.h" // Deprecated source compatibility alias
// Include all other classes
#include "AudioBufferSource.h"
diff --git a/src/Qt/AudioPlaybackThread.cpp b/src/Qt/AudioPlaybackThread.cpp
index dd7c5f7d5..9d39e7e44 100644
--- a/src/Qt/AudioPlaybackThread.cpp
+++ b/src/Qt/AudioPlaybackThread.cpp
@@ -19,7 +19,7 @@
#include "../AudioReaderSource.h"
#include "../AudioDevices.h"
#include "../Settings.h"
-#include "../ZmqLogger.h"
+#include "../Logger.h"
#include
#include // for std::this_thread::sleep_for
@@ -61,7 +61,7 @@ namespace openshot
constructor_title << "AudioDeviceManagerSingleton::Instance (default audio device type: " <<
Settings::Instance()->PLAYBACK_AUDIO_DEVICE_TYPE << ", default audio device name: " <<
Settings::Instance()->PLAYBACK_AUDIO_DEVICE_NAME << ")";
- ZmqLogger::Instance()->AppendDebugMethod(constructor_title.str(), "channels", channels, "buffer", Settings::Instance()->PLAYBACK_AUDIO_BUFFER_SIZE);
+ Logger::Instance()->AppendDebugMethod(constructor_title.str(), "channels", channels, "buffer", Settings::Instance()->PLAYBACK_AUDIO_BUFFER_SIZE);
// Get preferred audio device type and name (if any - these can be blank)
openshot::AudioDeviceInfo requested_device = {Settings::Instance()->PLAYBACK_AUDIO_DEVICE_TYPE,
@@ -85,7 +85,7 @@ namespace openshot
for (const auto t : mgr->getAvailableDeviceTypes()) {
std::stringstream type_debug;
type_debug << "AudioDeviceManagerSingleton::Instance (iterate audio device type: " << t->getTypeName() << ")";
- ZmqLogger::Instance()->AppendDebugMethod(type_debug.str(), "rate", rate, "channels", channels);
+ Logger::Instance()->AppendDebugMethod(type_debug.str(), "rate", rate, "channels", channels);
t->scanForDevices();
for (const auto n : t->getDeviceNames()) {
@@ -93,7 +93,7 @@ namespace openshot
devices.push_back(device);
std::stringstream device_debug;
device_debug << "AudioDeviceManagerSingleton::Instance (iterate audio device name: " << device.name << ", type: " << t->getTypeName() << ")";
- ZmqLogger::Instance()->AppendDebugMethod(device_debug.str(), "rate", rate, "channels", channels);
+ Logger::Instance()->AppendDebugMethod(device_debug.str(), "rate", rate, "channels", channels);
}
}
@@ -122,7 +122,7 @@ namespace openshot
for(int attempt_rate : possible_rates) {
std::stringstream title_rate;
title_rate << "AudioDeviceManagerSingleton::Instance (attempt audio device name: " << attempt_device.name << ")";
- ZmqLogger::Instance()->AppendDebugMethod(title_rate.str(), "rate", attempt_rate, "channels", channels);
+ Logger::Instance()->AppendDebugMethod(title_rate.str(), "rate", attempt_rate, "channels", channels);
// Update the audio device setup for the current sample rate
m_pInstance->defaultSampleRate = attempt_rate;
@@ -147,7 +147,7 @@ namespace openshot
std::stringstream title_error;
title_error << "AudioDeviceManagerSingleton::Instance (audio device error: " <<
m_pInstance->initialise_error << ")";
- ZmqLogger::Instance()->AppendDebugMethod(title_error.str(), "rate", attempt_rate, "channels", channels);
+ Logger::Instance()->AppendDebugMethod(title_error.str(), "rate", attempt_rate, "channels", channels);
}
// Determine if audio device was opened successfully, and matches the attempted sample rate
@@ -158,7 +158,7 @@ namespace openshot
std::stringstream title_found;
title_found << "AudioDeviceManagerSingleton::Instance (successful audio device found: " <<
foundAudioIODevice->getTypeName() << ", name: " << foundAudioIODevice->getName() << ")";
- ZmqLogger::Instance()->AppendDebugMethod(title_found.str(), "rate", attempt_rate, "channels", channels);
+ Logger::Instance()->AppendDebugMethod(title_found.str(), "rate", attempt_rate, "channels", channels);
break;
}
}
@@ -169,7 +169,7 @@ namespace openshot
}
}
- ZmqLogger::Instance()->AppendDebugMethod("AudioDeviceManagerSingleton::Instance (audio device initialization completed)");
+ Logger::Instance()->AppendDebugMethod("AudioDeviceManagerSingleton::Instance (audio device initialization completed)");
}
return m_pInstance;
}
@@ -220,7 +220,7 @@ namespace openshot
sampleRate = reader->info.sample_rate;
numChannels = reader->info.channels;
- ZmqLogger::Instance()->AppendDebugMethod("AudioPlaybackThread::Reader", "rate", sampleRate, "channel", numChannels);
+ Logger::Instance()->AppendDebugMethod("AudioPlaybackThread::Reader", "rate", sampleRate, "channel", numChannels);
// Set video cache thread
source->setVideoCache(videoCache);
diff --git a/src/Qt/VideoPlaybackThread.cpp b/src/Qt/VideoPlaybackThread.cpp
index b8c53e583..e716244c9 100644
--- a/src/Qt/VideoPlaybackThread.cpp
+++ b/src/Qt/VideoPlaybackThread.cpp
@@ -12,11 +12,11 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "VideoPlaybackThread.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#include "Frame.h"
#include "RendererBase.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
namespace openshot
{
@@ -51,7 +51,7 @@ namespace openshot
if (need_render && frame)
{
// Debug
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"VideoPlaybackThread::run (before render)",
"frame->number", frame->number,
"need_render", need_render);
diff --git a/src/Settings.h b/src/Settings.h
index 7a013c9aa..2ae646d03 100644
--- a/src/Settings.h
+++ b/src/Settings.h
@@ -122,7 +122,7 @@ namespace openshot {
/// paths depend on the location of OpenShot transitions and files)
std::string PATH_OPENSHOT_INSTALL = "";
- /// Whether to dump ZeroMQ debug messages to stderr
+ /// Legacy native debug-to-stderr switch (explicit Logger levels take precedence)
bool DEBUG_TO_STDERR = false;
/// Return the effective OpenMP worker budget used by libopenshot heuristics
diff --git a/src/Timeline.cpp b/src/Timeline.cpp
index fec00f649..544c910ad 100644
--- a/src/Timeline.cpp
+++ b/src/Timeline.cpp
@@ -561,7 +561,7 @@ double Timeline::calculate_time(int64_t number, Fraction rate)
std::shared_ptr Timeline::apply_effects(std::shared_ptr frame, int64_t timeline_frame_number, int layer, TimelineInfoStruct* options)
{
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::apply_effects",
"frame->number", frame->number,
"timeline_frame_number", timeline_frame_number,
@@ -591,7 +591,7 @@ std::shared_ptr Timeline::apply_effects(std::shared_ptr frame, int
continue; // skip effect, if this filter does not match
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::apply_effects (Process Effect)",
"effect_frame_number", effect_frame_number,
"does_effect_intersect", does_effect_intersect);
@@ -616,7 +616,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(std::shared_ptr backgro
try {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetOrCreateFrame (from reader)",
"number", number,
"samples_in_frame", samples_in_frame);
@@ -634,7 +634,7 @@ std::shared_ptr Timeline::GetOrCreateFrame(std::shared_ptr backgro
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetOrCreateFrame (create blank)",
"number", number,
"samples_in_frame", samples_in_frame);
@@ -660,7 +660,7 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in
return;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::add_layer",
"new_frame->number", new_frame->number,
"clip_frame_number", clip_frame_number);
@@ -668,7 +668,7 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in
/* COPY AUDIO - with correct volume */
if (source_clip->Reader()->info.has_audio) {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::add_layer (Copy Audio)",
"source_clip->Reader()->info.has_audio", source_clip->Reader()->info.has_audio,
"source_frame->GetAudioChannelsCount()", source_frame->GetAudioChannelsCount(),
@@ -730,7 +730,7 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in
}
else
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::add_layer (No Audio Copied - Wrong # of Channels)",
"source_clip->Reader()->info.has_audio",
source_clip->Reader()->info.has_audio,
@@ -741,7 +741,7 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::add_layer (Transform: Composite Image Layer: Completed)",
"source_frame->number", source_frame->number,
"new_frame->GetImage()->width()", new_frame->GetWidth(),
@@ -754,7 +754,7 @@ void Timeline::update_open_clips(Clip *clip, bool does_clip_intersect)
// Get lock (prevent getting frames while this happens)
const std::lock_guard guard(getFrameMutex);
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::update_open_clips (before)",
"does_clip_intersect", does_clip_intersect,
"closing_clips.size()", closing_clips.size(),
@@ -807,7 +807,7 @@ void Timeline::update_open_clips(Clip *clip, bool does_clip_intersect)
}
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::update_open_clips (after)",
"does_clip_intersect", does_clip_intersect,
"clip_found", clip_found,
@@ -870,7 +870,7 @@ void Timeline::sort_clips()
const std::lock_guard guard(getFrameMutex);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::SortClips",
"clips.size()", clips.size());
@@ -897,7 +897,7 @@ void Timeline::sort_effects()
// Clear all clips from timeline
void Timeline::Clear()
{
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::Clear");
+ Logger::Instance()->AppendDebugMethod("Timeline::Clear");
// Get lock (prevent getting frames while this happens)
const std::lock_guard guard(getFrameMutex);
@@ -943,7 +943,7 @@ void Timeline::Clear()
// Close the reader (and any resources it was consuming)
void Timeline::Close()
{
- ZmqLogger::Instance()->AppendDebugMethod("Timeline::Close");
+ Logger::Instance()->AppendDebugMethod("Timeline::Close");
// Get lock (prevent getting frames while this happens)
const std::lock_guard guard(getFrameMutex);
@@ -989,7 +989,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
frame = final_cache->GetFrame(requested_frame);
if (frame) {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (Cached frame found)",
"requested_frame", requested_frame);
@@ -1007,7 +1007,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
frame = final_cache->GetFrame(requested_frame);
if (frame) {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (Cached frame found on 2nd check)",
"requested_frame", requested_frame);
@@ -1020,7 +1020,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
nearby_clips = find_intersecting_clips(requested_frame, 1, true);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (processing frame)",
"requested_frame", requested_frame,
"omp_get_thread_num()", omp_get_thread_num());
@@ -1035,7 +1035,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
new_frame->ChannelsLayout(info.channel_layout);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (Adding solid color)",
"requested_frame", requested_frame,
"info.width", info.width,
@@ -1048,7 +1048,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
new_frame->AddColor(preview_width, preview_height, color.GetColorHex(requested_frame));
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (Loop through clips)",
"requested_frame", requested_frame,
"clips.size()", clips.size(),
@@ -1102,7 +1102,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
// Compose intersecting clips in a single pass
for (const auto& ci : clip_infos) {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (Does clip intersect)",
"requested_frame", requested_frame,
"clip->Position()", ci.clip->Position(),
@@ -1122,7 +1122,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
int64_t clip_frame_number = ci.frame_number;
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (Calculate clip's frame #)",
"clip->Position()", ci.clip->Position(),
"clip->Start()", ci.clip->Start(),
@@ -1134,7 +1134,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
} else {
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (clip does not intersect)",
"requested_frame", requested_frame,
"does_clip_intersect", ci.intersects);
@@ -1143,7 +1143,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame)
} // end clip loop
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::GetFrame (Add frame to cache)",
"requested_frame", requested_frame,
"info.width", info.width,
@@ -1186,7 +1186,7 @@ std::vector Timeline::find_intersecting_clips(int64_t requested_frame, in
(clip_end_position >= min_requested_frame || clip_end_position >= max_requested_frame);
// Debug output
- ZmqLogger::Instance()->AppendDebugMethod(
+ Logger::Instance()->AppendDebugMethod(
"Timeline::find_intersecting_clips (Is clip near or intersecting)",
"requested_frame", requested_frame,
"min_requested_frame", min_requested_frame,
diff --git a/src/WaylandScreenCaptureReader.cpp b/src/WaylandScreenCaptureReader.cpp
index 71f28db57..31097dcf0 100644
--- a/src/WaylandScreenCaptureReader.cpp
+++ b/src/WaylandScreenCaptureReader.cpp
@@ -36,7 +36,7 @@
#include "Exceptions.h"
#include "Frame.h"
#include "WaylandBufferUtilities.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
using namespace openshot;
@@ -312,7 +312,7 @@ class WaylandScreenCaptureReader final : public ScreenCaptureReader::CaptureBack
SelectPortalSources(session_handle);
stream_info = StartPortalSession(session_handle);
ApplyPortalStreamInfo();
- ZmqLogger::Instance()->Log(
+ Logger::Instance()->Log(
"Wayland portal stream selected: node_id=" + std::to_string(stream_info.node_id) +
" pipewire_serial=" + std::to_string(stream_info.pipewire_serial) +
" source_type=" + std::to_string(stream_info.source_type) +
@@ -684,7 +684,7 @@ class WaylandScreenCaptureReader final : public ScreenCaptureReader::CaptureBack
self->info.video_timebase = self->info.fps.Reciprocal();
}
if (self->stream_width > 0 && self->stream_height > 0) {
- ZmqLogger::Instance()->Log(
+ Logger::Instance()->Log(
"Wayland PipeWire stream format: " +
std::to_string(self->stream_width) + "x" + std::to_string(self->stream_height) +
" format=" + std::to_string(self->video_format) +
@@ -790,7 +790,7 @@ class WaylandScreenCaptureReader final : public ScreenCaptureReader::CaptureBack
crop_width = std::min(static_cast(crop->region.size.width), stream_width - crop_x);
crop_height = std::min(static_cast(crop->region.size.height), stream_height - crop_y);
if (!crop_logged) {
- ZmqLogger::Instance()->Log(
+ Logger::Instance()->Log(
"Wayland PipeWire video crop: x=" + std::to_string(crop_x) +
" y=" + std::to_string(crop_y) +
" width=" + std::to_string(crop_width) +
@@ -915,7 +915,7 @@ class WaylandScreenCaptureReader final : public ScreenCaptureReader::CaptureBack
if (header_drop_log_count >= 5) {
return;
}
- ZmqLogger::Instance()->Log(
+ Logger::Instance()->Log(
"Wayland PipeWire dropped non-monotonic frame: reason=" + reason +
" flags=" + std::to_string(header.flags) +
" seq=" + std::to_string(header.seq) +
diff --git a/src/ZmqLogger.cpp b/src/ZmqLogger.cpp
deleted file mode 100644
index 5607eae55..000000000
--- a/src/ZmqLogger.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/**
- * @file
- * @brief Source file for ZeroMQ-based Logger class
- * @author Jonathan Thomas
- *
- * @ref License
- */
-
-// Copyright (c) 2008-2019 OpenShot Studios, LLC
-//
-// SPDX-License-Identifier: LGPL-3.0-or-later
-
-#include "ZmqLogger.h"
-#include "Exceptions.h"
-#include "Settings.h"
-
-#if USE_RESVG == 1
- #include "ResvgQt.h"
-#endif
-
-using namespace openshot;
-
-#include
-#include
-#include
-#include
-#include // for std::this_thread::sleep_for
-#include // for std::duration::microseconds
-
-
-// Global reference to logger
-ZmqLogger *ZmqLogger::m_pInstance = NULL;
-
-// Create or Get an instance of the logger singleton
-ZmqLogger *ZmqLogger::Instance()
-{
- if (!m_pInstance) {
- // Create the actual instance of logger only once
- m_pInstance = new ZmqLogger;
-
- // init ZMQ variables
- m_pInstance->context = NULL;
- m_pInstance->publisher = NULL;
- m_pInstance->connection = "";
-
- // Default connection
- m_pInstance->Connection("tcp://*:5556");
-
- // Init enabled to False (force user to call Enable())
- m_pInstance->enabled = false;
-
- #if USE_RESVG == 1
- // Init resvg logging (if needed)
- // This can only happen 1 time or it will crash
- ResvgRenderer::initLog();
- #endif
- }
-
- return m_pInstance;
-}
-
-// Set the connection for this logger
-void ZmqLogger::Connection(std::string new_connection)
-{
- // Create a scoped lock, allowing only a single thread to run the following code at one time
- const std::lock_guard lock(loggerMutex);
-
- // Does anything need to happen?
- if (new_connection == connection)
- return;
- else
- // Set new connection
- connection = new_connection;
-
- if (context == NULL) {
- // Create ZMQ Context
- context = new zmq::context_t(1);
- }
-
- if (publisher != NULL) {
- // Close an existing bound publisher socket
- publisher->close();
- publisher = NULL;
- }
-
- // Create new publisher instance
- publisher = new zmq::socket_t(*context, ZMQ_PUB);
-
- // Bind to the socket
- try {
- publisher->bind(connection.c_str());
-
- } catch (zmq::error_t &e) {
- std::cout << "ZmqLogger::Connection - Error binding to " << connection << ". Switching to an available port." << std::endl;
- connection = "tcp://*:*";
- publisher->bind(connection.c_str());
- }
-
- // Sleeping to allow connection to wake up (0.25 seconds)
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
-}
-
-void ZmqLogger::Log(std::string message)
-{
- if (!enabled)
- // Don't do anything
- return;
-
- // Create a scoped lock, allowing only a single thread to run the following code at one time
- const std::lock_guard lock(loggerMutex);
-
- // Send message over socket (ZeroMQ)
- zmq::message_t reply (message.length());
- std::memcpy (reply.data(), message.c_str(), message.length());
-
-#if ZMQ_VERSION > ZMQ_MAKE_VERSION(4, 3, 1)
- // Set flags for immediate delivery (new API)
- publisher->send(reply, zmq::send_flags::dontwait);
-#else
- publisher->send(reply);
-#endif
-
- // Also log to file, if open
- LogToFile(message);
-}
-
-// Log message to a file (if path set)
-void ZmqLogger::LogToFile(std::string message)
-{
- // Write to log file (if opened, and force it to write to disk in case of a crash)
- if (log_file.is_open())
- log_file << message << std::flush;
-}
-
-void ZmqLogger::Path(std::string new_path)
-{
- // Update path
- file_path = new_path;
-
- // Close file (if already open)
- if (log_file.is_open())
- log_file.close();
-
- // Open file (write + append)
- log_file.open (file_path.c_str(), std::ios::out | std::ios::app);
-
- // Get current time and log first message
- std::time_t now = std::time(0);
- std::tm* localtm = std::localtime(&now);
- log_file << "------------------------------------------" << std::endl;
- log_file << "libopenshot logging: " << std::asctime(localtm);
- log_file << "------------------------------------------" << std::endl;
-}
-
-void ZmqLogger::Close()
-{
- // Disable logger as it no longer needed
- enabled = false;
-
- // Close file (if already open)
- if (log_file.is_open())
- log_file.close();
-
- // Close socket (if any)
- if (publisher != NULL) {
- // Close an existing bound publisher socket
- publisher->close();
- publisher = NULL;
- }
-
- // Terminate zmq threads
- if (context != NULL) {
- context->close();
- }
-}
-
-// Append debug information
-void ZmqLogger::AppendDebugMethod(std::string method_name,
- std::string arg1_name, float arg1_value,
- std::string arg2_name, float arg2_value,
- std::string arg3_name, float arg3_value,
- std::string arg4_name, float arg4_value,
- std::string arg5_name, float arg5_value,
- std::string arg6_name, float arg6_value)
-{
- if (!enabled && !openshot::Settings::Instance()->DEBUG_TO_STDERR)
- // Don't do anything
- return;
-
- {
- // Create a scoped lock, allowing only a single thread to run the following code at one time
- const std::lock_guard lock(loggerMutex);
-
- std::stringstream message;
- message << std::fixed << std::setprecision(4);
-
- // Construct message
- message << method_name << " (";
-
- if (arg1_name.length() > 0)
- message << arg1_name << "=" << arg1_value;
-
- if (arg2_name.length() > 0)
- message << ", " << arg2_name << "=" << arg2_value;
-
- if (arg3_name.length() > 0)
- message << ", " << arg3_name << "=" << arg3_value;
-
- if (arg4_name.length() > 0)
- message << ", " << arg4_name << "=" << arg4_value;
-
- if (arg5_name.length() > 0)
- message << ", " << arg5_name << "=" << arg5_value;
-
- if (arg6_name.length() > 0)
- message << ", " << arg6_name << "=" << arg6_value;
-
- message << ")" << std::endl;
-
- if (openshot::Settings::Instance()->DEBUG_TO_STDERR) {
- // Print message to stderr
- std::clog << message.str();
- }
-
- if (enabled) {
- // Send message through ZMQ
- Log(message.str());
- }
- }
-}
diff --git a/src/ZmqLogger.h b/src/ZmqLogger.h
index e2be22e54..31f4d2188 100644
--- a/src/ZmqLogger.h
+++ b/src/ZmqLogger.h
@@ -1,104 +1,10 @@
-/**
- * @file
- * @brief Header file for ZeroMQ-based Logger class
- * @author Jonathan Thomas
- *
- * @ref License
- */
-
-// Copyright (c) 2008-2019 OpenShot Studios, LLC
-//
+// SPDX-FileCopyrightText: 2008-2026 OpenShot Studios, LLC
// SPDX-License-Identifier: LGPL-3.0-or-later
-
-#ifndef OPENSHOT_LOGGER_H
-#define OPENSHOT_LOGGER_H
-
-
-#include
-#include
-#include
-#include
-
-#include
-
+#ifndef OPENSHOT_ZMQLOGGER_COMPAT_H
+#define OPENSHOT_ZMQLOGGER_COMPAT_H
+#include "Logger.h"
namespace openshot {
-
- /**
- * @brief This class is used for logging and sending those logs over a ZemoMQ socket to a listener
- *
- * OpenShot desktop editor listens to this port, to receive libopenshot debug output. It both logs to
- * a file and sends the stdout over a socket.
- */
- class ZmqLogger {
- private:
- std::recursive_mutex loggerMutex;
- std::string connection;
-
- // Logfile related vars
- std::string file_path;
- std::ofstream log_file;
- bool enabled;
-
- /// ZMQ Context
- zmq::context_t *context;
-
- /// ZMQ Socket
- zmq::socket_t *publisher;
-
- /// Default constructor
- ZmqLogger(){}; // Don't allow user to create an instance of this singleton
-
-#if __GNUC__ >=7
- /// Default copy method
- ZmqLogger(ZmqLogger const&) = delete; // Don't allow the user to assign this instance
-
- /// Default assignment operator
- ZmqLogger & operator=(ZmqLogger const&) = delete; // Don't allow the user to assign this instance
-#else
- /// Default copy method
- ZmqLogger(ZmqLogger const&) {}; // Don't allow the user to assign this instance
-
- /// Default assignment operator
- ZmqLogger & operator=(ZmqLogger const&); // Don't allow the user to assign this instance
-#endif
-
- /// Private variable to keep track of singleton instance
- static ZmqLogger * m_pInstance;
-
- public:
- /// Create or get an instance of this logger singleton (invoke the class with this method)
- static ZmqLogger * Instance();
-
- /// Append debug information
- void AppendDebugMethod(
- std::string method_name,
- std::string arg1_name="", float arg1_value=-1.0,
- std::string arg2_name="", float arg2_value=-1.0,
- std::string arg3_name="", float arg3_value=-1.0,
- std::string arg4_name="", float arg4_value=-1.0,
- std::string arg5_name="", float arg5_value=-1.0,
- std::string arg6_name="", float arg6_value=-1.0
- );
-
- /// Close logger (sockets and/or files)
- void Close();
-
- /// Set or change connection info for logger (i.e. tcp://*:5556)
- void Connection(std::string new_connection);
-
- /// Enable/Disable logging
- void Enable(bool is_enabled) { enabled = is_enabled;};
-
- /// Set or change the file path (optional)
- void Path(std::string new_path);
-
- /// Log message to all subscribers of this logger (if any)
- void Log(std::string message);
-
- /// Log message to a file (if path set)
- void LogToFile(std::string message);
- };
-
+/// Deprecated source compatibility alias. Rebuild clients against this release.
+using ZmqLogger = Logger;
}
-
#endif
diff --git a/src/effects/Displace.cpp b/src/effects/Displace.cpp
index b4b0520ac..cf571fb52 100644
--- a/src/effects/Displace.cpp
+++ b/src/effects/Displace.cpp
@@ -15,7 +15,7 @@
#include "Exceptions.h"
#include "ReaderBase.h"
#include "Timeline.h"
-#include "ZmqLogger.h"
+#include "Logger.h"
#include
#include
@@ -135,7 +135,7 @@ std::shared_ptr Displace::GetMapImage(std::shared_ptr target_ima
source_map = std::make_shared(*source_frame->GetImage());
}
} catch (const std::exception& e) {
- ZmqLogger::Instance()->Log(
+ Logger::Instance()->Log(
std::string("Displace::GetMapImage unable to read displacement frame: ") + e.what());
source_map.reset();
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index ee6ae8d97..4d5c72e34 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -44,6 +44,7 @@ set(OPENSHOT_TESTS
FrameScope
FrameMapper
KeyFrame
+ Logger
Point
Profiles
QtPlayer
diff --git a/tests/Logger.cpp b/tests/Logger.cpp
new file mode 100644
index 000000000..a039f4eb3
--- /dev/null
+++ b/tests/Logger.cpp
@@ -0,0 +1,84 @@
+// Copyright (c) 2026 OpenShot Studios, LLC
+// SPDX-License-Identifier: LGPL-3.0-or-later
+#include "openshot_catch.h"
+#include "Logger.h"
+#include "ZmqLogger.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace openshot;
+
+TEST_CASE("Independent logging destinations and crash writes", "[logger]") {
+ auto* logger = Logger::Instance();
+ CHECK(logger == ZmqLogger::Instance());
+ QTemporaryDir directory;
+ REQUIRE(directory.isValid());
+ auto path = directory.filePath(QString::fromUtf8("openshot-logging-\xc3\xa9.log"));
+ logger->Path(path.toUtf8().toStdString());
+ logger->SetFileLevel("warning");
+ logger->SetConsoleLevel("debug");
+ CHECK(logger->ShouldLog(Logger::LevelDebug));
+ std::ostringstream console;
+ auto* previous = std::clog.rdbuf(console.rdbuf());
+ logger->Log("console-only-debug", Logger::LevelDebug);
+ logger->Log("both-warning", Logger::LevelWarning);
+ std::clog.rdbuf(previous);
+ logger->SetFileLevel("off");
+ logger->SetConsoleLevel("off");
+ CHECK_FALSE(logger->ShouldLog(Logger::LevelCritical));
+ logger->Log("filtered-error", Logger::LevelError);
+ logger->LogToFile("---- Unhandled Exception: Stack Trace ----\ncrash-evidence\n---- End of Stack Trace ----\n");
+ CHECK_THROWS_AS(logger->SetFileLevel("not-a-level"), std::invalid_argument);
+ logger->Close();
+ QFile file(path);
+ REQUIRE(file.open(QIODevice::ReadOnly));
+ std::string content = file.readAll().toStdString();
+ CHECK(content.find("console-only-debug") == std::string::npos);
+ CHECK(console.str().find("console-only-debug") != std::string::npos);
+ CHECK(content.find("both-warning") != std::string::npos);
+ CHECK(content.find("filtered-error") == std::string::npos);
+ CHECK(content.find("crash-evidence") != std::string::npos);
+ CHECK(content.find("libopenshot logging:") != std::string::npos);
+ file.close();
+}
+
+TEST_CASE("Concurrent records remain complete and path can reopen", "[logger]") {
+ auto* logger = Logger::Instance();
+ QTemporaryDir directory;
+ REQUIRE(directory.isValid());
+ auto path = directory.filePath("openshot-logging-threads.log");
+ logger->Path(path.toUtf8().toStdString());
+ logger->SetFileLevel("debug");
+ logger->SetConsoleLevel("off");
+ std::vector workers;
+ for (int i = 0; i < 4; ++i) workers.emplace_back([logger, i]() {
+ for (int j = 0; j < 100; ++j)
+ logger->Log("record-" + std::to_string(i) + "-" + std::to_string(j));
+ });
+ for (auto& worker : workers) worker.join();
+ logger->Close();
+ logger->Path(path.toUtf8().toStdString());
+ logger->Enable(true);
+ logger->AppendDebugMethod("reopened", "frame", 42);
+ logger->Close();
+ QFile file(path);
+ REQUIRE(file.open(QIODevice::ReadOnly));
+ std::istringstream content(file.readAll().toStdString());
+ std::string line;
+ int records = 0;
+ bool reopened = false;
+ while (std::getline(content, line)) {
+ if (line.find("record-") != std::string::npos) {
+ ++records;
+ CHECK(line.find("record-", line.find("record-") + 1) == std::string::npos);
+ }
+ if (line.find("reopened (frame=42.0000)") != std::string::npos) reopened = true;
+ }
+ CHECK(records == 400);
+ CHECK(reopened);
+ file.close();
+}
diff --git a/tests/ObjectMask.cpp b/tests/ObjectMask.cpp
index 8b6432e5a..62f4f7509 100644
--- a/tests/ObjectMask.cpp
+++ b/tests/ObjectMask.cpp
@@ -21,13 +21,12 @@
#endif
#include
+#include
#include
+#include
-#include
#include
-#include
#include
-#include
using namespace openshot;
@@ -37,15 +36,6 @@ static std::shared_ptr make_object_mask_frame(int64_t number, int width,
return frame;
}
-static std::string temp_object_mask_path() {
- char path[] = "/tmp/libopenshot_object_mask_XXXXXX";
- int fd = mkstemp(path);
- REQUIRE(fd != -1);
- close(fd);
- std::remove(path);
- return std::string(path) + ".data";
-}
-
static void append_varint(std::string& output, uint64_t value) {
while (value >= 0x80) {
output.push_back(static_cast((value & 0x7f) | 0x80));
@@ -67,9 +57,7 @@ static void append_length_delimited(std::string& output, uint32_t field_number,
output.append(value);
}
-static std::string create_object_mask_data() {
- const std::string path = temp_object_mask_path();
-
+static void create_object_mask_data(const QString& path) {
std::string mask;
append_varint(mask, 8);
append_varint(mask, 4);
@@ -106,10 +94,10 @@ static std::string create_object_mask_data() {
append_length_delimited(data, 1, frame);
append_length_delimited(data, 3, "object mask");
- std::ofstream output(path, std::ios::out | std::ios::binary);
- output.write(data.data(), static_cast(data.size()));
- REQUIRE(output.good());
- return path;
+ QFile output(path);
+ REQUIRE(output.open(QIODevice::WriteOnly));
+ REQUIRE(output.write(data.data(), static_cast(data.size())) == static_cast(data.size()));
+ REQUIRE(output.flush());
}
TEST_CASE("ObjectMask effect is registered", "[effect][object_mask]") {
@@ -121,11 +109,14 @@ TEST_CASE("ObjectMask effect is registered", "[effect][object_mask]") {
}
TEST_CASE("ObjectMask loads protobuf masks and exposes style controls", "[effect][object_mask]") {
- const std::string protobuf_path = create_object_mask_data();
+ QTemporaryDir directory;
+ REQUIRE(directory.isValid());
+ const QString protobuf_path = directory.filePath("object_mask.data");
+ create_object_mask_data(protobuf_path);
ObjectMask effect;
Json::Value config;
- config["protobuf_data_path"] = protobuf_path;
+ config["protobuf_data_path"] = protobuf_path.toUtf8().toStdString();
config["mask_alpha"] = Keyframe(0.5).JsonValue();
config["stroke_width"] = Keyframe(2.0).JsonValue();
effect.SetJsonValue(config);
@@ -151,8 +142,6 @@ TEST_CASE("ObjectMask loads protobuf masks and exposes style controls", "[effect
auto output = effect.GetFrame(frame, 1)->GetImage();
CHECK(output->pixelColor(0, 0) != QColor(64, 64, 64, 255));
CHECK(output->pixelColor(3, 3) == QColor(64, 64, 64, 255));
-
- std::remove(protobuf_path.c_str());
}
#ifdef USE_OPENCV