mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 09:09:03 -04:00
Split up notcurses/notcurses-core (#1297)
Extract `libnotcurses-core` from `libnotcurses`. The former contains everything except multimedia code. The latter contains multimedia stuff (a wrapper around FFmpeg or OIIO). If built with `-DUSE_MULTIMEDIA=none`, there will not be any `libnotcurses.so` generated. `libnotcurses.so` uses library constructors/destructors to insert its implementation into the `ncvisual` stack at runtime. Users linking `-lnotcurses` will get the full implementation; users linking `-lnotcurses-core` only will get the stack less multimedia code. The upshot of this is that someone can compile/install only `libnotcurses-core`, and a program linked against it will work just fine. This eliminates the need to install the full (large) dependency stack of the multimedia code unless necessary. This will hopefully be useful for e.g. installers etc. Closes #339.
This commit is contained in:
parent
c62a3f7464
commit
9112185657
295
CMakeLists.txt
295
CMakeLists.txt
@ -60,6 +60,12 @@ if("${USE_COVERAGE}")
|
|||||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage -fprofile-instr-generate -fcoverage-mapping")
|
string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage -fprofile-instr-generate -fcoverage-mapping")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
|
set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||||
|
else()
|
||||||
|
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
|
||||||
|
endif()
|
||||||
|
|
||||||
# global compiler flags
|
# global compiler flags
|
||||||
add_compile_definitions(FORTIFY_SOURCE=2)
|
add_compile_definitions(FORTIFY_SOURCE=2)
|
||||||
add_compile_options(-Wall -Wextra -W -Wshadow -Wformat -fexceptions)
|
add_compile_options(-Wall -Wextra -W -Wshadow -Wformat -fexceptions)
|
||||||
@ -114,11 +120,97 @@ endif()
|
|||||||
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND qrcodegen)
|
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND qrcodegen)
|
||||||
endif()
|
endif()
|
||||||
find_library(LIBRT rt)
|
find_library(LIBRT rt)
|
||||||
|
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
|
||||||
file(GLOB COMPATSRC CONFIGURE_DEPENDS src/compat/compat.c)
|
file(GLOB COMPATSRC CONFIGURE_DEPENDS src/compat/compat.c)
|
||||||
|
|
||||||
# libnotcurses (core shared library, core static library)
|
############################################################################
|
||||||
file(GLOB NCSRCS CONFIGURE_DEPENDS src/lib/*.c src/lib/*.cpp)
|
# libnotcurses-core (core shared library, core static library)
|
||||||
|
file(GLOB NCCORESRCS CONFIGURE_DEPENDS src/lib/*.c src/lib/*.cpp)
|
||||||
|
add_library(notcurses-core SHARED ${NCCORESRCS} ${COMPATSRC})
|
||||||
|
if(${USE_STATIC})
|
||||||
|
add_library(notcurses-core-static STATIC ${NCCORESRCS})
|
||||||
|
else()
|
||||||
|
add_library(notcurses-core-static STATIC EXCLUDE_FROM_ALL ${NCCORESRCS})
|
||||||
|
endif()
|
||||||
|
set_target_properties(
|
||||||
|
notcurses-core-static PROPERTIES
|
||||||
|
OUTPUT_NAME notcurses-core
|
||||||
|
)
|
||||||
|
set_target_properties(notcurses-core PROPERTIES
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||||
|
)
|
||||||
|
set_target_properties(notcurses-core-static PROPERTIES
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
)
|
||||||
|
target_include_directories(notcurses-core
|
||||||
|
PRIVATE
|
||||||
|
include
|
||||||
|
src
|
||||||
|
"${PROJECT_BINARY_DIR}/include"
|
||||||
|
"${TERMINFO_INCLUDE_DIRS}"
|
||||||
|
"${READLINE_INCLUDE_DIRS}"
|
||||||
|
)
|
||||||
|
target_include_directories(notcurses-core-static
|
||||||
|
PRIVATE
|
||||||
|
include
|
||||||
|
src
|
||||||
|
"${PROJECT_BINARY_DIR}/include"
|
||||||
|
"${TERMINFO_STATIC_INCLUDE_DIRS}"
|
||||||
|
"${READLINE_STATIC_INCLUDE_DIRS}"
|
||||||
|
)
|
||||||
|
target_link_libraries(notcurses-core
|
||||||
|
PRIVATE
|
||||||
|
"${TERMINFO_LIBRARIES}"
|
||||||
|
"${READLINE_LIBRARIES}"
|
||||||
|
"${LIBRT}"
|
||||||
|
unistring
|
||||||
|
PUBLIC
|
||||||
|
Threads::Threads
|
||||||
|
)
|
||||||
|
target_link_libraries(notcurses-core-static
|
||||||
|
PRIVATE
|
||||||
|
"${TERMINFO_STATIC_LIBRARIES}"
|
||||||
|
"${READLINE_STATIC_LIBRARIES}"
|
||||||
|
"${LIBRT}"
|
||||||
|
unistring
|
||||||
|
PUBLIC
|
||||||
|
Threads::Threads
|
||||||
|
)
|
||||||
|
target_link_directories(notcurses-core
|
||||||
|
PRIVATE
|
||||||
|
"${TERMINFO_LIBRARY_DIRS}"
|
||||||
|
"${READLINE_LIBRARY_DIRS}"
|
||||||
|
)
|
||||||
|
target_link_directories(notcurses-core-static
|
||||||
|
PRIVATE
|
||||||
|
"${TERMINFO_STATIC_LIBRARY_DIRS}"
|
||||||
|
"${READLINE_STATIC_LIBRARY_DIRS}"
|
||||||
|
)
|
||||||
|
# don't want these on freebsd/dragonfly/osx
|
||||||
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
|
target_compile_definitions(notcurses-core
|
||||||
|
PUBLIC
|
||||||
|
_XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
|
||||||
|
PRIVATE
|
||||||
|
_GNU_SOURCE _DEFAULT_SOURCE
|
||||||
|
)
|
||||||
|
target_compile_definitions(notcurses-core-static
|
||||||
|
PUBLIC
|
||||||
|
_XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
|
||||||
|
PRIVATE
|
||||||
|
_GNU_SOURCE _DEFAULT_SOURCE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(${USE_QRCODEGEN})
|
||||||
|
target_link_libraries(notcurses-core PRIVATE qrcodegen)
|
||||||
|
target_link_libraries(notcurses-core-static PRIVATE qrcodegen)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# libnotcurses (multimedia shared library+static library)
|
||||||
|
file(GLOB NCSRCS CONFIGURE_DEPENDS src/media/*.c src/media/*.cpp)
|
||||||
add_library(notcurses SHARED ${NCSRCS} ${COMPATSRC})
|
add_library(notcurses SHARED ${NCSRCS} ${COMPATSRC})
|
||||||
if(${USE_STATIC})
|
if(${USE_STATIC})
|
||||||
add_library(notcurses-static STATIC ${NCSRCS})
|
add_library(notcurses-static STATIC ${NCSRCS})
|
||||||
@ -129,76 +221,38 @@ set_target_properties(
|
|||||||
notcurses-static PROPERTIES
|
notcurses-static PROPERTIES
|
||||||
OUTPUT_NAME notcurses
|
OUTPUT_NAME notcurses
|
||||||
)
|
)
|
||||||
|
|
||||||
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
|
||||||
|
|
||||||
set_target_properties(notcurses PROPERTIES
|
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
|
||||||
)
|
|
||||||
set_target_properties(notcurses-static PROPERTIES
|
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
)
|
|
||||||
target_include_directories(notcurses
|
target_include_directories(notcurses
|
||||||
PRIVATE
|
PRIVATE
|
||||||
include
|
include
|
||||||
src
|
src
|
||||||
|
src/lib
|
||||||
"${PROJECT_BINARY_DIR}/include"
|
"${PROJECT_BINARY_DIR}/include"
|
||||||
"${TERMINFO_INCLUDE_DIRS}"
|
|
||||||
"${READLINE_INCLUDE_DIRS}"
|
|
||||||
)
|
)
|
||||||
target_include_directories(notcurses-static
|
target_include_directories(notcurses-static
|
||||||
PRIVATE
|
PRIVATE
|
||||||
include
|
include
|
||||||
src
|
src
|
||||||
|
src/lib
|
||||||
"${PROJECT_BINARY_DIR}/include"
|
"${PROJECT_BINARY_DIR}/include"
|
||||||
"${TERMINFO_STATIC_INCLUDE_DIRS}"
|
|
||||||
"${READLINE_STATIC_INCLUDE_DIRS}"
|
|
||||||
)
|
)
|
||||||
target_link_libraries(notcurses
|
target_link_libraries(notcurses
|
||||||
PRIVATE
|
|
||||||
"${TERMINFO_LIBRARIES}"
|
|
||||||
"${READLINE_LIBRARIES}"
|
|
||||||
"${LIBRT}"
|
|
||||||
unistring
|
|
||||||
PUBLIC
|
PUBLIC
|
||||||
Threads::Threads
|
notcurses-core
|
||||||
)
|
)
|
||||||
target_link_libraries(notcurses-static
|
target_link_libraries(notcurses-static
|
||||||
PRIVATE
|
|
||||||
"${TERMINFO_STATIC_LIBRARIES}"
|
|
||||||
"${READLINE_STATIC_LIBRARIES}"
|
|
||||||
"${LIBRT}"
|
|
||||||
unistring
|
|
||||||
PUBLIC
|
PUBLIC
|
||||||
Threads::Threads
|
notcurses-core-static
|
||||||
)
|
)
|
||||||
target_link_directories(notcurses
|
|
||||||
PRIVATE
|
|
||||||
"${TERMINFO_LIBRARY_DIRS}"
|
|
||||||
"${READLINE_LIBRARY_DIRS}"
|
|
||||||
)
|
|
||||||
target_link_directories(notcurses-static
|
|
||||||
PRIVATE
|
|
||||||
"${TERMINFO_STATIC_LIBRARY_DIRS}"
|
|
||||||
"${READLINE_STATIC_LIBRARY_DIRS}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(${USE_QRCODEGEN})
|
|
||||||
target_link_libraries(notcurses PRIVATE qrcodegen)
|
|
||||||
target_link_libraries(notcurses-static PRIVATE qrcodegen)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(${USE_FFMPEG})
|
if(${USE_FFMPEG})
|
||||||
target_include_directories(notcurses
|
target_include_directories(notcurses
|
||||||
PUBLIC
|
PRIVATE
|
||||||
"${AVCODEC_INCLUDE_DIRS}"
|
"${AVCODEC_INCLUDE_DIRS}"
|
||||||
"${AVFORMAT_INCLUDE_DIRS}"
|
"${AVFORMAT_INCLUDE_DIRS}"
|
||||||
"${AVUTIL_INCLUDE_DIRS}"
|
"${AVUTIL_INCLUDE_DIRS}"
|
||||||
"${SWSCALE_INCLUDE_DIRS}"
|
"${SWSCALE_INCLUDE_DIRS}"
|
||||||
)
|
)
|
||||||
target_include_directories(notcurses-static
|
target_include_directories(notcurses-static
|
||||||
PUBLIC
|
PRIVATE
|
||||||
"${AVCODEC_STATIC_INCLUDE_DIRS}"
|
"${AVCODEC_STATIC_INCLUDE_DIRS}"
|
||||||
"${AVFORMAT_STATIC_INCLUDE_DIRS}"
|
"${AVFORMAT_STATIC_INCLUDE_DIRS}"
|
||||||
"${AVUTIL_STATIC_INCLUDE_DIRS}"
|
"${AVUTIL_STATIC_INCLUDE_DIRS}"
|
||||||
@ -209,7 +263,6 @@ target_link_libraries(notcurses
|
|||||||
"${AVCODEC_LIBRARIES}"
|
"${AVCODEC_LIBRARIES}"
|
||||||
"${AVFORMAT_LIBRARIES}"
|
"${AVFORMAT_LIBRARIES}"
|
||||||
"${SWSCALE_LIBRARIES}"
|
"${SWSCALE_LIBRARIES}"
|
||||||
PUBLIC
|
|
||||||
"${AVUTIL_LIBRARIES}"
|
"${AVUTIL_LIBRARIES}"
|
||||||
)
|
)
|
||||||
target_link_libraries(notcurses-static
|
target_link_libraries(notcurses-static
|
||||||
@ -217,7 +270,6 @@ target_link_libraries(notcurses-static
|
|||||||
"${AVCODEC_STATIC_LIBRARIES}"
|
"${AVCODEC_STATIC_LIBRARIES}"
|
||||||
"${AVFORMAT_STATIC_LIBRARIES}"
|
"${AVFORMAT_STATIC_LIBRARIES}"
|
||||||
"${SWSCALE_STATIC_LIBRARIES}"
|
"${SWSCALE_STATIC_LIBRARIES}"
|
||||||
PUBLIC
|
|
||||||
"${AVUTIL_STATIC_LIBRARIES}"
|
"${AVUTIL_STATIC_LIBRARIES}"
|
||||||
)
|
)
|
||||||
target_link_directories(notcurses
|
target_link_directories(notcurses
|
||||||
@ -225,7 +277,6 @@ target_link_directories(notcurses
|
|||||||
"${AVCODEC_LIBRARY_DIRS}"
|
"${AVCODEC_LIBRARY_DIRS}"
|
||||||
"${AVFORMAT_LIBRARY_DIRS}"
|
"${AVFORMAT_LIBRARY_DIRS}"
|
||||||
"${SWSCALE_LIBRARY_DIRS}"
|
"${SWSCALE_LIBRARY_DIRS}"
|
||||||
PUBLIC
|
|
||||||
"${AVUTIL_LIBRARY_DIRS}"
|
"${AVUTIL_LIBRARY_DIRS}"
|
||||||
)
|
)
|
||||||
target_link_directories(notcurses-static
|
target_link_directories(notcurses-static
|
||||||
@ -233,7 +284,6 @@ target_link_directories(notcurses-static
|
|||||||
"${AVCODEC_STATIC_LIBRARY_DIRS}"
|
"${AVCODEC_STATIC_LIBRARY_DIRS}"
|
||||||
"${AVFORMAT_STATIC_LIBRARY_DIRS}"
|
"${AVFORMAT_STATIC_LIBRARY_DIRS}"
|
||||||
"${SWSCALE_STATIC_LIBRARY_DIRS}"
|
"${SWSCALE_STATIC_LIBRARY_DIRS}"
|
||||||
PUBLIC
|
|
||||||
"${AVUTIL_STATIC_LIBRARY_DIRS}"
|
"${AVUTIL_STATIC_LIBRARY_DIRS}"
|
||||||
)
|
)
|
||||||
elseif(${USE_OIIO})
|
elseif(${USE_OIIO})
|
||||||
@ -245,28 +295,8 @@ target_link_directories(notcurses PRIVATE ${OIIO_LIBRARY_DIRS})
|
|||||||
target_link_directories(notcurses-static PRIVATE ${OIIO_STATIC_LIBRARY_DIRS})
|
target_link_directories(notcurses-static PRIVATE ${OIIO_STATIC_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# don't want these on freebsd
|
############################################################################
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
# libnotcurses++ (C++ wrappers)
|
||||||
target_compile_definitions(notcurses
|
|
||||||
PUBLIC
|
|
||||||
_XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
|
|
||||||
PRIVATE
|
|
||||||
_GNU_SOURCE _DEFAULT_SOURCE
|
|
||||||
)
|
|
||||||
target_compile_definitions(notcurses-static
|
|
||||||
PUBLIC
|
|
||||||
_XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
|
|
||||||
PRIVATE
|
|
||||||
_GNU_SOURCE _DEFAULT_SOURCE
|
|
||||||
)
|
|
||||||
set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
|
||||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
|
||||||
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
|
|
||||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly")
|
|
||||||
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# libnotcurses++
|
|
||||||
set(NCPP_SOURCES
|
set(NCPP_SOURCES
|
||||||
src/libcpp/FDPlane.cc
|
src/libcpp/FDPlane.cc
|
||||||
src/libcpp/Menu.cc
|
src/libcpp/Menu.cc
|
||||||
@ -375,31 +405,6 @@ install(FILES ${NOTCURSES_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/notcu
|
|||||||
install(FILES ${NCPP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ncpp)
|
install(FILES ${NCPP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ncpp)
|
||||||
install(FILES ${NCPP_INTERNAL_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ncpp/internal)
|
install(FILES ${NCPP_INTERNAL_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ncpp/internal)
|
||||||
|
|
||||||
# notcurses-demo
|
|
||||||
file(GLOB DEMOSRCS CONFIGURE_DEPENDS src/demo/*.c)
|
|
||||||
add_executable(notcurses-demo ${DEMOSRCS} ${COMPATSRC})
|
|
||||||
target_compile_definitions(notcurses-demo
|
|
||||||
PRIVATE
|
|
||||||
_GNU_SOURCE
|
|
||||||
)
|
|
||||||
target_include_directories(notcurses-demo
|
|
||||||
PRIVATE
|
|
||||||
include
|
|
||||||
src
|
|
||||||
"${PROJECT_BINARY_DIR}/include"
|
|
||||||
PUBLIC
|
|
||||||
"${AVCODEC_INCLUDE_DIRS}"
|
|
||||||
"${AVFORMAT_INCLUDE_DIRS}"
|
|
||||||
"${AVUTIL_INCLUDE_DIRS}"
|
|
||||||
"${SWSCALE_INCLUDE_DIRS}"
|
|
||||||
)
|
|
||||||
target_link_libraries(notcurses-demo
|
|
||||||
PRIVATE
|
|
||||||
notcurses
|
|
||||||
unistring
|
|
||||||
${MATH_LIBRARIES}
|
|
||||||
)
|
|
||||||
|
|
||||||
# tiny proofs of concept, one binary per source file
|
# tiny proofs of concept, one binary per source file
|
||||||
if(USE_POC)
|
if(USE_POC)
|
||||||
file(GLOB POCSRCS CONFIGURE_DEPENDS src/poc/*.c src/poc/*.cpp)
|
file(GLOB POCSRCS CONFIGURE_DEPENDS src/poc/*.c src/poc/*.cpp)
|
||||||
@ -496,19 +501,35 @@ if(USE_DOXYGEN)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ncneofetch
|
############################################################################
|
||||||
file(GLOB FETCHSRCS CONFIGURE_DEPENDS src/fetch/*.c)
|
# notcurses-demo
|
||||||
add_executable(ncneofetch ${FETCHSRCS})
|
file(GLOB DEMOSRCS CONFIGURE_DEPENDS src/demo/*.c)
|
||||||
target_include_directories(ncneofetch
|
add_executable(notcurses-demo ${DEMOSRCS} ${COMPATSRC})
|
||||||
|
target_compile_definitions(notcurses-demo
|
||||||
|
PRIVATE
|
||||||
|
_GNU_SOURCE
|
||||||
|
)
|
||||||
|
target_include_directories(notcurses-demo
|
||||||
PRIVATE
|
PRIVATE
|
||||||
include
|
include
|
||||||
|
src
|
||||||
"${PROJECT_BINARY_DIR}/include"
|
"${PROJECT_BINARY_DIR}/include"
|
||||||
|
PUBLIC
|
||||||
|
"${AVCODEC_INCLUDE_DIRS}"
|
||||||
|
"${AVFORMAT_INCLUDE_DIRS}"
|
||||||
|
"${AVUTIL_INCLUDE_DIRS}"
|
||||||
|
"${SWSCALE_INCLUDE_DIRS}"
|
||||||
)
|
)
|
||||||
target_link_libraries(ncneofetch
|
target_link_libraries(notcurses-demo
|
||||||
PRIVATE
|
PRIVATE
|
||||||
notcurses
|
notcurses
|
||||||
|
unistring
|
||||||
|
${MATH_LIBRARIES}
|
||||||
|
PUBLIC
|
||||||
|
Threads::Threads
|
||||||
)
|
)
|
||||||
|
|
||||||
|
############################################################################
|
||||||
# notcurses-input
|
# notcurses-input
|
||||||
file(GLOB INPUTSRCS CONFIGURE_DEPENDS src/input/input.cpp)
|
file(GLOB INPUTSRCS CONFIGURE_DEPENDS src/input/input.cpp)
|
||||||
add_executable(notcurses-input ${INPUTSRCS})
|
add_executable(notcurses-input ${INPUTSRCS})
|
||||||
@ -522,19 +543,7 @@ target_link_libraries(notcurses-input
|
|||||||
notcurses++
|
notcurses++
|
||||||
)
|
)
|
||||||
|
|
||||||
# ncls
|
############################################################################
|
||||||
file(GLOB LSSRC CONFIGURE_DEPENDS src/ls/*.cpp)
|
|
||||||
add_executable(ncls ${LSSRC})
|
|
||||||
target_include_directories(ncls
|
|
||||||
PRIVATE
|
|
||||||
include
|
|
||||||
"${PROJECT_BINARY_DIR}/include"
|
|
||||||
)
|
|
||||||
target_link_libraries(ncls
|
|
||||||
PRIVATE
|
|
||||||
notcurses++
|
|
||||||
)
|
|
||||||
|
|
||||||
# notcurses-tetris
|
# notcurses-tetris
|
||||||
file(GLOB TETRISSRC CONFIGURE_DEPENDS src/tetris/*.cpp)
|
file(GLOB TETRISSRC CONFIGURE_DEPENDS src/tetris/*.cpp)
|
||||||
add_executable(notcurses-tetris ${TETRISSRC})
|
add_executable(notcurses-tetris ${TETRISSRC})
|
||||||
@ -548,8 +557,39 @@ target_link_libraries(notcurses-tetris
|
|||||||
notcurses++
|
notcurses++
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# all further binaries require multimedia support
|
||||||
|
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
|
||||||
|
############################################################################
|
||||||
|
# ncneofetch
|
||||||
|
file(GLOB FETCHSRCS CONFIGURE_DEPENDS src/fetch/*.c)
|
||||||
|
add_executable(ncneofetch ${FETCHSRCS})
|
||||||
|
target_include_directories(ncneofetch
|
||||||
|
PRIVATE
|
||||||
|
include
|
||||||
|
"${PROJECT_BINARY_DIR}/include"
|
||||||
|
)
|
||||||
|
target_link_libraries(ncneofetch
|
||||||
|
PRIVATE
|
||||||
|
notcurses
|
||||||
|
)
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# ncls
|
||||||
|
file(GLOB LSSRC CONFIGURE_DEPENDS src/ls/*.cpp)
|
||||||
|
add_executable(ncls ${LSSRC})
|
||||||
|
target_include_directories(ncls
|
||||||
|
PRIVATE
|
||||||
|
include
|
||||||
|
"${PROJECT_BINARY_DIR}/include"
|
||||||
|
)
|
||||||
|
target_link_libraries(ncls
|
||||||
|
PRIVATE
|
||||||
|
notcurses++
|
||||||
|
)
|
||||||
|
|
||||||
|
############################################################################
|
||||||
# notcurses-view
|
# notcurses-view
|
||||||
if(${USE_FFMPEG} OR ${USE_OIIO})
|
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
|
||||||
file(GLOB VIEWSRCS CONFIGURE_DEPENDS src/view/*.cpp)
|
file(GLOB VIEWSRCS CONFIGURE_DEPENDS src/view/*.cpp)
|
||||||
add_executable(notcurses-view ${VIEWSRCS} ${COMPATSRC})
|
add_executable(notcurses-view ${VIEWSRCS} ${COMPATSRC})
|
||||||
target_include_directories(notcurses-view
|
target_include_directories(notcurses-view
|
||||||
@ -563,8 +603,10 @@ target_link_libraries(notcurses-view
|
|||||||
notcurses++
|
notcurses++
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Testing
|
############################################################################
|
||||||
|
# testing
|
||||||
if(${BUILD_TESTING})
|
if(${BUILD_TESTING})
|
||||||
#set(CMAKE_CTEST_ARGUMENTS "-V")
|
#set(CMAKE_CTEST_ARGUMENTS "-V")
|
||||||
if(${USE_DOCTEST})
|
if(${USE_DOCTEST})
|
||||||
@ -579,8 +621,8 @@ target_include_directories(notcurses-tester
|
|||||||
)
|
)
|
||||||
target_link_libraries(notcurses-tester
|
target_link_libraries(notcurses-tester
|
||||||
PRIVATE
|
PRIVATE
|
||||||
unistring
|
|
||||||
notcurses++
|
notcurses++
|
||||||
|
unistring
|
||||||
"${TERMINFO_LIBRARIES}"
|
"${TERMINFO_LIBRARIES}"
|
||||||
)
|
)
|
||||||
add_test(
|
add_test(
|
||||||
@ -629,11 +671,14 @@ add_custom_target(demo
|
|||||||
)
|
)
|
||||||
|
|
||||||
# pkg-config support
|
# pkg-config support
|
||||||
|
configure_file(tools/notcurses-core.pc.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/notcurses-core.pc
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
configure_file(tools/notcurses.pc.in
|
configure_file(tools/notcurses.pc.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/notcurses.pc
|
${CMAKE_CURRENT_BINARY_DIR}/notcurses.pc
|
||||||
@ONLY
|
@ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file(tools/notcurses++.pc.in
|
configure_file(tools/notcurses++.pc.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/notcurses++.pc
|
${CMAKE_CURRENT_BINARY_DIR}/notcurses++.pc
|
||||||
@ONLY
|
@ONLY
|
||||||
@ -686,7 +731,7 @@ install(FILES
|
|||||||
DESTINATION ${PKGCONFIG_DIR}
|
DESTINATION ${PKGCONFIG_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(${USE_FFMPEG} OR ${USE_OIIO})
|
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
|
||||||
file(GLOB TESTDATA CONFIGURE_DEPENDS data/*)
|
file(GLOB TESTDATA CONFIGURE_DEPENDS data/*)
|
||||||
# Don't install source materia for self-originated multimedia
|
# Don't install source materia for self-originated multimedia
|
||||||
list(FILTER TESTDATA EXCLUDE REGEX ".*xcf$")
|
list(FILTER TESTDATA EXCLUDE REGEX ".*xcf$")
|
||||||
@ -701,14 +746,14 @@ install(FILES ${MARKDOWN} DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
|||||||
|
|
||||||
install(TARGETS notcurses-demo DESTINATION bin)
|
install(TARGETS notcurses-demo DESTINATION bin)
|
||||||
install(TARGETS notcurses-input DESTINATION bin)
|
install(TARGETS notcurses-input DESTINATION bin)
|
||||||
install(TARGETS ncls DESTINATION bin)
|
|
||||||
install(TARGETS ncneofetch DESTINATION bin)
|
|
||||||
install(TARGETS notcurses-tetris DESTINATION bin)
|
install(TARGETS notcurses-tetris DESTINATION bin)
|
||||||
if(${USE_FFMPEG} OR ${USE_OIIO})
|
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
|
||||||
|
install(TARGETS ncneofetch DESTINATION bin)
|
||||||
|
install(TARGETS ncls DESTINATION bin)
|
||||||
install(TARGETS notcurses-view DESTINATION bin)
|
install(TARGETS notcurses-view DESTINATION bin)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS notcurses notcurses++
|
install(TARGETS notcurses-core notcurses notcurses++
|
||||||
LIBRARY
|
LIBRARY
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
COMPONENT Libraries
|
COMPONENT Libraries
|
||||||
@ -716,7 +761,7 @@ install(TARGETS notcurses notcurses++
|
|||||||
)
|
)
|
||||||
if(${USE_STATIC})
|
if(${USE_STATIC})
|
||||||
install(
|
install(
|
||||||
TARGETS notcurses-static notcurses++-static
|
TARGETS notcurses-core-static notcurses-static notcurses++-static
|
||||||
LIBRARY
|
LIBRARY
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
COMPONENT Libraries
|
COMPONENT Libraries
|
||||||
|
16
INSTALL.md
16
INSTALL.md
@ -14,6 +14,14 @@ On an APT-based distribution, run:
|
|||||||
|
|
||||||
`apt-get install build-essential cmake doctest-dev libavformat-dev libavutil-dev libncurses-dev libreadline-dev libqrcodegen-dev libswscale-dev libunistring-dev pandoc pkg-config`
|
`apt-get install build-essential cmake doctest-dev libavformat-dev libavutil-dev libncurses-dev libreadline-dev libqrcodegen-dev libswscale-dev libunistring-dev pandoc pkg-config`
|
||||||
|
|
||||||
|
If you only intend to build core Notcurses (without multimedia support), run:
|
||||||
|
|
||||||
|
`apt-get install build-essential cmake libncurses-dev libreadline-dev libqrcodegen-dev pandoc pkg-config`
|
||||||
|
|
||||||
|
If you only intend to build core Notcurses (without multimedia support), run:
|
||||||
|
|
||||||
|
`apt-get install build-essential cmake libncurses-dev libreadline-dev libqrcodegen-dev pandoc pkg-config`
|
||||||
|
|
||||||
If you want to build the Python wrappers, you'll also need:
|
If you want to build the Python wrappers, you'll also need:
|
||||||
|
|
||||||
`apt-get install python3-cffi python3-dev python3-pypandoc python3-setuptools`
|
`apt-get install python3-cffi python3-dev python3-pypandoc python3-setuptools`
|
||||||
@ -28,8 +36,11 @@ If you want to build the Rust wrappers, you'll also need:
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
* Create a subdirectory, traditionally `build`. Enter the directory.
|
* Create a subdirectory, traditionally `build` (this is not strictly necessary,
|
||||||
* `cmake ..`. You might want to set e.g. `CMAKE_BUILD_TYPE`.
|
but it keeps your source tree clean). Enter the directory.
|
||||||
|
* `cmake ..`
|
||||||
|
** You might want to set e.g. `CMAKE_BUILD_TYPE`. Use `-DVAR=val`.
|
||||||
|
** To build without multimedia support, use `-DUSE_MULTIMEDIA=none`.
|
||||||
* `make`
|
* `make`
|
||||||
* `make test`
|
* `make test`
|
||||||
* `make install`
|
* `make install`
|
||||||
@ -89,4 +100,3 @@ but must be `Debug` for use of `USE_COVERAGE`.
|
|||||||
* `USE_POC`: build small, uninstalled proof-of-concept binaries
|
* `USE_POC`: build small, uninstalled proof-of-concept binaries
|
||||||
* `USE_QRCODEGEN`: build qrcode support via libqrcodegen
|
* `USE_QRCODEGEN`: build qrcode support via libqrcodegen
|
||||||
* `USE_STATIC`: build static libraries (in addition to shared ones)
|
* `USE_STATIC`: build static libraries (in addition to shared ones)
|
||||||
|
|
||||||
|
6
NEWS.md
6
NEWS.md
@ -1,6 +1,12 @@
|
|||||||
This document attempts to list user-visible changes and any major internal
|
This document attempts to list user-visible changes and any major internal
|
||||||
rearrangements of Notcurses.
|
rearrangements of Notcurses.
|
||||||
|
|
||||||
|
* 2.1.6 (not yet released):
|
||||||
|
* Notcurses has been split into two libraries, `notcurses-core` and
|
||||||
|
`notcurses`. The latter contains the heavyweight multimedia code,
|
||||||
|
so that applications which don't need this functionality can link against
|
||||||
|
only the former. `pkg-config` support is present for both.
|
||||||
|
|
||||||
* 2.1.5 (2021-01-15):
|
* 2.1.5 (2021-01-15):
|
||||||
* Notcurses **now depends on GNU Readline at build and runtime**, entirely
|
* Notcurses **now depends on GNU Readline at build and runtime**, entirely
|
||||||
for the benefit of direct mode, which now prepares GNU Readline for safe
|
for the benefit of direct mode, which now prepares GNU Readline for safe
|
||||||
|
@ -86,6 +86,8 @@ Why use this non-standard library?
|
|||||||
* The library object exports a minimal set of symbols. Where reasonable,
|
* The library object exports a minimal set of symbols. Where reasonable,
|
||||||
`static inline` header-only code is used. This facilitates compiler
|
`static inline` header-only code is used. This facilitates compiler
|
||||||
optimizations, and reduces loader time.
|
optimizations, and reduces loader time.
|
||||||
|
* Notcurses can be built without its multimedia functionality, requiring a
|
||||||
|
significantly lesser set of dependencies.
|
||||||
|
|
||||||
* All APIs natively support the Universal Character Set (Unicode). The `cell`
|
* All APIs natively support the Universal Character Set (Unicode). The `cell`
|
||||||
API is based around Unicode's [Extended Grapheme Cluster](https://unicode.org/reports/tr29/) concept.
|
API is based around Unicode's [Extended Grapheme Cluster](https://unicode.org/reports/tr29/) concept.
|
||||||
|
15
USAGE.md
15
USAGE.md
@ -1,7 +1,7 @@
|
|||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
As of version 2.0.0, Notcurses honors Semantic Versioning, the API is stable,
|
As of version 2.0.0, the Notcurses API is stable, and the project is committed
|
||||||
and the project is committed to backwards compatibility.
|
to backwards compatibility.
|
||||||
|
|
||||||
* [Direct Mode](#direct-mode)
|
* [Direct Mode](#direct-mode)
|
||||||
* [Alignment](#alignment)
|
* [Alignment](#alignment)
|
||||||
@ -15,16 +15,17 @@ and the project is committed to backwards compatibility.
|
|||||||
* [Stats](#stats)
|
* [Stats](#stats)
|
||||||
* [C++](#c++)
|
* [C++](#c++)
|
||||||
|
|
||||||
A full API reference [is available](https://nick-black.com/notcurses/). Manual
|
A full API reference [is available](https://nick-black.com/notcurses/) in the
|
||||||
pages ought have been installed along with Notcurses. This document is a
|
form of manual pages; these ought have been installed along with Notcurses. This document is a
|
||||||
secondary reference, and should not be considered authoritative. For a more
|
secondary reference, and should not be considered authoritative. For a more
|
||||||
unified commentary, consider the [paperback](https://www.amazon.com/dp/B086PNVNC9)
|
unified commentary, consider the [paperback](https://www.amazon.com/dp/B086PNVNC9)
|
||||||
(also available as a free PDF from https://nick-black.com).
|
(also available as a [free PDF](https://nick-black.com/dankwiki/index.php?title=Hacking_The_Planet!_with_Notcurses).
|
||||||
|
|
||||||
A program wishing to use Notcurses will need to link it, ideally using the
|
A program wishing to use Notcurses will need to link it, ideally using the
|
||||||
output of `pkg-config --libs notcurses`. It is advised to compile with the
|
output of `pkg-config --libs notcurses`. It is advised to compile with the
|
||||||
output of `pkg-config --cflags notcurses`. If using CMake, a support file is
|
output of `pkg-config --cflags notcurses`. To use the minimal core Notcurses,
|
||||||
provided, and can be accessed as `Notcurses`.
|
without multimedia support, use `pkg-config --libs notcurses-core` etc. If
|
||||||
|
using CMake, a support file is provided, and can be accessed as `Notcurses`.
|
||||||
|
|
||||||
Before calling into Notcurses—and usually as one of the first calls of the
|
Before calling into Notcurses—and usually as one of the first calls of the
|
||||||
program—be sure to call `setlocale(3)` with an appropriate UTF-8 locale. It is
|
program—be sure to call `setlocale(3)` with an appropriate UTF-8 locale. It is
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 23 KiB |
@ -8,9 +8,10 @@ notcurses - TUI library for modern terminal emulators
|
|||||||
|
|
||||||
# SYNOPSIS
|
# SYNOPSIS
|
||||||
|
|
||||||
**#include <notcurses/notcurses.h>**
|
**#include <notcurses/notcurses.h>** or
|
||||||
|
**#include <notcurses/notcurses-core.h>**
|
||||||
|
|
||||||
**-lnotcurses**
|
**-lnotcurses** or **-lnotcurses-core**
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
@ -23,7 +24,8 @@ A program wishing to use Notcurses will need to link it, ideally using the
|
|||||||
output of **pkg-config --libs notcurses** (see **pkg-config(1)**). It is
|
output of **pkg-config --libs notcurses** (see **pkg-config(1)**). It is
|
||||||
advised to compile with the output of **pkg-config --cflags notcurses**. If
|
advised to compile with the output of **pkg-config --cflags notcurses**. If
|
||||||
using CMake, a support file is provided, and can be accessed as **Notcurses**
|
using CMake, a support file is provided, and can be accessed as **Notcurses**
|
||||||
(see **cmake(1)**).
|
(see **cmake(1)**). If multimedia capabilities are not needed, it is possible
|
||||||
|
to link against a minimal Notcurses using **pkg-config --libs notcurses-core**.
|
||||||
|
|
||||||
**notcurses_init(3)** can then be used to initialize a Notcurses instance for a
|
**notcurses_init(3)** can then be used to initialize a Notcurses instance for a
|
||||||
given **FILE** (usually **stdout**, usually attached to a terminal).
|
given **FILE** (usually **stdout**, usually attached to a terminal).
|
||||||
|
@ -105,7 +105,6 @@ int dragon_demo(struct notcurses* nc){
|
|||||||
}
|
}
|
||||||
DEMO_RENDER(nc);
|
DEMO_RENDER(nc);
|
||||||
demo_nanosleep(nc, &scaled);
|
demo_nanosleep(nc, &scaled);
|
||||||
ncplane_erase(n);
|
|
||||||
}while(lasttotal != r);
|
}while(lasttotal != r);
|
||||||
ncvisual_destroy(ncv);
|
ncvisual_destroy(ncv);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -878,13 +878,6 @@ int ncblit_rgba(const void* data, int linesize, const struct ncvisual_options* v
|
|||||||
leny, lenx, blend);
|
leny, lenx, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
int rgba_blit_dispatch(ncplane* nc, const struct blitset* bset, int placey,
|
|
||||||
int placex, int linesize, const void* data, int begy,
|
|
||||||
int begx, int leny, int lenx, bool blendcolors){
|
|
||||||
return bset->blit(nc, placey, placex, linesize, data, begy, begx,
|
|
||||||
leny, lenx, blendcolors);
|
|
||||||
}
|
|
||||||
|
|
||||||
ncblitter_e ncvisual_media_defblitter(const notcurses* nc, ncscale_e scale){
|
ncblitter_e ncvisual_media_defblitter(const notcurses* nc, ncscale_e scale){
|
||||||
return rgba_blitter_default(nc->utf8, scale, nc->tcache.sextants);
|
return rgba_blitter_default(nc->utf8, scale, nc->tcache.sextants);
|
||||||
}
|
}
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
#ifndef NOTCURSES_FFMPEG
|
|
||||||
#define NOTCURSES_FFMPEG
|
|
||||||
|
|
||||||
#include "version.h"
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
#include <libavutil/error.h>
|
|
||||||
#include <libavutil/frame.h>
|
|
||||||
#include <libavutil/pixdesc.h>
|
|
||||||
#include <libavutil/version.h>
|
|
||||||
#include <libavutil/imgutils.h>
|
|
||||||
#include <libavutil/rational.h>
|
|
||||||
#include <libswscale/swscale.h>
|
|
||||||
#include <libswscale/version.h>
|
|
||||||
#include <libavformat/version.h>
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
struct AVFormatContext;
|
|
||||||
struct AVCodecContext;
|
|
||||||
struct AVFrame;
|
|
||||||
struct AVCodec;
|
|
||||||
struct AVCodecParameters;
|
|
||||||
struct AVPacket;
|
|
||||||
|
|
||||||
typedef struct ncvisual_details {
|
|
||||||
int packet_outstanding;
|
|
||||||
struct AVFormatContext* fmtctx;
|
|
||||||
struct AVCodecContext* codecctx; // video codec context
|
|
||||||
struct AVCodecContext* subtcodecctx; // subtitle codec context
|
|
||||||
struct AVFrame* frame; // frame as read
|
|
||||||
struct AVFrame* oframe; // RGBA frame
|
|
||||||
struct AVCodec* codec;
|
|
||||||
struct AVCodecParameters* cparams;
|
|
||||||
struct AVCodec* subtcodec;
|
|
||||||
struct AVPacket* packet;
|
|
||||||
struct SwsContext* swsctx;
|
|
||||||
AVSubtitle subtitle;
|
|
||||||
int stream_index; // match against this following av_read_frame()
|
|
||||||
int sub_stream_index; // subtitle stream index, can be < 0 if no subtitles
|
|
||||||
} ncvisual_details;
|
|
||||||
|
|
||||||
static inline auto
|
|
||||||
ncvisual_details_init(ncvisual_details* deets) -> int {
|
|
||||||
memset(deets, 0, sizeof(*deets));
|
|
||||||
deets->stream_index = -1;
|
|
||||||
deets->sub_stream_index = -1;
|
|
||||||
if((deets->frame = av_frame_alloc()) == nullptr){
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline auto
|
|
||||||
ncvisual_details_destroy(ncvisual_details* deets) -> void {
|
|
||||||
avcodec_close(deets->codecctx);
|
|
||||||
avcodec_free_context(&deets->codecctx);
|
|
||||||
av_frame_free(&deets->frame);
|
|
||||||
av_freep(&deets->oframe);
|
|
||||||
//avcodec_parameters_free(&ncv->cparams);
|
|
||||||
sws_freeContext(deets->swsctx);
|
|
||||||
av_packet_free(&deets->packet);
|
|
||||||
avformat_close_input(&deets->fmtctx);
|
|
||||||
avsubtitle_free(&deets->subtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -681,9 +681,13 @@ memdup(const void* src, size_t len){
|
|||||||
|
|
||||||
void* bgra_to_rgba(const void* data, int rows, int rowstride, int cols);
|
void* bgra_to_rgba(const void* data, int rows, int rowstride, int cols);
|
||||||
|
|
||||||
int rgba_blit_dispatch(ncplane* nc, const struct blitset* bset, int placey,
|
static inline int
|
||||||
int placex, int linesize, const void* data, int begy,
|
rgba_blit_dispatch(ncplane* nc, const struct blitset* bset, int placey,
|
||||||
int begx, int leny, int lenx, bool blendcolors);
|
int placex, int linesize, const void* data, int begy,
|
||||||
|
int begx, int leny, int lenx, bool blendcolors){
|
||||||
|
return bset->blit(nc, placey, placex, linesize, data, begy, begx,
|
||||||
|
leny, lenx, blendcolors);
|
||||||
|
}
|
||||||
|
|
||||||
// find the "center" cell of two lengths. in the case of even rows/columns, we
|
// find the "center" cell of two lengths. in the case of even rows/columns, we
|
||||||
// place the center on the top/left. in such a case there will be one more
|
// place the center on the top/left. in such a case there will be one more
|
||||||
@ -1068,6 +1072,8 @@ int setup_signals(void* nc, bool no_quit_sigs, bool no_winch_sig,
|
|||||||
int(*handler)(void*));
|
int(*handler)(void*));
|
||||||
int drop_signals(void* nc);
|
int drop_signals(void* nc);
|
||||||
|
|
||||||
|
void ncvisual_printbanner(const notcurses* nc);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -802,19 +802,7 @@ init_banner(const notcurses* nc){
|
|||||||
#error "No __BYTE_ORDER__ definition"
|
#error "No __BYTE_ORDER__ definition"
|
||||||
#endif
|
#endif
|
||||||
, curses_version());
|
, curses_version());
|
||||||
#ifdef USE_FFMPEG
|
ncvisual_printbanner(nc);
|
||||||
printf(" avformat %u.%u.%u avutil %u.%u.%u swscale %u.%u.%u\n",
|
|
||||||
LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO,
|
|
||||||
LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO,
|
|
||||||
LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO);
|
|
||||||
#else
|
|
||||||
#ifdef USE_OIIO
|
|
||||||
printf(" openimageio %s\n", oiio_version());
|
|
||||||
#else
|
|
||||||
term_fg_palindex(nc, stderr, nc->tcache.colors <= 88 ? 1 % nc->tcache.colors : 0xcb);
|
|
||||||
fprintf(stderr, "\n Warning! Notcurses was built without multimedia support.\n");
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
term_fg_palindex(nc, stderr, nc->tcache.colors <= 88 ? 1 % nc->tcache.colors : 0xcb);
|
term_fg_palindex(nc, stderr, nc->tcache.colors <= 88 ? 1 % nc->tcache.colors : 0xcb);
|
||||||
if(!nc->tcache.RGBflag){ // FIXME
|
if(!nc->tcache.RGBflag){ // FIXME
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
#ifndef NOTCURSES_OIIO
|
|
||||||
#define NOTCURSES_OIIO
|
|
||||||
|
|
||||||
// OpenImageIO implementation of ncvisual_details
|
|
||||||
#include "version.h"
|
|
||||||
#ifdef USE_OIIO
|
|
||||||
#include <OpenImageIO/filter.h>
|
|
||||||
#include <OpenImageIO/version.h>
|
|
||||||
#include <OpenImageIO/imageio.h>
|
|
||||||
#include <OpenImageIO/imagebuf.h>
|
|
||||||
#include <OpenImageIO/imagebufalgo.h>
|
|
||||||
|
|
||||||
typedef struct ncvisual_details {
|
|
||||||
std::unique_ptr<OIIO::ImageInput> image; // must be close()d
|
|
||||||
std::unique_ptr<uint32_t[]> frame;
|
|
||||||
std::unique_ptr<OIIO::ImageBuf> ibuf;
|
|
||||||
uint64_t framenum;
|
|
||||||
} ncvisual_details;
|
|
||||||
|
|
||||||
static inline auto
|
|
||||||
ncvisual_details_init(ncvisual_details *deets) -> int {
|
|
||||||
deets->image = nullptr;
|
|
||||||
deets->frame = nullptr;
|
|
||||||
deets->ibuf = nullptr;
|
|
||||||
deets->framenum = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline auto
|
|
||||||
ncvisual_details_destroy(ncvisual_details* deets) -> void {
|
|
||||||
if(deets->image){
|
|
||||||
deets->image->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -4,48 +4,40 @@
|
|||||||
#include "builddef.h"
|
#include "builddef.h"
|
||||||
#include "notcurses/notcurses.h"
|
#include "notcurses/notcurses.h"
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
|
||||||
#include "ffmpeg.h"
|
|
||||||
#else
|
|
||||||
#ifdef USE_OIIO
|
|
||||||
#include "oiio.h"
|
|
||||||
#else
|
|
||||||
|
|
||||||
typedef struct ncvisual_details {
|
|
||||||
} ncvisual_details;
|
|
||||||
|
|
||||||
static inline auto ncvisual_details_init(ncvisual_details* deets) -> int {
|
|
||||||
(void)deets;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline auto
|
|
||||||
ncvisual_details_destroy(ncvisual_details* deets) -> void {
|
|
||||||
(void)deets;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ncplane;
|
struct ncplane;
|
||||||
|
struct ncvisual_details;
|
||||||
|
|
||||||
typedef struct ncvisual {
|
typedef struct ncvisual {
|
||||||
|
struct ncvisual_details* details;// implementation-specific details
|
||||||
|
uint32_t* data; // (scaled) RGBA image data, rowstride bytes per row
|
||||||
int cols, rows;
|
int cols, rows;
|
||||||
// lines are sometimes padded. this many true bytes per row in data.
|
// lines are sometimes padded. this many true bytes per row in data.
|
||||||
int rowstride;
|
int rowstride;
|
||||||
ncvisual_details details;// implementation-specific details
|
|
||||||
uint32_t* data; // (scaled) RGBA image data, rowstride bytes per row
|
|
||||||
bool owndata; // we own data iff owndata == true
|
bool owndata; // we own data iff owndata == true
|
||||||
} ncvisual;
|
} ncvisual;
|
||||||
|
|
||||||
static inline auto
|
typedef struct ncvisual_implementation {
|
||||||
ncvisual_create(void) -> ncvisual* {
|
int (*ncvisual_init)(int loglevel);
|
||||||
auto ret = new ncvisual{};
|
int (*ncvisual_decode)(ncvisual*);
|
||||||
if(ret == nullptr){
|
int (*ncvisual_blit)(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||||
return nullptr;
|
const struct blitset* bset, int placey, int placex,
|
||||||
}
|
int begy, int begx, int leny, int lenx,
|
||||||
ncvisual_details_init(&ret->details);
|
bool blendcolors);
|
||||||
return ret;
|
ncvisual* (*ncvisual_create)(void);
|
||||||
}
|
ncvisual* (*ncvisual_from_file)(const char* s);
|
||||||
|
void (*ncvisual_printbanner)(const struct notcurses* nc);
|
||||||
|
// ncv constructors other than ncvisual_from_file() need to set up the
|
||||||
|
// AVFrame* 'frame' according to their own data, which is assumed to
|
||||||
|
// have been prepared already in 'ncv'.
|
||||||
|
void (*ncvisual_details_seed)(ncvisual* ncv);
|
||||||
|
void (*ncvisual_details_destroy)(struct ncvisual_details* deets);
|
||||||
|
bool canopen_images;
|
||||||
|
bool canopen_videos;
|
||||||
|
} ncvisual_implementation;
|
||||||
|
|
||||||
|
// ugh! need export this for pluggable multimedia modules without dlopen()
|
||||||
|
__attribute__((visibility("default")))
|
||||||
|
int notcurses_set_ncvisual_implementation(const ncvisual_implementation* imp);
|
||||||
|
|
||||||
static inline auto
|
static inline auto
|
||||||
ncvisual_set_data(ncvisual* ncv, uint32_t* data, bool owned) -> void {
|
ncvisual_set_data(ncvisual* ncv, uint32_t* data, bool owned) -> void {
|
||||||
|
@ -4,10 +4,111 @@
|
|||||||
#include "visual-details.h"
|
#include "visual-details.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
// ncv constructors other than ncvisual_from_file() need to set up the
|
// FIXME make this a weak symbol instead so we work with static linking
|
||||||
// AVFrame* 'frame' according to their own data, which is assumed to
|
static const ncvisual_implementation* impl;
|
||||||
// have been prepared already in 'ncv'.
|
static pthread_rwlock_t impllock = PTHREAD_RWLOCK_INITIALIZER;
|
||||||
auto ncvisual_details_seed(struct ncvisual* ncv) -> void;
|
|
||||||
|
int notcurses_set_ncvisual_implementation(const ncvisual_implementation* imp){
|
||||||
|
int ret = -1;
|
||||||
|
if(pthread_rwlock_wrlock(&impllock)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(impl == nullptr){
|
||||||
|
impl = imp;
|
||||||
|
}
|
||||||
|
ret |= pthread_rwlock_unlock(&impllock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ncvisual_decode(ncvisual* nc) -> int {
|
||||||
|
int ret = -1;
|
||||||
|
if(pthread_rwlock_rdlock(&impllock)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(impl){
|
||||||
|
ret = impl->ncvisual_decode(nc);
|
||||||
|
}
|
||||||
|
ret |= pthread_rwlock_unlock(&impllock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||||
|
const struct blitset* bset, int placey, int placex,
|
||||||
|
int begy, int begx, int leny, int lenx,
|
||||||
|
bool blendcolors) -> int {
|
||||||
|
int ret = -1;
|
||||||
|
if(pthread_rwlock_rdlock(&impllock)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(impl){
|
||||||
|
if(impl->ncvisual_blit(ncv, rows, cols, n, bset, placey, placex,
|
||||||
|
begy, begx, leny, lenx, blendcolors) >= 0){
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(rgba_blit_dispatch(n, bset, placey, placex, ncv->rowstride, ncv->data,
|
||||||
|
begy, begx, leny, lenx, blendcolors) >= 0){
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret |= pthread_rwlock_unlock(&impllock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ncvisual_details_seed(struct ncvisual* ncv) -> void {
|
||||||
|
pthread_rwlock_rdlock(&impllock);
|
||||||
|
if(impl){
|
||||||
|
impl->ncvisual_details_seed(ncv);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&impllock);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ncvisual_init(int loglevel) -> int {
|
||||||
|
int ret = 0; // default to success here
|
||||||
|
if(pthread_rwlock_rdlock(&impllock)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(impl){
|
||||||
|
ret = impl->ncvisual_init(loglevel);
|
||||||
|
}
|
||||||
|
ret |= pthread_rwlock_unlock(&impllock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ncvisual_from_file(const char* filename) -> ncvisual* {
|
||||||
|
ncvisual* ret = nullptr;
|
||||||
|
if(pthread_rwlock_rdlock(&impllock) == 0){
|
||||||
|
if(impl){
|
||||||
|
ret = impl->ncvisual_from_file(filename);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&impllock);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ncvisual_create(void) -> ncvisual* {
|
||||||
|
ncvisual* ret = nullptr;
|
||||||
|
if(pthread_rwlock_rdlock(&impllock) == 0){
|
||||||
|
if(impl){
|
||||||
|
ret = impl->ncvisual_create();
|
||||||
|
}else{
|
||||||
|
ret = new ncvisual{};
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&impllock);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ncvisual_printbanner(const notcurses* nc) -> void {
|
||||||
|
pthread_rwlock_rdlock(&impllock);
|
||||||
|
if(impl){
|
||||||
|
impl->ncvisual_printbanner(nc);
|
||||||
|
}else{
|
||||||
|
term_fg_palindex(nc, stderr, nc->tcache.colors <= 88 ? 1 % nc->tcache.colors : 0xcb);
|
||||||
|
fprintf(stderr, "\n Warning! Notcurses was built without multimedia support.\n");
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&impllock);
|
||||||
|
}
|
||||||
|
|
||||||
auto ncvisual_geom(const notcurses* nc, const ncvisual* n,
|
auto ncvisual_geom(const notcurses* nc, const ncvisual* n,
|
||||||
const struct ncvisual_options* vopts,
|
const struct ncvisual_options* vopts,
|
||||||
@ -484,11 +585,15 @@ auto ncvisual_from_plane(const ncplane* n, ncblitter_e blit, int begy, int begx,
|
|||||||
|
|
||||||
auto ncvisual_destroy(ncvisual* ncv) -> void {
|
auto ncvisual_destroy(ncvisual* ncv) -> void {
|
||||||
if(ncv){
|
if(ncv){
|
||||||
ncvisual_details_destroy(&ncv->details);
|
pthread_rwlock_rdlock(&impllock);
|
||||||
|
if(impl){
|
||||||
|
impl->ncvisual_details_destroy(ncv->details);
|
||||||
|
}
|
||||||
if(ncv->owndata){
|
if(ncv->owndata){
|
||||||
free(ncv->data);
|
free(ncv->data);
|
||||||
}
|
}
|
||||||
delete ncv;
|
delete ncv;
|
||||||
|
pthread_rwlock_unlock(&impllock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,24 +672,18 @@ auto ncvisual_polyfill_yx(ncvisual* n, int y, int x, uint32_t rgba) -> int {
|
|||||||
return ncvisual_polyfill_recurse(n, y, x, rgba, *pixel);
|
return ncvisual_polyfill_recurse(n, y, x, rgba, *pixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef USE_OIIO // built without ffmpeg or oiio
|
|
||||||
#ifndef USE_FFMPEG
|
|
||||||
auto ncvisual_from_file(const char* filename) -> ncvisual* {
|
|
||||||
(void)filename;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto notcurses_canopen_images(const notcurses* nc __attribute__ ((unused))) -> bool {
|
auto notcurses_canopen_images(const notcurses* nc __attribute__ ((unused))) -> bool {
|
||||||
return false;
|
if(!impl){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return impl->canopen_images;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto notcurses_canopen_videos(const notcurses* nc __attribute__ ((unused))) -> bool {
|
auto notcurses_canopen_videos(const notcurses* nc __attribute__ ((unused))) -> bool {
|
||||||
return false;
|
if(!impl){
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
auto ncvisual_decode(ncvisual* nc) -> int {
|
return impl->canopen_videos;
|
||||||
(void)nc;
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ncvisual_decode_loop(ncvisual* nc) -> int {
|
auto ncvisual_decode_loop(ncvisual* nc) -> int {
|
||||||
@ -609,28 +708,6 @@ auto ncvisual_subtitle(const ncvisual* ncv) -> char* {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ncvisual_init(int loglevel) -> int {
|
|
||||||
(void)loglevel;
|
|
||||||
return 0; // allow success here
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
|
||||||
const struct blitset* bset, int placey, int placex,
|
|
||||||
int begy, int begx, int leny, int lenx,
|
|
||||||
bool blendcolors) -> int {
|
|
||||||
(void)rows;
|
|
||||||
(void)cols;
|
|
||||||
if(rgba_blit_dispatch(n, bset, placey, placex, ncv->rowstride, ncv->data,
|
|
||||||
begy, begx, leny, lenx, blendcolors) < 0){
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ncvisual_details_seed(struct ncvisual* ncv) -> void {
|
|
||||||
(void)ncv;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ncvisual_resize(ncvisual* nc, int rows, int cols) -> int {
|
auto ncvisual_resize(ncvisual* nc, int rows, int cols) -> int {
|
||||||
// we'd need to verify that it's RGBA as well, except that if we've got no
|
// we'd need to verify that it's RGBA as well, except that if we've got no
|
||||||
// multimedia engine, we've only got memory-assembled ncvisuals, which are
|
// multimedia engine, we've only got memory-assembled ncvisuals, which are
|
||||||
@ -640,6 +717,3 @@ auto ncvisual_resize(ncvisual* nc, int rows, int cols) -> int {
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
@ -1,18 +1,47 @@
|
|||||||
#include "builddef.h"
|
#include "builddef.h"
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
#include "ffmpeg.h"
|
extern "C" {
|
||||||
|
#include <libavutil/error.h>
|
||||||
|
#include <libavutil/frame.h>
|
||||||
|
#include <libavutil/pixdesc.h>
|
||||||
|
#include <libavutil/version.h>
|
||||||
|
#include <libavutil/imgutils.h>
|
||||||
|
#include <libavutil/rational.h>
|
||||||
|
#include <libswscale/swscale.h>
|
||||||
|
#include <libswscale/version.h>
|
||||||
|
#include <libavformat/version.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
}
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "visual-details.h"
|
#include "visual-details.h"
|
||||||
|
|
||||||
|
struct AVFormatContext;
|
||||||
|
struct AVCodecContext;
|
||||||
|
struct AVFrame;
|
||||||
|
struct AVCodec;
|
||||||
|
struct AVCodecParameters;
|
||||||
|
struct AVPacket;
|
||||||
|
|
||||||
|
typedef struct ncvisual_details {
|
||||||
|
int packet_outstanding;
|
||||||
|
struct AVFormatContext* fmtctx;
|
||||||
|
struct AVCodecContext* codecctx; // video codec context
|
||||||
|
struct AVCodecContext* subtcodecctx; // subtitle codec context
|
||||||
|
struct AVFrame* frame; // frame as read
|
||||||
|
struct AVFrame* oframe; // RGBA frame
|
||||||
|
struct AVCodec* codec;
|
||||||
|
struct AVCodecParameters* cparams;
|
||||||
|
struct AVCodec* subtcodec;
|
||||||
|
struct AVPacket* packet;
|
||||||
|
struct SwsContext* swsctx;
|
||||||
|
AVSubtitle subtitle;
|
||||||
|
int stream_index; // match against this following av_read_frame()
|
||||||
|
int sub_stream_index; // subtitle stream index, can be < 0 if no subtitles
|
||||||
|
} ncvisual_details;
|
||||||
|
|
||||||
#define IMGALLOCALIGN 32
|
#define IMGALLOCALIGN 32
|
||||||
|
|
||||||
bool notcurses_canopen_images(const notcurses* nc __attribute__ ((unused))) {
|
static void inject_implementation(void) __attribute__ ((constructor));
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool notcurses_canopen_videos(const notcurses* nc __attribute__ ((unused))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static void
|
/*static void
|
||||||
print_frame_summary(const AVCodecContext* cctx, const AVFrame* f) {
|
print_frame_summary(const AVCodecContext* cctx, const AVFrame* f) {
|
||||||
@ -86,8 +115,8 @@ deass(const char* ass) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto ncvisual_subtitle(const ncvisual* ncv) -> char* {
|
auto ncvisual_subtitle(const ncvisual* ncv) -> char* {
|
||||||
for(unsigned i = 0 ; i < ncv->details.subtitle.num_rects ; ++i){
|
for(unsigned i = 0 ; i < ncv->details->subtitle.num_rects ; ++i){
|
||||||
const AVSubtitleRect* rect = ncv->details.subtitle.rects[i];
|
const AVSubtitleRect* rect = ncv->details->subtitle.rects[i];
|
||||||
if(rect->type == SUBTITLE_ASS){
|
if(rect->type == SUBTITLE_ASS){
|
||||||
return deass(rect->ass);
|
return deass(rect->ass);
|
||||||
}else if(rect->type == SUBTITLE_TEXT) {;
|
}else if(rect->type == SUBTITLE_TEXT) {;
|
||||||
@ -107,49 +136,50 @@ averr2ncerr(int averr){
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ncvisual_decode(ncvisual* nc){
|
static int
|
||||||
if(nc->details.fmtctx == nullptr){ // not a file-backed ncvisual
|
ffmpeg_decode(ncvisual* nc){
|
||||||
|
if(nc->details->fmtctx == nullptr){ // not a file-backed ncvisual
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
bool have_frame = false;
|
bool have_frame = false;
|
||||||
bool unref = false;
|
bool unref = false;
|
||||||
// FIXME what if this was set up with e.g. ncvisual_from_rgba()?
|
// FIXME what if this was set up with e.g. ncvisual_from_rgba()?
|
||||||
if(nc->details.oframe){
|
if(nc->details->oframe){
|
||||||
av_freep(&nc->details.oframe->data[0]);
|
av_freep(&nc->details->oframe->data[0]);
|
||||||
}
|
}
|
||||||
do{
|
do{
|
||||||
do{
|
do{
|
||||||
if(nc->details.packet_outstanding){
|
if(nc->details->packet_outstanding){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(unref){
|
if(unref){
|
||||||
av_packet_unref(nc->details.packet);
|
av_packet_unref(nc->details->packet);
|
||||||
}
|
}
|
||||||
int averr;
|
int averr;
|
||||||
if((averr = av_read_frame(nc->details.fmtctx, nc->details.packet)) < 0){
|
if((averr = av_read_frame(nc->details->fmtctx, nc->details->packet)) < 0){
|
||||||
/*if(averr != AVERROR_EOF){
|
/*if(averr != AVERROR_EOF){
|
||||||
fprintf(stderr, "Error reading frame info (%s)\n", av_err2str(averr));
|
fprintf(stderr, "Error reading frame info (%s)\n", av_err2str(averr));
|
||||||
}*/
|
}*/
|
||||||
return averr2ncerr(averr);
|
return averr2ncerr(averr);
|
||||||
}
|
}
|
||||||
unref = true;
|
unref = true;
|
||||||
if(nc->details.packet->stream_index == nc->details.sub_stream_index){
|
if(nc->details->packet->stream_index == nc->details->sub_stream_index){
|
||||||
int result = 0, ret;
|
int result = 0, ret;
|
||||||
ret = avcodec_decode_subtitle2(nc->details.subtcodecctx, &nc->details.subtitle, &result, nc->details.packet);
|
ret = avcodec_decode_subtitle2(nc->details->subtcodecctx, &nc->details->subtitle, &result, nc->details->packet);
|
||||||
if(ret >= 0 && result){
|
if(ret >= 0 && result){
|
||||||
// FIXME?
|
// FIXME?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}while(nc->details.packet->stream_index != nc->details.stream_index);
|
}while(nc->details->packet->stream_index != nc->details->stream_index);
|
||||||
++nc->details.packet_outstanding;
|
++nc->details->packet_outstanding;
|
||||||
int averr = avcodec_send_packet(nc->details.codecctx, nc->details.packet);
|
int averr = avcodec_send_packet(nc->details->codecctx, nc->details->packet);
|
||||||
if(averr < 0){
|
if(averr < 0){
|
||||||
//fprintf(stderr, "Error processing AVPacket\n");
|
//fprintf(stderr, "Error processing AVPacket\n");
|
||||||
return averr2ncerr(averr);
|
return averr2ncerr(averr);
|
||||||
}
|
}
|
||||||
--nc->details.packet_outstanding;
|
--nc->details->packet_outstanding;
|
||||||
av_packet_unref(nc->details.packet);
|
av_packet_unref(nc->details->packet);
|
||||||
averr = avcodec_receive_frame(nc->details.codecctx, nc->details.frame);
|
averr = avcodec_receive_frame(nc->details->codecctx, nc->details->frame);
|
||||||
if(averr >= 0){
|
if(averr >= 0){
|
||||||
have_frame = true;
|
have_frame = true;
|
||||||
}else if(averr == AVERROR(EAGAIN) || averr == AVERROR_EOF){
|
}else if(averr == AVERROR(EAGAIN) || averr == AVERROR_EOF){
|
||||||
@ -159,12 +189,12 @@ int ncvisual_decode(ncvisual* nc){
|
|||||||
return averr2ncerr(averr);
|
return averr2ncerr(averr);
|
||||||
}
|
}
|
||||||
}while(!have_frame);
|
}while(!have_frame);
|
||||||
//print_frame_summary(nc->details.codecctx, nc->details.frame);
|
//print_frame_summary(nc->details->codecctx, nc->details->frame);
|
||||||
const AVFrame* f = nc->details.frame;
|
const AVFrame* f = nc->details->frame;
|
||||||
nc->rowstride = f->linesize[0];
|
nc->rowstride = f->linesize[0];
|
||||||
nc->cols = nc->details.frame->width;
|
nc->cols = nc->details->frame->width;
|
||||||
nc->rows = nc->details.frame->height;
|
nc->rows = nc->details->frame->height;
|
||||||
//fprintf(stderr, "good decode! %d/%d %d %p\n", nc->details.frame->height, nc->details.frame->width, nc->rowstride, f->data);
|
//fprintf(stderr, "good decode! %d/%d %d %p\n", nc->details->frame->height, nc->details->frame->width, nc->rowstride, f->data);
|
||||||
ncvisual_set_data(nc, reinterpret_cast<uint32_t*>(f->data[0]), false);
|
ncvisual_set_data(nc, reinterpret_cast<uint32_t*>(f->data[0]), false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -172,7 +202,7 @@ int ncvisual_decode(ncvisual* nc){
|
|||||||
// resize frame to oframe, converting to RGBA (if necessary) along the way
|
// resize frame to oframe, converting to RGBA (if necessary) along the way
|
||||||
int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
||||||
const int targformat = AV_PIX_FMT_RGBA;
|
const int targformat = AV_PIX_FMT_RGBA;
|
||||||
AVFrame* inf = nc->details.oframe ? nc->details.oframe : nc->details.frame;
|
AVFrame* inf = nc->details->oframe ? nc->details->oframe : nc->details->frame;
|
||||||
//fprintf(stderr, "got format: %d (%d/%d) want format: %d (%d/%d)\n", inf->format, nc->rows, nc->cols, targformat, rows, cols);
|
//fprintf(stderr, "got format: %d (%d/%d) want format: %d (%d/%d)\n", inf->format, nc->rows, nc->cols, targformat, rows, cols);
|
||||||
if(inf->format == targformat && nc->rows == rows && nc->cols == cols){
|
if(inf->format == targformat && nc->rows == rows && nc->cols == cols){
|
||||||
return 0;
|
return 0;
|
||||||
@ -231,72 +261,92 @@ int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
|||||||
nc->rows = rows;
|
nc->rows = rows;
|
||||||
nc->cols = cols;
|
nc->cols = cols;
|
||||||
ncvisual_set_data(nc, reinterpret_cast<uint32_t*>(sframe->data[0]), true);
|
ncvisual_set_data(nc, reinterpret_cast<uint32_t*>(sframe->data[0]), true);
|
||||||
if(nc->details.oframe){
|
if(nc->details->oframe){
|
||||||
//av_freep(nc->details.oframe->data);
|
//av_freep(nc->details->oframe->data);
|
||||||
av_freep(&nc->details.oframe);
|
av_freep(&nc->details->oframe);
|
||||||
}
|
}
|
||||||
nc->details.oframe = sframe;
|
nc->details->oframe = sframe;
|
||||||
|
|
||||||
//fprintf(stderr, "SIZE SCALED: %d %d (%u)\n", nc->details.oframe->height, nc->details.oframe->width, nc->details.oframe->linesize[0]);
|
//fprintf(stderr, "SIZE SCALED: %d %d (%u)\n", nc->details->oframe->height, nc->details->oframe->width, nc->details->oframe->linesize[0]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ncvisual* ncvisual_from_file(const char* filename) {
|
auto ffmpeg_details_init(void) -> ncvisual_details* {
|
||||||
|
auto deets = new ncvisual_details{};
|
||||||
|
deets->stream_index = -1;
|
||||||
|
deets->sub_stream_index = -1;
|
||||||
|
if((deets->frame = av_frame_alloc()) == nullptr){
|
||||||
|
delete deets;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return deets;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ffmpeg_create() -> ncvisual* {
|
||||||
|
auto nc = new ncvisual{};
|
||||||
|
if((nc->details = ffmpeg_details_init()) == nullptr){
|
||||||
|
delete nc;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return nc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ncvisual* ffmpeg_from_file(const char* filename) {
|
||||||
AVStream* st;
|
AVStream* st;
|
||||||
ncvisual* ncv = ncvisual_create();
|
ncvisual* ncv = ffmpeg_create();
|
||||||
if(ncv == nullptr){
|
if(ncv == nullptr){
|
||||||
// fprintf(stderr, "Couldn't create %s (%s)\n", filename, strerror(errno));
|
// fprintf(stderr, "Couldn't create %s (%s)\n", filename, strerror(errno));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details.frame);
|
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details->frame);
|
||||||
int averr = avformat_open_input(&ncv->details.fmtctx, filename, nullptr, nullptr);
|
int averr = avformat_open_input(&ncv->details->fmtctx, filename, nullptr, nullptr);
|
||||||
if(averr < 0){
|
if(averr < 0){
|
||||||
//fprintf(stderr, "Couldn't open %s (%d)\n", filename, averr);
|
//fprintf(stderr, "Couldn't open %s (%d)\n", filename, averr);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
averr = avformat_find_stream_info(ncv->details.fmtctx, nullptr);
|
averr = avformat_find_stream_info(ncv->details->fmtctx, nullptr);
|
||||||
if(averr < 0){
|
if(averr < 0){
|
||||||
//fprintf(stderr, "Error extracting stream info from %s (%d)\n", filename, averr);
|
//fprintf(stderr, "Error extracting stream info from %s (%d)\n", filename, averr);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
//av_dump_format(ncv->details.fmtctx, 0, filename, false);
|
//av_dump_format(ncv->details->fmtctx, 0, filename, false);
|
||||||
if((averr = av_find_best_stream(ncv->details.fmtctx, AVMEDIA_TYPE_SUBTITLE, -1, -1, &ncv->details.subtcodec, 0)) >= 0){
|
if((averr = av_find_best_stream(ncv->details->fmtctx, AVMEDIA_TYPE_SUBTITLE, -1, -1, &ncv->details->subtcodec, 0)) >= 0){
|
||||||
ncv->details.sub_stream_index = averr;
|
ncv->details->sub_stream_index = averr;
|
||||||
if((ncv->details.subtcodecctx = avcodec_alloc_context3(ncv->details.subtcodec)) == nullptr){
|
if((ncv->details->subtcodecctx = avcodec_alloc_context3(ncv->details->subtcodec)) == nullptr){
|
||||||
//fprintf(stderr, "Couldn't allocate decoder for %s\n", filename);
|
//fprintf(stderr, "Couldn't allocate decoder for %s\n", filename);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
// FIXME do we need avcodec_parameters_to_context() here?
|
// FIXME do we need avcodec_parameters_to_context() here?
|
||||||
if(avcodec_open2(ncv->details.subtcodecctx, ncv->details.subtcodec, nullptr) < 0){
|
if(avcodec_open2(ncv->details->subtcodecctx, ncv->details->subtcodec, nullptr) < 0){
|
||||||
//fprintf(stderr, "Couldn't open codec for %s (%s)\n", filename, av_err2str(*averr));
|
//fprintf(stderr, "Couldn't open codec for %s (%s)\n", filename, av_err2str(*averr));
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
ncv->details.sub_stream_index = -1;
|
ncv->details->sub_stream_index = -1;
|
||||||
}
|
}
|
||||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details.frame);
|
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details->frame);
|
||||||
if((ncv->details.packet = av_packet_alloc()) == nullptr){
|
if((ncv->details->packet = av_packet_alloc()) == nullptr){
|
||||||
// fprintf(stderr, "Couldn't allocate packet for %s\n", filename);
|
// fprintf(stderr, "Couldn't allocate packet for %s\n", filename);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if((averr = av_find_best_stream(ncv->details.fmtctx, AVMEDIA_TYPE_VIDEO, -1, -1, &ncv->details.codec, 0)) < 0){
|
if((averr = av_find_best_stream(ncv->details->fmtctx, AVMEDIA_TYPE_VIDEO, -1, -1, &ncv->details->codec, 0)) < 0){
|
||||||
// fprintf(stderr, "Couldn't find visuals in %s (%s)\n", filename, av_err2str(*averr));
|
// fprintf(stderr, "Couldn't find visuals in %s (%s)\n", filename, av_err2str(*averr));
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
ncv->details.stream_index = averr;
|
ncv->details->stream_index = averr;
|
||||||
if(ncv->details.codec == nullptr){
|
if(ncv->details->codec == nullptr){
|
||||||
//fprintf(stderr, "Couldn't find decoder for %s\n", filename);
|
//fprintf(stderr, "Couldn't find decoder for %s\n", filename);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
st = ncv->details.fmtctx->streams[ncv->details.stream_index];
|
st = ncv->details->fmtctx->streams[ncv->details->stream_index];
|
||||||
if((ncv->details.codecctx = avcodec_alloc_context3(ncv->details.codec)) == nullptr){
|
if((ncv->details->codecctx = avcodec_alloc_context3(ncv->details->codec)) == nullptr){
|
||||||
//fprintf(stderr, "Couldn't allocate decoder for %s\n", filename);
|
//fprintf(stderr, "Couldn't allocate decoder for %s\n", filename);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if(avcodec_parameters_to_context(ncv->details.codecctx, st->codecpar) < 0){
|
if(avcodec_parameters_to_context(ncv->details->codecctx, st->codecpar) < 0){
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if(avcodec_open2(ncv->details.codecctx, ncv->details.codec, nullptr) < 0){
|
if(avcodec_open2(ncv->details->codecctx, ncv->details->codec, nullptr) < 0){
|
||||||
//fprintf(stderr, "Couldn't open codec for %s (%s)\n", filename, av_err2str(*averr));
|
//fprintf(stderr, "Couldn't open codec for %s (%s)\n", filename, av_err2str(*averr));
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -304,11 +354,11 @@ ncvisual* ncvisual_from_file(const char* filename) {
|
|||||||
//fprintf(stderr, "Couldn't allocate codec params for %s\n", filename);
|
//fprintf(stderr, "Couldn't allocate codec params for %s\n", filename);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if((*averr = avcodec_parameters_from_context(ncv->cparams, ncv->details.codecctx)) < 0){
|
if((*averr = avcodec_parameters_from_context(ncv->cparams, ncv->details->codecctx)) < 0){
|
||||||
//fprintf(stderr, "Couldn't get codec params for %s (%s)\n", filename, av_err2str(*averr));
|
//fprintf(stderr, "Couldn't get codec params for %s (%s)\n", filename, av_err2str(*averr));
|
||||||
goto err;
|
goto err;
|
||||||
}*/
|
}*/
|
||||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details.frame);
|
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details->frame);
|
||||||
// frame is set up in prep_details(), so that format can be set there, as
|
// frame is set up in prep_details(), so that format can be set there, as
|
||||||
// is necessary when it is prepared from inputs other than files. oframe
|
// is necessary when it is prepared from inputs other than files. oframe
|
||||||
// is set up whenever we convert to RGBA.
|
// is set up whenever we convert to RGBA.
|
||||||
@ -344,8 +394,8 @@ int ncvisual_stream(notcurses* nc, ncvisual* ncv, float timescale,
|
|||||||
do{
|
do{
|
||||||
// codecctx seems to be off by a factor of 2 regularly. instead, go with
|
// codecctx seems to be off by a factor of 2 regularly. instead, go with
|
||||||
// the time_base from the avformatctx.
|
// the time_base from the avformatctx.
|
||||||
double tbase = av_q2d(ncv->details.fmtctx->streams[ncv->details.stream_index]->time_base);
|
double tbase = av_q2d(ncv->details->fmtctx->streams[ncv->details->stream_index]->time_base);
|
||||||
int64_t ts = ncv->details.frame->best_effort_timestamp;
|
int64_t ts = ncv->details->frame->best_effort_timestamp;
|
||||||
if(frame == 1 && ts){
|
if(frame == 1 && ts){
|
||||||
usets = true;
|
usets = true;
|
||||||
}
|
}
|
||||||
@ -362,8 +412,8 @@ int ncvisual_stream(notcurses* nc, ncvisual* ncv, float timescale,
|
|||||||
activevopts.n = newn;
|
activevopts.n = newn;
|
||||||
}
|
}
|
||||||
++frame;
|
++frame;
|
||||||
uint64_t duration = ncv->details.frame->pkt_duration * tbase * NANOSECS_IN_SEC;
|
uint64_t duration = ncv->details->frame->pkt_duration * tbase * NANOSECS_IN_SEC;
|
||||||
//fprintf(stderr, "use: %u dur: %ju ts: %ju cctx: %f fctx: %f\n", usets, duration, ts, av_q2d(ncv->details.codecctx->time_base), av_q2d(ncv->details.fmtctx->streams[ncv->stream_index]->time_base));
|
//fprintf(stderr, "use: %u dur: %ju ts: %ju cctx: %f fctx: %f\n", usets, duration, ts, av_q2d(ncv->details->codecctx->time_base), av_q2d(ncv->details->fmtctx->streams[ncv->stream_index]->time_base));
|
||||||
double schedns = nsbegin;
|
double schedns = nsbegin;
|
||||||
if(usets){
|
if(usets){
|
||||||
if(tbase == 0){
|
if(tbase == 0){
|
||||||
@ -401,7 +451,7 @@ int ncvisual_stream(notcurses* nc, ncvisual* ncv, float timescale,
|
|||||||
int ncvisual_decode_loop(ncvisual* ncv){
|
int ncvisual_decode_loop(ncvisual* ncv){
|
||||||
int r = ncvisual_decode(ncv);
|
int r = ncvisual_decode(ncv);
|
||||||
if(r == 1){
|
if(r == 1){
|
||||||
if(av_seek_frame(ncv->details.fmtctx, ncv->details.stream_index, 0, AVSEEK_FLAG_FRAME) < 0){
|
if(av_seek_frame(ncv->details->fmtctx, ncv->details->stream_index, 0, AVSEEK_FLAG_FRAME) < 0){
|
||||||
// FIXME log error
|
// FIXME log error
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -412,12 +462,11 @@ int ncvisual_decode_loop(ncvisual* ncv){
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
int ffmpeg_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||||
const struct blitset* bset, int placey, int placex,
|
const struct blitset* bset, int placey, int placex,
|
||||||
int begy, int begx, int leny, int lenx,
|
int begy, int begx, int leny, int lenx, bool blendcolors) {
|
||||||
bool blendcolors) {
|
const AVFrame* inframe = ncv->details->oframe ? ncv->details->oframe : ncv->details->frame;
|
||||||
const AVFrame* inframe = ncv->details.oframe ? ncv->details.oframe : ncv->details.frame;
|
//fprintf(stderr, "inframe: %p oframe: %p frame: %p\n", inframe, ncv->details->oframe, ncv->details->frame);
|
||||||
//fprintf(stderr, "inframe: %p oframe: %p frame: %p\n", inframe, ncv->details.oframe, ncv->details.frame);
|
|
||||||
void* data = nullptr;
|
void* data = nullptr;
|
||||||
int stride = 0;
|
int stride = 0;
|
||||||
AVFrame* sframe = nullptr;
|
AVFrame* sframe = nullptr;
|
||||||
@ -431,14 +480,14 @@ int ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//fprintf(stderr, "WHN NCV: %d/%d\n", inframe->width, inframe->height);
|
//fprintf(stderr, "WHN NCV: %d/%d\n", inframe->width, inframe->height);
|
||||||
ncv->details.swsctx = sws_getCachedContext(ncv->details.swsctx,
|
ncv->details->swsctx = sws_getCachedContext(ncv->details->swsctx,
|
||||||
ncv->cols, ncv->rows,
|
ncv->cols, ncv->rows,
|
||||||
static_cast<AVPixelFormat>(inframe->format),
|
static_cast<AVPixelFormat>(inframe->format),
|
||||||
cols, rows,
|
cols, rows,
|
||||||
static_cast<AVPixelFormat>(targformat),
|
static_cast<AVPixelFormat>(targformat),
|
||||||
SWS_LANCZOS, nullptr, nullptr, nullptr);
|
SWS_LANCZOS, nullptr, nullptr, nullptr);
|
||||||
if(ncv->details.swsctx == nullptr){
|
if(ncv->details->swsctx == nullptr){
|
||||||
//fprintf(stderr, "Error retrieving details.swsctx\n");
|
//fprintf(stderr, "Error retrieving details->swsctx\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(sframe, inframe, sizeof(*inframe));
|
memcpy(sframe, inframe, sizeof(*inframe));
|
||||||
@ -453,7 +502,7 @@ int ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
|||||||
//fprintf(stderr, "Error allocating visual data (%d X %d)\n", sframe->height, sframe->width);
|
//fprintf(stderr, "Error allocating visual data (%d X %d)\n", sframe->height, sframe->width);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int height = sws_scale(ncv->details.swsctx, (const uint8_t* const*)inframe->data,
|
int height = sws_scale(ncv->details->swsctx, (const uint8_t* const*)inframe->data,
|
||||||
inframe->linesize, 0, inframe->height, sframe->data,
|
inframe->linesize, 0, inframe->height, sframe->data,
|
||||||
sframe->linesize);
|
sframe->linesize);
|
||||||
if(height < 0){
|
if(height < 0){
|
||||||
@ -484,20 +533,58 @@ int ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ncvisual_details_seed(ncvisual* ncv) -> void {
|
auto ffmpeg_details_seed(ncvisual* ncv) -> void {
|
||||||
assert(nullptr == ncv->details.oframe);
|
assert(nullptr == ncv->details->oframe);
|
||||||
ncv->details.frame->data[0] = reinterpret_cast<uint8_t*>(ncv->data);
|
ncv->details->frame->data[0] = reinterpret_cast<uint8_t*>(ncv->data);
|
||||||
ncv->details.frame->data[1] = nullptr;
|
ncv->details->frame->data[1] = nullptr;
|
||||||
ncv->details.frame->linesize[0] = ncv->rowstride;
|
ncv->details->frame->linesize[0] = ncv->rowstride;
|
||||||
ncv->details.frame->linesize[1] = 0;
|
ncv->details->frame->linesize[1] = 0;
|
||||||
ncv->details.frame->width = ncv->cols;
|
ncv->details->frame->width = ncv->cols;
|
||||||
ncv->details.frame->height = ncv->rows;
|
ncv->details->frame->height = ncv->rows;
|
||||||
ncv->details.frame->format = AV_PIX_FMT_RGBA;
|
ncv->details->frame->format = AV_PIX_FMT_RGBA;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ncvisual_init(int loglevel) {
|
auto ffmpeg_init(int loglevel) -> int {
|
||||||
av_log_set_level(loglevel);
|
av_log_set_level(loglevel);
|
||||||
// FIXME could also use av_log_set_callback() and capture the message...
|
// FIXME could also use av_log_set_callback() and capture the message...
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ffmpeg_printbanner(const notcurses* nc __attribute__ ((unused))){
|
||||||
|
printf(" avformat %u.%u.%u avutil %u.%u.%u swscale %u.%u.%u\n",
|
||||||
|
LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO,
|
||||||
|
LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO,
|
||||||
|
LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ffmpeg_details_destroy(ncvisual_details* deets) -> void {
|
||||||
|
avcodec_close(deets->codecctx);
|
||||||
|
avcodec_free_context(&deets->codecctx);
|
||||||
|
av_frame_free(&deets->frame);
|
||||||
|
av_freep(&deets->oframe);
|
||||||
|
//avcodec_parameters_free(&ncv->cparams);
|
||||||
|
sws_freeContext(deets->swsctx);
|
||||||
|
av_packet_free(&deets->packet);
|
||||||
|
avformat_close_input(&deets->fmtctx);
|
||||||
|
avsubtitle_free(&deets->subtitle);
|
||||||
|
delete deets;
|
||||||
|
}
|
||||||
|
|
||||||
|
const static ncvisual_implementation ffmpeg_impl = {
|
||||||
|
.ncvisual_init = ffmpeg_init,
|
||||||
|
.ncvisual_decode = ffmpeg_decode,
|
||||||
|
.ncvisual_blit = ffmpeg_blit,
|
||||||
|
.ncvisual_create = ffmpeg_create,
|
||||||
|
.ncvisual_from_file = ffmpeg_from_file,
|
||||||
|
.ncvisual_printbanner = ffmpeg_printbanner,
|
||||||
|
.ncvisual_details_seed = ffmpeg_details_seed,
|
||||||
|
.ncvisual_details_destroy = ffmpeg_details_destroy,
|
||||||
|
.canopen_images = true,
|
||||||
|
.canopen_videos = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void inject_implementation(void){
|
||||||
|
notcurses_set_ncvisual_implementation(&ffmpeg_impl);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -1,29 +1,63 @@
|
|||||||
#include "builddef.h"
|
#include "builddef.h"
|
||||||
#ifdef USE_OIIO
|
#ifdef USE_OIIO
|
||||||
#include "oiio.h"
|
#include <OpenImageIO/filter.h>
|
||||||
|
#include <OpenImageIO/version.h>
|
||||||
|
#include <OpenImageIO/imageio.h>
|
||||||
|
#include <OpenImageIO/imagebuf.h>
|
||||||
|
#include <OpenImageIO/imagebufalgo.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "visual-details.h"
|
#include "visual-details.h"
|
||||||
|
|
||||||
bool notcurses_canopen_images(const notcurses* nc __attribute__ ((unused))) {
|
static void inject_implementation(void) __attribute__ ((constructor));
|
||||||
return true;
|
|
||||||
|
typedef struct ncvisual_details {
|
||||||
|
std::unique_ptr<OIIO::ImageInput> image; // must be close()d
|
||||||
|
std::unique_ptr<uint32_t[]> frame;
|
||||||
|
std::unique_ptr<OIIO::ImageBuf> ibuf;
|
||||||
|
uint64_t framenum;
|
||||||
|
} ncvisual_details;
|
||||||
|
|
||||||
|
static inline auto
|
||||||
|
oiio_details_init(void) -> ncvisual_details* {
|
||||||
|
auto deets = new ncvisual_details{};
|
||||||
|
if(deets){
|
||||||
|
deets->image = nullptr;
|
||||||
|
deets->frame = nullptr;
|
||||||
|
deets->ibuf = nullptr;
|
||||||
|
deets->framenum = 0;
|
||||||
|
}
|
||||||
|
return deets;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notcurses_canopen_videos(const notcurses* nc __attribute__ ((unused))) {
|
static inline auto
|
||||||
return false; // too slow for reliable use at the moment
|
oiio_details_destroy(ncvisual_details* deets) -> void {
|
||||||
|
if(deets->image){
|
||||||
|
deets->image->close();
|
||||||
|
}
|
||||||
|
delete deets;
|
||||||
}
|
}
|
||||||
|
|
||||||
ncvisual* ncvisual_from_file(const char* filename) {
|
auto oiio_create() -> ncvisual* {
|
||||||
ncvisual* ncv = ncvisual_create();
|
auto nc = new ncvisual{};
|
||||||
|
if((nc->details = oiio_details_init()) == nullptr){
|
||||||
|
delete nc;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return nc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ncvisual* oiio_from_file(const char* filename) {
|
||||||
|
ncvisual* ncv = oiio_create();
|
||||||
if(ncv == nullptr){
|
if(ncv == nullptr){
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ncv->details.image = OIIO::ImageInput::open(filename);
|
ncv->details->image = OIIO::ImageInput::open(filename);
|
||||||
if(!ncv->details.image){
|
if(!ncv->details->image){
|
||||||
// fprintf(stderr, "Couldn't create %s (%s)\n", filename, strerror(errno));
|
// fprintf(stderr, "Couldn't create %s (%s)\n", filename, strerror(errno));
|
||||||
ncvisual_destroy(ncv);
|
ncvisual_destroy(ncv);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
/*const auto &spec = ncv->details.image->spec_dimensions(0);
|
/*const auto &spec = ncv->details->image->spec_dimensions(0);
|
||||||
std::cout << "Opened " << filename << ": " << spec.height << "x" <<
|
std::cout << "Opened " << filename << ": " << spec.height << "x" <<
|
||||||
spec.width << "@" << spec.nchannels << " (" << spec.format << ")" << std::endl;*/
|
spec.width << "@" << spec.nchannels << " (" << spec.format << ")" << std::endl;*/
|
||||||
if(ncvisual_decode(ncv)){
|
if(ncvisual_decode(ncv)){
|
||||||
@ -33,38 +67,38 @@ spec.width << "@" << spec.nchannels << " (" << spec.format << ")" << std::endl;*
|
|||||||
return ncv;
|
return ncv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ncvisual_decode(ncvisual* nc) {
|
int oiio_decode(ncvisual* nc) {
|
||||||
//fprintf(stderr, "current subimage: %d frame: %p\n", nc->details.image->current_subimage(), nc->details.frame.get());
|
//fprintf(stderr, "current subimage: %d frame: %p\n", nc->details->image->current_subimage(), nc->details->frame.get());
|
||||||
const auto &spec = nc->details.image->spec_dimensions(nc->details.framenum);
|
const auto &spec = nc->details->image->spec_dimensions(nc->details->framenum);
|
||||||
if(nc->details.frame){
|
if(nc->details->frame){
|
||||||
//fprintf(stderr, "seeking subimage: %d\n", nc->details.image->current_subimage() + 1);
|
//fprintf(stderr, "seeking subimage: %d\n", nc->details->image->current_subimage() + 1);
|
||||||
OIIO::ImageSpec newspec;
|
OIIO::ImageSpec newspec;
|
||||||
if(!nc->details.image->seek_subimage(nc->details.image->current_subimage() + 1, 0, newspec)){
|
if(!nc->details->image->seek_subimage(nc->details->image->current_subimage() + 1, 0, newspec)){
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// FIXME check newspec vis-a-vis image->spec()?
|
// FIXME check newspec vis-a-vis image->spec()?
|
||||||
}
|
}
|
||||||
//fprintf(stderr, "SUBIMAGE: %d\n", nc->details.image->current_subimage());
|
//fprintf(stderr, "SUBIMAGE: %d\n", nc->details->image->current_subimage());
|
||||||
auto pixels = spec.width * spec.height;// * spec.nchannels;
|
auto pixels = spec.width * spec.height;// * spec.nchannels;
|
||||||
if(spec.nchannels < 3 || spec.nchannels > 4){
|
if(spec.nchannels < 3 || spec.nchannels > 4){
|
||||||
return -1; // FIXME get some to test with
|
return -1; // FIXME get some to test with
|
||||||
}
|
}
|
||||||
nc->details.frame = std::make_unique<uint32_t[]>(pixels);
|
nc->details->frame = std::make_unique<uint32_t[]>(pixels);
|
||||||
if(spec.nchannels == 3){ // FIXME replace with channel shuffle
|
if(spec.nchannels == 3){ // FIXME replace with channel shuffle
|
||||||
std::fill(nc->details.frame.get(), nc->details.frame.get() + pixels, 0xfffffffful);
|
std::fill(nc->details->frame.get(), nc->details->frame.get() + pixels, 0xfffffffful);
|
||||||
}
|
}
|
||||||
//fprintf(stderr, "READING: %d %ju\n", nc->details.image->current_subimage(), nc->details.framenum);
|
//fprintf(stderr, "READING: %d %ju\n", nc->details->image->current_subimage(), nc->details->framenum);
|
||||||
if(!nc->details.image->read_image(nc->details.framenum++, 0, 0, spec.nchannels, OIIO::TypeDesc(OIIO::TypeDesc::UINT8, 4), nc->details.frame.get(), 4)){
|
if(!nc->details->image->read_image(nc->details->framenum++, 0, 0, spec.nchannels, OIIO::TypeDesc(OIIO::TypeDesc::UINT8, 4), nc->details->frame.get(), 4)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//fprintf(stderr, "READ: %d %ju\n", nc->details.image->current_subimage(), nc->details.framenum);
|
//fprintf(stderr, "READ: %d %ju\n", nc->details->image->current_subimage(), nc->details->framenum);
|
||||||
/*for(int i = 0 ; i < pixels ; ++i){
|
/*for(int i = 0 ; i < pixels ; ++i){
|
||||||
//fprintf(stderr, "%06d %02x %02x %02x %02x\n", i,
|
//fprintf(stderr, "%06d %02x %02x %02x %02x\n", i,
|
||||||
fprintf(stderr, "%06d %d %d %d %d\n", i,
|
fprintf(stderr, "%06d %d %d %d %d\n", i,
|
||||||
(nc->details.frame[i]) & 0xff,
|
(nc->details->frame[i]) & 0xff,
|
||||||
(nc->details.frame[i] >> 8) & 0xff,
|
(nc->details->frame[i] >> 8) & 0xff,
|
||||||
(nc->details.frame[i] >> 16) & 0xff,
|
(nc->details->frame[i] >> 16) & 0xff,
|
||||||
nc->details.frame[i] >> 24
|
nc->details->frame[i] >> 24
|
||||||
);
|
);
|
||||||
}*/
|
}*/
|
||||||
nc->cols = spec.width;
|
nc->cols = spec.width;
|
||||||
@ -72,10 +106,10 @@ int ncvisual_decode(ncvisual* nc) {
|
|||||||
nc->rowstride = nc->cols * 4;
|
nc->rowstride = nc->cols * 4;
|
||||||
OIIO::ImageSpec rgbaspec = spec;
|
OIIO::ImageSpec rgbaspec = spec;
|
||||||
rgbaspec.nchannels = 4;
|
rgbaspec.nchannels = 4;
|
||||||
nc->details.ibuf = std::make_unique<OIIO::ImageBuf>(rgbaspec, nc->details.frame.get());
|
nc->details->ibuf = std::make_unique<OIIO::ImageBuf>(rgbaspec, nc->details->frame.get());
|
||||||
//fprintf(stderr, "SUBS: %d\n", nc->details.ibuf->nsubimages());
|
//fprintf(stderr, "SUBS: %d\n", nc->details->ibuf->nsubimages());
|
||||||
ncvisual_set_data(nc, static_cast<uint32_t*>(nc->details.ibuf->localpixels()), false);
|
ncvisual_set_data(nc, static_cast<uint32_t*>(nc->details->ibuf->localpixels()), false);
|
||||||
//fprintf(stderr, "POST-DECODE DATA: %d %d %p %p\n", nc->rows, nc->cols, nc->data, nc->details.ibuf->localpixels());
|
//fprintf(stderr, "POST-DECODE DATA: %d %d %p %p\n", nc->rows, nc->cols, nc->data, nc->details->ibuf->localpixels());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +117,10 @@ int ncvisual_decode_loop(ncvisual* ncv){
|
|||||||
int r = ncvisual_decode(ncv);
|
int r = ncvisual_decode(ncv);
|
||||||
if(r == 1){
|
if(r == 1){
|
||||||
OIIO::ImageSpec newspec;
|
OIIO::ImageSpec newspec;
|
||||||
if(ncv->details.image->seek_subimage(0, 0, newspec)){
|
if(ncv->details->image->seek_subimage(0, 0, newspec)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ncv->details.framenum = 0;
|
ncv->details->framenum = 0;
|
||||||
if(ncvisual_decode(ncv) < 0){
|
if(ncvisual_decode(ncv) < 0){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -98,13 +132,13 @@ int ncvisual_decode_loop(ncvisual* ncv){
|
|||||||
int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
||||||
//fprintf(stderr, "%d/%d -> %d/%d on the resize\n", ncv->rows, ncv->cols, rows, cols);
|
//fprintf(stderr, "%d/%d -> %d/%d on the resize\n", ncv->rows, ncv->cols, rows, cols);
|
||||||
auto ibuf = std::make_unique<OIIO::ImageBuf>();
|
auto ibuf = std::make_unique<OIIO::ImageBuf>();
|
||||||
if(nc->details.ibuf && (nc->cols != cols || nc->rows != rows)){ // scale it
|
if(nc->details->ibuf && (nc->cols != cols || nc->rows != rows)){ // scale it
|
||||||
OIIO::ImageSpec sp{};
|
OIIO::ImageSpec sp{};
|
||||||
sp.width = cols;
|
sp.width = cols;
|
||||||
sp.height = rows;
|
sp.height = rows;
|
||||||
ibuf->reset(sp, OIIO::InitializePixels::Yes);
|
ibuf->reset(sp, OIIO::InitializePixels::Yes);
|
||||||
OIIO::ROI roi(0, cols, 0, rows, 0, 1, 0, 4);
|
OIIO::ROI roi(0, cols, 0, rows, 0, 1, 0, 4);
|
||||||
if(!OIIO::ImageBufAlgo::resize(*ibuf, *nc->details.ibuf, "", 0, roi)){
|
if(!OIIO::ImageBufAlgo::resize(*ibuf, *nc->details->ibuf, "", 0, roi)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
nc->cols = cols;
|
nc->cols = cols;
|
||||||
@ -112,12 +146,12 @@ int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
|||||||
nc->rowstride = cols * 4;
|
nc->rowstride = cols * 4;
|
||||||
ncvisual_set_data(nc, static_cast<uint32_t*>(ibuf->localpixels()), false);
|
ncvisual_set_data(nc, static_cast<uint32_t*>(ibuf->localpixels()), false);
|
||||||
//fprintf(stderr, "HAVE SOME NEW DATA: %p\n", ibuf->localpixels());
|
//fprintf(stderr, "HAVE SOME NEW DATA: %p\n", ibuf->localpixels());
|
||||||
nc->details.ibuf = std::move(ibuf);
|
nc->details->ibuf = std::move(ibuf);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ncvisual_blit(struct ncvisual* ncv, int rows, int cols,
|
int oiio_blit(struct ncvisual* ncv, int rows, int cols,
|
||||||
ncplane* n, const struct blitset* bset,
|
ncplane* n, const struct blitset* bset,
|
||||||
int placey, int placex, int begy, int begx,
|
int placey, int placex, int begy, int begx,
|
||||||
int leny, int lenx, bool blendcolors) {
|
int leny, int lenx, bool blendcolors) {
|
||||||
@ -125,13 +159,13 @@ int ncvisual_blit(struct ncvisual* ncv, int rows, int cols,
|
|||||||
void* data = nullptr;
|
void* data = nullptr;
|
||||||
int stride = 0;
|
int stride = 0;
|
||||||
auto ibuf = std::make_unique<OIIO::ImageBuf>();
|
auto ibuf = std::make_unique<OIIO::ImageBuf>();
|
||||||
if(ncv->details.ibuf && (ncv->cols != cols || ncv->rows != rows)){ // scale it
|
if(ncv->details->ibuf && (ncv->cols != cols || ncv->rows != rows)){ // scale it
|
||||||
OIIO::ImageSpec sp{};
|
OIIO::ImageSpec sp{};
|
||||||
sp.width = cols;
|
sp.width = cols;
|
||||||
sp.height = rows;
|
sp.height = rows;
|
||||||
ibuf->reset(sp, OIIO::InitializePixels::Yes);
|
ibuf->reset(sp, OIIO::InitializePixels::Yes);
|
||||||
OIIO::ROI roi(0, cols, 0, rows, 0, 1, 0, 4);
|
OIIO::ROI roi(0, cols, 0, rows, 0, 1, 0, 4);
|
||||||
if(!OIIO::ImageBufAlgo::resize(*ibuf, *ncv->details.ibuf, "", 0, roi)){
|
if(!OIIO::ImageBufAlgo::resize(*ibuf, *ncv->details->ibuf, "", 0, roi)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
stride = cols * 4;
|
stride = cols * 4;
|
||||||
@ -205,37 +239,53 @@ char* ncvisual_subtitle(const ncvisual* ncv) { // no support in OIIO
|
|||||||
/*
|
/*
|
||||||
auto ncvisual_rotate(ncvisual* ncv, double rads) -> int {
|
auto ncvisual_rotate(ncvisual* ncv, double rads) -> int {
|
||||||
OIIO::ROI roi(0, ncv->cols, 0, ncv->rows, 0, 1, 0, 4);
|
OIIO::ROI roi(0, ncv->cols, 0, ncv->rows, 0, 1, 0, 4);
|
||||||
auto tmpibuf = std::move(*ncv->details.ibuf);
|
auto tmpibuf = std::move(*ncv->details->ibuf);
|
||||||
ncv->details.ibuf = std::make_unique<OIIO::ImageBuf>();
|
ncv->details->ibuf = std::make_unique<OIIO::ImageBuf>();
|
||||||
OIIO::ImageSpec sp{};
|
OIIO::ImageSpec sp{};
|
||||||
sp.set_format(OIIO::TypeDesc(OIIO::TypeDesc::UINT8, 4));
|
sp.set_format(OIIO::TypeDesc(OIIO::TypeDesc::UINT8, 4));
|
||||||
sp.nchannels = 4;
|
sp.nchannels = 4;
|
||||||
ncv->details.ibuf->reset();
|
ncv->details->ibuf->reset();
|
||||||
if(!OIIO::ImageBufAlgo::rotate(*ncv->details.ibuf, tmpibuf, rads, "", 0, true, roi)){
|
if(!OIIO::ImageBufAlgo::rotate(*ncv->details->ibuf, tmpibuf, rads, "", 0, true, roi)){
|
||||||
return NCERR_DECODE; // FIXME need we do anything further?
|
return NCERR_DECODE; // FIXME need we do anything further?
|
||||||
}
|
}
|
||||||
ncv->rowstride = ncv->cols * 4;
|
ncv->rowstride = ncv->cols * 4;
|
||||||
ncvisual_set_data(ncv, static_cast<uint32_t*>(ncv->details.ibuf->localpixels()), false);
|
ncvisual_set_data(ncv, static_cast<uint32_t*>(ncv->details->ibuf->localpixels()), false);
|
||||||
return NCERR_SUCCESS;
|
return NCERR_SUCCESS;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
auto ncvisual_details_seed(ncvisual* ncv) -> void {
|
auto oiio_details_seed(ncvisual* ncv) -> void {
|
||||||
(void)ncv;
|
(void)ncv;
|
||||||
// FIXME?
|
// FIXME?
|
||||||
}
|
}
|
||||||
|
|
||||||
int ncvisual_init(int loglevel) {
|
int oiio_init(int loglevel) {
|
||||||
// FIXME set OIIO global attribute "debug" based on loglevel
|
// FIXME set OIIO global attribute "debug" based on loglevel
|
||||||
(void)loglevel;
|
(void)loglevel;
|
||||||
// FIXME check OIIO_VERSION_STRING components against linked openimageio_version()
|
// FIXME check OIIO_VERSION_STRING components against linked openimageio_version()
|
||||||
return 0; // allow success here
|
return 0; // allow success here
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
// FIXME would be nice to have OIIO::attributes("libraries") in here
|
// FIXME would be nice to have OIIO::attributes("libraries") in here
|
||||||
const char* oiio_version(void){
|
void oiio_printbanner(const notcurses* nc __attribute__ ((unused))){
|
||||||
return OIIO_VERSION_STRING;
|
printf(" openimageio %s\n", OIIO_VERSION_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const static ncvisual_implementation oiio_impl = {
|
||||||
|
.ncvisual_init = oiio_init,
|
||||||
|
.ncvisual_decode = oiio_decode,
|
||||||
|
.ncvisual_blit = oiio_blit,
|
||||||
|
.ncvisual_create = oiio_create,
|
||||||
|
.ncvisual_from_file = oiio_from_file,
|
||||||
|
.ncvisual_printbanner = oiio_printbanner,
|
||||||
|
.ncvisual_details_seed = oiio_details_seed,
|
||||||
|
.ncvisual_details_destroy = oiio_details_destroy,
|
||||||
|
.canopen_images = true,
|
||||||
|
.canopen_videos = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void inject_implementation(void){
|
||||||
|
notcurses_set_ncvisual_implementation(&oiio_impl);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -8,5 +8,5 @@ Description: C++ bindings for notcurses
|
|||||||
Version: @PROJECT_VERSION@
|
Version: @PROJECT_VERSION@
|
||||||
|
|
||||||
Requires: notcurses >= @PROJECT_VERSION@
|
Requires: notcurses >= @PROJECT_VERSION@
|
||||||
Libs: -L${libdir} -lnotcurses++
|
Libs: -L${libdir} -lnotcurses -lnotcurses++
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}
|
||||||
|
12
tools/notcurses-core.pc.in
Normal file
12
tools/notcurses-core.pc.in
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
|
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
|
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||||
|
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||||
|
|
||||||
|
Name: @PROJECT_NAME@
|
||||||
|
Description: TUI library for modern terminal emulators
|
||||||
|
Version: @PROJECT_VERSION@
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
Libs: -L${libdir} -lnotcurses-core
|
||||||
|
Cflags: -I${includedir}
|
@ -8,5 +8,5 @@ Description: TUI library for modern terminal emulators
|
|||||||
Version: @PROJECT_VERSION@
|
Version: @PROJECT_VERSION@
|
||||||
|
|
||||||
Requires:
|
Requires:
|
||||||
Libs: -L${libdir} -lnotcurses
|
Libs: -L${libdir} -lnotcurses-core -lnotcurses
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user