mirror of
https://github.com/k4yt3x/video2x.git
synced 2024-12-29 16:09:10 +00:00
Merge pull request #1208 from k4yt3x/dev
feat(*): fixed wide character string paths and rewritten the CLI
This commit is contained in:
commit
27c76189d9
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -31,7 +31,8 @@ jobs:
|
|||||||
libvulkan-dev \
|
libvulkan-dev \
|
||||||
glslang-tools \
|
glslang-tools \
|
||||||
libomp-dev \
|
libomp-dev \
|
||||||
libopencv-dev
|
libopencv-dev \
|
||||||
|
libboost-program-options-dev
|
||||||
- name: Build Video2X
|
- name: Build Video2X
|
||||||
run: |
|
run: |
|
||||||
mkdir -p /tmp/build /tmp/install
|
mkdir -p /tmp/build /tmp/install
|
||||||
@ -59,6 +60,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vulkan-query-version: 1.3.204.0
|
vulkan-query-version: 1.3.204.0
|
||||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang, SPIRV-Tools, SPIRV-Headers
|
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang, SPIRV-Tools, SPIRV-Headers
|
||||||
|
vulkan-use-cache: true
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
@ -83,7 +85,7 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build `
|
cmake -S . -B build `
|
||||||
-DUSE_SYSTEM_NCNN=OFF -DUSE_SYSTEM_SPDLOG=OFF -DUSE_SYSTEM_OPENCV=OFF `
|
-DUSE_SYSTEM_NCNN=OFF -DUSE_SYSTEM_SPDLOG=OFF -DUSE_SYSTEM_OPENCV=OFF -DUSE_SYSTEM_BOOST=OFF `
|
||||||
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=build/video2x_install
|
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=build/video2x_install
|
||||||
cmake --build build --config Debug --parallel --target install
|
cmake --build build --config Debug --parallel --target install
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -10,3 +10,6 @@
|
|||||||
[submodule "third_party/opencv"]
|
[submodule "third_party/opencv"]
|
||||||
path = third_party/opencv
|
path = third_party/opencv
|
||||||
url = https://github.com/opencv/opencv.git
|
url = https://github.com/opencv/opencv.git
|
||||||
|
[submodule "third_party/boost"]
|
||||||
|
path = third_party/boost
|
||||||
|
url = https://github.com/boostorg/boost.git
|
||||||
|
117
CMakeLists.txt
117
CMakeLists.txt
@ -1,9 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(video2x VERSION 6.0.0 LANGUAGES C CXX)
|
project(video2x VERSION 6.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
# Set the C standard
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
# Set the C++ standard
|
# Set the C++ standard
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
@ -18,10 +14,8 @@ endif()
|
|||||||
# Set the default optimization flags for Release builds
|
# Set the default optimization flags for Release builds
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Ox /GL /LTCG /MD /DNDEBUG")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ox /GL /LTCG /MD /DNDEBUG")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ox /GL /LTCG /MD /DNDEBUG")
|
||||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -march=native -flto")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -flto")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -flto")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -s")
|
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -s")
|
||||||
@ -38,9 +32,10 @@ endif()
|
|||||||
# Build options
|
# Build options
|
||||||
option(BUILD_SHARED_LIBS "Build libvideo2x as a shared library" ON)
|
option(BUILD_SHARED_LIBS "Build libvideo2x as a shared library" ON)
|
||||||
option(BUILD_VIDEO2X_CLI "Build the video2x executable" ON)
|
option(BUILD_VIDEO2X_CLI "Build the video2x executable" ON)
|
||||||
option(USE_SYSTEM_SPDLOG "Use system spdlog library" ON)
|
|
||||||
option(USE_SYSTEM_OPENCV "Use system OpenCV library" ON)
|
option(USE_SYSTEM_OPENCV "Use system OpenCV library" ON)
|
||||||
option(USE_SYSTEM_NCNN "Use system ncnn library" ON)
|
option(USE_SYSTEM_NCNN "Use system ncnn library" ON)
|
||||||
|
option(USE_SYSTEM_SPDLOG "Use system spdlog library" ON)
|
||||||
|
option(USE_SYSTEM_BOOST "Use system Boost library" ON)
|
||||||
|
|
||||||
# Generate the version header file
|
# Generate the version header file
|
||||||
configure_file(
|
configure_file(
|
||||||
@ -53,16 +48,6 @@ configure_file(
|
|||||||
set(ALL_INCLUDE_DIRS)
|
set(ALL_INCLUDE_DIRS)
|
||||||
set(ALL_LIBRARIES)
|
set(ALL_LIBRARIES)
|
||||||
|
|
||||||
# spdlog
|
|
||||||
if (USE_SYSTEM_SPDLOG)
|
|
||||||
find_package(spdlog REQUIRED)
|
|
||||||
list(APPEND ALL_INCLUDE_DIRS ${spdlog_INCLUDE_DIRS})
|
|
||||||
list(APPEND ALL_LIBRARIES spdlog::spdlog)
|
|
||||||
else()
|
|
||||||
add_subdirectory(third_party/spdlog)
|
|
||||||
list(APPEND ALL_LIBRARIES spdlog::spdlog_header_only)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Platform-specific dependencies
|
# Platform-specific dependencies
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# Define base paths for FFmpeg and ncnn
|
# Define base paths for FFmpeg and ncnn
|
||||||
@ -71,7 +56,7 @@ if(WIN32)
|
|||||||
set(OPENCV_BASE_PATH ${PROJECT_SOURCE_DIR}/third_party/opencv-shared)
|
set(OPENCV_BASE_PATH ${PROJECT_SOURCE_DIR}/third_party/opencv-shared)
|
||||||
|
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
list(APPEND ALL_LIBRARIES
|
set(FFMPEG_LIB
|
||||||
${FFMPEG_BASE_PATH}/lib/avcodec.lib
|
${FFMPEG_BASE_PATH}/lib/avcodec.lib
|
||||||
${FFMPEG_BASE_PATH}/lib/avdevice.lib
|
${FFMPEG_BASE_PATH}/lib/avdevice.lib
|
||||||
${FFMPEG_BASE_PATH}/lib/avfilter.lib
|
${FFMPEG_BASE_PATH}/lib/avfilter.lib
|
||||||
@ -79,6 +64,7 @@ if(WIN32)
|
|||||||
${FFMPEG_BASE_PATH}/lib/avutil.lib
|
${FFMPEG_BASE_PATH}/lib/avutil.lib
|
||||||
${FFMPEG_BASE_PATH}/lib/swscale.lib
|
${FFMPEG_BASE_PATH}/lib/swscale.lib
|
||||||
)
|
)
|
||||||
|
list(APPEND ALL_LIBRARIES ${FFMPEG_LIB})
|
||||||
list(APPEND ALL_INCLUDE_DIRS ${FFMPEG_BASE_PATH}/include)
|
list(APPEND ALL_INCLUDE_DIRS ${FFMPEG_BASE_PATH}/include)
|
||||||
|
|
||||||
# ncnn
|
# ncnn
|
||||||
@ -99,7 +85,7 @@ if(WIN32)
|
|||||||
list(APPEND ALL_INCLUDE_DIRS ${NCNN_BASE_PATH}/include/ncnn)
|
list(APPEND ALL_INCLUDE_DIRS ${NCNN_BASE_PATH}/include/ncnn)
|
||||||
|
|
||||||
# OpenCV
|
# OpenCV
|
||||||
list(APPEND ALL_LIBRARIES ${OPENCV_BASE_PATH}/build/x64/vc16/lib/opencv_world4100.lib)
|
# list(APPEND ALL_LIBRARIES ${OPENCV_BASE_PATH}/build/x64/vc16/lib/opencv_world4100.lib)
|
||||||
list(APPEND ALL_INCLUDE_DIRS ${OPENCV_BASE_PATH}/build/include)
|
list(APPEND ALL_INCLUDE_DIRS ${OPENCV_BASE_PATH}/build/include)
|
||||||
else()
|
else()
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
@ -114,17 +100,19 @@ else()
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Loop through each package to find and collect include dirs and libraries
|
# Loop through each package to find and collect include dirs and libraries
|
||||||
|
set(FFMPEG_LIB)
|
||||||
foreach(PKG ${FFMPEG_REQUIRED_PKGS})
|
foreach(PKG ${FFMPEG_REQUIRED_PKGS})
|
||||||
pkg_check_modules(${PKG} REQUIRED ${PKG})
|
pkg_check_modules(${PKG} REQUIRED ${PKG})
|
||||||
list(APPEND ALL_INCLUDE_DIRS ${${PKG}_INCLUDE_DIRS})
|
list(APPEND ALL_INCLUDE_DIRS ${${PKG}_INCLUDE_DIRS})
|
||||||
list(APPEND ALL_LIBRARIES ${${PKG}_LIBRARIES})
|
list(APPEND FFMPEG_LIB ${${PKG}_LIBRARIES})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
list(APPEND ALL_LIBRARIES ${FFMPEG_LIB})
|
||||||
|
|
||||||
# OpenCV
|
# OpenCV
|
||||||
if (USE_SYSTEM_OPENCV)
|
if (USE_SYSTEM_OPENCV)
|
||||||
find_package(OpenCV REQUIRED)
|
find_package(OpenCV REQUIRED)
|
||||||
list(APPEND ALL_INCLUDE_DIRS ${OpenCV_INCLUDE_DIRS}/opencv2)
|
list(APPEND ALL_INCLUDE_DIRS ${OpenCV_INCLUDE_DIRS}/opencv2)
|
||||||
list(APPEND ALL_LIBRARIES opencv_core opencv_videoio)
|
# list(APPEND ALL_LIBRARIES opencv_core opencv_videoio)
|
||||||
else()
|
else()
|
||||||
option(BUILD_opencv_calib3d "" OFF)
|
option(BUILD_opencv_calib3d "" OFF)
|
||||||
option(BUILD_opencv_core "" ON)
|
option(BUILD_opencv_core "" ON)
|
||||||
@ -165,7 +153,7 @@ else()
|
|||||||
${PROJECT_SOURCE_DIR}/third_party/opencv/modules/core/include
|
${PROJECT_SOURCE_DIR}/third_party/opencv/modules/core/include
|
||||||
${PROJECT_SOURCE_DIR}/third_party/opencv/modules/videoio/include
|
${PROJECT_SOURCE_DIR}/third_party/opencv/modules/videoio/include
|
||||||
)
|
)
|
||||||
list(APPEND ALL_LIBRARIES opencv_core opencv_videoio)
|
# list(APPEND ALL_LIBRARIES opencv_core opencv_videoio)
|
||||||
endif() # USE_SYSTEM_OPENCV
|
endif() # USE_SYSTEM_OPENCV
|
||||||
endif() # WIN32
|
endif() # WIN32
|
||||||
|
|
||||||
@ -268,6 +256,32 @@ else()
|
|||||||
add_subdirectory(third_party/ncnn)
|
add_subdirectory(third_party/ncnn)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# spdlog
|
||||||
|
if (USE_SYSTEM_SPDLOG)
|
||||||
|
find_package(spdlog REQUIRED)
|
||||||
|
list(APPEND ALL_INCLUDE_DIRS ${spdlog_INCLUDE_DIRS})
|
||||||
|
set(SPDLOG_LIB spdlog::spdlog)
|
||||||
|
else()
|
||||||
|
add_subdirectory(third_party/spdlog)
|
||||||
|
set(SPDLOG_LIB spdlog::spdlog_header_only)
|
||||||
|
endif()
|
||||||
|
list(APPEND ALL_LIBRARIES ${SPDLOG_LIB})
|
||||||
|
|
||||||
|
# Boost
|
||||||
|
if (USE_SYSTEM_BOOST)
|
||||||
|
find_package(Boost REQUIRED COMPONENTS program_options)
|
||||||
|
list(APPEND ALL_INCLUDE_DIRS ${Boost_INCLUDE_DIRS})
|
||||||
|
else()
|
||||||
|
option(Boost_USE_STATIC_LIBS "" ON)
|
||||||
|
option(Boost_USE_STATIC_RUNTIME "" ON)
|
||||||
|
option(Boost_COMPONENTS "program_options")
|
||||||
|
|
||||||
|
add_subdirectory(third_party/boost)
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/third_party/boost/libs/program_options/include)
|
||||||
|
set(BOOST_BASE_PATH ${CMAKE_BINARY_DIR}/third_party/boost/libs/program_options/${CMAKE_BUILD_TYPE})
|
||||||
|
endif()
|
||||||
|
set(BOOST_LIB Boost::program_options)
|
||||||
|
|
||||||
# Include ExternalProject module
|
# Include ExternalProject module
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
@ -288,11 +302,17 @@ ExternalProject_Add(
|
|||||||
list(REMOVE_DUPLICATES ALL_INCLUDE_DIRS)
|
list(REMOVE_DUPLICATES ALL_INCLUDE_DIRS)
|
||||||
list(REMOVE_DUPLICATES ALL_LIBRARIES)
|
list(REMOVE_DUPLICATES ALL_LIBRARIES)
|
||||||
|
|
||||||
# Add all source files for libvideo2x
|
|
||||||
file(GLOB LIBVIDEO2X_SOURCES src/*.cpp)
|
|
||||||
|
|
||||||
# Create the shared library 'libvideo2x'
|
# Create the shared library 'libvideo2x'
|
||||||
add_library(libvideo2x ${LIBVIDEO2X_SOURCES})
|
add_library(libvideo2x
|
||||||
|
src/conversions.cpp
|
||||||
|
src/decoder.cpp
|
||||||
|
src/encoder.cpp
|
||||||
|
src/fsutils.cpp
|
||||||
|
src/libplacebo_filter.cpp
|
||||||
|
src/libplacebo.cpp
|
||||||
|
src/libvideo2x.cpp
|
||||||
|
src/realesrgan_filter.cpp
|
||||||
|
)
|
||||||
target_compile_definitions(libvideo2x PRIVATE LIBVIDEO2X_EXPORTS)
|
target_compile_definitions(libvideo2x PRIVATE LIBVIDEO2X_EXPORTS)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set_target_properties(libvideo2x PROPERTIES OUTPUT_NAME libvideo2x)
|
set_target_properties(libvideo2x PROPERTIES OUTPUT_NAME libvideo2x)
|
||||||
@ -321,13 +341,13 @@ target_compile_options(libvideo2x PRIVATE
|
|||||||
|
|
||||||
# Define the path to the built libresrgan-ncnn-vulkan library
|
# Define the path to the built libresrgan-ncnn-vulkan library
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(REALESRGAN_LIB ${CMAKE_BINARY_DIR}/realesrgan_install/lib/librealesrgan-ncnn-vulkan.lib)
|
list(APPEND ALL_LIBRARIES ${CMAKE_BINARY_DIR}/realesrgan_install/lib/librealesrgan-ncnn-vulkan.lib)
|
||||||
else()
|
else()
|
||||||
set(REALESRGAN_LIB ${CMAKE_BINARY_DIR}/realesrgan_install/lib/librealesrgan-ncnn-vulkan.so)
|
list(APPEND ALL_LIBRARIES ${CMAKE_BINARY_DIR}/realesrgan_install/lib/librealesrgan-ncnn-vulkan.so)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Link the shared library with the dependencies
|
# Link the shared library with the dependencies
|
||||||
target_link_libraries(libvideo2x PRIVATE ${ALL_LIBRARIES} ${REALESRGAN_LIB})
|
target_link_libraries(libvideo2x PRIVATE ${ALL_LIBRARIES})
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
if (USE_SYSTEM_NCNN)
|
if (USE_SYSTEM_NCNN)
|
||||||
@ -339,7 +359,7 @@ endif()
|
|||||||
|
|
||||||
# Create the executable 'video2x'
|
# Create the executable 'video2x'
|
||||||
if (BUILD_VIDEO2X_CLI)
|
if (BUILD_VIDEO2X_CLI)
|
||||||
add_executable(video2x src/video2x.c src/getopt.c)
|
add_executable(video2x src/video2x.cpp)
|
||||||
set_target_properties(video2x PROPERTIES OUTPUT_NAME video2x)
|
set_target_properties(video2x PROPERTIES OUTPUT_NAME video2x)
|
||||||
|
|
||||||
# Include directories for the executable
|
# Include directories for the executable
|
||||||
@ -353,7 +373,7 @@ if (BUILD_VIDEO2X_CLI)
|
|||||||
target_compile_options(video2x PRIVATE $<$<CONFIG:Debug>:-g -DDEBUG>)
|
target_compile_options(video2x PRIVATE $<$<CONFIG:Debug>:-g -DDEBUG>)
|
||||||
|
|
||||||
# Link the executable with the shared library
|
# Link the executable with the shared library
|
||||||
target_link_libraries(video2x PRIVATE ${ALL_LIBRARIES} libvideo2x)
|
target_link_libraries(video2x PRIVATE libvideo2x ${FFMPEG_LIB} ${SPDLOG_LIB} ${BOOST_LIB})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Define the default installation directories
|
# Define the default installation directories
|
||||||
@ -408,13 +428,15 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libvideo2x/version.h
|
|||||||
# Platform-specific installation rules
|
# Platform-specific installation rules
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# Install Windows-specific dependencies
|
# Install Windows-specific dependencies
|
||||||
install(FILES ${CMAKE_BINARY_DIR}/realesrgan_install/bin/librealesrgan-ncnn-vulkan.dll
|
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
DESTINATION ${INSTALL_BIN_DESTINATION}
|
set(BOOST_DLL_PATH ${BOOST_BASE_PATH}/boost_program_options-vc143-mt-x64-1_86.dll)
|
||||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
else()
|
||||||
GROUP_READ GROUP_EXECUTE
|
set(BOOST_DLL_PATH ${BOOST_BASE_PATH}/boost_program_options-vc143-mt-gd-x64-1_86.dll)
|
||||||
WORLD_READ WORLD_EXECUTE
|
endif()
|
||||||
)
|
|
||||||
install(FILES ${FFMPEG_BASE_PATH}/bin/swscale-8.dll
|
install(FILES
|
||||||
|
${CMAKE_BINARY_DIR}/realesrgan_install/bin/librealesrgan-ncnn-vulkan.dll
|
||||||
|
${FFMPEG_BASE_PATH}/bin/swscale-8.dll
|
||||||
${FFMPEG_BASE_PATH}/bin/avcodec-61.dll
|
${FFMPEG_BASE_PATH}/bin/avcodec-61.dll
|
||||||
${FFMPEG_BASE_PATH}/bin/avdevice-61.dll
|
${FFMPEG_BASE_PATH}/bin/avdevice-61.dll
|
||||||
${FFMPEG_BASE_PATH}/bin/avfilter-10.dll
|
${FFMPEG_BASE_PATH}/bin/avfilter-10.dll
|
||||||
@ -422,19 +444,10 @@ if(WIN32)
|
|||||||
${FFMPEG_BASE_PATH}/bin/avutil-59.dll
|
${FFMPEG_BASE_PATH}/bin/avutil-59.dll
|
||||||
${FFMPEG_BASE_PATH}/bin/postproc-58.dll
|
${FFMPEG_BASE_PATH}/bin/postproc-58.dll
|
||||||
${FFMPEG_BASE_PATH}/bin/swresample-5.dll
|
${FFMPEG_BASE_PATH}/bin/swresample-5.dll
|
||||||
DESTINATION ${INSTALL_BIN_DESTINATION}
|
# ${OPENCV_BASE_PATH}/build/x64/vc16/bin/opencv_world4100.dll
|
||||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
# ${OPENCV_BASE_PATH}/build/x64/vc16/bin/opencv_videoio_msmf4100_64.dll
|
||||||
GROUP_READ GROUP_EXECUTE
|
${NCNN_BASE_PATH}/bin/ncnn.dll
|
||||||
WORLD_READ WORLD_EXECUTE
|
${BOOST_DLL_PATH}
|
||||||
)
|
|
||||||
install(FILES ${OPENCV_BASE_PATH}/build/x64/vc16/bin/opencv_world4100.dll
|
|
||||||
${OPENCV_BASE_PATH}/build/x64/vc16/bin/opencv_videoio_msmf4100_64.dll
|
|
||||||
DESTINATION ${INSTALL_BIN_DESTINATION}
|
|
||||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
|
||||||
GROUP_READ GROUP_EXECUTE
|
|
||||||
WORLD_READ WORLD_EXECUTE
|
|
||||||
)
|
|
||||||
install(FILES ${NCNN_BASE_PATH}/bin/ncnn.dll
|
|
||||||
DESTINATION ${INSTALL_BIN_DESTINATION}
|
DESTINATION ${INSTALL_BIN_DESTINATION}
|
||||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
||||||
GROUP_READ GROUP_EXECUTE
|
GROUP_READ GROUP_EXECUTE
|
||||||
|
6
Makefile
6
Makefile
@ -52,7 +52,8 @@ debian:
|
|||||||
glslang-tools \
|
glslang-tools \
|
||||||
libomp-dev \
|
libomp-dev \
|
||||||
libspdlog-dev \
|
libspdlog-dev \
|
||||||
libopencv-dev
|
libopencv-dev \
|
||||||
|
libboost-program-options-dev
|
||||||
cmake -B /tmp/build -S . -DUSE_SYSTEM_NCNN=OFF \
|
cmake -B /tmp/build -S . -DUSE_SYSTEM_NCNN=OFF \
|
||||||
-DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) \
|
-DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) \
|
||||||
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/install \
|
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/install \
|
||||||
@ -74,7 +75,8 @@ ubuntu:
|
|||||||
libvulkan-dev \
|
libvulkan-dev \
|
||||||
glslang-tools \
|
glslang-tools \
|
||||||
libomp-dev \
|
libomp-dev \
|
||||||
libopencv-dev
|
libopencv-dev \
|
||||||
|
libboost-program-options-dev
|
||||||
cmake -B build -S . -DUSE_SYSTEM_NCNN=OFF -DUSE_SYSTEM_SPDLOG=OFF -DSPDLOG_NO_EXCEPTIONS=ON \
|
cmake -B build -S . -DUSE_SYSTEM_NCNN=OFF -DUSE_SYSTEM_SPDLOG=OFF -DSPDLOG_NO_EXCEPTIONS=ON \
|
||||||
-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ \
|
-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ \
|
||||||
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=build/video2x_package/usr
|
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=build/video2x_package/usr
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
#ifndef __GETOPT_H__
|
|
||||||
#define __GETOPT_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern int opterr; /* if error message should be printed */
|
|
||||||
extern int optind; /* index into parent argv vector */
|
|
||||||
extern int optopt; /* character checked for validity */
|
|
||||||
extern int optreset; /* reset getopt */
|
|
||||||
extern char *optarg; /* argument associated with option */
|
|
||||||
|
|
||||||
struct option {
|
|
||||||
const char *name;
|
|
||||||
int has_arg;
|
|
||||||
int *flag;
|
|
||||||
int val;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define no_argument 0
|
|
||||||
#define required_argument 1
|
|
||||||
#define optional_argument 2
|
|
||||||
|
|
||||||
// int getopt(int, char **, const char *);
|
|
||||||
int getopt_long(int, char **, const char *, const struct option *, int *);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __GETOPT_H__ */
|
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef DECODER_H
|
#ifndef DECODER_H
|
||||||
#define DECODER_H
|
#define DECODER_H
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
@ -9,7 +11,7 @@ extern "C" {
|
|||||||
int init_decoder(
|
int init_decoder(
|
||||||
AVHWDeviceType hw_type,
|
AVHWDeviceType hw_type,
|
||||||
AVBufferRef *hw_ctx,
|
AVBufferRef *hw_ctx,
|
||||||
const char *in_fname,
|
std::filesystem::path in_fpath,
|
||||||
AVFormatContext **fmt_ctx,
|
AVFormatContext **fmt_ctx,
|
||||||
AVCodecContext **dec_ctx,
|
AVCodecContext **dec_ctx,
|
||||||
int *vstream_idx
|
int *vstream_idx
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef ENCODER_H
|
#ifndef ENCODER_H
|
||||||
#define ENCODER_H
|
#define ENCODER_H
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
@ -11,7 +13,7 @@ extern "C" {
|
|||||||
|
|
||||||
int init_encoder(
|
int init_encoder(
|
||||||
AVBufferRef *hw_ctx,
|
AVBufferRef *hw_ctx,
|
||||||
const char *out_fname,
|
std::filesystem::path out_fpath,
|
||||||
AVFormatContext *ifmt_ctx,
|
AVFormatContext *ifmt_ctx,
|
||||||
AVFormatContext **ofmt_ctx,
|
AVFormatContext **ofmt_ctx,
|
||||||
AVCodecContext **enc_ctx,
|
AVCodecContext **enc_ctx,
|
||||||
|
@ -43,7 +43,11 @@ enum Libvideo2xLogLevel {
|
|||||||
struct LibplaceboConfig {
|
struct LibplaceboConfig {
|
||||||
int out_width;
|
int out_width;
|
||||||
int out_height;
|
int out_height;
|
||||||
|
#ifdef _WIN32
|
||||||
|
const wchar_t *shader_path;
|
||||||
|
#else
|
||||||
const char *shader_path;
|
const char *shader_path;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configuration for RealESRGAN filter
|
// Configuration for RealESRGAN filter
|
||||||
@ -51,7 +55,11 @@ struct RealESRGANConfig {
|
|||||||
int gpuid;
|
int gpuid;
|
||||||
bool tta_mode;
|
bool tta_mode;
|
||||||
int scaling_factor;
|
int scaling_factor;
|
||||||
const char *model;
|
#ifdef _WIN32
|
||||||
|
const wchar_t *model_path;
|
||||||
|
#else
|
||||||
|
const char *model_path;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unified filter configuration
|
// Unified filter configuration
|
||||||
@ -87,8 +95,13 @@ struct VideoProcessingContext {
|
|||||||
|
|
||||||
// C-compatible process_video function
|
// C-compatible process_video function
|
||||||
LIBVIDEO2X_API int process_video(
|
LIBVIDEO2X_API int process_video(
|
||||||
|
#ifdef _WIN32
|
||||||
|
const wchar_t *in_fname,
|
||||||
|
const wchar_t *out_fname,
|
||||||
|
#else
|
||||||
const char *in_fname,
|
const char *in_fname,
|
||||||
const char *out_fname,
|
const char *out_fname,
|
||||||
|
#endif
|
||||||
enum Libvideo2xLogLevel log_level,
|
enum Libvideo2xLogLevel log_level,
|
||||||
bool benchmark,
|
bool benchmark,
|
||||||
enum AVHWDeviceType hw_device_type,
|
enum AVHWDeviceType hw_device_type,
|
||||||
|
@ -17,7 +17,7 @@ class RealesrganFilter : public Filter {
|
|||||||
int gpuid;
|
int gpuid;
|
||||||
bool tta_mode;
|
bool tta_mode;
|
||||||
int scaling_factor;
|
int scaling_factor;
|
||||||
const char *model;
|
const std::filesystem::path model_path;
|
||||||
const std::filesystem::path custom_model_param_path;
|
const std::filesystem::path custom_model_param_path;
|
||||||
const std::filesystem::path custom_model_bin_path;
|
const std::filesystem::path custom_model_bin_path;
|
||||||
AVRational in_time_base;
|
AVRational in_time_base;
|
||||||
@ -30,7 +30,7 @@ class RealesrganFilter : public Filter {
|
|||||||
int gpuid = 0,
|
int gpuid = 0,
|
||||||
bool tta_mode = false,
|
bool tta_mode = false,
|
||||||
int scaling_factor = 4,
|
int scaling_factor = 4,
|
||||||
const char *model = "realesr-animevideov3",
|
const std::filesystem::path model = std::filesystem::path("realesr-animevideov3"),
|
||||||
const std::filesystem::path custom_model_param_path = std::filesystem::path(),
|
const std::filesystem::path custom_model_param_path = std::filesystem::path(),
|
||||||
const std::filesystem::path custom_model_bin_path = std::filesystem::path()
|
const std::filesystem::path custom_model_bin_path = std::filesystem::path()
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
pkgname=video2x
|
pkgname=video2x
|
||||||
pkgver=r843.e09f348
|
pkgver=r874.66c623f
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="A machine learning-based lossless video super resolution framework"
|
pkgdesc="A machine learning-based lossless video super resolution framework"
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url="https://github.com/k4yt3x/video2x"
|
url="https://github.com/k4yt3x/video2x"
|
||||||
license=('AGPL3')
|
license=('AGPL3')
|
||||||
depends=('ffmpeg' 'ncnn' 'vulkan-driver' 'opencv' 'spdlog')
|
depends=('ffmpeg' 'ncnn' 'vulkan-driver' 'opencv' 'spdlog' 'boost-libs')
|
||||||
makedepends=('git' 'cmake' 'make' 'clang' 'pkgconf' 'vulkan-headers' 'openmp')
|
makedepends=('git' 'cmake' 'make' 'clang' 'pkgconf' 'vulkan-headers' 'openmp' 'boost')
|
||||||
|
|
||||||
pkgver() {
|
pkgver() {
|
||||||
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||||
|
@ -4,5 +4,5 @@ Section: video
|
|||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Maintainer: K4YT3X <i@k4yt3x.com>
|
Maintainer: K4YT3X <i@k4yt3x.com>
|
||||||
Depends: ffmpeg, libopencv-videoio406t64
|
Depends: ffmpeg, libboost-program-options1.83.0
|
||||||
Description: A machine learning-based lossless video super resolution framework.
|
Description: A machine learning-based lossless video super resolution framework.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Name: Video2X Dockerfile
|
# Name: Video2X Dockerfile
|
||||||
# Creator: K4YT3X
|
# Creator: K4YT3X
|
||||||
# Date Created: February 3, 2022
|
# Date Created: February 3, 2022
|
||||||
# Last Modified: October 30, 2024
|
# Last Modified: November 1, 2024
|
||||||
|
|
||||||
# stage 1: build the python components into wheels
|
# stage 1: build the python components into wheels
|
||||||
FROM docker.io/archlinux:latest AS builder
|
FROM docker.io/archlinux:latest AS builder
|
||||||
@ -9,7 +9,7 @@ FROM docker.io/archlinux:latest AS builder
|
|||||||
# Install dependencies and create a non-root user
|
# Install dependencies and create a non-root user
|
||||||
RUN pacman -Syy --noconfirm \
|
RUN pacman -Syy --noconfirm \
|
||||||
base-devel git cmake make clang pkgconf sudo \
|
base-devel git cmake make clang pkgconf sudo \
|
||||||
ffmpeg ncnn vulkan-headers openmp spdlog opencv \
|
ffmpeg ncnn vulkan-headers openmp spdlog opencv boost \
|
||||||
nvidia-utils vulkan-radeon vulkan-intel vulkan-swrast \
|
nvidia-utils vulkan-radeon vulkan-intel vulkan-swrast \
|
||||||
&& useradd -m builder \
|
&& useradd -m builder \
|
||||||
&& echo 'builder ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/builder
|
&& echo 'builder ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/builder
|
||||||
@ -38,7 +38,7 @@ ENV VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/nvidia_icd.json\
|
|||||||
|
|
||||||
COPY --from=builder /tmp/video2x.pkg.tar.zst /video2x.pkg.tar.zst
|
COPY --from=builder /tmp/video2x.pkg.tar.zst /video2x.pkg.tar.zst
|
||||||
RUN pacman -Sy --noconfirm nvidia-utils vulkan-radeon vulkan-intel vulkan-swrast \
|
RUN pacman -Sy --noconfirm nvidia-utils vulkan-radeon vulkan-intel vulkan-swrast \
|
||||||
ffmpeg ncnn spdlog opencv \
|
ffmpeg ncnn spdlog opencv boost-libs \
|
||||||
&& pacman -U --noconfirm /video2x.pkg.tar.zst \
|
&& pacman -U --noconfirm /video2x.pkg.tar.zst \
|
||||||
&& rm -rf /video2x.pkg.tar.zst /var/cache/pacman/pkg/*
|
&& rm -rf /video2x.pkg.tar.zst /var/cache/pacman/pkg/*
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ static enum AVPixelFormat get_hw_format(AVCodecContext *_, const enum AVPixelFor
|
|||||||
int init_decoder(
|
int init_decoder(
|
||||||
AVHWDeviceType hw_type,
|
AVHWDeviceType hw_type,
|
||||||
AVBufferRef *hw_ctx,
|
AVBufferRef *hw_ctx,
|
||||||
const char *in_fname,
|
std::filesystem::path in_fpath,
|
||||||
AVFormatContext **fmt_ctx,
|
AVFormatContext **fmt_ctx,
|
||||||
AVCodecContext **dec_ctx,
|
AVCodecContext **dec_ctx,
|
||||||
int *vstream_idx
|
int *vstream_idx
|
||||||
@ -31,8 +31,8 @@ int init_decoder(
|
|||||||
AVCodecContext *codec_ctx = NULL;
|
AVCodecContext *codec_ctx = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ((ret = avformat_open_input(&ifmt_ctx, in_fname, NULL, NULL)) < 0) {
|
if ((ret = avformat_open_input(&ifmt_ctx, in_fpath.u8string().c_str(), NULL, NULL)) < 0) {
|
||||||
spdlog::error("Could not open input file '{}'", in_fname);
|
spdlog::error("Could not open input file '{}'", in_fpath.u8string().c_str());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ static enum AVPixelFormat get_encoder_default_pix_fmt(const AVCodec *encoder) {
|
|||||||
|
|
||||||
int init_encoder(
|
int init_encoder(
|
||||||
AVBufferRef *hw_ctx,
|
AVBufferRef *hw_ctx,
|
||||||
const char *out_fname,
|
std::filesystem::path out_fpath,
|
||||||
AVFormatContext *ifmt_ctx,
|
AVFormatContext *ifmt_ctx,
|
||||||
AVFormatContext **ofmt_ctx,
|
AVFormatContext **ofmt_ctx,
|
||||||
AVCodecContext **enc_ctx,
|
AVCodecContext **enc_ctx,
|
||||||
@ -33,7 +33,7 @@ int init_encoder(
|
|||||||
int stream_index = 0;
|
int stream_index = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, out_fname);
|
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, out_fpath.u8string().c_str());
|
||||||
if (!fmt_ctx) {
|
if (!fmt_ctx) {
|
||||||
spdlog::error("Could not create output context");
|
spdlog::error("Could not create output context");
|
||||||
return AVERROR_UNKNOWN;
|
return AVERROR_UNKNOWN;
|
||||||
@ -174,9 +174,9 @@ int init_encoder(
|
|||||||
|
|
||||||
// Open the output file
|
// Open the output file
|
||||||
if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
|
if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
|
||||||
ret = avio_open(&fmt_ctx->pb, out_fname, AVIO_FLAG_WRITE);
|
ret = avio_open(&fmt_ctx->pb, out_fpath.u8string().c_str(), AVIO_FLAG_WRITE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
spdlog::error("Could not open output file '{}'", out_fname);
|
spdlog::error("Could not open output file '{}'", out_fpath.u8string().c_str());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
249
src/getopt.c
249
src/getopt.c
@ -1,249 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 1987, 1993, 1994, 1996
|
|
||||||
* The Regents of the University of California. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the names of the copyright holders nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
|
|
||||||
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "getopt.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
extern int opterr;
|
|
||||||
extern int optind;
|
|
||||||
extern int optopt;
|
|
||||||
extern int optreset;
|
|
||||||
extern char *optarg;
|
|
||||||
*/
|
|
||||||
|
|
||||||
int opterr = 1; /* if error message should be printed */
|
|
||||||
int optind = 1; /* index into parent argv vector */
|
|
||||||
int optopt = 0; /* character checked for validity */
|
|
||||||
int optreset = 0; /* reset getopt */
|
|
||||||
char *optarg = NULL; /* argument associated with option */
|
|
||||||
|
|
||||||
#ifndef __P
|
|
||||||
#define __P(x) x
|
|
||||||
#endif
|
|
||||||
#define _DIAGASSERT(x) assert(x)
|
|
||||||
|
|
||||||
static char *__progname __P((char *));
|
|
||||||
int getopt_internal __P((int, char *const *, const char *));
|
|
||||||
|
|
||||||
static char *__progname(char *nargv0) {
|
|
||||||
char *tmp;
|
|
||||||
|
|
||||||
_DIAGASSERT(nargv0 != NULL);
|
|
||||||
|
|
||||||
tmp = strrchr(nargv0, '/');
|
|
||||||
if (tmp) {
|
|
||||||
tmp++;
|
|
||||||
} else {
|
|
||||||
tmp = nargv0;
|
|
||||||
}
|
|
||||||
return (tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BADCH (int)'?'
|
|
||||||
#define BADARG (int)':'
|
|
||||||
#define EMSG ""
|
|
||||||
|
|
||||||
/*
|
|
||||||
* getopt --
|
|
||||||
* Parse argc/argv argument vector.
|
|
||||||
*/
|
|
||||||
int getopt_internal(int nargc, char *const *nargv, const char *ostr) {
|
|
||||||
static char *place = EMSG; /* option letter processing */
|
|
||||||
char *oli; /* option letter list index */
|
|
||||||
|
|
||||||
_DIAGASSERT(nargv != NULL);
|
|
||||||
_DIAGASSERT(ostr != NULL);
|
|
||||||
|
|
||||||
if (optreset || !*place) { /* update scanning pointer */
|
|
||||||
optreset = 0;
|
|
||||||
if (optind >= nargc || *(place = nargv[optind]) != '-') {
|
|
||||||
place = EMSG;
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
if (place[1] && *++place == '-') { /* found "--" */
|
|
||||||
/* ++optind; */
|
|
||||||
place = EMSG;
|
|
||||||
return (-2);
|
|
||||||
}
|
|
||||||
} /* option letter okay? */
|
|
||||||
if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) {
|
|
||||||
/*
|
|
||||||
* if the user didn't specify '-' as an option,
|
|
||||||
* assume it means -1.
|
|
||||||
*/
|
|
||||||
if (optopt == (int)'-') {
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
if (!*place) {
|
|
||||||
++optind;
|
|
||||||
}
|
|
||||||
if (opterr && *ostr != ':') {
|
|
||||||
(void)fprintf(stderr, "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
|
|
||||||
}
|
|
||||||
return (BADCH);
|
|
||||||
}
|
|
||||||
if (*++oli != ':') { /* don't need argument */
|
|
||||||
optarg = NULL;
|
|
||||||
if (!*place) {
|
|
||||||
++optind;
|
|
||||||
}
|
|
||||||
} else { /* need an argument */
|
|
||||||
if (*place) { /* no white space */
|
|
||||||
optarg = place;
|
|
||||||
} else if (nargc <= ++optind) { /* no arg */
|
|
||||||
place = EMSG;
|
|
||||||
if ((opterr) && (*ostr != ':')) {
|
|
||||||
(void)fprintf(
|
|
||||||
stderr, "%s: option requires an argument -- %c\n", __progname(nargv[0]), optopt
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (BADARG);
|
|
||||||
} else { /* white space */
|
|
||||||
optarg = nargv[optind];
|
|
||||||
}
|
|
||||||
place = EMSG;
|
|
||||||
++optind;
|
|
||||||
}
|
|
||||||
return (optopt); /* dump back option letter */
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/*
|
|
||||||
* getopt --
|
|
||||||
* Parse argc/argv argument vector.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
getopt2(nargc, nargv, ostr)
|
|
||||||
int nargc;
|
|
||||||
char * const *nargv;
|
|
||||||
const char *ostr;
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
|
|
||||||
retval = -1;
|
|
||||||
++optind;
|
|
||||||
}
|
|
||||||
return(retval);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* getopt_long --
|
|
||||||
* Parse argc/argv argument vector.
|
|
||||||
*/
|
|
||||||
int getopt_long(
|
|
||||||
int nargc,
|
|
||||||
char **nargv,
|
|
||||||
const char *options,
|
|
||||||
const struct option *long_options,
|
|
||||||
int *index
|
|
||||||
) {
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
_DIAGASSERT(nargv != NULL);
|
|
||||||
_DIAGASSERT(options != NULL);
|
|
||||||
_DIAGASSERT(long_options != NULL);
|
|
||||||
/* index may be NULL */
|
|
||||||
|
|
||||||
if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
|
|
||||||
char *current_argv = nargv[optind++] + 2, *has_equal;
|
|
||||||
int i, match = -1;
|
|
||||||
size_t current_argv_len;
|
|
||||||
|
|
||||||
if (*current_argv == '\0') {
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
|
||||||
current_argv_len = (size_t)(has_equal - current_argv);
|
|
||||||
has_equal++;
|
|
||||||
} else {
|
|
||||||
current_argv_len = strlen(current_argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; long_options[i].name; i++) {
|
|
||||||
if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen(long_options[i].name) == current_argv_len) {
|
|
||||||
match = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (match == -1) {
|
|
||||||
match = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (match != -1) {
|
|
||||||
if (long_options[match].has_arg == required_argument ||
|
|
||||||
long_options[match].has_arg == optional_argument) {
|
|
||||||
if (has_equal) {
|
|
||||||
optarg = has_equal;
|
|
||||||
} else {
|
|
||||||
optarg = nargv[optind++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) {
|
|
||||||
/*
|
|
||||||
* Missing argument, leading :
|
|
||||||
* indicates no error should be generated
|
|
||||||
*/
|
|
||||||
if ((opterr) && (*options != ':')) {
|
|
||||||
(void)fprintf(
|
|
||||||
stderr,
|
|
||||||
"%s: option requires an argument -- %s\n",
|
|
||||||
__progname(nargv[0]),
|
|
||||||
current_argv
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (BADARG);
|
|
||||||
}
|
|
||||||
} else { /* No matching argument */
|
|
||||||
if ((opterr) && (*options != ':')) {
|
|
||||||
(void
|
|
||||||
)fprintf(stderr, "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
|
|
||||||
}
|
|
||||||
return (BADCH);
|
|
||||||
}
|
|
||||||
if (long_options[match].flag) {
|
|
||||||
*long_options[match].flag = long_options[match].val;
|
|
||||||
retval = 0;
|
|
||||||
} else {
|
|
||||||
retval = long_options[match].val;
|
|
||||||
}
|
|
||||||
if (index) {
|
|
||||||
*index = match;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (retval);
|
|
||||||
}
|
|
@ -5,8 +5,11 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavutil/avutil.h>
|
||||||
|
}
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <opencv2/videoio.hpp>
|
|
||||||
|
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "encoder.h"
|
#include "encoder.h"
|
||||||
@ -46,28 +49,46 @@ static int process_frames(
|
|||||||
std::vector<AVFrame *> flushed_frames;
|
std::vector<AVFrame *> flushed_frames;
|
||||||
|
|
||||||
// Get the total number of frames in the video with OpenCV
|
// Get the total number of frames in the video with OpenCV
|
||||||
spdlog::debug("Reading total number of frames with OpenCV");
|
spdlog::debug("Reading total number of frames");
|
||||||
cv::VideoCapture cap(ifmt_ctx->url);
|
proc_ctx->total_frames = ifmt_ctx->streams[vstream_idx]->nb_frames;
|
||||||
if (!cap.isOpened()) {
|
if (proc_ctx->total_frames > 0) {
|
||||||
spdlog::error("Failed to open video file with OpenCV");
|
spdlog::debug("Read total number of frames from 'nb_frames': {}", proc_ctx->total_frames);
|
||||||
return -1;
|
} else {
|
||||||
|
spdlog::warn("Estimating the total number of frames from duration * fps");
|
||||||
|
// Calculate duration in seconds
|
||||||
|
double duration_secs = static_cast<double>(ifmt_ctx->streams[vstream_idx]->duration) *
|
||||||
|
av_q2d(ifmt_ctx->streams[vstream_idx]->time_base);
|
||||||
|
spdlog::debug("Video duration: {}s", duration_secs);
|
||||||
|
|
||||||
|
// Calculate average FPS
|
||||||
|
double fps = av_q2d(ifmt_ctx->streams[vstream_idx]->avg_frame_rate);
|
||||||
|
if (fps <= 0) {
|
||||||
|
spdlog::debug("Unable to read the average frame rate from 'avg_frame_rate'");
|
||||||
|
fps = av_q2d(ifmt_ctx->streams[vstream_idx]->r_frame_rate);
|
||||||
|
}
|
||||||
|
if (fps <= 0) {
|
||||||
|
spdlog::debug("Unable to read the average frame rate from 'r_frame_rate'");
|
||||||
|
fps = av_q2d(av_guess_frame_rate(ifmt_ctx, ifmt_ctx->streams[vstream_idx], nullptr));
|
||||||
|
}
|
||||||
|
if (fps <= 0) {
|
||||||
|
spdlog::debug("Unable to estimate the average frame rate with 'av_guess_frame_rate'");
|
||||||
|
fps = av_q2d(ifmt_ctx->streams[vstream_idx]->time_base);
|
||||||
|
}
|
||||||
|
if (fps <= 0) {
|
||||||
|
spdlog::debug("Unable to estimate the video's average frame rate");
|
||||||
|
} else {
|
||||||
|
// Calculate total frames
|
||||||
|
proc_ctx->total_frames = static_cast<int64_t>(duration_secs * fps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
proc_ctx->total_frames = static_cast<int64_t>(cap.get(cv::CAP_PROP_FRAME_COUNT));
|
|
||||||
cap.release();
|
|
||||||
|
|
||||||
// Check if the total number of frames is still 0
|
// Check if the total number of frames is still 0
|
||||||
if (proc_ctx->total_frames == 0) {
|
if (proc_ctx->total_frames == 0) {
|
||||||
spdlog::warn("Unable to determine total number of frames");
|
spdlog::warn("Unable to determine the total number of frames");
|
||||||
} else {
|
} else {
|
||||||
spdlog::debug("{} frames to process", proc_ctx->total_frames);
|
spdlog::debug("{} frames to process", proc_ctx->total_frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get start time
|
|
||||||
proc_ctx->start_time = time(NULL);
|
|
||||||
if (proc_ctx->start_time == -1) {
|
|
||||||
perror("time");
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFrame *frame = av_frame_alloc();
|
AVFrame *frame = av_frame_alloc();
|
||||||
if (frame == nullptr) {
|
if (frame == nullptr) {
|
||||||
ret = AVERROR(ENOMEM);
|
ret = AVERROR(ENOMEM);
|
||||||
@ -236,8 +257,13 @@ static int process_frames(
|
|||||||
* @return int 0 on success, non-zero value on error
|
* @return int 0 on success, non-zero value on error
|
||||||
*/
|
*/
|
||||||
extern "C" int process_video(
|
extern "C" int process_video(
|
||||||
|
#ifdef _WIN32
|
||||||
|
const wchar_t *in_fname,
|
||||||
|
const wchar_t *out_fname,
|
||||||
|
#else
|
||||||
const char *in_fname,
|
const char *in_fname,
|
||||||
const char *out_fname,
|
const char *out_fname,
|
||||||
|
#endif
|
||||||
Libvideo2xLogLevel log_level,
|
Libvideo2xLogLevel log_level,
|
||||||
bool benchmark,
|
bool benchmark,
|
||||||
AVHWDeviceType hw_type,
|
AVHWDeviceType hw_type,
|
||||||
@ -328,6 +354,10 @@ extern "C" int process_video(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert the file names to std::filesystem::path
|
||||||
|
std::filesystem::path in_fpath(in_fname);
|
||||||
|
std::filesystem::path out_fpath(out_fname);
|
||||||
|
|
||||||
// Initialize hardware device context
|
// Initialize hardware device context
|
||||||
if (hw_type != AV_HWDEVICE_TYPE_NONE) {
|
if (hw_type != AV_HWDEVICE_TYPE_NONE) {
|
||||||
ret = av_hwdevice_ctx_create(&hw_ctx, hw_type, NULL, NULL, 0);
|
ret = av_hwdevice_ctx_create(&hw_ctx, hw_type, NULL, NULL, 0);
|
||||||
@ -340,7 +370,7 @@ extern "C" int process_video(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize input
|
// Initialize input
|
||||||
ret = init_decoder(hw_type, hw_ctx, in_fname, &ifmt_ctx, &dec_ctx, &vstream_idx);
|
ret = init_decoder(hw_type, hw_ctx, in_fpath, &ifmt_ctx, &dec_ctx, &vstream_idx);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
av_strerror(ret, errbuf, sizeof(errbuf));
|
av_strerror(ret, errbuf, sizeof(errbuf));
|
||||||
spdlog::error("Failed to initialize decoder: {}", errbuf);
|
spdlog::error("Failed to initialize decoder: {}", errbuf);
|
||||||
@ -371,7 +401,7 @@ extern "C" int process_video(
|
|||||||
encoder_config->out_height = output_height;
|
encoder_config->out_height = output_height;
|
||||||
ret = init_encoder(
|
ret = init_encoder(
|
||||||
hw_ctx,
|
hw_ctx,
|
||||||
out_fname,
|
out_fpath,
|
||||||
ifmt_ctx,
|
ifmt_ctx,
|
||||||
&ofmt_ctx,
|
&ofmt_ctx,
|
||||||
&enc_ctx,
|
&enc_ctx,
|
||||||
@ -409,13 +439,13 @@ extern "C" int process_video(
|
|||||||
};
|
};
|
||||||
} else if (filter_config->filter_type == FILTER_REALESRGAN) {
|
} else if (filter_config->filter_type == FILTER_REALESRGAN) {
|
||||||
const auto &config = filter_config->config.realesrgan;
|
const auto &config = filter_config->config.realesrgan;
|
||||||
if (!config.model) {
|
if (!config.model_path) {
|
||||||
spdlog::error("Model name must be provided for the RealESRGAN filter");
|
spdlog::error("Model name must be provided for the RealESRGAN filter");
|
||||||
cleanup();
|
cleanup();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
filter = new RealesrganFilter{
|
filter = new RealesrganFilter{
|
||||||
config.gpuid, config.tta_mode, config.scaling_factor, config.model
|
config.gpuid, config.tta_mode, config.scaling_factor, config.model_path
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
spdlog::error("Unknown filter type");
|
spdlog::error("Unknown filter type");
|
||||||
|
@ -13,7 +13,7 @@ RealesrganFilter::RealesrganFilter(
|
|||||||
int gpuid,
|
int gpuid,
|
||||||
bool tta_mode,
|
bool tta_mode,
|
||||||
int scaling_factor,
|
int scaling_factor,
|
||||||
const char *model,
|
const std::filesystem::path model_path,
|
||||||
const std::filesystem::path custom_model_param_path,
|
const std::filesystem::path custom_model_param_path,
|
||||||
const std::filesystem::path custom_model_bin_path
|
const std::filesystem::path custom_model_bin_path
|
||||||
)
|
)
|
||||||
@ -21,7 +21,7 @@ RealesrganFilter::RealesrganFilter(
|
|||||||
gpuid(gpuid),
|
gpuid(gpuid),
|
||||||
tta_mode(tta_mode),
|
tta_mode(tta_mode),
|
||||||
scaling_factor(scaling_factor),
|
scaling_factor(scaling_factor),
|
||||||
model(model),
|
model_path(std::move(model_path)),
|
||||||
custom_model_param_path(std::move(custom_model_param_path)),
|
custom_model_param_path(std::move(custom_model_param_path)),
|
||||||
custom_model_bin_path(std::move(custom_model_bin_path)) {}
|
custom_model_bin_path(std::move(custom_model_bin_path)) {}
|
||||||
|
|
||||||
@ -37,12 +37,13 @@ int RealesrganFilter::init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVB
|
|||||||
std::filesystem::path model_param_path;
|
std::filesystem::path model_param_path;
|
||||||
std::filesystem::path model_bin_path;
|
std::filesystem::path model_bin_path;
|
||||||
|
|
||||||
if (model) {
|
if (!model_path.empty()) {
|
||||||
// Find the model paths by model name if provided
|
// Find the model paths by model name if provided
|
||||||
|
// TODO: ensure this works with wide strings on Windows
|
||||||
model_param_path = std::filesystem::path("models") / "realesrgan" /
|
model_param_path = std::filesystem::path("models") / "realesrgan" /
|
||||||
(std::string(model) + "-x" + std::to_string(scaling_factor) + ".param");
|
(model_path.string() + "-x" + std::to_string(scaling_factor) + ".param");
|
||||||
model_bin_path = std::filesystem::path("models") / "realesrgan" /
|
model_bin_path = std::filesystem::path("models") / "realesrgan" /
|
||||||
(std::string(model) + "-x" + std::to_string(scaling_factor) + ".bin");
|
(model_path.string() + "-x" + std::to_string(scaling_factor) + ".bin");
|
||||||
} else if (!custom_model_param_path.empty() && !custom_model_bin_path.empty()) {
|
} else if (!custom_model_param_path.empty() && !custom_model_bin_path.empty()) {
|
||||||
// Use the custom model paths if provided
|
// Use the custom model paths if provided
|
||||||
model_param_path = custom_model_param_path;
|
model_param_path = custom_model_param_path;
|
||||||
|
599
src/video2x.c
599
src/video2x.c
@ -1,599 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <threads.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <conio.h>
|
|
||||||
#else
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libavutil/hwcontext.h>
|
|
||||||
#include <libavutil/pixdesc.h>
|
|
||||||
#include <libavutil/pixfmt.h>
|
|
||||||
|
|
||||||
#include <libvideo2x/libvideo2x.h>
|
|
||||||
#include <libvideo2x/version.h>
|
|
||||||
|
|
||||||
#include "getopt.h"
|
|
||||||
|
|
||||||
// Define command line options
|
|
||||||
static struct option long_options[] = {
|
|
||||||
{"loglevel", required_argument, NULL, 0},
|
|
||||||
{"noprogress", no_argument, NULL, 0},
|
|
||||||
{"version", no_argument, NULL, 'v'},
|
|
||||||
{"help", no_argument, NULL, 0},
|
|
||||||
|
|
||||||
// General options
|
|
||||||
{"input", required_argument, NULL, 'i'},
|
|
||||||
{"output", required_argument, NULL, 'o'},
|
|
||||||
{"filter", required_argument, NULL, 'f'},
|
|
||||||
{"hwaccel", required_argument, NULL, 'a'},
|
|
||||||
{"nocopystreams", no_argument, NULL, 0},
|
|
||||||
{"benchmark", no_argument, NULL, 0},
|
|
||||||
|
|
||||||
// Encoder options
|
|
||||||
{"codec", required_argument, NULL, 'c'},
|
|
||||||
{"preset", required_argument, NULL, 'p'},
|
|
||||||
{"pixfmt", required_argument, NULL, 'x'},
|
|
||||||
{"bitrate", required_argument, NULL, 'b'},
|
|
||||||
{"crf", required_argument, NULL, 'q'},
|
|
||||||
|
|
||||||
// libplacebo options
|
|
||||||
{"shader", required_argument, NULL, 's'},
|
|
||||||
{"width", required_argument, NULL, 'w'},
|
|
||||||
{"height", required_argument, NULL, 'h'},
|
|
||||||
|
|
||||||
// RealESRGAN options
|
|
||||||
{"gpuid", required_argument, NULL, 'g'},
|
|
||||||
{"model", required_argument, NULL, 'm'},
|
|
||||||
{"scale", required_argument, NULL, 'r'},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
// List of valid RealESRGAN models
|
|
||||||
const char *valid_realesrgan_models[] = {
|
|
||||||
"realesrgan-plus",
|
|
||||||
"realesrgan-plus-anime",
|
|
||||||
"realesr-animevideov3",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Indicate if a newline needs to be printed before the next output
|
|
||||||
bool newline_required = false;
|
|
||||||
|
|
||||||
// Structure to hold parsed arguments
|
|
||||||
struct arguments {
|
|
||||||
// General options
|
|
||||||
const char *loglevel;
|
|
||||||
bool noprogress;
|
|
||||||
const char *in_fname;
|
|
||||||
const char *out_fname;
|
|
||||||
const char *filter_type;
|
|
||||||
const char *hwaccel;
|
|
||||||
bool nocopystreams;
|
|
||||||
bool benchmark;
|
|
||||||
|
|
||||||
// Encoder options
|
|
||||||
const char *codec;
|
|
||||||
const char *pix_fmt;
|
|
||||||
const char *preset;
|
|
||||||
int64_t bitrate;
|
|
||||||
float crf;
|
|
||||||
|
|
||||||
// libplacebo options
|
|
||||||
const char *shader_path;
|
|
||||||
int out_width;
|
|
||||||
int out_height;
|
|
||||||
|
|
||||||
// RealESRGAN options
|
|
||||||
int gpuid;
|
|
||||||
const char *model;
|
|
||||||
int scaling_factor;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ProcessVideoThreadArguments {
|
|
||||||
struct arguments *arguments;
|
|
||||||
enum AVHWDeviceType hw_device_type;
|
|
||||||
struct FilterConfig *filter_config;
|
|
||||||
struct EncoderConfig *encoder_config;
|
|
||||||
struct VideoProcessingContext *proc_ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set UNIX terminal input to non-blocking mode
|
|
||||||
#ifndef _WIN32
|
|
||||||
void set_nonblocking_input(bool enable) {
|
|
||||||
static struct termios oldt, newt;
|
|
||||||
if (enable) {
|
|
||||||
tcgetattr(STDIN_FILENO, &oldt);
|
|
||||||
newt = oldt;
|
|
||||||
newt.c_lflag &= ~(tcflag_t)(ICANON | ECHO);
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
|
||||||
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
|
|
||||||
} else {
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
|
||||||
fcntl(STDIN_FILENO, F_SETFL, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Newline-safe log callback for FFmpeg
|
|
||||||
void newline_safe_ffmpeg_log_callback(void *ptr, int level, const char *fmt, va_list vl) {
|
|
||||||
if (level <= av_log_get_level() && newline_required) {
|
|
||||||
putchar('\n');
|
|
||||||
newline_required = false;
|
|
||||||
}
|
|
||||||
av_log_default_callback(ptr, level, fmt, vl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_valid_realesrgan_model(const char *model) {
|
|
||||||
if (!model) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for (unsigned long i = 0;
|
|
||||||
i < sizeof(valid_realesrgan_models) / sizeof(valid_realesrgan_models[0]);
|
|
||||||
i++) {
|
|
||||||
if (strcmp(model, valid_realesrgan_models[i]) == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_help(void) {
|
|
||||||
printf("Usage: video2x [OPTIONS]\n");
|
|
||||||
printf("\nOptions:\n");
|
|
||||||
printf(" --loglevel Set log level \n");
|
|
||||||
printf(" (trace, debug, info, warn, error, critical, none)\n");
|
|
||||||
printf(" --noprogress Do not display the progress bar\n");
|
|
||||||
printf(" -v, --version Print program version\n");
|
|
||||||
printf(" -?, --help Display this help page\n");
|
|
||||||
printf("\nGeneral Processing Options:\n");
|
|
||||||
printf(" -i, --input Input video file path\n");
|
|
||||||
printf(" -o, --output Output video file path\n");
|
|
||||||
printf(" -f, --filter Filter to use: 'libplacebo' or 'realesrgan'\n");
|
|
||||||
printf(" -a, --hwaccel Hardware acceleration method (default: none)\n");
|
|
||||||
printf(" --nocopystreams Do not copy audio and subtitle streams\n");
|
|
||||||
printf(" --benchmark Discard processed frames and calculate average FPS\n");
|
|
||||||
|
|
||||||
printf("\nEncoder Options (Optional):\n");
|
|
||||||
printf(" -c, --codec Output codec (default: libx264)\n");
|
|
||||||
printf(" -p, --preset Encoder preset (default: slow)\n");
|
|
||||||
printf(" -x, --pixfmt Output pixel format (default: auto)\n");
|
|
||||||
printf(" -b, --bitrate Bitrate in bits per second (default: 0 (VBR))\n");
|
|
||||||
printf(" -q, --crf Constant Rate Factor (default: 20.0)\n");
|
|
||||||
|
|
||||||
printf("\nlibplacebo Options:\n");
|
|
||||||
printf(" -s, --shader Name or path of the GLSL shader file to use \n");
|
|
||||||
printf(" (built-in: 'anime4k-a', 'anime4k-b', 'anime4k-c',\n");
|
|
||||||
printf(" 'anime4k-a+a', 'anime4k-b+b', 'anime4k-c+a')\n");
|
|
||||||
printf(" -w, --width Output width\n");
|
|
||||||
printf(" -h, --height Output height\n");
|
|
||||||
|
|
||||||
printf("\nRealESRGAN Options:\n");
|
|
||||||
printf(" -g, --gpuid Vulkan GPU ID (default: 0)\n");
|
|
||||||
printf(" -m, --model Name of the model to use\n");
|
|
||||||
printf(" -r, --scale Scaling factor (2, 3, or 4)\n");
|
|
||||||
|
|
||||||
printf("\nExamples Usage:\n");
|
|
||||||
printf(" video2x -i in.mp4 -o out.mp4 -f libplacebo -s anime4k-v4-a+a -w 3840 -h 2160\n");
|
|
||||||
printf(" video2x -i in.mp4 -o out.mp4 -f realesrgan -m realesr-animevideov3 -r 4\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_arguments(int argc, char **argv, struct arguments *arguments) {
|
|
||||||
int option_index = 0;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
// Default argument values
|
|
||||||
arguments->loglevel = "info";
|
|
||||||
arguments->noprogress = false;
|
|
||||||
arguments->in_fname = NULL;
|
|
||||||
arguments->out_fname = NULL;
|
|
||||||
arguments->filter_type = NULL;
|
|
||||||
arguments->hwaccel = "none";
|
|
||||||
arguments->nocopystreams = false;
|
|
||||||
arguments->benchmark = false;
|
|
||||||
|
|
||||||
// Encoder options
|
|
||||||
arguments->codec = "libx264";
|
|
||||||
arguments->preset = "slow";
|
|
||||||
arguments->pix_fmt = NULL;
|
|
||||||
arguments->bitrate = 0;
|
|
||||||
arguments->crf = 20.0;
|
|
||||||
|
|
||||||
// libplacebo options
|
|
||||||
arguments->shader_path = NULL;
|
|
||||||
arguments->out_width = 0;
|
|
||||||
arguments->out_height = 0;
|
|
||||||
|
|
||||||
// RealESRGAN options
|
|
||||||
arguments->gpuid = 0;
|
|
||||||
arguments->model = NULL;
|
|
||||||
arguments->scaling_factor = 0;
|
|
||||||
|
|
||||||
while ((c = getopt_long(
|
|
||||||
argc, argv, "i:o:f:a:c:x:p:b:q:s:w:h:g:m:r:v", long_options, &option_index
|
|
||||||
)) != -1) {
|
|
||||||
switch (c) {
|
|
||||||
case 'i':
|
|
||||||
arguments->in_fname = optarg;
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
arguments->out_fname = optarg;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
arguments->filter_type = optarg;
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
arguments->hwaccel = optarg;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
arguments->codec = optarg;
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
arguments->pix_fmt = optarg;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
arguments->preset = optarg;
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
arguments->bitrate = strtoll(optarg, NULL, 10);
|
|
||||||
if (arguments->bitrate <= 0) {
|
|
||||||
fprintf(stderr, "Error: Invalid bitrate specified.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
arguments->crf = (float)atof(optarg);
|
|
||||||
if (arguments->crf < 0.0 || arguments->crf > 51.0) {
|
|
||||||
fprintf(stderr, "Error: CRF must be between 0 and 51.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
arguments->shader_path = optarg;
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
arguments->out_width = atoi(optarg);
|
|
||||||
if (arguments->out_width <= 0) {
|
|
||||||
fprintf(stderr, "Error: Output width must be greater than 0.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
arguments->out_height = atoi(optarg);
|
|
||||||
if (arguments->out_height <= 0) {
|
|
||||||
fprintf(stderr, "Error: Output height must be greater than 0.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
arguments->gpuid = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
arguments->model = optarg;
|
|
||||||
if (!is_valid_realesrgan_model(arguments->model)) {
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"Error: Invalid model specified. Must be 'realesrgan-plus', "
|
|
||||||
"'realesrgan-plus-anime', or 'realesr-animevideov3'.\n"
|
|
||||||
);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
arguments->scaling_factor = atoi(optarg);
|
|
||||||
if (arguments->scaling_factor != 2 && arguments->scaling_factor != 3 &&
|
|
||||||
arguments->scaling_factor != 4) {
|
|
||||||
fprintf(stderr, "Error: Scaling factor must be 2, 3, or 4.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
printf("Video2X version %s\n", LIBVIDEO2X_VERSION_STRING);
|
|
||||||
exit(0);
|
|
||||||
case 0: // Long-only options without short equivalents
|
|
||||||
if (strcmp(long_options[option_index].name, "loglevel") == 0) {
|
|
||||||
arguments->loglevel = optarg;
|
|
||||||
} else if (strcmp(long_options[option_index].name, "noprogress") == 0) {
|
|
||||||
arguments->noprogress = true;
|
|
||||||
} else if (strcmp(long_options[option_index].name, "help") == 0) {
|
|
||||||
print_help();
|
|
||||||
exit(0);
|
|
||||||
} else if (strcmp(long_options[option_index].name, "nocopystreams") == 0) {
|
|
||||||
arguments->nocopystreams = true;
|
|
||||||
} else if (strcmp(long_options[option_index].name, "benchmark") == 0) {
|
|
||||||
arguments->benchmark = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Invalid options provided.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for required arguments
|
|
||||||
if (!arguments->in_fname) {
|
|
||||||
fprintf(stderr, "Error: Input file path is required.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!arguments->out_fname && !arguments->benchmark) {
|
|
||||||
fprintf(stderr, "Error: Output file path is required.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!arguments->filter_type) {
|
|
||||||
fprintf(stderr, "Error: Filter type is required (libplacebo or realesrgan).\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(arguments->filter_type, "libplacebo") == 0) {
|
|
||||||
if (!arguments->shader_path || arguments->out_width == 0 || arguments->out_height == 0) {
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"Error: For libplacebo, shader name/path (-s), width (-w), "
|
|
||||||
"and height (-h) are required.\n"
|
|
||||||
);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
} else if (strcmp(arguments->filter_type, "realesrgan") == 0) {
|
|
||||||
if (arguments->scaling_factor == 0 || !arguments->model) {
|
|
||||||
fprintf(
|
|
||||||
stderr, "Error: For realesrgan, scaling factor (-r) and model (-m) are required.\n"
|
|
||||||
);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Libvideo2xLogLevel parse_log_level(const char *level_name) {
|
|
||||||
if (strcmp(level_name, "trace") == 0) {
|
|
||||||
return LIBVIDEO2X_LOG_LEVEL_TRACE;
|
|
||||||
} else if (strcmp(level_name, "debug") == 0) {
|
|
||||||
return LIBVIDEO2X_LOG_LEVEL_DEBUG;
|
|
||||||
} else if (strcmp(level_name, "info") == 0) {
|
|
||||||
return LIBVIDEO2X_LOG_LEVEL_INFO;
|
|
||||||
} else if (strcmp(level_name, "warning") == 0) {
|
|
||||||
return LIBVIDEO2X_LOG_LEVEL_WARNING;
|
|
||||||
} else if (strcmp(level_name, "error") == 0) {
|
|
||||||
return LIBVIDEO2X_LOG_LEVEL_ERROR;
|
|
||||||
} else if (strcmp(level_name, "critical") == 0) {
|
|
||||||
return LIBVIDEO2X_LOG_LEVEL_CRITICAL;
|
|
||||||
} else if (strcmp(level_name, "off") == 0) {
|
|
||||||
return LIBVIDEO2X_LOG_LEVEL_OFF;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Warning: Invalid log level specified. Defaulting to 'info'.\n");
|
|
||||||
return LIBVIDEO2X_LOG_LEVEL_INFO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function for video processing thread
|
|
||||||
int process_video_thread(void *arg) {
|
|
||||||
struct ProcessVideoThreadArguments *thread_args = (struct ProcessVideoThreadArguments *)arg;
|
|
||||||
|
|
||||||
// Extract individual arguments
|
|
||||||
enum Libvideo2xLogLevel log_level = parse_log_level(thread_args->arguments->loglevel);
|
|
||||||
struct arguments *arguments = thread_args->arguments;
|
|
||||||
enum AVHWDeviceType hw_device_type = thread_args->hw_device_type;
|
|
||||||
struct FilterConfig *filter_config = thread_args->filter_config;
|
|
||||||
struct EncoderConfig *encoder_config = thread_args->encoder_config;
|
|
||||||
struct VideoProcessingContext *proc_ctx = thread_args->proc_ctx;
|
|
||||||
|
|
||||||
// Call the process_video function
|
|
||||||
int result = process_video(
|
|
||||||
arguments->in_fname,
|
|
||||||
arguments->out_fname,
|
|
||||||
log_level,
|
|
||||||
arguments->benchmark,
|
|
||||||
hw_device_type,
|
|
||||||
filter_config,
|
|
||||||
encoder_config,
|
|
||||||
proc_ctx
|
|
||||||
);
|
|
||||||
|
|
||||||
proc_ctx->completed = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
// Print help if no arguments are provided
|
|
||||||
if (argc < 2) {
|
|
||||||
print_help();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse command line arguments
|
|
||||||
struct arguments arguments;
|
|
||||||
parse_arguments(argc, argv, &arguments);
|
|
||||||
|
|
||||||
// Setup filter configurations based on the parsed arguments
|
|
||||||
struct FilterConfig filter_config;
|
|
||||||
if (strcmp(arguments.filter_type, "libplacebo") == 0) {
|
|
||||||
filter_config.filter_type = FILTER_LIBPLACEBO;
|
|
||||||
filter_config.config.libplacebo.out_width = arguments.out_width;
|
|
||||||
filter_config.config.libplacebo.out_height = arguments.out_height;
|
|
||||||
filter_config.config.libplacebo.shader_path = arguments.shader_path;
|
|
||||||
} else if (strcmp(arguments.filter_type, "realesrgan") == 0) {
|
|
||||||
filter_config.filter_type = FILTER_REALESRGAN;
|
|
||||||
filter_config.config.realesrgan.gpuid = arguments.gpuid;
|
|
||||||
filter_config.config.realesrgan.tta_mode = 0;
|
|
||||||
filter_config.config.realesrgan.scaling_factor = arguments.scaling_factor;
|
|
||||||
filter_config.config.realesrgan.model = arguments.model;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Error: Invalid filter type specified.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse codec to AVCodec
|
|
||||||
const AVCodec *codec = avcodec_find_encoder_by_name(arguments.codec);
|
|
||||||
if (!codec) {
|
|
||||||
fprintf(stderr, "Error: Codec '%s' not found.\n", arguments.codec);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse pixel format to AVPixelFormat
|
|
||||||
enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
|
|
||||||
if (arguments.pix_fmt) {
|
|
||||||
pix_fmt = av_get_pix_fmt(arguments.pix_fmt);
|
|
||||||
if (pix_fmt == AV_PIX_FMT_NONE) {
|
|
||||||
fprintf(stderr, "Error: Invalid pixel format '%s'.\n", arguments.pix_fmt);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup encoder configuration
|
|
||||||
struct EncoderConfig encoder_config = {
|
|
||||||
.out_width = 0, // To be filled by libvideo2x
|
|
||||||
.out_height = 0, // To be filled by libvideo2x
|
|
||||||
.copy_streams = !arguments.nocopystreams,
|
|
||||||
.codec = codec->id,
|
|
||||||
.pix_fmt = pix_fmt,
|
|
||||||
.preset = arguments.preset,
|
|
||||||
.bit_rate = arguments.bitrate,
|
|
||||||
.crf = arguments.crf,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse hardware acceleration method
|
|
||||||
enum AVHWDeviceType hw_device_type = AV_HWDEVICE_TYPE_NONE;
|
|
||||||
if (strcmp(arguments.hwaccel, "none") != 0) {
|
|
||||||
hw_device_type = av_hwdevice_find_type_by_name(arguments.hwaccel);
|
|
||||||
if (hw_device_type == AV_HWDEVICE_TYPE_NONE) {
|
|
||||||
fprintf(stderr, "Error: Invalid hardware device type '%s'.\n", arguments.hwaccel);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup struct to store processing context
|
|
||||||
struct VideoProcessingContext proc_ctx = {
|
|
||||||
.processed_frames = 0,
|
|
||||||
.total_frames = 0,
|
|
||||||
.start_time = time(NULL),
|
|
||||||
.pause = false,
|
|
||||||
.abort = false,
|
|
||||||
.completed = false
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a ThreadArguments struct to hold all the arguments for the thread
|
|
||||||
struct ProcessVideoThreadArguments thread_args = {
|
|
||||||
.arguments = &arguments,
|
|
||||||
.hw_device_type = hw_device_type,
|
|
||||||
.filter_config = &filter_config,
|
|
||||||
.encoder_config = &encoder_config,
|
|
||||||
.proc_ctx = &proc_ctx
|
|
||||||
};
|
|
||||||
|
|
||||||
// Register a newline-safe log callback for FFmpeg
|
|
||||||
// This will ensure that log messages are printed on a new line after the progress bar
|
|
||||||
av_log_set_callback(newline_safe_ffmpeg_log_callback);
|
|
||||||
|
|
||||||
// Create a thread for video processing
|
|
||||||
thrd_t processing_thread;
|
|
||||||
if (thrd_create(&processing_thread, process_video_thread, &thread_args) != thrd_success) {
|
|
||||||
fprintf(stderr, "Failed to create processing thread\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
printf("Video processing started; press SPACE to pause/resume, 'q' to abort.\n");
|
|
||||||
|
|
||||||
// Enable non-blocking input
|
|
||||||
#ifndef _WIN32
|
|
||||||
set_nonblocking_input(true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Main thread loop to display progress and handle input
|
|
||||||
while (!proc_ctx.completed) {
|
|
||||||
// Check for key presses
|
|
||||||
int ch = -1;
|
|
||||||
|
|
||||||
// Check for key press
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (_kbhit()) {
|
|
||||||
ch = _getch();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
ch = getchar();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ch == ' ' || ch == '\n') {
|
|
||||||
// Toggle pause state
|
|
||||||
proc_ctx.pause = !proc_ctx.pause;
|
|
||||||
if (proc_ctx.pause) {
|
|
||||||
printf("\nProcessing paused. Press SPACE to resume, 'q' to abort.\n");
|
|
||||||
} else {
|
|
||||||
printf("Resuming processing...\n");
|
|
||||||
}
|
|
||||||
} else if (ch == 'q' || ch == 'Q') {
|
|
||||||
// Abort processing
|
|
||||||
printf("\nAborting processing...\n");
|
|
||||||
proc_ctx.abort = true;
|
|
||||||
newline_required = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display progress
|
|
||||||
if (!arguments.noprogress && !proc_ctx.pause && proc_ctx.total_frames > 0) {
|
|
||||||
printf(
|
|
||||||
"\rProcessing frame %ld/%ld (%.2f%%); time elapsed: %lds",
|
|
||||||
proc_ctx.processed_frames,
|
|
||||||
proc_ctx.total_frames,
|
|
||||||
proc_ctx.total_frames > 0
|
|
||||||
? (double)proc_ctx.processed_frames * 100.0 / (double)proc_ctx.total_frames
|
|
||||||
: 0.0,
|
|
||||||
time(NULL) - proc_ctx.start_time
|
|
||||||
);
|
|
||||||
fflush(stdout);
|
|
||||||
newline_required = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sleep for 50ms
|
|
||||||
thrd_sleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 100000000}, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore terminal to blocking mode
|
|
||||||
#ifndef _WIN32
|
|
||||||
set_nonblocking_input(false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Join the processing thread to ensure it completes before exiting
|
|
||||||
int process_result;
|
|
||||||
thrd_join(processing_thread, &process_result);
|
|
||||||
|
|
||||||
// Print a newline if progress bar was displayed
|
|
||||||
if (newline_required) {
|
|
||||||
putchar('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print final message based on processing result
|
|
||||||
if (proc_ctx.abort) {
|
|
||||||
fprintf(stderr, "Video processing aborted\n");
|
|
||||||
return 2;
|
|
||||||
} else if (process_result != 0) {
|
|
||||||
fprintf(stderr, "Video processing failed\n");
|
|
||||||
return process_result;
|
|
||||||
} else {
|
|
||||||
printf("Video processing completed successfully\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate statistics
|
|
||||||
time_t time_elapsed = time(NULL) - proc_ctx.start_time;
|
|
||||||
float average_speed_fps =
|
|
||||||
(float)proc_ctx.processed_frames / (time_elapsed > 0 ? (float)time_elapsed : 1);
|
|
||||||
|
|
||||||
// Print processing summary
|
|
||||||
printf("====== Video2X %s summary ======\n", arguments.benchmark ? "Benchmark" : "Processing");
|
|
||||||
printf("Video file processed: %s\n", arguments.in_fname);
|
|
||||||
printf("Total frames processed: %ld\n", proc_ctx.processed_frames);
|
|
||||||
printf("Total time taken: %lds\n", time_elapsed);
|
|
||||||
printf("Average processing speed: %.2f FPS\n", average_speed_fps);
|
|
||||||
|
|
||||||
// Print additional information if not in benchmark mode
|
|
||||||
if (!arguments.benchmark) {
|
|
||||||
printf("Output written to: %s\n", arguments.out_fname);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
704
src/video2x.cpp
Normal file
704
src/video2x.cpp
Normal file
@ -0,0 +1,704 @@
|
|||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <csignal>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <conio.h>
|
||||||
|
#else
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavutil/hwcontext.h>
|
||||||
|
#include <libavutil/pixdesc.h>
|
||||||
|
#include <libavutil/pixfmt.h>
|
||||||
|
|
||||||
|
#include <libvideo2x/libvideo2x.h>
|
||||||
|
#include <libvideo2x/version.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define BOOST_PROGRAM_OPTIONS_WCHAR_T
|
||||||
|
#endif
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
// Indicate if a newline needs to be printed before the next output
|
||||||
|
std::atomic<bool> newline_required = false;
|
||||||
|
|
||||||
|
// Structure to hold parsed arguments
|
||||||
|
struct Arguments {
|
||||||
|
// General options
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::wstring loglevel = L"info";
|
||||||
|
std::wstring filter_type;
|
||||||
|
std::wstring hwaccel = L"none";
|
||||||
|
#else
|
||||||
|
std::string loglevel = "info";
|
||||||
|
std::string filter_type;
|
||||||
|
std::string hwaccel = "none";
|
||||||
|
#endif
|
||||||
|
bool noprogress = false;
|
||||||
|
std::filesystem::path in_fname;
|
||||||
|
std::filesystem::path out_fname;
|
||||||
|
bool nocopystreams = false;
|
||||||
|
bool benchmark = false;
|
||||||
|
|
||||||
|
// Encoder options
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::wstring codec = L"libx264";
|
||||||
|
std::wstring pix_fmt;
|
||||||
|
std::wstring preset = L"slow";
|
||||||
|
#else
|
||||||
|
std::string codec = "libx264";
|
||||||
|
std::string pix_fmt;
|
||||||
|
std::string preset = "slow";
|
||||||
|
#endif
|
||||||
|
int64_t bitrate = 0;
|
||||||
|
float crf = 20.0f;
|
||||||
|
|
||||||
|
// libplacebo options
|
||||||
|
std::filesystem::path shader_path;
|
||||||
|
int out_width = 0;
|
||||||
|
int out_height = 0;
|
||||||
|
|
||||||
|
// RealESRGAN options
|
||||||
|
int gpuid = 0;
|
||||||
|
std::filesystem::path model_path;
|
||||||
|
int scaling_factor = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set UNIX terminal input to non-blocking mode
|
||||||
|
#ifndef _WIN32
|
||||||
|
void set_nonblocking_input(bool enable) {
|
||||||
|
static struct termios oldt, newt;
|
||||||
|
if (enable) {
|
||||||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||||||
|
newt = oldt;
|
||||||
|
newt.c_lflag &= static_cast<unsigned int>(~(ICANON | ECHO));
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||||
|
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
|
||||||
|
} else {
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||||
|
fcntl(STDIN_FILENO, F_SETFL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Convert a wide string to UTF-8 string
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string wstring_to_utf8(const std::wstring &wstr) {
|
||||||
|
if (wstr.empty()) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
int size_needed = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr
|
||||||
|
);
|
||||||
|
std::string converted_str(size_needed, 0);
|
||||||
|
WideCharToMultiByte(
|
||||||
|
CP_UTF8,
|
||||||
|
0,
|
||||||
|
wstr.data(),
|
||||||
|
static_cast<int>(wstr.size()),
|
||||||
|
&converted_str[0],
|
||||||
|
size_needed,
|
||||||
|
nullptr,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
return converted_str;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::string wstring_to_utf8(const std::string &str) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Newline-safe log callback for FFmpeg
|
||||||
|
void newline_safe_ffmpeg_log_callback(void *ptr, int level, const char *fmt, va_list vl) {
|
||||||
|
if (level <= av_log_get_level() && newline_required) {
|
||||||
|
putchar('\n');
|
||||||
|
newline_required = false;
|
||||||
|
}
|
||||||
|
av_log_default_callback(ptr, level, fmt, vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid_realesrgan_model(const std::string &model) {
|
||||||
|
static const std::unordered_set<std::string> valid_realesrgan_models = {
|
||||||
|
"realesrgan-plus", "realesrgan-plus-anime", "realesr-animevideov3"
|
||||||
|
};
|
||||||
|
return valid_realesrgan_models.count(model) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Libvideo2xLogLevel parse_log_level(const std::string &level_name) {
|
||||||
|
if (level_name == "trace") {
|
||||||
|
return LIBVIDEO2X_LOG_LEVEL_TRACE;
|
||||||
|
} else if (level_name == "debug") {
|
||||||
|
return LIBVIDEO2X_LOG_LEVEL_DEBUG;
|
||||||
|
} else if (level_name == "info") {
|
||||||
|
return LIBVIDEO2X_LOG_LEVEL_INFO;
|
||||||
|
} else if (level_name == "warning" || level_name == "warn") {
|
||||||
|
return LIBVIDEO2X_LOG_LEVEL_WARNING;
|
||||||
|
} else if (level_name == "error") {
|
||||||
|
return LIBVIDEO2X_LOG_LEVEL_ERROR;
|
||||||
|
} else if (level_name == "critical") {
|
||||||
|
return LIBVIDEO2X_LOG_LEVEL_CRITICAL;
|
||||||
|
} else if (level_name == "off" || level_name == "none") {
|
||||||
|
return LIBVIDEO2X_LOG_LEVEL_OFF;
|
||||||
|
} else {
|
||||||
|
spdlog::warn("Invalid log level specified. Defaulting to 'info'.");
|
||||||
|
return LIBVIDEO2X_LOG_LEVEL_INFO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutex for synchronizing access to VideoProcessingContext
|
||||||
|
std::mutex proc_ctx_mutex;
|
||||||
|
|
||||||
|
// Wrapper function for video processing thread
|
||||||
|
void process_video_thread(
|
||||||
|
Arguments *arguments,
|
||||||
|
AVHWDeviceType hw_device_type,
|
||||||
|
FilterConfig *filter_config,
|
||||||
|
EncoderConfig *encoder_config,
|
||||||
|
VideoProcessingContext *proc_ctx
|
||||||
|
) {
|
||||||
|
enum Libvideo2xLogLevel log_level = parse_log_level(wstring_to_utf8(arguments->loglevel));
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
const wchar_t *in_fname = arguments->in_fname.c_str();
|
||||||
|
const wchar_t *out_fname = arguments->out_fname.c_str();
|
||||||
|
#else
|
||||||
|
const char *in_fname = arguments->in_fname.c_str();
|
||||||
|
const char *out_fname = arguments->out_fname.c_str();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int result = process_video(
|
||||||
|
in_fname,
|
||||||
|
out_fname,
|
||||||
|
log_level,
|
||||||
|
arguments->benchmark,
|
||||||
|
hw_device_type,
|
||||||
|
filter_config,
|
||||||
|
encoder_config,
|
||||||
|
proc_ctx
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
|
||||||
|
proc_ctx->completed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
spdlog::error("Video processing failed with error code: {}", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wmain(int argc, wchar_t *argv[]) {
|
||||||
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
#else
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
#endif
|
||||||
|
// Initialize arguments structure
|
||||||
|
Arguments arguments;
|
||||||
|
|
||||||
|
// Parse command line arguments using Boost.Program_options
|
||||||
|
try {
|
||||||
|
po::options_description desc("Allowed options");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
desc.add_options()
|
||||||
|
("help", "Display this help page")
|
||||||
|
("version,v", "Print program version")
|
||||||
|
("loglevel", po::wvalue<std::wstring>(&arguments.loglevel), "Set log level (trace, debug, info, warn, error, critical, none)")
|
||||||
|
("noprogress", po::bool_switch(&arguments.noprogress), "Do not display the progress bar")
|
||||||
|
|
||||||
|
// General Processing Options
|
||||||
|
("input,i", po::wvalue<std::wstring>(), "Input video file path")
|
||||||
|
("output,o", po::wvalue<std::wstring>(), "Output video file path")
|
||||||
|
("filter,f", po::wvalue<std::wstring>(&arguments.filter_type), "Filter to use: 'libplacebo' or 'realesrgan'")
|
||||||
|
("hwaccel,a", po::wvalue<std::wstring>(&arguments.hwaccel), "Hardware acceleration method (default: none)")
|
||||||
|
("nocopystreams", po::bool_switch(&arguments.nocopystreams), "Do not copy audio and subtitle streams")
|
||||||
|
("benchmark", po::bool_switch(&arguments.benchmark), "Discard processed frames and calculate average FPS")
|
||||||
|
|
||||||
|
// Encoder options
|
||||||
|
("codec,c", po::wvalue<std::wstring>(&arguments.codec), "Output codec (default: libx264)")
|
||||||
|
("preset,p", po::wvalue<std::wstring>(&arguments.preset), "Encoder preset (default: slow)")
|
||||||
|
("pixfmt,x", po::wvalue<std::wstring>(&arguments.pix_fmt), "Output pixel format (default: auto)")
|
||||||
|
("bitrate,b", po::value<int64_t>(&arguments.bitrate)->default_value(0), "Bitrate in bits per second (default: 0 (VBR))")
|
||||||
|
("crf,q", po::value<float>(&arguments.crf)->default_value(20.0f), "Constant Rate Factor (default: 20.0)")
|
||||||
|
|
||||||
|
// libplacebo options
|
||||||
|
("shader,s", po::wvalue<std::wstring>(), "Name or path of the GLSL shader file to use")
|
||||||
|
("width,w", po::value<int>(&arguments.out_width), "Output width")
|
||||||
|
("height,h", po::value<int>(&arguments.out_height), "Output height")
|
||||||
|
|
||||||
|
// RealESRGAN options
|
||||||
|
("gpuid,g", po::value<int>(&arguments.gpuid)->default_value(0), "Vulkan GPU ID (default: 0)")
|
||||||
|
("model,m", po::wvalue<std::wstring>(), "Name of the model to use")
|
||||||
|
("scale,r", po::value<int>(&arguments.scaling_factor), "Scaling factor (2, 3, or 4)")
|
||||||
|
;
|
||||||
|
#else
|
||||||
|
desc.add_options()
|
||||||
|
("help", "Display this help page")
|
||||||
|
("version,v", "Print program version")
|
||||||
|
("loglevel", po::value<std::string>(&arguments.loglevel)->default_value("info"), "Set log level (trace, debug, info, warn, error, critical, none)")
|
||||||
|
("noprogress", po::bool_switch(&arguments.noprogress), "Do not display the progress bar")
|
||||||
|
|
||||||
|
// General Processing Options
|
||||||
|
("input,i", po::value<std::string>(), "Input video file path")
|
||||||
|
("output,o", po::value<std::string>(), "Output video file path")
|
||||||
|
("filter,f", po::value<std::string>(&arguments.filter_type), "Filter to use: 'libplacebo' or 'realesrgan'")
|
||||||
|
("hwaccel,a", po::value<std::string>(&arguments.hwaccel)->default_value("none"), "Hardware acceleration method (default: none)")
|
||||||
|
("nocopystreams", po::bool_switch(&arguments.nocopystreams), "Do not copy audio and subtitle streams")
|
||||||
|
("benchmark", po::bool_switch(&arguments.benchmark), "Discard processed frames and calculate average FPS")
|
||||||
|
|
||||||
|
// Encoder options
|
||||||
|
("codec,c", po::value<std::string>(&arguments.codec)->default_value("libx264"), "Output codec (default: libx264)")
|
||||||
|
("preset,p", po::value<std::string>(&arguments.preset)->default_value("slow"), "Encoder preset (default: slow)")
|
||||||
|
("pixfmt,x", po::value<std::string>(&arguments.pix_fmt), "Output pixel format (default: auto)")
|
||||||
|
("bitrate,b", po::value<int64_t>(&arguments.bitrate)->default_value(0), "Bitrate in bits per second (default: 0 (VBR))")
|
||||||
|
("crf,q", po::value<float>(&arguments.crf)->default_value(20.0f), "Constant Rate Factor (default: 20.0)")
|
||||||
|
|
||||||
|
// libplacebo options
|
||||||
|
("shader,s", po::value<std::string>(), "Name or path of the GLSL shader file to use (built-in: 'anime4k-a', 'anime4k-b', 'anime4k-c', 'anime4k-a+a', 'anime4k-b+b', 'anime4k-c+a')")
|
||||||
|
("width,w", po::value<int>(&arguments.out_width), "Output width")
|
||||||
|
("height,h", po::value<int>(&arguments.out_height), "Output height")
|
||||||
|
|
||||||
|
// RealESRGAN options
|
||||||
|
("gpuid,g", po::value<int>(&arguments.gpuid)->default_value(0), "Vulkan GPU ID (default: 0)")
|
||||||
|
("model,m", po::value<std::string>(), "Name of the model to use")
|
||||||
|
("scale,r", po::value<int>(&arguments.scaling_factor), "Scaling factor (2, 3, or 4)")
|
||||||
|
;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Positional arguments
|
||||||
|
po::positional_options_description p;
|
||||||
|
p.add("input", 1).add("output", 1).add("filter", 1);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
po::variables_map vm;
|
||||||
|
po::store(po::wcommand_line_parser(argc, argv).options(desc).positional(p).run(), vm);
|
||||||
|
#else
|
||||||
|
po::variables_map vm;
|
||||||
|
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
|
||||||
|
#endif
|
||||||
|
po::notify(vm);
|
||||||
|
|
||||||
|
// Set default values for optional arguments
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!vm.count("loglevel")) {
|
||||||
|
arguments.loglevel = L"info";
|
||||||
|
}
|
||||||
|
if (!vm.count("hwaccel")) {
|
||||||
|
arguments.hwaccel = L"none";
|
||||||
|
}
|
||||||
|
if (!vm.count("codec")) {
|
||||||
|
arguments.codec = L"libx264";
|
||||||
|
}
|
||||||
|
if (!vm.count("preset")) {
|
||||||
|
arguments.preset = L"slow";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (vm.count("help")) {
|
||||||
|
std::cout << desc << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("version")) {
|
||||||
|
std::cout << "Video2X version " << LIBVIDEO2X_VERSION_STRING << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign positional arguments
|
||||||
|
if (vm.count("input")) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
arguments.in_fname = std::filesystem::path(vm["input"].as<std::wstring>());
|
||||||
|
#else
|
||||||
|
arguments.in_fname = std::filesystem::path(vm["input"].as<std::string>());
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
spdlog::error("Error: Input file path is required.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("output")) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
arguments.out_fname = std::filesystem::path(vm["output"].as<std::wstring>());
|
||||||
|
#else
|
||||||
|
arguments.out_fname = std::filesystem::path(vm["output"].as<std::string>());
|
||||||
|
#endif
|
||||||
|
} else if (!arguments.benchmark) {
|
||||||
|
spdlog::error("Error: Output file path is required.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vm.count("filter")) {
|
||||||
|
spdlog::error("Error: Filter type is required (libplacebo or realesrgan).");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("shader")) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
arguments.shader_path = std::filesystem::path(vm["shader"].as<std::wstring>());
|
||||||
|
#else
|
||||||
|
arguments.shader_path = std::filesystem::path(vm["shader"].as<std::string>());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("model")) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
arguments.model_path = std::filesystem::path(vm["model"].as<std::wstring>());
|
||||||
|
#else
|
||||||
|
arguments.model_path = vm["model"].as<std::string>();
|
||||||
|
#endif
|
||||||
|
if (!is_valid_realesrgan_model(vm["model"].as<std::string>())) {
|
||||||
|
spdlog::error(
|
||||||
|
"Error: Invalid model specified. Must be 'realesrgan-plus', "
|
||||||
|
"'realesrgan-plus-anime', or 'realesr-animevideov3'."
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const po::error &e) {
|
||||||
|
spdlog::error("Error parsing options: {}", e.what());
|
||||||
|
return 1;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
spdlog::error("Exception caught: {}", e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validations
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (arguments.filter_type == L"libplacebo") {
|
||||||
|
#else
|
||||||
|
if (arguments.filter_type == "libplacebo") {
|
||||||
|
#endif
|
||||||
|
if (arguments.shader_path.empty() || arguments.out_width == 0 ||
|
||||||
|
arguments.out_height == 0) {
|
||||||
|
spdlog::error(
|
||||||
|
"Error: For libplacebo, shader name/path (-s), width (-w), "
|
||||||
|
"and height (-h) are required."
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
} else if (arguments.filter_type == L"realesrgan") {
|
||||||
|
#else
|
||||||
|
} else if (arguments.filter_type == "realesrgan") {
|
||||||
|
#endif
|
||||||
|
if (arguments.scaling_factor == 0 || arguments.model_path.empty()) {
|
||||||
|
spdlog::error("Error: For realesrgan, scaling factor (-r) and model (-m) are required."
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (arguments.scaling_factor != 2 && arguments.scaling_factor != 3 &&
|
||||||
|
arguments.scaling_factor != 4) {
|
||||||
|
spdlog::error("Error: Scaling factor must be 2, 3, or 4.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spdlog::error("Error: Invalid filter type specified. Must be 'libplacebo' or 'realesrgan'."
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate bitrate
|
||||||
|
if (arguments.bitrate < 0) {
|
||||||
|
spdlog::error("Error: Invalid bitrate specified.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate CRF
|
||||||
|
if (arguments.crf < 0.0f || arguments.crf > 51.0f) {
|
||||||
|
spdlog::error("Error: CRF must be between 0 and 51.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse codec to AVCodec
|
||||||
|
const AVCodec *codec = avcodec_find_encoder_by_name(wstring_to_utf8(arguments.codec).c_str());
|
||||||
|
if (!codec) {
|
||||||
|
spdlog::error("Error: Codec '{}' not found.", wstring_to_utf8(arguments.codec));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse pixel format to AVPixelFormat
|
||||||
|
enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
|
||||||
|
if (!arguments.pix_fmt.empty()) {
|
||||||
|
pix_fmt = av_get_pix_fmt(wstring_to_utf8(arguments.pix_fmt).c_str());
|
||||||
|
if (pix_fmt == AV_PIX_FMT_NONE) {
|
||||||
|
spdlog::error("Error: Invalid pixel format '{}'.", wstring_to_utf8(arguments.pix_fmt));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set spdlog log level
|
||||||
|
auto log_level = parse_log_level(wstring_to_utf8(arguments.loglevel));
|
||||||
|
switch (log_level) {
|
||||||
|
case LIBVIDEO2X_LOG_LEVEL_TRACE:
|
||||||
|
spdlog::set_level(spdlog::level::trace);
|
||||||
|
break;
|
||||||
|
case LIBVIDEO2X_LOG_LEVEL_DEBUG:
|
||||||
|
spdlog::set_level(spdlog::level::debug);
|
||||||
|
break;
|
||||||
|
case LIBVIDEO2X_LOG_LEVEL_INFO:
|
||||||
|
spdlog::set_level(spdlog::level::info);
|
||||||
|
break;
|
||||||
|
case LIBVIDEO2X_LOG_LEVEL_WARNING:
|
||||||
|
spdlog::set_level(spdlog::level::warn);
|
||||||
|
break;
|
||||||
|
case LIBVIDEO2X_LOG_LEVEL_ERROR:
|
||||||
|
spdlog::set_level(spdlog::level::err);
|
||||||
|
break;
|
||||||
|
case LIBVIDEO2X_LOG_LEVEL_CRITICAL:
|
||||||
|
spdlog::set_level(spdlog::level::critical);
|
||||||
|
break;
|
||||||
|
case LIBVIDEO2X_LOG_LEVEL_OFF:
|
||||||
|
spdlog::set_level(spdlog::level::off);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
spdlog::set_level(spdlog::level::info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup filter configurations based on the parsed arguments
|
||||||
|
FilterConfig filter_config;
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (arguments.filter_type == L"libplacebo") {
|
||||||
|
#else
|
||||||
|
if (arguments.filter_type == "libplacebo") {
|
||||||
|
#endif
|
||||||
|
filter_config.filter_type = FILTER_LIBPLACEBO;
|
||||||
|
filter_config.config.libplacebo.out_width = arguments.out_width;
|
||||||
|
filter_config.config.libplacebo.out_height = arguments.out_height;
|
||||||
|
#ifdef _WIN32
|
||||||
|
filter_config.config.libplacebo.shader_path = arguments.shader_path.c_str();
|
||||||
|
#else
|
||||||
|
filter_config.config.libplacebo.shader_path = arguments.shader_path.c_str();
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
} else if (arguments.filter_type == L"realesrgan") {
|
||||||
|
#else
|
||||||
|
} else if (arguments.filter_type == "realesrgan") {
|
||||||
|
#endif
|
||||||
|
filter_config.filter_type = FILTER_REALESRGAN;
|
||||||
|
filter_config.config.realesrgan.gpuid = arguments.gpuid;
|
||||||
|
filter_config.config.realesrgan.tta_mode = false;
|
||||||
|
filter_config.config.realesrgan.scaling_factor = arguments.scaling_factor;
|
||||||
|
#ifdef _WIN32
|
||||||
|
filter_config.config.realesrgan.model_path = arguments.model_path.c_str();
|
||||||
|
#else
|
||||||
|
filter_config.config.realesrgan.model_path = arguments.model_path.c_str();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert arguments to UTF-8 encoded strings
|
||||||
|
std::string preset_str = wstring_to_utf8(arguments.preset);
|
||||||
|
|
||||||
|
// Setup encoder configuration
|
||||||
|
EncoderConfig encoder_config;
|
||||||
|
encoder_config.out_width = 0;
|
||||||
|
encoder_config.out_height = 0;
|
||||||
|
encoder_config.copy_streams = !arguments.nocopystreams;
|
||||||
|
encoder_config.codec = codec->id;
|
||||||
|
encoder_config.pix_fmt = pix_fmt;
|
||||||
|
encoder_config.preset = preset_str.c_str();
|
||||||
|
encoder_config.bit_rate = arguments.bitrate;
|
||||||
|
encoder_config.crf = arguments.crf;
|
||||||
|
|
||||||
|
// Parse hardware acceleration method
|
||||||
|
enum AVHWDeviceType hw_device_type = AV_HWDEVICE_TYPE_NONE;
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (arguments.hwaccel != L"none") {
|
||||||
|
#else
|
||||||
|
if (arguments.hwaccel != "none") {
|
||||||
|
#endif
|
||||||
|
hw_device_type = av_hwdevice_find_type_by_name(wstring_to_utf8(arguments.hwaccel).c_str());
|
||||||
|
if (hw_device_type == AV_HWDEVICE_TYPE_NONE) {
|
||||||
|
spdlog::error(
|
||||||
|
"Error: Invalid hardware device type '{}'.", wstring_to_utf8(arguments.hwaccel)
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup struct to store processing context
|
||||||
|
VideoProcessingContext proc_ctx;
|
||||||
|
proc_ctx.processed_frames = 0;
|
||||||
|
proc_ctx.total_frames = 0;
|
||||||
|
proc_ctx.pause = false;
|
||||||
|
proc_ctx.abort = false;
|
||||||
|
proc_ctx.completed = false;
|
||||||
|
|
||||||
|
// Register a newline-safe log callback for FFmpeg
|
||||||
|
av_log_set_callback(newline_safe_ffmpeg_log_callback);
|
||||||
|
|
||||||
|
// Create a thread for video processing
|
||||||
|
std::thread processing_thread(
|
||||||
|
process_video_thread, &arguments, hw_device_type, &filter_config, &encoder_config, &proc_ctx
|
||||||
|
);
|
||||||
|
spdlog::info("Press SPACE to pause/resume, 'q' to abort.");
|
||||||
|
|
||||||
|
// Setup variables to track processing time
|
||||||
|
auto start_time = std::chrono::steady_clock::now();
|
||||||
|
auto paused_start = std::chrono::steady_clock::time_point();
|
||||||
|
std::chrono::seconds total_paused_duration(0);
|
||||||
|
long long time_elapsed = 0;
|
||||||
|
|
||||||
|
// Enable non-blocking input
|
||||||
|
#ifndef _WIN32
|
||||||
|
set_nonblocking_input(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Main thread loop to display progress and handle input
|
||||||
|
while (true) {
|
||||||
|
bool completed;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
|
||||||
|
completed = proc_ctx.completed;
|
||||||
|
}
|
||||||
|
if (completed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for key presses
|
||||||
|
int ch = -1;
|
||||||
|
|
||||||
|
// Check for key press
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (_kbhit()) {
|
||||||
|
ch = _getch();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ch = getchar();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ch == ' ' || ch == '\n') {
|
||||||
|
// Toggle pause state
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
|
||||||
|
proc_ctx.pause = !proc_ctx.pause;
|
||||||
|
if (proc_ctx.pause) {
|
||||||
|
putchar('\n');
|
||||||
|
spdlog::info("Processing paused. Press SPACE to resume, 'q' to abort.");
|
||||||
|
paused_start = std::chrono::steady_clock::now();
|
||||||
|
} else {
|
||||||
|
spdlog::info("Resuming processing...");
|
||||||
|
total_paused_duration += std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
std::chrono::steady_clock::now() - paused_start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ch == 'q' || ch == 'Q') {
|
||||||
|
// Abort processing
|
||||||
|
putchar('\n');
|
||||||
|
spdlog::info("Aborting processing...");
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
|
||||||
|
proc_ctx.abort = true;
|
||||||
|
newline_required = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display progress
|
||||||
|
if (!arguments.noprogress) {
|
||||||
|
int64_t processed_frames, total_frames;
|
||||||
|
bool pause;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
|
||||||
|
processed_frames = proc_ctx.processed_frames;
|
||||||
|
total_frames = proc_ctx.total_frames;
|
||||||
|
pause = proc_ctx.pause;
|
||||||
|
}
|
||||||
|
if (!pause && total_frames > 0) {
|
||||||
|
double percentage = total_frames > 0 ? static_cast<double>(processed_frames) *
|
||||||
|
100.0 / static_cast<double>(total_frames)
|
||||||
|
: 0.0;
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
time_elapsed = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
now - start_time - total_paused_duration
|
||||||
|
)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
std::cout << "\rProcessing frame " << processed_frames << "/" << total_frames
|
||||||
|
<< " (" << percentage << "%); time elapsed: " << time_elapsed << "s";
|
||||||
|
std::cout.flush();
|
||||||
|
newline_required = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep for 100ms
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore terminal to blocking mode
|
||||||
|
#ifndef _WIN32
|
||||||
|
set_nonblocking_input(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Join the processing thread to ensure it completes before exiting
|
||||||
|
processing_thread.join();
|
||||||
|
|
||||||
|
// Print a newline if progress bar was displayed
|
||||||
|
if (newline_required) {
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print final message based on processing result
|
||||||
|
bool aborted, completed;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
|
||||||
|
aborted = proc_ctx.abort;
|
||||||
|
completed = proc_ctx.completed;
|
||||||
|
}
|
||||||
|
if (aborted) {
|
||||||
|
spdlog::warn("Video processing aborted");
|
||||||
|
return 2;
|
||||||
|
} else if (!completed) {
|
||||||
|
spdlog::error("Video processing failed");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
spdlog::info("Video processed successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate statistics
|
||||||
|
int64_t processed_frames;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
|
||||||
|
processed_frames = proc_ctx.processed_frames;
|
||||||
|
}
|
||||||
|
float average_speed_fps = static_cast<float>(processed_frames) /
|
||||||
|
(time_elapsed > 0 ? static_cast<float>(time_elapsed) : 1);
|
||||||
|
|
||||||
|
// Print processing summary
|
||||||
|
printf("====== Video2X %s summary ======\n", arguments.benchmark ? "Benchmark" : "Processing");
|
||||||
|
printf("Video file processed: %s\n", arguments.in_fname.u8string().c_str());
|
||||||
|
printf("Total frames processed: %ld\n", proc_ctx.processed_frames);
|
||||||
|
printf("Total time taken: %llds\n", time_elapsed);
|
||||||
|
printf("Average processing speed: %.2f FPS\n", average_speed_fps);
|
||||||
|
|
||||||
|
// Print additional information if not in benchmark mode
|
||||||
|
if (!arguments.benchmark) {
|
||||||
|
printf("Output written to: %s\n", arguments.out_fname.u8string().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
1
third_party/boost
vendored
Submodule
1
third_party/boost
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 65c1319bb92fe7a9a4abd588eff5818d9c2bccf9
|
Loading…
Reference in New Issue
Block a user