From 4ba6e34faa12b2f04294f28679e997b2592b363b Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 27 Aug 2024 18:33:44 -0700 Subject: [PATCH] Refactoring --- CMakeLists.txt | 1 + SuperBuild.bat | 1 - SuperBuild.sh | 1 - bin/main.cpp | 2 +- cmake/SuperBuild/BuildFFmpeg.cmake | 414 ++++++++++ cmake/SuperBuild/BuildOpenImageIO.cmake | 15 +- cmake/SuperBuild/BuildZLIB.cmake | 5 + cmake/SuperBuild/CMakeLists.txt | 6 + .../OpenImageIO-patch/ffmpeginput.cpp | 722 ++++++++++++++++++ cmake/SuperBuild/ZLIB-patch/CMakeLists.txt | 19 +- cmake/SuperBuild/ZLIB-patch/zconf.h.cmakein | 541 +++++++++++++ data/Transform.otio | 18 +- lib/toucan/ImageGraph.cpp | 112 +-- lib/toucan/ImageGraph.h | 5 + lib/toucan/Read.cpp | 71 +- lib/toucan/Util.cpp | 17 + lib/toucan/Util.h | 3 + 17 files changed, 1874 insertions(+), 79 deletions(-) create mode 100644 cmake/SuperBuild/BuildFFmpeg.cmake create mode 100644 cmake/SuperBuild/OpenImageIO-patch/ffmpeginput.cpp create mode 100644 cmake/SuperBuild/ZLIB-patch/zconf.h.cmakein diff --git a/CMakeLists.txt b/CMakeLists.txt index 684267b..e76dd76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ find_package(PNG REQUIRED) find_package(JPEG REQUIRED) find_package(TIFF REQUIRED) find_package(OpenEXR REQUIRED) +find_package(FFmpeg) find_package(minizip REQUIRED) find_package(OpenColorIO REQUIRED) find_package(OpenImageIO REQUIRED) diff --git a/SuperBuild.bat b/SuperBuild.bat index 873d36d..ed04b4e 100644 --- a/SuperBuild.bat +++ b/SuperBuild.bat @@ -6,4 +6,3 @@ cmake --build superbuild-%BUILD_TYPE% -j 4 --config %BUILD_TYPE% cmake -S toucan -B build-%BUILD_TYPE% -DCMAKE_INSTALL_PREFIX=%CD%\install-%BUILD_TYPE% -DCMAKE_PREFIX_PATH=%CD%\install-%BUILD_TYPE% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% cmake --build build-%BUILD_TYPE% -j 4 --config %BUILD_TYPE% -cmake --build build-%BUILD_TYPE% -j 4 --config %BUILD_TYPE% --target RUN_TESTS diff --git a/SuperBuild.sh b/SuperBuild.sh index 307d98f..7f04e0f 100644 --- a/SuperBuild.sh +++ b/SuperBuild.sh @@ -12,4 +12,3 @@ cmake --build superbuild-$BUILD_TYPE -j 4 --config $BUILD_TYPE cmake -S toucan -B build-$BUILD_TYPE -DCMAKE_INSTALL_PREFIX=$PWD/install-$BUILD_TYPE -DCMAKE_PREFIX_PATH=$PWD/install-$BUILD_TYPE -DCMAKE_BUILD_TYPE=$BUILD_TYPE cmake --build build-$BUILD_TYPE -j 4 --config $BUILD_TYPE -cmake --build build-$BUILD_TYPE -j 4 --config $BUILD_TYPE --target test diff --git a/bin/main.cpp b/bin/main.cpp index 5beecba..750e60d 100644 --- a/bin/main.cpp +++ b/bin/main.cpp @@ -137,7 +137,7 @@ int main(int argc, char** argv) } // Execute the graph. - const auto buf = node->exec(time); + const auto buf = node->exec(time - startTime); // Save the image. if (!filmstrip) diff --git a/cmake/SuperBuild/BuildFFmpeg.cmake b/cmake/SuperBuild/BuildFFmpeg.cmake new file mode 100644 index 0000000..e5b7630 --- /dev/null +++ b/cmake/SuperBuild/BuildFFmpeg.cmake @@ -0,0 +1,414 @@ +include(ExternalProject) + +if(WIN32) + # Build FFmpeg with MSYS2 on Windows. + find_package(Msys REQUIRED) +endif() + +set(FFmpeg_DEPS ZLIB) +if(toucan_NET) + list(APPEND FFmpeg_DEPS OpenSSL) +endif() +if(NOT WIN32) + list(APPEND FFmpeg_DEPS NASM) +endif() + +set(FFmpeg_SHARED_LIBS ON) +set(FFmpeg_DEBUG OFF) +set(FFmpeg_CFLAGS "--extra-cflags=-I${CMAKE_INSTALL_PREFIX}/include") +set(FFmpeg_CXXFLAGS "--extra-cxxflags=-I${CMAKE_INSTALL_PREFIX}/include") +set(FFmpeg_OBJCFLAGS "--extra-objcflags=-I${CMAKE_INSTALL_PREFIX}/include") +set(FFmpeg_LDFLAGS) +if(WIN32) + list(APPEND FFmpeg_LDFLAGS "--extra-ldflags=/LIBPATH:${CMAKE_INSTALL_PREFIX}/lib") + if(CMAKE_BUILD_TYPE MATCHES "^Debug$") + list(APPEND FFmpeg_CFLAGS "--extra-cflags=-MDd") + list(APPEND FFmpeg_CXXFLAGS "--extra-cxxflags=-MDd") + list(APPEND FFmpeg_LDFLAGS "--extra-ldflags=-MDd") + else() + list(APPEND FFmpeg_CFLAGS "--extra-cflags=-MD") + list(APPEND FFmpeg_CXXFLAGS "--extra-cxxflags=-MD") + list(APPEND FFmpeg_LDFLAGS "--extra-ldflags=-MD") + endif() +elseif(APPLE) + list(APPEND FFmpeg_LDFLAGS "--extra-ldflags=-L${CMAKE_INSTALL_PREFIX}/lib") +else() + list(APPEND FFmpeg_LDFLAGS "--extra-ldflags=-L${CMAKE_INSTALL_PREFIX}/lib") + list(APPEND FFmpeg_LDFLAGS "--extra-ldflags=-L${CMAKE_INSTALL_PREFIX}/lib64") +endif() +if(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET) + list(APPEND FFmpeg_CFLAGS "--extra-cflags=-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + list(APPEND FFmpeg_CXXFLAGS "--extra-cxxflags=-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + list(APPEND FFmpeg_OBJCFLAGS "--extra-objcflags=-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + list(APPEND FFmpeg_LDFLAGS "--extra-ldflags=-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") +endif() +if(FFmpeg_DEBUG) + list(APPEND FFmpeg_CFLAGS "--extra-cflags=-g") + list(APPEND FFmpeg_CXXFLAGS "--extra-cxxflags=-g") + list(APPEND FFmpeg_OBJCFLAGS "--extra-objcflags=-g") + list(APPEND FFmpeg_LDFLAGS "--extra-ldflags=-g") +endif() +set(FFmpeg_CONFIGURE_ARGS + --prefix=${CMAKE_INSTALL_PREFIX} + --disable-programs + --disable-doc + --disable-postproc + --disable-avfilter + --disable-hwaccels + --disable-devices + --disable-filters + --disable-alsa + --disable-appkit + --disable-avfoundation + --disable-bzlib + --disable-coreimage + --disable-iconv + --disable-libxcb + --disable-libxcb-shm + --disable-libxcb-xfixes + --disable-libxcb-shape + --disable-lzma + --disable-metal + --disable-sndio + --disable-schannel + --disable-sdl2 + --disable-securetransport + --disable-vulkan + --disable-xlib + --enable-zlib + --disable-amf + --disable-audiotoolbox + --disable-cuda-llvm + --disable-cuvid + --disable-d3d11va + --disable-dxva2 + --disable-ffnvcodec + --disable-nvdec + --disable-nvenc + --disable-v4l2-m2m + --disable-vaapi + --disable-vdpau + --disable-videotoolbox + --enable-pic + ${FFmpeg_CFLAGS} + ${FFmpeg_CXXFLAGS} + ${FFmpeg_OBJCFLAGS} + ${FFmpeg_LDFLAGS}) +if(toucan_FFMPEG_MINIMAL) + list(APPEND FFmpeg_CONFIGURE_ARGS + --disable-decoders + --enable-decoder=aac + --enable-decoder=ac3 + --enable-decoder=av1 + --enable-decoder=ayuv + --enable-decoder=dnxhd + --enable-decoder=eac3 + --enable-decoder=flac + --enable-decoder=h264 + --enable-decoder=hevc + --enable-decoder=mjpeg + --enable-decoder=mp3 + --enable-decoder=mpeg2video + --enable-decoder=mpeg4 + --enable-decoder=pcm_alaw + --enable-decoder=pcm_alaw_at + --enable-decoder=pcm_bluray + --enable-decoder=pcm_dvd + --enable-decoder=pcm_f16le + --enable-decoder=pcm_f24le + --enable-decoder=pcm_f32be + --enable-decoder=pcm_f32le + --enable-decoder=pcm_f64be + --enable-decoder=pcm_f64le + --enable-decoder=pcm_lxf + --enable-decoder=pcm_mulaw + --enable-decoder=pcm_mulaw_at + --enable-decoder=pcm_s16be + --enable-decoder=pcm_s16be_planar + --enable-decoder=pcm_s16le + --enable-decoder=pcm_s16le_planar + --enable-decoder=pcm_s24be + --enable-decoder=pcm_s24daud + --enable-decoder=pcm_s24le + --enable-decoder=pcm_s24le_planar + --enable-decoder=pcm_s32be + --enable-decoder=pcm_s32le + --enable-decoder=pcm_s32le_planar + --enable-decoder=pcm_s64be + --enable-decoder=pcm_s64le + --enable-decoder=pcm_s8 + --enable-decoder=pcm_s8_planar + --enable-decoder=pcm_sga + --enable-decoder=pcm_u16be + --enable-decoder=pcm_u16le + --enable-decoder=pcm_u24be + --enable-decoder=pcm_u24le + --enable-decoder=pcm_u32be + --enable-decoder=pcm_u32le + --enable-decoder=pcm_u8 + --enable-decoder=pcm_vidc + --enable-decoder=prores + --enable-decoder=rawvideo + --enable-decoder=v210 + --enable-decoder=v210x + --enable-decoder=v308 + --enable-decoder=v408 + --enable-decoder=v410 + --enable-decoder=vp9 + --enable-decoder=yuv4 + --disable-encoders + --enable-encoder=aac + --enable-encoder=ac3 + --enable-encoder=ayuv + --enable-encoder=dnxhd + --enable-encoder=eac3 + --enable-encoder=mjpeg + --enable-encoder=mpeg2video + --enable-encoder=mpeg4 + --enable-encoder=pcm_alaw + --enable-encoder=pcm_alaw_at + --enable-encoder=pcm_bluray + --enable-encoder=pcm_dvd + --enable-encoder=pcm_f32be + --enable-encoder=pcm_f32le + --enable-encoder=pcm_f64be + --enable-encoder=pcm_f64le + --enable-encoder=pcm_mulaw + --enable-encoder=pcm_mulaw_at + --enable-encoder=pcm_s16be + --enable-encoder=pcm_s16be_planar + --enable-encoder=pcm_s16le + --enable-encoder=pcm_s16le_planar + --enable-encoder=pcm_s24be + --enable-encoder=pcm_s24daud + --enable-encoder=pcm_s24le + --enable-encoder=pcm_s24le_planar + --enable-encoder=pcm_s32be + --enable-encoder=pcm_s32le + --enable-encoder=pcm_s32le_planar + --enable-encoder=pcm_s64be + --enable-encoder=pcm_s64le + --enable-encoder=pcm_s8 + --enable-encoder=pcm_s8_planar + --enable-encoder=pcm_u16be + --enable-encoder=pcm_u16le + --enable-encoder=pcm_u24be + --enable-encoder=pcm_u24le + --enable-encoder=pcm_u32be + --enable-encoder=pcm_u32le + --enable-encoder=pcm_u8 + --enable-encoder=pcm_vidc + --enable-encoder=prores + --enable-encoder=rawvideo + --enable-encoder=v210 + --enable-encoder=v308 + --enable-encoder=v408 + --enable-encoder=v410 + --enable-encoder=yuv4 + --disable-demuxers + --enable-demuxer=aac + --enable-demuxer=ac3 + --enable-demuxer=aiff + --enable-demuxer=av1 + --enable-demuxer=dnxhd + --enable-demuxer=dts + --enable-demuxer=dtshd + --enable-demuxer=eac3 + --enable-demuxer=flac + --enable-demuxer=h264 + --enable-demuxer=hevc + --enable-demuxer=imf + --enable-demuxer=m4v + --enable-demuxer=mjpeg + --enable-demuxer=mov + --enable-demuxer=mp3 + --enable-demuxer=mxf + --enable-demuxer=pcm_alaw + --enable-demuxer=pcm_f32be + --enable-demuxer=pcm_f32le + --enable-demuxer=pcm_f64be + --enable-demuxer=pcm_f64le + --enable-demuxer=pcm_mulaw + --enable-demuxer=pcm_s16be + --enable-demuxer=pcm_s16le + --enable-demuxer=pcm_s24be + --enable-demuxer=pcm_s24le + --enable-demuxer=pcm_s32be + --enable-demuxer=pcm_s32le + --enable-demuxer=pcm_s8 + --enable-demuxer=pcm_u16be + --enable-demuxer=pcm_u16le + --enable-demuxer=pcm_u24be + --enable-demuxer=pcm_u24le + --enable-demuxer=pcm_u32be + --enable-demuxer=pcm_u32le + --enable-demuxer=pcm_u8 + --enable-demuxer=pcm_vidc + --enable-demuxer=rawvideo + --enable-demuxer=v210 + --enable-demuxer=v210x + --enable-demuxer=wav + --enable-demuxer=yuv4mpegpipe + --disable-muxers + --enable-muxer=ac3 + --enable-muxer=aiff + --enable-muxer=dnxhd + --enable-muxer=dts + --enable-muxer=eac3 + --enable-muxer=flac + --enable-muxer=h264 + --enable-muxer=hevc + --enable-muxer=m4v + --enable-muxer=mjpeg + --enable-muxer=mov + --enable-muxer=mp4 + --enable-muxer=mpeg2video + --enable-muxer=mxf + --enable-muxer=pcm_alaw + --enable-muxer=pcm_f32be + --enable-muxer=pcm_f32le + --enable-muxer=pcm_f64be + --enable-muxer=pcm_f64le + --enable-muxer=pcm_mulaw + --enable-muxer=pcm_s16be + --enable-muxer=pcm_s16le + --enable-muxer=pcm_s24be + --enable-muxer=pcm_s24le + --enable-muxer=pcm_s32be + --enable-muxer=pcm_s32le + --enable-muxer=pcm_s8 + --enable-muxer=pcm_u16be + --enable-muxer=pcm_u16le + --enable-muxer=pcm_u24be + --enable-muxer=pcm_u24le + --enable-muxer=pcm_u32be + --enable-muxer=pcm_u32le + --enable-muxer=pcm_u8 + --enable-muxer=pcm_vidc + --enable-muxer=rawvideo + --enable-muxer=wav + --enable-muxer=yuv4mpegpipe + --disable-parsers + --enable-parser=aac + --enable-parser=ac3 + --enable-parser=av1 + --enable-parser=dnxhd + --enable-parser=dolby_e + --enable-parser=flac + --enable-parser=h264 + --enable-parser=hevc + --enable-parser=mjpeg + --enable-parser=mpeg4video + --enable-parser=mpegaudio + --enable-parser=mpegvideo + --enable-parser=vp9 + --disable-protocols + --enable-protocol=crypto + --enable-protocol=file + --enable-protocol=ftp + --enable-protocol=http + --enable-protocol=httpproxy + --enable-protocol=https + --enable-protocol=md5) +endif() +if(NOT WIN32) + list(APPEND FFmpeg_CONFIGURE_ARGS + --x86asmexe=${CMAKE_INSTALL_PREFIX}/bin/nasm) +endif() +if(toucan_NET) + list(APPEND FFmpeg_CONFIGURE_ARGS + --enable-openssl) +endif() +if(FFmpeg_SHARED_LIBS) + list(APPEND FFmpeg_CONFIGURE_ARGS + --disable-static + --enable-shared) +endif() +if(FFmpeg_DEBUG) + list(APPEND FFmpeg_CONFIGURE_ARGS + --disable-optimizations + --disable-stripping + --enable-debug=3 + --assert-level=2) +endif() +include(ProcessorCount) +ProcessorCount(FFmpeg_BUILD_JOBS) +if(WIN32) + list(APPEND FFmpeg_CONFIGURE_ARGS + --arch=x86_64 + --toolchain=msvc) + set(FFmpeg_MSYS2 + ${MSYS_CMD} + -use-full-path + -defterm + -no-start + -here) + + # \bug Copy libssl.lib to ssl.lib and libcrypto.lib to crypto.lib so the + # FFmpeg configure script can find them. + set(FFmpeg_OPENSSL_COPY) + if(toucan_NET) + set(FFmpeg_OPENSSL_COPY + "cp ${CMAKE_INSTALL_PREFIX}/lib/libssl.lib ${CMAKE_INSTALL_PREFIX}/lib/ssl.lib && \ + cp ${CMAKE_INSTALL_PREFIX}/lib/libcrypto.lib ${CMAKE_INSTALL_PREFIX}/lib/crypto.lib &&") + endif() + + list(JOIN FFmpeg_CONFIGURE_ARGS " " FFmpeg_CONFIGURE_ARGS_TMP) + set(FFmpeg_CONFIGURE ${FFmpeg_MSYS2} + -c "pacman -S diffutils make nasm --noconfirm && \ + ${FFmpeg_OPENSSL_COPY} \ + ./configure ${FFmpeg_CONFIGURE_ARGS_TMP}") + set(FFmpeg_BUILD ${FFmpeg_MSYS2} -c "make -j${FFmpeg_BUILD_JOBS}") + set(FFmpeg_INSTALL ${FFmpeg_MSYS2} -c "make install" + COMMAND ${FFmpeg_MSYS2} -c "mv ${CMAKE_INSTALL_PREFIX}/bin/avcodec.lib ${CMAKE_INSTALL_PREFIX}/lib" + COMMAND ${FFmpeg_MSYS2} -c "mv ${CMAKE_INSTALL_PREFIX}/bin/avdevice.lib ${CMAKE_INSTALL_PREFIX}/lib" + COMMAND ${FFmpeg_MSYS2} -c "mv ${CMAKE_INSTALL_PREFIX}/bin/avformat.lib ${CMAKE_INSTALL_PREFIX}/lib" + COMMAND ${FFmpeg_MSYS2} -c "mv ${CMAKE_INSTALL_PREFIX}/bin/avutil.lib ${CMAKE_INSTALL_PREFIX}/lib" + COMMAND ${FFmpeg_MSYS2} -c "mv ${CMAKE_INSTALL_PREFIX}/bin/swresample.lib ${CMAKE_INSTALL_PREFIX}/lib" + COMMAND ${FFmpeg_MSYS2} -c "mv ${CMAKE_INSTALL_PREFIX}/bin/swscale.lib ${CMAKE_INSTALL_PREFIX}/lib") +else() + set(FFmpeg_CONFIGURE ./configure ${FFmpeg_CONFIGURE_ARGS}) + set(FFmpeg_BUILD make -j${FFmpeg_BUILD_JOBS}) + set(FFmpeg_INSTALL make install) + if(APPLE) + list(APPEND FFmpeg_INSTALL + COMMAND install_name_tool -id @rpath/libavcodec.61.3.100.dylib ${CMAKE_INSTALL_PREFIX}/lib/libavcodec.61.dylib + COMMAND install_name_tool -id @rpath/libavdevice.61.1.100.dylib ${CMAKE_INSTALL_PREFIX}/lib/libavdevice.61.dylib + COMMAND install_name_tool -id @rpath/libavformat.61.1.100.dylib ${CMAKE_INSTALL_PREFIX}/lib/libavformat.61.dylib + COMMAND install_name_tool -id @rpath/libavutil.59.8.100.dylib ${CMAKE_INSTALL_PREFIX}/lib/libavutil.59.dylib + COMMAND install_name_tool -id @rpath/libswresample.5.1.100.dylib ${CMAKE_INSTALL_PREFIX}/lib/libswresample.5.dylib + COMMAND install_name_tool -id @rpath/libswscale.8.1.100.dylib ${CMAKE_INSTALL_PREFIX}/lib/libswscale.8.dylib + COMMAND install_name_tool + -change ${CMAKE_INSTALL_PREFIX}/lib/libswresample.5.dylib @rpath/libswresample.5.dylib + -change ${CMAKE_INSTALL_PREFIX}/lib/libavutil.59.dylib @rpath/libavutil.59.dylib + ${CMAKE_INSTALL_PREFIX}/lib/libavcodec.61.3.100.dylib + COMMAND install_name_tool + -change ${CMAKE_INSTALL_PREFIX}/lib/libswscale.8.dylib @rpath/libswscale.8.dylib + -change ${CMAKE_INSTALL_PREFIX}/lib/libavformat.61.dylib @rpath/libavformat.61.dylib + -change ${CMAKE_INSTALL_PREFIX}/lib/libavcodec.61.dylib @rpath/libavcodec.61.dylib + -change ${CMAKE_INSTALL_PREFIX}/lib/libswresample.5.dylib @rpath/libswresample.5.dylib + -change ${CMAKE_INSTALL_PREFIX}/lib/libavutil.59.dylib @rpath/libavutil.59.dylib + ${CMAKE_INSTALL_PREFIX}/lib/libavdevice.61.1.100.dylib + COMMAND install_name_tool + -change ${CMAKE_INSTALL_PREFIX}/lib/libavcodec.61.dylib @rpath/libavcodec.61.dylib + -change ${CMAKE_INSTALL_PREFIX}/lib/libswresample.5.dylib @rpath/libswresample.5.dylib + -change ${CMAKE_INSTALL_PREFIX}/lib/libavutil.59.dylib @rpath/libavutil.59.dylib + ${CMAKE_INSTALL_PREFIX}/lib/libavformat.61.1.100.dylib + COMMAND install_name_tool + -change ${CMAKE_INSTALL_PREFIX}/lib/libavutil.59.dylib @rpath/libavutil.59.dylib + ${CMAKE_INSTALL_PREFIX}/lib/libswresample.5.1.100.dylib + COMMAND install_name_tool + -change ${CMAKE_INSTALL_PREFIX}/lib/libavutil.59.dylib @rpath/libavutil.59.dylib + ${CMAKE_INSTALL_PREFIX}/lib/libswscale.8.1.100.dylib) + endif() +endif() + +ExternalProject_Add( + FFmpeg + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/FFmpeg + DEPENDS ${FFmpeg_DEPS} + URL https://ffmpeg.org/releases/ffmpeg-7.0.1.tar.bz2 + CONFIGURE_COMMAND ${FFmpeg_CONFIGURE} + BUILD_COMMAND ${FFmpeg_BUILD} + INSTALL_COMMAND ${FFmpeg_INSTALL} + BUILD_IN_SOURCE 1) diff --git a/cmake/SuperBuild/BuildOpenImageIO.cmake b/cmake/SuperBuild/BuildOpenImageIO.cmake index 7af354e..94700a3 100644 --- a/cmake/SuperBuild/BuildOpenImageIO.cmake +++ b/cmake/SuperBuild/BuildOpenImageIO.cmake @@ -4,6 +4,14 @@ set(OpenImageIO_GIT_REPOSITORY "https://github.com/AcademySoftwareFoundation/Ope # Commit : admin: Relicense code under Apache 2.0 (#3905) set(OpenImageIO_GIT_TAG "64f829febd352686538beaba10e4ca716a9403a1") +set(OpenImageIO_GIT_REPOSITORY https://github.com/darbyjohnston/OpenImageIO.git) +set(OpenImageIO_GIT_TAG ffmpeg_add_metadata) + +set(OpenImageIO_DEPS TIFF PNG libjpeg-turbo OpenEXR OpenColorIO Freetype) +if(toucan_FFMPEG) + list(APPEND OpenImageIO_DEPS FFmpeg) +endif() + set(OpenImageIO_ARGS ${toucan_EXTERNAL_PROJECT_ARGS} -DOIIO_BUILD_TESTS=OFF @@ -13,10 +21,10 @@ set(OpenImageIO_ARGS -DOIIO_INSTALL_FONTS=ON -DUSE_FREETYPE=ON -DUSE_PNG=ON + -DUSE_FFMPEG=${toucan_FFMPEG} -DUSE_OPENCOLORIO=ON -DUSE_BZIP2=OFF -DUSE_DCMTK=OFF - -DUSE_FFMPEG=OFF -DUSE_GIF=OFF -DUSE_JXL=OFF -DUSE_LIBHEIF=OFF @@ -34,7 +42,10 @@ set(OpenImageIO_ARGS ExternalProject_Add( OpenImageIO PREFIX ${CMAKE_CURRENT_BINARY_DIR}/OpenImageIO - DEPENDS TIFF PNG libjpeg-turbo OpenEXR OpenColorIO Freetype + DEPENDS ${OpenImageIO_DEPS} GIT_REPOSITORY ${OpenImageIO_GIT_REPOSITORY} GIT_TAG ${OpenImageIO_GIT_TAG} + PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/OpenImageIO-patch/ffmpeginput.cpp + ${CMAKE_CURRENT_BINARY_DIR}/OpenImageIO/src/OpenImageIO/src/ffmpeg.imageio/ffmpeginput.cpp CMAKE_ARGS ${OpenImageIO_ARGS}) diff --git a/cmake/SuperBuild/BuildZLIB.cmake b/cmake/SuperBuild/BuildZLIB.cmake index bf156bf..eece1f2 100644 --- a/cmake/SuperBuild/BuildZLIB.cmake +++ b/cmake/SuperBuild/BuildZLIB.cmake @@ -6,6 +6,11 @@ set(ZLIB_GIT_TAG "v1.3.1") set(ZLIB_PATCH ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/ZLIB-patch/CMakeLists.txt ${CMAKE_CURRENT_BINARY_DIR}/ZLIB/src/ZLIB/CMakeLists.txt) +if(WIN32) + list(APPEND ZLIB_PATCH COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/ZLIB-patch/zconf.h.cmakein + ${CMAKE_CURRENT_BINARY_DIR}/ZLIB/src/ZLIB/zconf.h.cmakein) +endif() set(ZLIB_ARGS ${toucan_EXTERNAL_PROJECT_ARGS} diff --git a/cmake/SuperBuild/CMakeLists.txt b/cmake/SuperBuild/CMakeLists.txt index ca71225..b3d0d30 100644 --- a/cmake/SuperBuild/CMakeLists.txt +++ b/cmake/SuperBuild/CMakeLists.txt @@ -7,6 +7,9 @@ project( HOMEPAGE_URL "https://github.com/darbyjohnston/toucan" LANGUAGES CXX C) +set(toucan_FFMPEG TRUE CACHE BOOL "Enable FFmpeg") +set(toucan_FFMPEG_MINIMAL TRUE CACHE BOOL "Enable a minimal set of FFmpeg codecs") + list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) if(NOT CMAKE_CXX_STANDARD) @@ -53,6 +56,9 @@ include(BuildJPEG) include(BuildTIFF) include(BuildImath) include(BuildOpenEXR) +if(toucan_FFMPEG) + include(BuildFFmpeg) +endif() include(Buildexpat) include(Buildminizip-ng) include(Buildpystring) diff --git a/cmake/SuperBuild/OpenImageIO-patch/ffmpeginput.cpp b/cmake/SuperBuild/OpenImageIO-patch/ffmpeginput.cpp new file mode 100644 index 0000000..f7e8cf0 --- /dev/null +++ b/cmake/SuperBuild/OpenImageIO-patch/ffmpeginput.cpp @@ -0,0 +1,722 @@ +// Copyright Contributors to the OpenImageIO project. +// SPDX-License-Identifier: BSD-3-Clause and Apache-2.0 +// https://github.com/AcademySoftwareFoundation/OpenImageIO + +#include + +extern "C" { // ffmpeg is a C api +#include +#include +#include + +// It's hard to figure out FFMPEG versions from what they give us, so +// record some of the milestones once and for all for easy reference. +#define USE_FFMPEG_2_6 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 26, 100)) +#define USE_FFMPEG_2_7 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 41, 100)) +#define USE_FFMPEG_2_8 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 60, 100)) +#define USE_FFMPEG_3_0 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 24, 100)) +#define USE_FFMPEG_3_1 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 100)) +#define USE_FFMPEG_3_2 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 64, 100)) +#define USE_FFMPEG_3_3 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 89, 100)) +#define USE_FFMPEG_3_4 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 107, 100)) +#define USE_FFMPEG_4_0 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 18, 100)) +#define USE_FFMPEG_4_1 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 35, 100)) +#define USE_FFMPEG_4_2 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 54, 100)) +#define USE_FFMPEG_4_3 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 91, 100)) +#define USE_FFMPEG_4_4 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 134, 100)) + +#if !USE_FFMPEG_4_0 +# error "OIIO FFmpeg support requires FFmpeg >= 4.0" +#endif + +#include +} + + +inline int +avpicture_fill(AVFrame* picture, uint8_t* ptr, enum AVPixelFormat pix_fmt, + int width, int height) +{ + AVFrame* frame = reinterpret_cast(picture); + return av_image_fill_arrays(frame->data, frame->linesize, ptr, pix_fmt, + width, height, 1); +} + + +#define stream_codec(ix) m_format_context->streams[(ix)]->codecpar + + +// avcodec_decode_video2 was deprecated. +// This now works by sending `avpkt` to the decoder, which buffers the +// decoded image in `avctx`. Then `avcodec_receive_frame` will copy the +// frame to `picture`. +inline int +receive_frame(AVCodecContext* avctx, AVFrame* picture, AVPacket* avpkt) +{ + int ret; + + ret = avcodec_send_packet(avctx, avpkt); + + if (ret < 0) + return ret; + + ret = avcodec_receive_frame(avctx, picture); + + if (ret < 0) + return ret; + + return 1; +} + + + +#include +#include +#include + +OIIO_PLUGIN_NAMESPACE_BEGIN + + +class FFmpegInput final : public ImageInput { +public: + FFmpegInput(); + ~FFmpegInput() override; + const char* format_name(void) const override { return "FFmpeg movie"; } + int supports(string_view feature) const override + { + return (feature == "multiimage"); + } + bool valid_file(const std::string& name) const override; + bool open(const std::string& name, ImageSpec& spec) override; + bool close(void) override; + int current_subimage(void) const override + { + lock_guard lock(*this); + return m_subimage; + } + bool seek_subimage(int subimage, int miplevel) override; + bool read_native_scanline(int subimage, int miplevel, int y, int z, + void* data) override; + void read_frame(int pos); +#if 0 + const char *metadata (const char * key); + bool has_metadata (const char * key); +#endif + bool seek(int pos); + double fps() const; + int64_t time_stamp(int pos) const; + +private: + std::string m_filename; + int m_subimage; + int64_t m_nsubimages; + AVFormatContext* m_format_context = nullptr; + AVCodecContext* m_codec_context = nullptr; + const AVCodec* m_codec = nullptr; + AVFrame* m_frame = nullptr; + AVFrame* m_rgb_frame = nullptr; + size_t m_stride; // scanline width in bytes, a.k.a. scanline stride + AVPixelFormat m_dst_pix_format; + SwsContext* m_sws_rgb_context = nullptr; + AVRational m_frame_rate; + std::vector m_rgb_buffer; + std::vector m_video_indexes; + int m_video_stream; + int m_data_stream; + int64_t m_frames; + int m_last_search_pos; + int m_last_decoded_pos; + bool m_offset_time; + bool m_codec_cap_delay; + bool m_read_frame; + int64_t m_start_time; + + // init to initialize state + void init(void) + { + m_filename.clear(); + m_format_context = nullptr; + m_codec_context = nullptr; + m_codec = nullptr; + m_frame = nullptr; + m_rgb_frame = nullptr; + m_sws_rgb_context = nullptr; + m_stride = 0; + m_rgb_buffer.clear(); + m_video_indexes.clear(); + m_video_stream = -1; + m_data_stream = -1; + m_frames = 0; + m_last_search_pos = 0; + m_last_decoded_pos = 0; + m_offset_time = true; + m_read_frame = false; + m_codec_cap_delay = false; + m_subimage = 0; + m_start_time = 0; + } +}; + + + +// Obligatory material to make this a recognizable imageio plugin +OIIO_PLUGIN_EXPORTS_BEGIN + +OIIO_EXPORT int ffmpeg_imageio_version = OIIO_PLUGIN_VERSION; + +OIIO_EXPORT const char* +ffmpeg_imageio_library_version() +{ + return "FFMpeg " OIIO_FFMPEG_VERSION " (" LIBAVFORMAT_IDENT ")"; +} + +OIIO_EXPORT ImageInput* +ffmpeg_input_imageio_create() +{ + return new FFmpegInput; +} +// FFmpeg hints: +// AVI (Audio Video Interleaved) +// QuickTime / MOV +// raw MPEG-4 video +// MPEG-1 Systems / MPEG program stream +OIIO_EXPORT const char* ffmpeg_input_extensions[] = { + "avi", "mov", "qt", "mp4", "m4a", "3gp", "3g2", "mj2", "m4v", "mpg", nullptr +}; + + +OIIO_PLUGIN_EXPORTS_END + + + +FFmpegInput::FFmpegInput() { init(); } + + + +FFmpegInput::~FFmpegInput() { close(); } + + + +bool +FFmpegInput::valid_file(const std::string& name) const +{ + // Quick/naive test -- just make sure the extension is valid for one of + // the supported file types supported by this reader. + for (int i = 0; ffmpeg_input_extensions[i]; ++i) + if (Strutil::iends_with(name, ffmpeg_input_extensions[i])) + return true; + return false; +} + + + +bool +FFmpegInput::open(const std::string& name, ImageSpec& spec) +{ + // Temporary workaround: refuse to open a file whose name does not + // indicate that it's a movie file. This avoids the problem that ffmpeg + // is willing to open tiff and other files better handled by other + // plugins. The better long-term solution is to replace av_register_all + // with our own function that registers only the formats that we want + // this reader to handle. At some point, we will institute that superior + // approach, but in the mean time, this is a quick solution that 90% + // does the job. + bool valid_extension = false; + for (int i = 0; ffmpeg_input_extensions[i]; ++i) + if (Strutil::iends_with(name, ffmpeg_input_extensions[i])) { + valid_extension = true; + break; + } + if (!valid_extension) { + errorfmt("\"{}\" could not open input", name); + return false; + } + + const char* file_name = name.c_str(); + av_log_set_level(AV_LOG_FATAL); + if (avformat_open_input(&m_format_context, file_name, NULL, NULL) != 0) { + // avformat_open_input allocs format_context + errorfmt("\"{}\" could not open input", file_name); + return false; + } + if (avformat_find_stream_info(m_format_context, NULL) < 0) { + errorfmt("\"{}\" could not find stream info", file_name); + return false; + } + m_video_stream = -1; + for (unsigned int i = 0; i < m_format_context->nb_streams; i++) { + if (stream_codec(i)->codec_type == AVMEDIA_TYPE_VIDEO) { + if (m_video_stream < 0) { + m_video_stream = i; + } + m_video_indexes.push_back(i); // needed for later use + break; + } + } + if (m_video_stream == -1) { + errorfmt("\"{}\" could not find a valid videostream", file_name); + return false; + } + int first_data_stream = -1; + for (unsigned int i = 0; i < m_format_context->nb_streams; i++) { + if (stream_codec(i)->codec_type == AVMEDIA_TYPE_DATA) { + if (first_data_stream < 0) { + first_data_stream = i; + } + if (m_format_context->streams[i]->disposition == AV_DISPOSITION_DEFAULT) { + if (m_data_stream < 0) { + m_data_stream = i; + break; + } + } + } + } + if (m_data_stream == -1) { + // no default data stream was found, use the first one found + m_data_stream = first_data_stream; + } + + // codec context for videostream + AVCodecParameters* par = stream_codec(m_video_stream); + + m_codec = avcodec_find_decoder(par->codec_id); + if (!m_codec) { + errorfmt("\"{}\" can't find decoder", file_name); + return false; + } + + m_codec_context = avcodec_alloc_context3(m_codec); + if (!m_codec_context) { + errorfmt("\"{}\" can't allocate decoder context", file_name); + return false; + } + + int ret; + + ret = avcodec_parameters_to_context(m_codec_context, par); + if (ret < 0) { + errorfmt("\"{}\" unsupported codec", file_name); + return false; + } + + if (avcodec_open2(m_codec_context, m_codec, NULL) < 0) { + errorfmt("\"{}\" could not open codec", file_name); + return false; + } + if (!strcmp(m_codec_context->codec->name, "mjpeg") + || !strcmp(m_codec_context->codec->name, "dvvideo")) { + m_offset_time = false; + } + m_codec_cap_delay = (bool)(m_codec_context->codec->capabilities + & AV_CODEC_CAP_DELAY); + + AVStream* stream = m_format_context->streams[m_video_stream]; + m_frame_rate = av_guess_frame_rate(m_format_context, stream, NULL); + + m_frames = stream->nb_frames; + m_start_time = stream->start_time; + if (!m_frames) { + seek(0); + AVPacket pkt; + av_init_packet(&pkt); + av_read_frame(m_format_context, &pkt); + int64_t first_pts = pkt.pts; + int64_t max_pts = 0; + av_packet_unref(&pkt); //because seek(int) uses m_format_context + seek(1 << 29); + av_init_packet(&pkt); //Is this needed? + while (stream && av_read_frame(m_format_context, &pkt) >= 0) { + int64_t current_pts = static_cast( + av_q2d(stream->time_base) * (pkt.pts - first_pts) * fps()); + if (current_pts > max_pts) { + max_pts = current_pts + 1; + } + av_packet_unref(&pkt); //Always free before format_context usage + } + m_frames = max_pts; + } + m_frame = av_frame_alloc(); + m_rgb_frame = av_frame_alloc(); + + AVPixelFormat src_pix_format; + switch (m_codec_context->pix_fmt) { // deprecation warning for YUV formats + case AV_PIX_FMT_YUVJ420P: src_pix_format = AV_PIX_FMT_YUV420P; break; + case AV_PIX_FMT_YUVJ422P: src_pix_format = AV_PIX_FMT_YUV422P; break; + case AV_PIX_FMT_YUVJ444P: src_pix_format = AV_PIX_FMT_YUV444P; break; + case AV_PIX_FMT_YUVJ440P: src_pix_format = AV_PIX_FMT_YUV440P; break; + default: src_pix_format = m_codec_context->pix_fmt; break; + } + + // Assume by default that we're delivering RGB UINT8 + int nchannels = 3; + TypeDesc datatype = TypeUInt8; + m_dst_pix_format = AV_PIX_FMT_RGB24; + // Look for formats that indicate we should save some different number + // of channels or bit depth. + switch (src_pix_format) { + // support for 10-bit and 12-bit pix_fmts + case AV_PIX_FMT_RGB48BE: + case AV_PIX_FMT_RGB48LE: + case AV_PIX_FMT_BGR48BE: + case AV_PIX_FMT_BGR48LE: + case AV_PIX_FMT_YUV420P9BE: + case AV_PIX_FMT_YUV420P9LE: + case AV_PIX_FMT_YUV422P9BE: + case AV_PIX_FMT_YUV422P9LE: + case AV_PIX_FMT_YUV444P9BE: + case AV_PIX_FMT_YUV444P9LE: + case AV_PIX_FMT_YUV420P10BE: + case AV_PIX_FMT_YUV420P10LE: + case AV_PIX_FMT_YUV422P10BE: + case AV_PIX_FMT_YUV422P10LE: + case AV_PIX_FMT_YUV444P10BE: + case AV_PIX_FMT_YUV444P10LE: + case AV_PIX_FMT_YUV420P12BE: + case AV_PIX_FMT_YUV420P12LE: + case AV_PIX_FMT_YUV422P12BE: + case AV_PIX_FMT_YUV422P12LE: + case AV_PIX_FMT_YUV444P12BE: + case AV_PIX_FMT_YUV444P12LE: + case AV_PIX_FMT_YUV420P14BE: + case AV_PIX_FMT_YUV420P14LE: + case AV_PIX_FMT_YUV422P14BE: + case AV_PIX_FMT_YUV422P14LE: + case AV_PIX_FMT_YUV444P14BE: + case AV_PIX_FMT_YUV444P14LE: + case AV_PIX_FMT_GBRP9BE: + case AV_PIX_FMT_GBRP9LE: + case AV_PIX_FMT_GBRP10BE: + case AV_PIX_FMT_GBRP10LE: + case AV_PIX_FMT_GBRP16BE: + case AV_PIX_FMT_GBRP16LE: + case AV_PIX_FMT_GBRP12BE: + case AV_PIX_FMT_GBRP12LE: + case AV_PIX_FMT_GBRP14BE: + case AV_PIX_FMT_GBRP14LE: + case AV_PIX_FMT_BAYER_BGGR16LE: + case AV_PIX_FMT_BAYER_BGGR16BE: + case AV_PIX_FMT_BAYER_RGGB16LE: + case AV_PIX_FMT_BAYER_RGGB16BE: + case AV_PIX_FMT_BAYER_GBRG16LE: + case AV_PIX_FMT_BAYER_GBRG16BE: + case AV_PIX_FMT_BAYER_GRBG16LE: + case AV_PIX_FMT_BAYER_GRBG16BE: + case AV_PIX_FMT_GBRAP10BE: + case AV_PIX_FMT_GBRAP10LE: + case AV_PIX_FMT_GBRAP12BE: + case AV_PIX_FMT_GBRAP12LE: + case AV_PIX_FMT_P016LE: + case AV_PIX_FMT_P016BE: + datatype = TypeUInt16; + m_dst_pix_format = AV_PIX_FMT_RGB48; + break; + // Grayscale 8 bit + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_MONOWHITE: + case AV_PIX_FMT_MONOBLACK: + datatype = TypeUInt8; + m_dst_pix_format = AV_PIX_FMT_GRAY8; + break; + // Grayscale 16 bit + case AV_PIX_FMT_GRAY9BE: + case AV_PIX_FMT_GRAY9LE: + case AV_PIX_FMT_GRAY10BE: + case AV_PIX_FMT_GRAY10LE: + case AV_PIX_FMT_GRAY12BE: + case AV_PIX_FMT_GRAY12LE: + case AV_PIX_FMT_GRAY16BE: + case AV_PIX_FMT_GRAY16LE: + datatype = TypeUInt16; + m_dst_pix_format = AV_PIX_FMT_GRAY16; + break; + // RGBA 8 bit + case AV_PIX_FMT_YA8: // YA, but promote to RGBA because who cares + case AV_PIX_FMT_YUVA422P: + case AV_PIX_FMT_YUVA444P: + case AV_PIX_FMT_GBRAP: + nchannels = 4; + datatype = TypeUInt8; + m_dst_pix_format = AV_PIX_FMT_RGBA; + break; + // RGBA 16 bit + case AV_PIX_FMT_YA16: // YA, but promote to RGBA + case AV_PIX_FMT_YUVA420P9BE: + case AV_PIX_FMT_YUVA420P9LE: + case AV_PIX_FMT_YUVA422P9BE: + case AV_PIX_FMT_YUVA422P9LE: + case AV_PIX_FMT_YUVA444P9BE: + case AV_PIX_FMT_YUVA444P9LE: + case AV_PIX_FMT_YUVA420P10BE: + case AV_PIX_FMT_YUVA420P10LE: + case AV_PIX_FMT_YUVA422P10BE: + case AV_PIX_FMT_YUVA422P10LE: + case AV_PIX_FMT_YUVA444P10BE: + case AV_PIX_FMT_YUVA444P10LE: +#if USE_FFMPEG_4_2 + case AV_PIX_FMT_YUVA422P12BE: + case AV_PIX_FMT_YUVA422P12LE: + case AV_PIX_FMT_YUVA444P12BE: + case AV_PIX_FMT_YUVA444P12LE: +#endif + case AV_PIX_FMT_YUVA420P16BE: + case AV_PIX_FMT_YUVA420P16LE: + case AV_PIX_FMT_YUVA422P16BE: + case AV_PIX_FMT_YUVA422P16LE: + case AV_PIX_FMT_YUVA444P16BE: + case AV_PIX_FMT_YUVA444P16LE: + case AV_PIX_FMT_GBRAP16: + nchannels = 4; + datatype = TypeUInt16; + m_dst_pix_format = AV_PIX_FMT_RGBA64; + break; + // RGB float + case AV_PIX_FMT_GBRPF32BE: + case AV_PIX_FMT_GBRPF32LE: + nchannels = 3; + datatype = TypeFloat; + m_dst_pix_format = AV_PIX_FMT_RGB48; // ? AV_PIX_FMT_GBRPF32 + // FIXME: They don't have a type for RGB float, only GBR float. + // Yuck. Punt for now and save as uint16 RGB. If people care, we + // can return and ask for GBR float and swap order. + break; + // RGBA float + case AV_PIX_FMT_GBRAPF32BE: + case AV_PIX_FMT_GBRAPF32LE: + nchannels = 4; + datatype = TypeFloat; + m_dst_pix_format = AV_PIX_FMT_RGBA64; // ? AV_PIX_FMT_GBRAPF32 + // FIXME: They don't have a type for RGBA float, only GBRA float. + // Yuck. Punt for now and save as uint16 RGB. If people care, we + // can return and ask for GBRA float and swap order. + break; + + // Everything else is regular 8 bit RGB + default: break; + } + + m_spec = ImageSpec(m_codec_context->width, m_codec_context->height, + nchannels, datatype); + m_stride = (size_t)(m_spec.scanline_bytes()); + + m_rgb_buffer.resize(av_image_get_buffer_size(m_dst_pix_format, + m_codec_context->width, + m_codec_context->height, 1), + 0); + + m_sws_rgb_context + = sws_getContext(m_codec_context->width, m_codec_context->height, + src_pix_format, m_codec_context->width, + m_codec_context->height, m_dst_pix_format, SWS_AREA, + NULL, NULL, NULL); + + AVDictionaryEntry* tag = NULL; + if (m_data_stream >= 0) { + while ((tag = av_dict_get(m_format_context->streams[m_data_stream]->metadata, "", tag, + AV_DICT_IGNORE_SUFFIX))) { + m_spec.attribute(tag->key, tag->value); + } + } + tag = NULL; + while ((tag = av_dict_get(m_format_context->streams[m_video_stream]->metadata, "", tag, + AV_DICT_IGNORE_SUFFIX))) { + m_spec.attribute(tag->key, tag->value); + } + tag = NULL; + while ((tag = av_dict_get(m_format_context->metadata, "", tag, + AV_DICT_IGNORE_SUFFIX))) { + m_spec.attribute(tag->key, tag->value); + } + int rat[2] = { m_frame_rate.num, m_frame_rate.den }; + m_spec.attribute("FramesPerSecond", TypeRational, &rat); + m_spec.attribute("oiio:Movie", true); + m_spec.attribute("oiio:subimages", int(m_frames)); + m_spec.attribute("oiio:BitsPerSample", + m_codec_context->bits_per_raw_sample); + m_spec.attribute("ffmpeg:codec_name", m_codec_context->codec->long_name); + m_nsubimages = m_frames; + spec = m_spec; + m_filename = name; + return true; +} + + + +bool +FFmpegInput::seek_subimage(int subimage, int miplevel) +{ + if (subimage < 0 || subimage >= m_nsubimages || miplevel > 0) { + return false; + } + if (subimage == m_subimage) { + return true; + } + m_subimage = subimage; + m_read_frame = false; + return true; +} + + + +bool +FFmpegInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/, + void* data) +{ + lock_guard lock(*this); + if (!seek_subimage(subimage, miplevel)) + return false; + if (!m_read_frame) { + read_frame(m_subimage); + } + if (m_rgb_frame->data[0]) { + memcpy(data, m_rgb_frame->data[0] + y * m_rgb_frame->linesize[0], + m_stride); + return true; + } else { + errorfmt("Error reading frame"); + return false; + } +} + + + +bool +FFmpegInput::close(void) +{ + if (m_codec_context) + avcodec_close(m_codec_context); + if (m_format_context) { + avformat_close_input(&m_format_context); + av_free(m_format_context); // will free m_codec and m_codec_context + } + if (m_frame) + av_frame_free(&m_frame); // free after close input + if (m_rgb_frame) + av_frame_free(&m_rgb_frame); + if (m_sws_rgb_context) + sws_freeContext(m_sws_rgb_context); + init(); + return true; +} + + + +void +FFmpegInput::read_frame(int frame) +{ + if (m_last_decoded_pos + 1 != frame) { + seek(frame); + } + AVPacket pkt; + int finished = 0; + int ret = 0; + int eof = 0; + while (ret == 0 || m_codec_cap_delay) { + if (!eof) { + ret = av_read_frame(m_format_context, &pkt); + if (ret == AVERROR_EOF) { + eof = 1; + } + } + if (pkt.stream_index == m_video_stream) { + if (ret < 0 && m_codec_cap_delay) { + pkt.data = NULL; + pkt.size = 0; + } + + finished = receive_frame(m_codec_context, m_frame, eof ? NULL : &pkt); + + double pts = 0; + if (static_cast(m_frame->pts) != int64_t(AV_NOPTS_VALUE)) { + pts = av_q2d( + m_format_context->streams[m_video_stream]->time_base) + * m_frame->pts; + } + + int current_frame = int((pts - m_start_time) * fps() + 0.5f); //??? + //current_frame = m_frame->display_picture_number; + m_last_search_pos = current_frame; + + if (current_frame == frame && finished) { + avpicture_fill(m_rgb_frame, &m_rgb_buffer[0], m_dst_pix_format, + m_codec_context->width, m_codec_context->height); + sws_scale(m_sws_rgb_context, + static_cast(m_frame->data), + m_frame->linesize, 0, m_codec_context->height, + m_rgb_frame->data, m_rgb_frame->linesize); + m_last_decoded_pos = current_frame; + av_packet_unref(&pkt); + break; + } + + if (AVERROR_EOF == finished) { + av_packet_unref(&pkt); + break; + } + } + av_packet_unref(&pkt); + } + m_read_frame = true; +} + + + +#if 0 +const char * +FFmpegInput::metadata (const char * key) +{ + AVDictionaryEntry * entry = av_dict_get (m_format_context->metadata, key, NULL, 0); + return entry ? av_strdup(entry->value) : NULL; + // FIXME -- that looks suspiciously like a memory leak +} + + + +bool +FFmpegInput::has_metadata (const char * key) +{ + return av_dict_get (m_format_context->metadata, key, NULL, 0); // is there a better to check exists? +} +#endif + + + +bool +FFmpegInput::seek(int frame) +{ + int64_t offset = time_stamp(frame); + int flags = AVSEEK_FLAG_BACKWARD; + avcodec_flush_buffers(m_codec_context); + av_seek_frame(m_format_context, -1, offset, flags); + return true; +} + + + +int64_t +FFmpegInput::time_stamp(int frame) const +{ + int64_t timestamp = static_cast( + (static_cast(frame) + / (fps() + * av_q2d(m_format_context->streams[m_video_stream]->time_base)))); + if (static_cast(m_format_context->start_time) + != int64_t(AV_NOPTS_VALUE)) { + timestamp += static_cast( + static_cast(m_format_context->start_time) * AV_TIME_BASE + / av_q2d(m_format_context->streams[m_video_stream]->time_base)); + } + return timestamp; +} + + + +double +FFmpegInput::fps() const +{ + if (m_frame_rate.den) { + return av_q2d(m_frame_rate); + } + return 1.0f; +} + +OIIO_PLUGIN_NAMESPACE_END diff --git a/cmake/SuperBuild/ZLIB-patch/CMakeLists.txt b/cmake/SuperBuild/ZLIB-patch/CMakeLists.txt index ee4a5db..11ef78d 100644 --- a/cmake/SuperBuild/ZLIB-patch/CMakeLists.txt +++ b/cmake/SuperBuild/ZLIB-patch/CMakeLists.txt @@ -149,12 +149,13 @@ if(MINGW) set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) endif(MINGW) -add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) +#add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) +#target_include_directories(zlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) +set(CMAKE_DEBUG_POSTFIX) +add_library(zlib STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) target_include_directories(zlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) -add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) -target_include_directories(zlibstatic PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) -set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) -set_target_properties(zlib PROPERTIES SOVERSION 1) +#set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) +#set_target_properties(zlib PROPERTIES SOVERSION 1) if(NOT CYGWIN) # This property causes shared libraries on Linux to have the full version @@ -164,22 +165,22 @@ if(NOT CYGWIN) # # This has no effect with MSVC, on that platform the version info for # the DLL comes from the resource file win32/zlib1.rc - set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION}) + #set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION}) endif() if(UNIX) # On unix-like platforms the library is almost always called libz - set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z) + set_target_properties(zlib PROPERTIES OUTPUT_NAME z) if(NOT APPLE AND NOT(CMAKE_SYSTEM_NAME STREQUAL AIX)) set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"") endif() elseif(BUILD_SHARED_LIBS AND WIN32) # Creates zlib1.dll when building shared library version - set_target_properties(zlib PROPERTIES SUFFIX "1.dll") + #set_target_properties(zlib PROPERTIES SUFFIX "1.dll") endif() if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL ) - install(TARGETS zlibstatic + install(TARGETS zlib RUNTIME DESTINATION "${INSTALL_BIN_DIR}" ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" LIBRARY DESTINATION "${INSTALL_LIB_DIR}" ) diff --git a/cmake/SuperBuild/ZLIB-patch/zconf.h.cmakein b/cmake/SuperBuild/ZLIB-patch/zconf.h.cmakein new file mode 100644 index 0000000..fbba7de --- /dev/null +++ b/cmake/SuperBuild/ZLIB-patch/zconf.h.cmakein @@ -0,0 +1,541 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H +#cmakedefine Z_PREFIX +#cmakedefine Z_HAVE_UNISTD_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_combine_gen z_crc32_combine_gen +# define crc32_combine_gen64 z_crc32_combine_gen64 +# define crc32_combine_op z_crc32_combine_op +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO +# ifdef _WIN64 + typedef unsigned long long z_size_t; +# else + typedef unsigned long z_size_t; +# endif +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#ifndef Z_HAVE_UNISTD_H +# ifdef __WATCOMC__ +# define Z_HAVE_UNISTD_H +# endif +#endif +#ifndef Z_HAVE_UNISTD_H +# if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32) +# define Z_HAVE_UNISTD_H +# endif +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/data/Transform.otio b/data/Transform.otio index 1a82415..71afa34 100644 --- a/data/Transform.otio +++ b/data/Transform.otio @@ -17,7 +17,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 3 + "value": 2 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -47,7 +47,7 @@ "start_time": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 4 + "value": 0 } }, "name": "Crop" @@ -61,7 +61,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 3 + "value": 2 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -87,7 +87,7 @@ "start_time": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 4 + "value": 0 } }, "name": "Flip" @@ -101,7 +101,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 3 + "value": 2 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -127,7 +127,7 @@ "start_time": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 6 + "value": 0 } }, "name": "Flop" @@ -141,7 +141,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 3 + "value": 2 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -186,7 +186,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 3 + "value": 2 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -217,7 +217,7 @@ "start_time": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 2 + "value": 0 } }, "name": "Rotate" diff --git a/lib/toucan/ImageGraph.cpp b/lib/toucan/ImageGraph.cpp index 9b7a179..ae28ad7 100644 --- a/lib/toucan/ImageGraph.cpp +++ b/lib/toucan/ImageGraph.cpp @@ -31,16 +31,16 @@ namespace toucan _timeline(timeline), _options(options) { + const auto globalStartTime = timeline->global_start_time(); + _globalStartTime = globalStartTime.has_value() ? globalStartTime.value() : + OTIO_NS::RationalTime(0.0, timeline->duration().rate()); + // Get the image size from the first clip. for (auto clip : _timeline->find_clips()) { if (auto externalRef = dynamic_cast(clip->media_reference())) { - std::filesystem::path path = externalRef->target_url(); - if (!path.is_absolute()) - { - path = _path / path; - } + const std::filesystem::path path = _getMediaPath(externalRef->target_url()); const OIIO::ImageBuf buf(path.string()); const auto& spec = buf.spec(); if (spec.width > 0) @@ -52,13 +52,8 @@ namespace toucan } else if (auto sequenceRef = dynamic_cast(clip->media_reference())) { - std::filesystem::path path = sequenceRef->target_url_base(); - if (!path.is_absolute()) - { - path = _path / path; - } - path = getSequenceFrame( - path, + const std::filesystem::path path = getSequenceFrame( + _getMediaPath(sequenceRef->target_url_base()), sequenceRef->name_prefix(), sequenceRef->start_frame(), sequenceRef->frame_zero_padding(), @@ -108,29 +103,32 @@ namespace toucan { if (auto track = OTIO_NS::dynamic_retainer_cast(i)) { - // Process this track. - auto trackNode = _track(host, time, track); - - // Get the track effects. - const auto& effects = track->effects(); - if (!effects.empty()) + if (track->kind() == OTIO_NS::Track::Kind::video) { - trackNode = _effects(host, effects, trackNode); - } + // Process this track. + auto trackNode = _track(host, time - _globalStartTime, track); - // Composite this track over the previous track. - std::vector > nodes; - if (trackNode) - { - nodes.push_back(trackNode); - } - if (node) - { - nodes.push_back(node); + // Get the track effects. + const auto& effects = track->effects(); + if (!effects.empty()) + { + trackNode = _effects(host, effects, trackNode); + } + + // Composite this track over the previous track. + std::vector > nodes; + if (trackNode) + { + nodes.push_back(trackNode); + } + if (node) + { + nodes.push_back(node); + } + auto comp = std::make_shared(nodes); + comp->setPremult(true); + node = comp; } - auto comp = std::make_shared(nodes); - comp->setPremult(true); - node = comp; } } @@ -207,9 +205,16 @@ namespace toucan auto metaData = prevTransition->metadata(); metaData["value"] = value; - if (auto node = host->createNode( + auto node = host->createNode( prevTransition->transition_type(), - metaData)) + metaData); + if (!node) + { + node = host->createNode( + "Toucan:Dissolve", + metaData); + } + if (node) { auto a = _item( host, @@ -235,9 +240,16 @@ namespace toucan auto metaData = nextTransition->metadata(); metaData["value"] = value; - if (auto node = host->createNode( + auto node = host->createNode( nextTransition->transition_type(), - metaData)) + metaData); + if (!node) + { + node = host->createNode( + "Toucan:Dissolve", + metaData); + } + if (node) { auto b = _item( host, @@ -268,17 +280,15 @@ namespace toucan // Get the media reference. if (auto externalRef = dynamic_cast(clip->media_reference())) { - const std::string url = externalRef->target_url(); - const std::filesystem::path path = _path / url; + const std::filesystem::path path = _getMediaPath(externalRef->target_url()); auto read = std::make_shared(path); out = read; } else if (auto sequenceRef = dynamic_cast(clip->media_reference())) { - const std::string url = sequenceRef->target_url_base(); - const std::filesystem::path path = _path / url; + const std::filesystem::path path = _getMediaPath(sequenceRef->target_url_base()); auto read = std::make_shared( - path.string(), + path, sequenceRef->name_prefix(), sequenceRef->name_suffix(), sequenceRef->start_frame(), @@ -307,12 +317,9 @@ namespace toucan } if (out) { - OTIO_NS::RationalTime timeOffset = trimmedRangeInParent.start_time(); - const auto& sourceRange = item->source_range(); - if (sourceRange.has_value()) - { - timeOffset -= sourceRange.value().start_time(); - } + const OTIO_NS::RationalTime timeOffset = + trimmedRangeInParent.start_time() - + item->trimmed_range().start_time(); out->setTimeOffset(timeOffset); } @@ -352,4 +359,15 @@ namespace toucan } return out; } + + std::filesystem::path ImageGraph::_getMediaPath(const std::string& url) const + { + std::filesystem::path path = splitURLProtocol(url).second; + if (!path.is_absolute()) + { + path = _path / path; + } + return path; + } + } diff --git a/lib/toucan/ImageGraph.h b/lib/toucan/ImageGraph.h index c54109b..d2607a8 100644 --- a/lib/toucan/ImageGraph.h +++ b/lib/toucan/ImageGraph.h @@ -47,18 +47,23 @@ namespace toucan const std::shared_ptr&, const OTIO_NS::RationalTime&, const OTIO_NS::SerializableObject::Retainer&) const; + std::shared_ptr _item( const std::shared_ptr&, const OTIO_NS::TimeRange& trimmedRangeInParent, const OTIO_NS::RationalTime&, const OTIO_NS::SerializableObject::Retainer&) const; + std::shared_ptr _effects( const std::shared_ptr&, const std::vector >&, const std::shared_ptr&) const; + std::filesystem::path _getMediaPath(const std::string&) const; + std::filesystem::path _path; OTIO_NS::SerializableObject::Retainer _timeline; + OTIO_NS::RationalTime _globalStartTime; ImageGraphOptions _options; IMATH_NAMESPACE::V2i _imageSize = IMATH_NAMESPACE::V2i(0, 0); }; diff --git a/lib/toucan/Read.cpp b/lib/toucan/Read.cpp index 81cf691..2acbf2b 100644 --- a/lib/toucan/Read.cpp +++ b/lib/toucan/Read.cpp @@ -22,19 +22,69 @@ namespace toucan ReadNode::~ReadNode() {} - OIIO::ImageBuf ReadNode::exec(const OTIO_NS::RationalTime&) + OIIO::ImageBuf ReadNode::exec(const OTIO_NS::RationalTime& time) { - OIIO::ImageBuf buf(_path.string()); - const auto& spec = buf.spec(); + OIIO::ImageBuf out; + + OTIO_NS::RationalTime offsetTime = time; + if (!_timeOffset.is_invalid_time()) + { + offsetTime -= _timeOffset; + } + + OIIO::ImageSpec spec; + int64_t frameCount = 0; + if (auto in = OIIO::ImageInput::open(_path.string())) + { + spec = in->spec(); + if (auto param = spec.find_attribute("oiio:subimages")) + { + frameCount = param->get_int(); + } + int fps[2] = { 0, 0 }; + if (auto param = spec.find_attribute("FramesPerSecond")) + { + spec.getattribute("FramesPerSecond", OIIO::TypeDesc::TypeRational, &fps); + } + if (auto param = spec.find_attribute("timecode")) + { + const std::string timecode = param->get_string(); + double rate = 24.0; + if (fps[0] > 0 && fps[1] > 0) + { + rate = fps[0] / static_cast(fps[1]); + } + const OTIO_NS::RationalTime timecodeTime = OTIO_NS::RationalTime::from_timecode(timecode, rate); + if (!time.is_invalid_time()) + { + offsetTime -= timecodeTime; + } + } + } + + const int subImage = std::max( + static_cast(0), + std::min(static_cast(offsetTime.floor().value()), frameCount - 1)); + out = OIIO::ImageBuf(_path.string(), subImage); + if (3 == spec.nchannels) { // Add an alpha channel. - const int channelorder[] = { 0, 1, 2, -1 }; - const float channelvalues[] = { 0, 0, 0, 1.0 }; - const std::string channelnames[] = { "", "", "", "A" }; - buf = OIIO::ImageBufAlgo::channels(buf, 4, channelorder, channelvalues, channelnames); + //const int channelorder[] = { 0, 1, 2, -1 }; + //const float channelvalues[] = { 0, 0, 0, 1.0 }; + //const std::string channelnames[] = { "", "", "", "A" }; + //out = OIIO::ImageBufAlgo::channels(out, 4, channelorder, channelvalues, channelnames); + OIIO::ImageBuf tmp(OIIO::ImageSpec(spec.width, spec.height, 4, spec.format)); + OIIO::ImageBufAlgo::fill(tmp, { 0.F, 0.F, 0.F, 1.F }); + OIIO::ImageBufAlgo::copy( + tmp, + out, + OIIO::TypeUnknown, + OIIO::ROI(0, spec.width, 0, spec.height, 0, 1, 0, 3)); + out = tmp; } - return buf; + + return out; } std::string ReadNode::_getGraphLabel(const OTIO_NS::RationalTime&) const @@ -73,13 +123,16 @@ namespace toucan { offsetTime -= _timeOffset; } + + const int frame = offsetTime.floor().to_frames(); const std::filesystem::path path = getSequenceFrame( _base, _namePrefix, - offsetTime.to_frames(), + frame, _frameZeroPadding, _nameSuffix); OIIO::ImageBuf buf(path.string()); + const auto& spec = buf.spec(); if (3 == spec.nchannels) { diff --git a/lib/toucan/Util.cpp b/lib/toucan/Util.cpp index 6bc7c70..74c9643 100644 --- a/lib/toucan/Util.cpp +++ b/lib/toucan/Util.cpp @@ -87,6 +87,23 @@ namespace toucan } } + std::pair splitURLProtocol(const std::string& url) + { + std::pair out; + const size_t size = url.size(); + size_t pos = url.find("://"); + if (pos != std::string::npos) + { + out.first = url.substr(0, pos + 2); + out.second = url.substr(pos + 3); + } + else + { + out.second = url; + } + return out; + } + std::filesystem::path getSequenceFrame( const std::filesystem::path& path, const std::string& namePrefix, diff --git a/lib/toucan/Util.h b/lib/toucan/Util.h index 2737372..2c8d293 100644 --- a/lib/toucan/Util.h +++ b/lib/toucan/Util.h @@ -29,6 +29,9 @@ namespace toucan void anyToVec(const OTIO_NS::AnyVector&, IMATH_NAMESPACE::V2i&); void anyToVec(const OTIO_NS::AnyVector&, IMATH_NAMESPACE::V4f&); + //! Split the URL protocol. + std::pair splitURLProtocol(const std::string&); + //! Get an image sequence frame path. std::filesystem::path getSequenceFrame( const std::filesystem::path&,