mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-04-03 17:56:21 +03:00
0025382: Visualization, TKOpenGl - improved video recording capability
Image_VideoRecorder - added new class for video recording using FFmpeg framework. Draw Harness command vanimation has been extended with new options for video recording. New optional dependency has been introduced - CSF_FFmpeg.
This commit is contained in:
parent
14bbbdcbc1
commit
08f8a1854d
@ -561,6 +561,27 @@ else()
|
||||
OCCT_CHECK_AND_UNSET ("INSTALL_FREEIMAGE")
|
||||
endif()
|
||||
|
||||
# FFmpeg
|
||||
# search for CSF_FFmpeg variable in EXTERNLIB of each being used toolkit
|
||||
OCCT_IS_PRODUCT_REQUIRED (CSF_FFmpeg CAN_USE_FFMPEG)
|
||||
|
||||
if (CAN_USE_FFMPEG)
|
||||
set (USE_FFMPEG OFF CACHE BOOL "${USE_FFMPEG_DESCR}")
|
||||
|
||||
if (USE_FFMPEG)
|
||||
add_definitions (-DHAVE_FFMPEG)
|
||||
OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/ffmpeg")
|
||||
else()
|
||||
OCCT_CHECK_AND_UNSET_GROUP ("3RDPARTY_FFMPEG")
|
||||
OCCT_CHECK_AND_UNSET ("INSTALL_FFMPEG")
|
||||
endif()
|
||||
else()
|
||||
OCCT_CHECK_AND_UNSET ("USE_FFMPEG")
|
||||
|
||||
OCCT_CHECK_AND_UNSET_GROUP ("3RDPARTY_FFMPEG")
|
||||
OCCT_CHECK_AND_UNSET ("INSTALL_FFMPEG")
|
||||
endif()
|
||||
|
||||
# OpenGL ES 2.0
|
||||
if (WIN32 AND CAN_USE_GLES2)
|
||||
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "WindowsStore")
|
||||
|
228
adm/cmake/ffmpeg.cmake
Normal file
228
adm/cmake/ffmpeg.cmake
Normal file
@ -0,0 +1,228 @@
|
||||
# FFmpeg
|
||||
|
||||
if (NOT DEFINED INSTALL_FFMPEG)
|
||||
set (INSTALL_FFMPEG OFF CACHE BOOL "${INSTALL_FFMPEG_DESCR}")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED 3RDPARTY_FFMPEG_DIR)
|
||||
set (3RDPARTY_FFMPEG_DIR "" CACHE PATH "The directory containing FFmpeg")
|
||||
endif()
|
||||
|
||||
# include occt macros. compiler_bitness, os_wiht_bit, compiler
|
||||
OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/occt_macros")
|
||||
|
||||
# specify FFMPEG folder in connection with 3RDPARTY_DIR
|
||||
if (3RDPARTY_DIR AND EXISTS "${3RDPARTY_DIR}")
|
||||
if (NOT 3RDPARTY_FFMPEG_DIR OR NOT EXISTS "${3RDPARTY_FFMPEG_DIR}")
|
||||
FIND_PRODUCT_DIR ("${3RDPARTY_DIR}" FFMPEG FFMPEG_DIR_NAME)
|
||||
if (FFMPEG_DIR_NAME)
|
||||
set (3RDPARTY_FFMPEG_DIR "${3RDPARTY_DIR}/${FFMPEG_DIR_NAME}" CACHE PATH "The directory containing FFmpeg" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
endif()
|
||||
|
||||
# define required FFMPEG variables
|
||||
if (NOT DEFINED 3RDPARTY_FFMPEG_INCLUDE_DIR)
|
||||
set (3RDPARTY_FFMPEG_INCLUDE_DIR "" CACHE PATH "the path of headers directory")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED 3RDPARTY_FFMPEG_LIBRARY OR NOT 3RDPARTY_FFMPEG_LIBRARY_DIR OR NOT EXISTS "${3RDPARTY_FFMPEG_LIBRARY_DIR}")
|
||||
set (3RDPARTY_FFMPEG_LIBRARY "" CACHE FILEPATH "FFmpeg framework" FORCE)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED 3RDPARTY_FFMPEG_LIBRARY_DIR)
|
||||
set (3RDPARTY_FFMPEG_LIBRARY_DIR "" CACHE PATH "The directory containing FFmpeg framework")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (NOT DEFINED 3RDPARTY_FFMPEG_DLL OR NOT 3RDPARTY_FFMPEG_DLL_DIR OR NOT EXISTS "${3RDPARTY_FFMPEG_DLL_DIR}")
|
||||
set (3RDPARTY_FFMPEG_DLL "" CACHE FILEPATH "FFmpeg shared libraries" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (NOT DEFINED 3RDPARTY_FFMPEG_DLL_DIR)
|
||||
set (3RDPARTY_FFMPEG_DLL_DIR "" CACHE PATH "The directory containing FFmpeg shared libraries")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# check 3RDPARTY_${PRODUCT_NAME}_ paths for consistency with specified 3RDPARTY_${PRODUCT_NAME}_DIR
|
||||
if (3RDPARTY_FFMPEG_DIR AND EXISTS "${3RDPARTY_FFMPEG_DIR}")
|
||||
CHECK_PATH_FOR_CONSISTENCY (3RDPARTY_FFMPEG_DIR 3RDPARTY_FFMPEG_INCLUDE_DIR PATH "the path to FFmpeg")
|
||||
CHECK_PATH_FOR_CONSISTENCY (3RDPARTY_FFMPEG_DIR 3RDPARTY_FFMPEG_LIBRARY FILEPATH "the path to FFmpeg framework")
|
||||
|
||||
if (3RDPARTY_FFMPEG_LIBRARY AND EXISTS "${3RDPARTY_FFMPEG_LIBRARY}")
|
||||
get_filename_component (3RDPARTY_FFMPEG_LIBRARY_DIR "${3RDPARTY_FFMPEG_LIBRARY}" PATH)
|
||||
set (3RDPARTY_FFMPEG_LIBRARY_DIR "${3RDPARTY_FFMPEG_LIBRARY_DIR}" CACHE PATH "The directory containing FFmpeg libraries" FORCE)
|
||||
else()
|
||||
CHECK_PATH_FOR_CONSISTENCY (3RDPARTY_FFMPEG_DIR 3RDPARTY_FFMPEG_LIBRARY_DIR PATH "The directory containing FFmpeg libraries")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
CHECK_PATH_FOR_CONSISTENCY (3RDPARTY_FFMPEG_DIR 3RDPARTY_FFMPEG_DLL FILEPATH "the path to FFmpeg shared libraries")
|
||||
|
||||
if (3RDPARTY_FFMPEG_DLL AND EXISTS "${3RDPARTY_FFMPEG_DLL}")
|
||||
get_filename_component (3RDPARTY_FFMPEG_DLL_DIR "${3RDPARTY_FFMPEG_DLL}" PATH)
|
||||
set (3RDPARTY_FFMPEG_DLL_DIR "${3RDPARTY_FFMPEG_DLL_DIR}" CACHE PATH "The directory containing FFmpeg shared libraries" FORCE)
|
||||
else()
|
||||
CHECK_PATH_FOR_CONSISTENCY (3RDPARTY_FFMPEG_DIR 3RDPARTY_FFMPEG_DLL_DIR PATH "The directory containing FFmpeg shared libraries")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# header
|
||||
if (NOT 3RDPARTY_FFMPEG_INCLUDE_DIR OR NOT EXISTS "${3RDPARTY_FFMPEG_INCLUDE_DIR}")
|
||||
set (HEADER_NAMES avutil.h libavutil/avutil.h)
|
||||
|
||||
# set 3RDPARTY_FFMPEG_INCLUDE_DIR as notfound, otherwise find_library can't assign a new value to 3RDPARTY_FFMPEG_INCLUDE_DIR
|
||||
set (3RDPARTY_FFMPEG_INCLUDE_DIR "3RDPARTY_FFMPEG_INCLUDE_DIR-NOTFOUND" CACHE FILEPATH "the path to header directory" FORCE)
|
||||
|
||||
if (3RDPARTY_FFMPEG_DIR AND EXISTS "${3RDPARTY_FFMPEG_DIR}")
|
||||
find_path (3RDPARTY_FFMPEG_INCLUDE_DIR NAMES ${HEADER_NAMES}
|
||||
PATHS ${3RDPARTY_FFMPEG_DIR}
|
||||
PATH_SUFFIXES include
|
||||
CMAKE_FIND_ROOT_PATH_BOTH
|
||||
NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_path (3RDPARTY_FFMPEG_INCLUDE_DIR NAMES ${HEADER_NAMES}
|
||||
PATH_SUFFIXES include
|
||||
CMAKE_FIND_ROOT_PATH_BOTH)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (3RDPARTY_FFMPEG_INCLUDE_DIR AND EXISTS "${3RDPARTY_FFMPEG_INCLUDE_DIR}")
|
||||
list (APPEND 3RDPARTY_INCLUDE_DIRS "${3RDPARTY_FFMPEG_INCLUDE_DIR}")
|
||||
else()
|
||||
list (APPEND 3RDPARTY_NOT_INCLUDED 3RDPARTY_FFMPEG_INCLUDE_DIR)
|
||||
|
||||
set (3RDPARTY_FFMPEG_INCLUDE_DIR "" CACHE FILEPATH "the path to avutil.h" FORCE)
|
||||
endif()
|
||||
|
||||
# library
|
||||
if (NOT 3RDPARTY_FFMPEG_LIBRARY OR NOT EXISTS "${3RDPARTY_FFMPEG_LIBRARY}")
|
||||
set (CMAKE_FIND_LIBRARY_SUFFIXES .lib .so .dylib .a)
|
||||
|
||||
set (FFMPEG_PATH_SUFFIXES lib)
|
||||
if (ANDROID)
|
||||
set (FFMPEG_PATH_SUFFIXES ${FFMPEG_PATH_SUFFIXES} libs/${ANDROID_ABI})
|
||||
elseif(APPLE)
|
||||
set (FFMPEG_PATH_SUFFIXES ${FFMPEG_PATH_SUFFIXES} Frameworks)
|
||||
endif()
|
||||
|
||||
# set 3RDPARTY_FFMPEG_LIBRARY as notfound, otherwise find_library can't assign a new value to 3RDPARTY_FFMPEG_LIBRARY
|
||||
set (3RDPARTY_FFMPEG_LIBRARY "3RDPARTY_FFMPEG_LIBRARY-NOTFOUND" CACHE FILEPATH "The path to FFmpeg library" FORCE)
|
||||
|
||||
if (3RDPARTY_FFMPEG_DIR AND EXISTS "${3RDPARTY_FFMPEG_DIR}")
|
||||
find_library (3RDPARTY_FFMPEG_LIBRARY NAMES avutil
|
||||
PATHS "${3RDPARTY_FFMPEG_LIBRARY_DIR}" "${3RDPARTY_FFMPEG_DIR}"
|
||||
PATH_SUFFIXES ${FFMPEG_PATH_SUFFIXES}
|
||||
CMAKE_FIND_ROOT_PATH_BOTH
|
||||
NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_library (3RDPARTY_FFMPEG_LIBRARY NAMES avutil
|
||||
PATH_SUFFIXES ${FFMPEG_PATH_SUFFIXES}
|
||||
CMAKE_FIND_ROOT_PATH_BOTH)
|
||||
endif()
|
||||
|
||||
if (3RDPARTY_FFMPEG_LIBRARY AND EXISTS "${3RDPARTY_FFMPEG_LIBRARY}")
|
||||
get_filename_component (3RDPARTY_FFMPEG_LIBRARY_DIR "${3RDPARTY_FFMPEG_LIBRARY}" PATH)
|
||||
set (3RDPARTY_FFMPEG_LIBRARY_DIR "${3RDPARTY_FFMPEG_LIBRARY_DIR}" CACHE PATH "The directory containing FFmpeg library" FORCE)
|
||||
else()
|
||||
set (3RDPARTY_FFMPEG_LIBRARY_DIR "" CACHE PATH "The directory containing FFmpeg library" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (3RDPARTY_FFMPEG_LIBRARY_DIR AND EXISTS "${3RDPARTY_FFMPEG_LIBRARY_DIR}")
|
||||
list (APPEND 3RDPARTY_LIBRARY_DIRS "${3RDPARTY_FFMPEG_LIBRARY_DIR}")
|
||||
else()
|
||||
list (APPEND 3RDPARTY_NOT_INCLUDED 3RDPARTY_FFMPEG_LIBRARY_DIR)
|
||||
|
||||
set (3RDPARTY_FFMPEG_LIBRARY "" CACHE FILEPATH "The path to FFmpeg library" FORCE)
|
||||
endif()
|
||||
|
||||
# shared library
|
||||
if (WIN32)
|
||||
if (NOT 3RDPARTY_FFMPEG_DLL OR NOT EXISTS "${3RDPARTY_FFMPEG_DLL}")
|
||||
|
||||
set (CMAKE_FIND_LIBRARY_SUFFIXES .dll)
|
||||
set (3RDPARTY_FFMPEG_DLL "3RDPARTY_FFMPEG_DLL-NOTFOUND" CACHE FILEPATH "The path to FFmpeg shared library" FORCE)
|
||||
|
||||
# find FFmpeg shared library
|
||||
file (GLOB 3RDPARTY_FFMPEG_DLL "${3RDPARTY_FFMPEG_DIR}/bin/avutil[-][0-9]*")
|
||||
|
||||
if (3RDPARTY_FFMPEG_DLL AND EXISTS "${3RDPARTY_FFMPEG_DLL}")
|
||||
set (3RDPARTY_FFMPEG_DLL "${3RDPARTY_FFMPEG_DLL}" CACHE FILEPATH "FFmpeg shared library" FORCE)
|
||||
get_filename_component (3RDPARTY_FFMPEG_DLL_DIR "${3RDPARTY_FFMPEG_DLL}" PATH)
|
||||
set (3RDPARTY_FFMPEG_DLL_DIR "${3RDPARTY_FFMPEG_DLL_DIR}" CACHE PATH "The directory containing FFmpeg library" FORCE)
|
||||
else()
|
||||
set (3RDPARTY_FFMPEG_DLL_DIR "" CACHE PATH "The directory containing FFmpeg shared library" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (3RDPARTY_FFMPEG_DLL_DIR OR EXISTS "${3RDPARTY_FFMPEG_DLL_DIR}")
|
||||
list (APPEND 3RDPARTY_DLL_DIRS "${3RDPARTY_FFMPEG_DLL_DIR}")
|
||||
else()
|
||||
list (APPEND 3RDPARTY_NOT_INCLUDED 3RDPARTY_FFMPEG_DLL_DIR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# install instructions
|
||||
if (INSTALL_FFMPEG)
|
||||
OCCT_MAKE_OS_WITH_BITNESS()
|
||||
OCCT_MAKE_COMPILER_SHORT_NAME()
|
||||
|
||||
if (WIN32)
|
||||
if (DEFINED INSTALL_BIN_DIR)
|
||||
install (FILES "${3RDPARTY_FFMPEG_DLL}" DESTINATION "${INSTALL_BIN_DIR}")
|
||||
else()
|
||||
install (FILES "${3RDPARTY_FFMPEG_DLL}"
|
||||
CONFIGURATIONS Release
|
||||
DESTINATION "${INSTALL_DIR}/${OS_WITH_BIT}/${COMPILER}/bin")
|
||||
install (FILES "${3RDPARTY_FFMPEG_DLL}"
|
||||
CONFIGURATIONS RelWithDebInfo
|
||||
DESTINATION "${INSTALL_DIR}/${OS_WITH_BIT}/${COMPILER}/bini")
|
||||
install (FILES "${3RDPARTY_FFMPEG_DLL}"
|
||||
CONFIGURATIONS Debug
|
||||
DESTINATION "${INSTALL_DIR}/${OS_WITH_BIT}/${COMPILER}/bind")
|
||||
endif()
|
||||
else()
|
||||
get_filename_component(3RDPARTY_FFMPEG_LIBRARY_ABS ${3RDPARTY_FFMPEG_LIBRARY} REALPATH)
|
||||
get_filename_component(3RDPARTY_FFMPEG_LIBRARY_NAME ${3RDPARTY_FFMPEG_LIBRARY} NAME)
|
||||
|
||||
if (DEFINED INSTALL_LIB_DIR)
|
||||
install (FILES "${3RDPARTY_FFMPEG_LIBRARY_ABS}"
|
||||
DESTINATION "${INSTALL_LIB_DIR}"
|
||||
RENAME ${3RDPARTY_FFMPEG_LIBRARY_NAME}.6)
|
||||
else()
|
||||
install (FILES "${3RDPARTY_FFMPEG_LIBRARY_ABS}"
|
||||
CONFIGURATIONS Release
|
||||
DESTINATION "${INSTALL_DIR}/${OS_WITH_BIT}/${COMPILER}/lib"
|
||||
RENAME ${3RDPARTY_FFMPEG_LIBRARY_NAME}.6)
|
||||
install (FILES "${3RDPARTY_FFMPEG_LIBRARY_ABS}"
|
||||
CONFIGURATIONS RelWithDebInfo
|
||||
DESTINATION "${INSTALL_DIR}/${OS_WITH_BIT}/${COMPILER}/libi"
|
||||
RENAME ${3RDPARTY_FFMPEG_LIBRARY_NAME}.6)
|
||||
install (FILES "${3RDPARTY_FFMPEG_LIBRARY_ABS}"
|
||||
CONFIGURATIONS Debug
|
||||
DESTINATION "${INSTALL_DIR}/${OS_WITH_BIT}/${COMPILER}/libd"
|
||||
RENAME ${3RDPARTY_FFMPEG_LIBRARY_NAME}.6)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set (USED_3RDPARTY_FFMPEG_DIR "")
|
||||
else()
|
||||
# the library directory for using by the executable
|
||||
if (WIN32)
|
||||
set (USED_3RDPARTY_FFMPEG_DIR ${3RDPARTY_FFMPEG_DLL_DIR})
|
||||
else()
|
||||
set (USED_3RDPARTY_FFMPEG_DIR ${3RDPARTY_FFMPEG_LIBRARY_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# unset all redundant variables
|
||||
OCCT_CHECK_AND_UNSET (FFMPEG_INCLUDE_DIRS)
|
||||
OCCT_CHECK_AND_UNSET (FFMPEG_LIBRARY_DIRS)
|
||||
OCCT_CHECK_AND_UNSET (FFMPEG_DIR)
|
||||
|
||||
mark_as_advanced (3RDPARTY_FFMPEG_LIBRARY 3RDPARTY_FFMPEG_DLL)
|
@ -24,6 +24,13 @@ else()
|
||||
set (CSF_FREETYPE)
|
||||
endif()
|
||||
|
||||
# FFmpeg
|
||||
if (USE_FFMPEG)
|
||||
set (CSF_FFmpeg "avcodec avformat swscale avutil")
|
||||
else()
|
||||
set (CSF_FFmpeg)
|
||||
endif()
|
||||
|
||||
# FREEIMAGE
|
||||
if (USE_FREEIMAGE)
|
||||
set (CSF_FreeImagePlus "freeimage")
|
||||
|
@ -316,6 +316,7 @@ if (BUILD_USE_PCH)
|
||||
|
||||
# workaround for old gcc
|
||||
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
add_definitions("-D__STDC_CONSTANT_MACROS")
|
||||
add_definitions("-D__STDC_FORMAT_MACROS")
|
||||
endif()
|
||||
|
||||
|
@ -82,6 +82,7 @@ endmacro()
|
||||
INSTALL_MESSAGE (INSTALL_SAMPLES "OCCT samples")
|
||||
INSTALL_MESSAGE (INSTALL_TEST_CASES "non-regression OCCT test scripts")
|
||||
INSTALL_MESSAGE (INSTALL_DOC_Overview "OCCT overview documentation (HTML format)")
|
||||
INSTALL_MESSAGE (INSTALL_FFMPEG "FFmpeg binaries")
|
||||
INSTALL_MESSAGE (INSTALL_FREEIMAGE "FreeImage binaries")
|
||||
INSTALL_MESSAGE (INSTALL_EGL "EGL binaries")
|
||||
INSTALL_MESSAGE (INSTALL_GLES2 "OpenGL ES 2.0 binaries")
|
||||
@ -136,6 +137,10 @@ set (3RDPARTY_DIR_DESCR
|
||||
third-party product have been found - corresponding CMake variables will be specified
|
||||
(VTK: 3RDPARTY_VTK_DIR, 3RDPARTY_VTK_INCLUDE_DIR, 3RDPARTY_VTK_LIBRARY_DIR)")
|
||||
|
||||
set (USE_FFMPEG_DESCR
|
||||
"Indicates whether FFmpeg framework is used or not. FFmpeg stands for
|
||||
multimedia data handling, open-source software libraries used for video encoding and decoding.")
|
||||
|
||||
set (USE_FREEIMAGE_DESCR
|
||||
"Indicates whether Freeimage product should be used in OCCT visualization
|
||||
module for support of popular graphics image formats (PNG, BMP etc)")
|
||||
|
@ -119,6 +119,21 @@ proc wokdep:SearchHeader {theHeader} {
|
||||
if { [file exists "$aPath"] } {
|
||||
return "$aPath"
|
||||
}
|
||||
|
||||
if { "$::tcl_platform(os)" == "Linux" } {
|
||||
if { "$::ARCH" == "64" } {
|
||||
set aPath "/usr/include/x86_64-linux-gnu/${theHeader}"
|
||||
if { [file exists "$aPath"] } {
|
||||
return "$aPath"
|
||||
}
|
||||
} else {
|
||||
set aPath "/usr/include/i386-linux-gnu/${theHeader}"
|
||||
if { [file exists "$aPath"] } {
|
||||
return "$aPath"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@ set (OpenCASCADE_WITH_FREEIMAGE @USE_FREEIMAGE@)
|
||||
set (OpenCASCADE_WITH_GL2PS @USE_GL2PS@)
|
||||
set (OpenCASCADE_WITH_TBB @USE_TBB@)
|
||||
set (OpenCASCADE_WITH_VTK @USE_VTK@)
|
||||
set (OpenCASCADE_WITH_FFMPEG @USE_FFMPEG@)
|
||||
set (OpenCASCADE_WITH_GLES2 @USE_GLES2@)
|
||||
@SET_OpenCASCADE_WITH_D3D@
|
||||
@SET_OpenCASCADE_WITH_GLX@
|
||||
|
@ -14,6 +14,7 @@ if /I "%VCVER%" == "@COMPILER@" (
|
||||
set "GL2PS_DIR=@3RDPARTY_GL2PS_DLL_DIRS@"
|
||||
set "TBB_DIR=@3RDPARTY_TBB_DLL_DIR@"
|
||||
set "VTK_DIR=@3RDPARTY_VTK_DLL_DIR@"
|
||||
set "FFMPEG_DIR=@3RDPARTY_FFMPEG_DLL_DIR@"
|
||||
|
||||
if not "@3RDPARTY_QT_DIR@" == "" (
|
||||
set "QTDIR=@3RDPARTY_QT_DIR@"
|
||||
|
@ -12,6 +12,7 @@ if [ "$1" == "@BIN_LETTER@" ]; then
|
||||
export GL2PS_DIR="@3RDPARTY_GL2PS_LIBRARY_DIRS@"
|
||||
export TBB_DIR="@3RDPARTY_TBB_LIBRARY_DIR@"
|
||||
export VTK_DIR="@3RDPARTY_VTK_LIBRARY_DIR@"
|
||||
export FFMPEG_DIR="@3RDPARTY_FFMPEG_LIBRARY_DIR@"
|
||||
|
||||
if [ "x@3RDPARTY_QT_DIR" != "x" ]; then
|
||||
export QTDIR="@3RDPARTY_QT_DIR@"
|
||||
|
@ -16,6 +16,7 @@ if /I "%VCVER%" == "@COMPILER@" (
|
||||
set "GL2PS_DIR=@USED_3RDPARTY_GL2PS_DIRS@"
|
||||
set "TBB_DIR=@USED_3RDPARTY_TBB_DIR@"
|
||||
set "VTK_DIR=@USED_3RDPARTY_VTK_DIR@"
|
||||
set "FFMPEG_DIR=@USED_3RDPARTY_FFMPEG_DIR@"
|
||||
|
||||
if not "@USED_3RDPARTY_QT_DIR@" == "" (
|
||||
set "QTDIR=@USED_3RDPARTY_QT_DIR@"
|
||||
|
@ -12,6 +12,7 @@ if [ "$1" == "@BIN_LETTER@" ]; then
|
||||
export GL2PS_DIR="@USED_3RDPARTY_GL2PS_DIRS@"
|
||||
export TBB_DIR="@USED_3RDPARTY_TBB_DIR@"
|
||||
export VTK_DIR="@USED_3RDPARTY_VTK_DIR@"
|
||||
export FFMPEG_DIR="@USED_3RDPARTY_FFMPEG_DIR@"
|
||||
|
||||
if [ "x@USED_3RDPARTY_QT_DIR@" != "x" ]; then
|
||||
export QTDIR="@USED_3RDPARTY_QT_DIR@"
|
||||
|
@ -115,6 +115,7 @@ if not ["%GLES2_DIR%"] == [""] set "PATH=%GLES2_DIR%;%PATH%"
|
||||
if not ["%GL2PS_DIR%"] == [""] set "PATH=%GL2PS_DIR%;%PATH%"
|
||||
if not ["%TBB_DIR%"] == [""] set "PATH=%TBB_DIR%;%PATH%"
|
||||
if not ["%VTK_DIR%"] == [""] set "PATH=%VTK_DIR%;%PATH%"
|
||||
if not ["%FFMPEG_DIR%"] == [""] set "PATH=%FFMPEG_DIR%;%PATH%"
|
||||
if not ["%QTDIR%"] == [""] set "PATH=%QTDIR%/bin;%PATH%"
|
||||
|
||||
rem ----- Set path to 3rd party and OCCT libraries -----
|
||||
|
@ -66,6 +66,10 @@ if [ "$VTK_DIR" != "" ]; then
|
||||
THRDPARTY_PATH="${VTK_DIR}:${THRDPARTY_PATH}"
|
||||
fi
|
||||
|
||||
if [ "$FFMPEG_DIR" != "" ]; then
|
||||
THRDPARTY_PATH="${FFMPEG_DIR}:${THRDPARTY_PATH}"
|
||||
fi
|
||||
|
||||
if [ "$QTDIR" != "" ]; then
|
||||
THRDPARTY_PATH="${QTDIR}/lib:${THRDPARTY_PATH}"
|
||||
fi
|
||||
|
@ -135,6 +135,9 @@ by fully automating techniques as precompiled header usage and single compilatio
|
||||
Cotire is included in OCCT repository and used optionally by OCCT CMake scripts to accelerate builds by use of precompiled headers.
|
||||
Cotire is licensed under the MIT license (https://github.com/sakra/cotire/blob/master/license).
|
||||
|
||||
**FFmpeg** is an Open Source framework supporting various image, video and audio codecs.
|
||||
FFmpeg is optionally used by OCCT for video recording, on LGPL conditions (https://www.ffmpeg.org/legal.html).
|
||||
|
||||
**MikTEX** is up-to-date implementation of TeX/LaTeX and related programs for Windows. It is used
|
||||
for generation of User and Developer Guides in PDF format. See https://miktex.org for information
|
||||
on this tool.
|
||||
@ -210,6 +213,7 @@ for which OCCT is certified to work.
|
||||
| TCL (for testing tools) | Tcl/Tk 8.6.3+ http://www.tcl.tk/software/tcltk/download.html <br> or ActiveTcl 8.6 http://www.activestate.com/activetcl/downloads (for Windows)|
|
||||
| Freetype (for text rendering) | FreeType 2.4.11-2.5.5 http://sourceforge.net/projects/freetype/files/ |
|
||||
| FreeImage (optional, for support of common 2D graphic formats) | FreeImage 3.17.0+ http://sourceforge.net/projects/freeimage/files |
|
||||
| FFmpeg (optional, for video recording) | FFmpeg 3.1+ https://www.ffmpeg.org |
|
||||
| gl2ps (optional, for export contents of OCCT viewer to vector formats) | gl2ps-1.3.8+ http://geuz.org/gl2ps/ |
|
||||
| Intel TBB (optional, for multithreaded algorithms) | TBB 4.x or 5.x http://www.threadingbuildingblocks.org/ |
|
||||
| VTK (for VTK Integration Services | VTK 6.1+ http://www.vtk.org/VTK/resources/software.html |
|
||||
|
4
genconf
4
genconf
@ -6,8 +6,10 @@
|
||||
# initialize environment
|
||||
aScriptPath=${BASH_SOURCE%/*}; if [ -d "${aScriptPath}" ]; then cd "$aScriptPath"; fi; aScriptPath="$PWD";
|
||||
if [ ! -e "${aScriptPath}/env.sh" ]; then
|
||||
cat ${aScriptPath}/adm/templates/env.sh | sed -e '/__CASROOT__/d' > ${aScriptPath}/env.sh
|
||||
cat ${aScriptPath}/adm/templates/env.sh | sed -e '/__CASROOT__/d' > ${aScriptPath}/env.sh
|
||||
fi
|
||||
|
||||
if [ -e "${aScriptPath}/custom.sh" ]; then source "${aScriptPath}/custom.sh"; fi
|
||||
|
||||
# run GUI tool
|
||||
tclsh "${aScriptPath}/adm/genconf.tcl"
|
||||
|
@ -350,6 +350,11 @@ static Standard_Integer dversion(Draw_Interpretor& di, Standard_Integer, const c
|
||||
#else
|
||||
di << "FreeImage disabled\n";
|
||||
#endif
|
||||
#ifdef HAVE_FFMPEG
|
||||
di << "FFmpeg enabled (HAVE_FFMPEG)\n";
|
||||
#else
|
||||
di << "FFmpeg disabled\n";
|
||||
#endif
|
||||
#ifdef HAVE_GLES2
|
||||
di << "OpenGL: ES2\n";
|
||||
#else
|
||||
|
@ -7,3 +7,5 @@ Image_Format.hxx
|
||||
Image_PixMap.cxx
|
||||
Image_PixMap.hxx
|
||||
Image_PixMapData.hxx
|
||||
Image_VideoRecorder.cxx
|
||||
Image_VideoRecorder.hxx
|
||||
|
509
src/Image/Image_VideoRecorder.cxx
Normal file
509
src/Image/Image_VideoRecorder.cxx
Normal file
@ -0,0 +1,509 @@
|
||||
// Created on: 2016-04-01
|
||||
// Created by: Anastasia BORISOVA
|
||||
// Copyright (c) 2016 OPEN CASCADE SAS
|
||||
//
|
||||
// This file is part of Open CASCADE Technology software library.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||
// by the Free Software Foundation, with special exception defined in the file
|
||||
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||
// distribution for complete text of the license and disclaimer of any warranty.
|
||||
//
|
||||
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||
// commercial license or contractual agreement.
|
||||
|
||||
// activate some C99 macros like UINT64_C in "stdint.h" which used by FFmpeg
|
||||
#ifndef __STDC_CONSTANT_MACROS
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#endif
|
||||
|
||||
#include <Image_VideoRecorder.hxx>
|
||||
|
||||
#include <Message.hxx>
|
||||
#include <Message_Messenger.hxx>
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
|
||||
// Suppress deprecation warnings - it is difficult to provide compatibility with old and new API at once
|
||||
// since new APIs are introduced too often.
|
||||
// Should be disabled from time to time to clean up usage of old APIs.
|
||||
#if (defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
|
||||
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
|
||||
#else
|
||||
Standard_DISABLE_DEPRECATION_WARNINGS
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
// suppress some common warnings in FFmpeg headers
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default : 4244)
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
IMPLEMENT_STANDARD_RTTIEXT(Image_VideoRecorder, Standard_Transient)
|
||||
|
||||
//=============================================================================
|
||||
//function : Constructor
|
||||
//purpose :
|
||||
//=============================================================================
|
||||
Image_VideoRecorder::Image_VideoRecorder()
|
||||
: myAVContext (NULL),
|
||||
myVideoStream (NULL),
|
||||
myVideoCodec (NULL),
|
||||
myFrame (NULL),
|
||||
myScaleCtx (NULL),
|
||||
myFrameCount (0)
|
||||
{
|
||||
myFrameRate.num = 1;
|
||||
myFrameRate.den = 30;
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
// initialize libavcodec, and register all codecs and formats, should be done once
|
||||
av_register_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//function : ~Image_VideoRecorder
|
||||
//purpose :
|
||||
//=============================================================================
|
||||
Image_VideoRecorder::~Image_VideoRecorder()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//function : formatAvError
|
||||
//purpose :
|
||||
//=============================================================================
|
||||
TCollection_AsciiString Image_VideoRecorder::formatAvError (const int theError) const
|
||||
{
|
||||
#ifdef HAVE_FFMPEG
|
||||
char anErrBuf[AV_ERROR_MAX_STRING_SIZE] = {};
|
||||
av_strerror (theError, anErrBuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
return anErrBuf;
|
||||
#else
|
||||
return TCollection_AsciiString(theError);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//function : Close
|
||||
//purpose :
|
||||
//=============================================================================
|
||||
void Image_VideoRecorder::Close()
|
||||
{
|
||||
#ifdef HAVE_FFMPEG
|
||||
if (myScaleCtx != NULL)
|
||||
{
|
||||
sws_freeContext (myScaleCtx);
|
||||
myScaleCtx = NULL;
|
||||
}
|
||||
|
||||
if (myAVContext == NULL)
|
||||
{
|
||||
myFrameCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Write the trailer, if any. The trailer must be written before you close the CodecContexts open when you wrote the header;
|
||||
// otherwise av_write_trailer() may try to use memory that was freed on av_codec_close().
|
||||
if (myFrameCount != 0)
|
||||
{
|
||||
av_write_trailer (myAVContext);
|
||||
myFrameCount = 0;
|
||||
}
|
||||
|
||||
// close each codec
|
||||
if (myVideoStream != NULL)
|
||||
{
|
||||
avcodec_close (myVideoStream->codec);
|
||||
myVideoStream = NULL;
|
||||
}
|
||||
if (myFrame != NULL)
|
||||
{
|
||||
av_free (myFrame->data[0]);
|
||||
av_frame_free (&myFrame);
|
||||
myFrame = NULL;
|
||||
}
|
||||
|
||||
if (!(myAVContext->oformat->flags & AVFMT_NOFILE))
|
||||
{
|
||||
// close the output file
|
||||
avio_close (myAVContext->pb);
|
||||
}
|
||||
|
||||
// free the stream
|
||||
avformat_free_context (myAVContext);
|
||||
myAVContext = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//function : Open
|
||||
//purpose :
|
||||
//=============================================================================
|
||||
Standard_Boolean Image_VideoRecorder::Open (const char* theFileName,
|
||||
const Image_VideoParams& theParams)
|
||||
{
|
||||
#ifdef HAVE_FFMPEG
|
||||
Close();
|
||||
if (theParams.Width <= 0
|
||||
|| theParams.Height <= 0)
|
||||
{
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
// allocate the output media context
|
||||
avformat_alloc_output_context2 (&myAVContext, NULL, theParams.Format.IsEmpty() ? NULL : theParams.Format.ToCString(), theFileName);
|
||||
if (myAVContext == NULL)
|
||||
{
|
||||
::Message::DefaultMessenger()->Send ("ViewerTest_VideoRecorder, could not deduce output format from file extension", Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
// add the audio stream using the default format codecs and initialize the codecs
|
||||
if (!addVideoStream (theParams, myAVContext->oformat->video_codec))
|
||||
{
|
||||
Close();
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
// open video codec and allocate the necessary encode buffers
|
||||
if (!openVideoCodec (theParams))
|
||||
{
|
||||
Close();
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
#ifdef OCCT_DEBUG
|
||||
av_dump_format (myAVContext, 0, theFileName, 1);
|
||||
#endif
|
||||
|
||||
// open the output file, if needed
|
||||
if ((myAVContext->oformat->flags & AVFMT_NOFILE) == 0)
|
||||
{
|
||||
const int aResAv = avio_open (&myAVContext->pb, theFileName, AVIO_FLAG_WRITE);
|
||||
if (aResAv < 0)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: could not open '") + theFileName + "', " + formatAvError (aResAv);
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
Close();
|
||||
return Standard_False;
|
||||
}
|
||||
}
|
||||
|
||||
// write the stream header, if any
|
||||
const int aResAv = avformat_write_header (myAVContext, NULL);
|
||||
if (aResAv < 0)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not open output file '") + theFileName + "', " + formatAvError (aResAv);
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
Close();
|
||||
return Standard_False;
|
||||
}
|
||||
#else
|
||||
(void )theFileName;
|
||||
(void )theParams;
|
||||
#endif
|
||||
return Standard_True;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//function : addVideoStream
|
||||
//purpose :
|
||||
//=============================================================================
|
||||
Standard_Boolean Image_VideoRecorder::addVideoStream (const Image_VideoParams& theParams,
|
||||
const Standard_Integer theDefCodecId)
|
||||
{
|
||||
myFrameRate.num = theParams.FpsNum;
|
||||
myFrameRate.den = theParams.FpsDen;
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
// find the encoder
|
||||
TCollection_AsciiString aCodecName;
|
||||
if (!theParams.VideoCodec.IsEmpty())
|
||||
{
|
||||
myVideoCodec = avcodec_find_encoder_by_name (theParams.VideoCodec.ToCString());
|
||||
aCodecName = theParams.VideoCodec;
|
||||
}
|
||||
else
|
||||
{
|
||||
myVideoCodec = avcodec_find_encoder ((AVCodecID )theDefCodecId);
|
||||
aCodecName = avcodec_get_name ((AVCodecID )theDefCodecId);
|
||||
}
|
||||
if (myVideoCodec == NULL)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not find encoder for ") + aCodecName;
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
const AVCodecID aCodecId = myVideoCodec->id;
|
||||
myVideoStream = avformat_new_stream (myAVContext, myVideoCodec);
|
||||
if (myVideoStream == NULL)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not allocate stream!");
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
myVideoStream->id = myAVContext->nb_streams - 1;
|
||||
|
||||
AVCodecContext* aCodecCtx = myVideoStream->codec;
|
||||
aCodecCtx->codec_id = aCodecId;
|
||||
// resolution must be a multiple of two
|
||||
aCodecCtx->width = theParams.Width;
|
||||
aCodecCtx->height = theParams.Height;
|
||||
// Timebase is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented.
|
||||
// For fixed-fps content, timebase should be 1/framerate and timestamp increments should be identical to 1.
|
||||
aCodecCtx->time_base.den = myFrameRate.num;
|
||||
aCodecCtx->time_base.num = myFrameRate.den;
|
||||
aCodecCtx->gop_size = 12; // emit one intra frame every twelve frames at most
|
||||
|
||||
// some formats want stream headers to be separate
|
||||
if (myAVContext->oformat->flags & AVFMT_GLOBALHEADER)
|
||||
{
|
||||
aCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
return Standard_True;
|
||||
#else
|
||||
(void )theParams;
|
||||
(void )theDefCodecId;
|
||||
return Standard_False;
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//function : openVideoCodec
|
||||
//purpose :
|
||||
//=============================================================================
|
||||
Standard_Boolean Image_VideoRecorder::openVideoCodec (const Image_VideoParams& theParams)
|
||||
{
|
||||
#ifdef HAVE_FFMPEG
|
||||
AVDictionary* anOptions = NULL;
|
||||
AVCodecContext* aCodecCtx = myVideoStream->codec;
|
||||
|
||||
// setup default values
|
||||
aCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
|
||||
//av_dict_set (&anOptions, "threads", "auto", 0);
|
||||
if (aCodecCtx->codec == avcodec_find_encoder_by_name ("mpeg2video"))
|
||||
{
|
||||
// just for testing, we also add B frames
|
||||
aCodecCtx->max_b_frames = 2;
|
||||
aCodecCtx->bit_rate = 6000000;
|
||||
}
|
||||
else if (aCodecCtx->codec == avcodec_find_encoder_by_name ("mpeg4"))
|
||||
{
|
||||
//
|
||||
}
|
||||
else if (aCodecCtx->codec == avcodec_find_encoder_by_name ("mjpeg"))
|
||||
{
|
||||
aCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
|
||||
aCodecCtx->qmin = aCodecCtx->qmax = 5;
|
||||
}
|
||||
else if (aCodecCtx->codec == avcodec_find_encoder_by_name ("huffyuv"))
|
||||
{
|
||||
aCodecCtx->pix_fmt = AV_PIX_FMT_RGB24;
|
||||
}
|
||||
else if (aCodecCtx->codec == avcodec_find_encoder_by_name ("png"))
|
||||
{
|
||||
aCodecCtx->pix_fmt = AV_PIX_FMT_RGB24;
|
||||
aCodecCtx->compression_level = 9; // 0..9
|
||||
}
|
||||
else if (aCodecCtx->codec == avcodec_find_encoder_by_name ("h264")
|
||||
|| aCodecCtx->codec == avcodec_find_encoder_by_name ("libx264"))
|
||||
{
|
||||
// use CRF (Constant Rate Factor) as best single-pass compressing method
|
||||
av_dict_set (&anOptions, "crf", "20", 0); // quality 18-28, 23 is default (normal), 18 is almost lossless
|
||||
av_dict_set (&anOptions, "preset", "slow", 0); // good compression (see also "veryslow", "ultrafast")
|
||||
|
||||
// live-capturing
|
||||
//av_dict_set (&anOptions, "qp", "0", 0); // instead of crf
|
||||
//av_dict_set (&anOptions, "preset", "ultrafast", 0);
|
||||
|
||||
// compatibility with devices
|
||||
//av_dict_set (&anOptions, "profile", "baseline", 0);
|
||||
//av_dict_set (&anOptions, "level", "3.0", 0);
|
||||
}
|
||||
else if (aCodecCtx->codec == avcodec_find_encoder_by_name ("vp8")
|
||||
|| aCodecCtx->codec == avcodec_find_encoder_by_name ("vp9"))
|
||||
{
|
||||
av_dict_set (&anOptions, "crf", "20", 0); // quality 4–63, 10 is normal
|
||||
}
|
||||
|
||||
// override defaults with specified options
|
||||
if (!theParams.PixelFormat.IsEmpty())
|
||||
{
|
||||
const AVPixelFormat aPixFormat = av_get_pix_fmt (theParams.PixelFormat.ToCString());
|
||||
if (aPixFormat == AV_PIX_FMT_NONE)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: unknown pixel format has been specified '") + theParams.PixelFormat + "'";
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
aCodecCtx->pix_fmt = aPixFormat;
|
||||
for (Resource_DataMapOfAsciiStringAsciiString::Iterator aParamIter (theParams.VideoCodecParams);
|
||||
aParamIter.More(); aParamIter.Next())
|
||||
{
|
||||
av_dict_set (&anOptions, aParamIter.Key().ToCString(), aParamIter.Value().ToCString(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// open codec
|
||||
int aResAv = avcodec_open2 (aCodecCtx, myVideoCodec, &anOptions);
|
||||
if (anOptions != NULL)
|
||||
{
|
||||
av_dict_free (&anOptions);
|
||||
}
|
||||
if (aResAv < 0)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not open video codec, ") + formatAvError (aResAv);
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
// allocate and init a re-usable frame
|
||||
myFrame = av_frame_alloc();
|
||||
if (myFrame == NULL)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not allocate video frame!");
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
// allocate the encoded raw picture
|
||||
aResAv = av_image_alloc (myFrame->data, myFrame->linesize,
|
||||
aCodecCtx->width, aCodecCtx->height, aCodecCtx->pix_fmt, 1);
|
||||
if (aResAv < 0)
|
||||
{
|
||||
memset (myFrame->data, 0, sizeof(myFrame->data));
|
||||
memset (myFrame->linesize, 0, sizeof(myFrame->linesize));
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not allocate picture ")
|
||||
+ aCodecCtx->width+ "x" + aCodecCtx->height + ", " + formatAvError (aResAv);
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
// copy data and linesize picture pointers to frame
|
||||
myFrame->format = aCodecCtx->pix_fmt;
|
||||
myFrame->width = aCodecCtx->width;
|
||||
myFrame->height = aCodecCtx->height;
|
||||
|
||||
const Standard_Size aStride = aCodecCtx->width + 16 - (aCodecCtx->width % 16);
|
||||
if (!myImgSrcRgba.InitZero (Image_PixMap::ImgRGBA, aCodecCtx->width, aCodecCtx->height, aStride))
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not allocate RGBA32 picture ")
|
||||
+ aCodecCtx->width+ "x" + aCodecCtx->height;
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
myScaleCtx = sws_getContext (aCodecCtx->width, aCodecCtx->height, AV_PIX_FMT_RGBA,
|
||||
aCodecCtx->width, aCodecCtx->height, aCodecCtx->pix_fmt,
|
||||
SWS_BICUBIC, NULL, NULL, NULL);
|
||||
if (myScaleCtx == NULL)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not initialize the conversion context!");
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
return Standard_True;
|
||||
#else
|
||||
(void )theParams;
|
||||
return Standard_False;
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//function : writeVideoFrame
|
||||
//purpose :
|
||||
//=============================================================================
|
||||
Standard_Boolean Image_VideoRecorder::writeVideoFrame (const Standard_Boolean theToFlush)
|
||||
{
|
||||
#ifdef HAVE_FFMPEG
|
||||
int aResAv = 0;
|
||||
AVCodecContext* aCodecCtx = myVideoStream->codec;
|
||||
if (!theToFlush)
|
||||
{
|
||||
uint8_t* aSrcData[4] = { (uint8_t*)myImgSrcRgba.ChangeData(), NULL, NULL, NULL };
|
||||
int aSrcLinesize[4] = { (int )myImgSrcRgba.SizeRowBytes(), 0, 0, 0 };
|
||||
sws_scale (myScaleCtx,
|
||||
aSrcData, aSrcLinesize,
|
||||
0, aCodecCtx->height,
|
||||
myFrame->data, myFrame->linesize);
|
||||
}
|
||||
|
||||
AVPacket aPacket;
|
||||
memset (&aPacket, 0, sizeof(aPacket));
|
||||
av_init_packet (&aPacket);
|
||||
if ((myAVContext->oformat->flags & AVFMT_RAWPICTURE) != 0
|
||||
&& !theToFlush)
|
||||
{
|
||||
// raw video case - directly store the picture in the packet
|
||||
aPacket.flags |= AV_PKT_FLAG_KEY;
|
||||
aPacket.stream_index = myVideoStream->index;
|
||||
aPacket.data = myFrame->data[0];
|
||||
aPacket.size = sizeof(AVPicture);
|
||||
|
||||
aResAv = av_interleaved_write_frame (myAVContext, &aPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
// encode the image
|
||||
myFrame->pts = myFrameCount;
|
||||
int isGotPacket = 0;
|
||||
aResAv = avcodec_encode_video2 (aCodecCtx, &aPacket, theToFlush ? NULL : myFrame, &isGotPacket);
|
||||
if (aResAv < 0)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not encode video frame, ") + formatAvError (aResAv);
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
// if size is zero, it means the image was buffered
|
||||
if (isGotPacket)
|
||||
{
|
||||
const AVRational& aTimeBase = aCodecCtx->time_base;
|
||||
|
||||
// rescale output packet timestamp values from codec to stream timebase
|
||||
aPacket.pts = av_rescale_q_rnd (aPacket.pts, aTimeBase, myVideoStream->time_base, AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
|
||||
aPacket.dts = av_rescale_q_rnd (aPacket.dts, aTimeBase, myVideoStream->time_base, AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
|
||||
aPacket.duration = av_rescale_q (aPacket.duration, aTimeBase, myVideoStream->time_base);
|
||||
aPacket.stream_index = myVideoStream->index;
|
||||
|
||||
// write the compressed frame to the media file
|
||||
aResAv = av_interleaved_write_frame (myAVContext, &aPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
aResAv = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (aResAv < 0)
|
||||
{
|
||||
const TCollection_AsciiString aMsg = TCollection_AsciiString ("Error: can not write video frame, ") + formatAvError (aResAv);
|
||||
::Message::DefaultMessenger()->Send (aMsg, Message_Fail);
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
++myFrameCount;
|
||||
return Standard_True;
|
||||
#else
|
||||
(void)theToFlush;
|
||||
return Standard_False;
|
||||
#endif
|
||||
}
|
141
src/Image/Image_VideoRecorder.hxx
Normal file
141
src/Image/Image_VideoRecorder.hxx
Normal file
@ -0,0 +1,141 @@
|
||||
// Created on: 2016-04-01
|
||||
// Created by: Anastasia BORISOVA
|
||||
// Copyright (c) 2016 OPEN CASCADE SAS
|
||||
//
|
||||
// This file is part of Open CASCADE Technology software library.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||
// by the Free Software Foundation, with special exception defined in the file
|
||||
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||
// distribution for complete text of the license and disclaimer of any warranty.
|
||||
//
|
||||
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||
// commercial license or contractual agreement.
|
||||
|
||||
#ifndef Image_VideoRecorder_HeaderFile_
|
||||
#define Image_VideoRecorder_HeaderFile_
|
||||
|
||||
#include <Image_PixMap.hxx>
|
||||
#include <Resource_DataMapOfAsciiStringAsciiString.hxx>
|
||||
#include <Standard_Transient.hxx>
|
||||
#include <TCollection_AsciiString.hxx>
|
||||
|
||||
// forward declarations
|
||||
struct AVFormatContext;
|
||||
struct AVStream;
|
||||
struct AVCodec;
|
||||
struct AVFrame;
|
||||
struct SwsContext;
|
||||
|
||||
//! Auxiliary structure defining video parameters.
|
||||
//! Please refer to FFmpeg documentation for defining text values.
|
||||
struct Image_VideoParams
|
||||
{
|
||||
TCollection_AsciiString Format; //!< [optional] video format (container), if empty - will be determined from the file name
|
||||
TCollection_AsciiString VideoCodec; //!< [optional] codec identifier, if empty - default codec from file format will be used
|
||||
TCollection_AsciiString PixelFormat; //!< [optional] pixel format, if empty - default codec pixel format will be used
|
||||
Standard_Integer Width; //!< [mandatory] video frame width
|
||||
Standard_Integer Height; //!< [mandatory] video frame height
|
||||
Standard_Integer FpsNum; //!< [mandatory] framerate numerator
|
||||
Standard_Integer FpsDen; //!< [mandatory] framerate denumerator
|
||||
Resource_DataMapOfAsciiStringAsciiString
|
||||
VideoCodecParams; //!< map of advanced video codec parameters
|
||||
|
||||
//! Empty constructor.
|
||||
Image_VideoParams() : Width (0), Height (0), FpsNum (0), FpsDen (1) {}
|
||||
|
||||
//! Setup playback FPS.
|
||||
void SetFramerate (const Standard_Integer theNumerator,
|
||||
const Standard_Integer theDenominator)
|
||||
{
|
||||
FpsNum = theNumerator;
|
||||
FpsDen = theDenominator;
|
||||
}
|
||||
|
||||
//! Setup playback FPS.
|
||||
//! For fixed-fps content, timebase should be 1/framerate and timestamp increments should be identical to 1.
|
||||
void SetFramerate (const Standard_Integer theValue)
|
||||
{
|
||||
FpsNum = theValue;
|
||||
FpsDen = 1;
|
||||
}
|
||||
};
|
||||
|
||||
//! Video recording tool based on FFmpeg framework.
|
||||
class Image_VideoRecorder : public Standard_Transient
|
||||
{
|
||||
DEFINE_STANDARD_RTTIEXT(Image_VideoRecorder, Standard_Transient)
|
||||
public:
|
||||
|
||||
//! Empty constructor.
|
||||
Standard_EXPORT Image_VideoRecorder();
|
||||
|
||||
//! Destructor.
|
||||
Standard_EXPORT virtual ~Image_VideoRecorder();
|
||||
|
||||
//! Close the stream - stop recorder.
|
||||
Standard_EXPORT void Close();
|
||||
|
||||
//! Open output stream - initialize recorder.
|
||||
//! @param theFileName [in] video filename
|
||||
//! @param theParams [in] video parameters
|
||||
Standard_EXPORT Standard_Boolean Open (const char* theFileName,
|
||||
const Image_VideoParams& theParams);
|
||||
|
||||
//! Access RGBA frame, should NOT be re-initialized outside.
|
||||
//! Note that image is expected to have upper-left origin.
|
||||
Image_PixMap& ChangeFrame() { return myImgSrcRgba; }
|
||||
|
||||
//! Return current frame index.
|
||||
int64_t FrameCount() const { return myFrameCount; }
|
||||
|
||||
//! Push new frame, should be called after Open().
|
||||
Standard_Boolean PushFrame()
|
||||
{
|
||||
return writeVideoFrame (Standard_False);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//! Wrapper for av_strerror().
|
||||
Standard_EXPORT TCollection_AsciiString formatAvError (const int theError) const;
|
||||
|
||||
//! Append video stream.
|
||||
//! theParams [in] video parameters
|
||||
//! theDefCodecId [in] identifier of codec managed by FFmpeg library (AVCodecID enum)
|
||||
Standard_EXPORT Standard_Boolean addVideoStream (const Image_VideoParams& theParams,
|
||||
const Standard_Integer theDefCodecId);
|
||||
|
||||
//! Open video codec.
|
||||
Standard_EXPORT Standard_Boolean openVideoCodec (const Image_VideoParams& theParams);
|
||||
|
||||
//! Write new video frame.
|
||||
Standard_EXPORT Standard_Boolean writeVideoFrame (const Standard_Boolean theToFlush);
|
||||
|
||||
protected:
|
||||
|
||||
//! AVRational alias.
|
||||
struct VideoRational
|
||||
{
|
||||
int num; //!< numerator
|
||||
int den; //!< denominator
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
AVFormatContext* myAVContext; //!< video context
|
||||
AVStream* myVideoStream; //!< video stream
|
||||
AVCodec* myVideoCodec; //!< video codec
|
||||
AVFrame* myFrame; //!< frame to record
|
||||
SwsContext* myScaleCtx; //!< scale context for conversion from RGBA to YUV
|
||||
|
||||
Image_PixMap myImgSrcRgba; //!< input RGBA image
|
||||
VideoRational myFrameRate; //!< video framerate
|
||||
int64_t myFrameCount; //!< current frame index
|
||||
|
||||
};
|
||||
|
||||
DEFINE_STANDARD_HANDLE(Image_VideoRecorder, Standard_Transient)
|
||||
|
||||
#endif // Image_VideoRecorder_HeaderFile_
|
@ -16,3 +16,4 @@ CSF_Appkit
|
||||
CSF_IOKit
|
||||
CSF_FreeImagePlus
|
||||
CSF_FREETYPE
|
||||
CSF_FFmpeg
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <AIS_ListOfInteractive.hxx>
|
||||
#include <AIS_ListIteratorOfListOfInteractive.hxx>
|
||||
#include <DBRep.hxx>
|
||||
#include <Draw_ProgressIndicator.hxx>
|
||||
#include <Graphic3d_ArrayOfPolylines.hxx>
|
||||
#include <Graphic3d_AspectMarker3d.hxx>
|
||||
#include <Graphic3d_ExportFormat.hxx>
|
||||
@ -46,6 +47,7 @@
|
||||
#include <V3d_DirectionalLight.hxx>
|
||||
#include <V3d_PositionalLight.hxx>
|
||||
#include <V3d_SpotLight.hxx>
|
||||
#include <Message_ProgressSentry.hxx>
|
||||
#include <NCollection_DoubleMap.hxx>
|
||||
#include <NCollection_List.hxx>
|
||||
#include <NCollection_Vector.hxx>
|
||||
@ -54,6 +56,7 @@
|
||||
#include <Draw.hxx>
|
||||
#include <Draw_Appli.hxx>
|
||||
#include <Image_AlienPixMap.hxx>
|
||||
#include <Image_VideoRecorder.hxx>
|
||||
#include <OpenGl_GraphicDriver.hxx>
|
||||
#include <OSD_Timer.hxx>
|
||||
#include <TColStd_HSequenceOfAsciiString.hxx>
|
||||
@ -6262,6 +6265,48 @@ namespace
|
||||
return Standard_True;
|
||||
}
|
||||
|
||||
//! Auxiliary class for flipping image upside-down.
|
||||
class ImageFlipper
|
||||
{
|
||||
public:
|
||||
|
||||
//! Empty constructor.
|
||||
ImageFlipper() : myTmp (NCollection_BaseAllocator::CommonBaseAllocator()) {}
|
||||
|
||||
//! Perform flipping.
|
||||
Standard_Boolean FlipY (Image_PixMap& theImage)
|
||||
{
|
||||
if (theImage.IsEmpty()
|
||||
|| theImage.SizeX() == 0
|
||||
|| theImage.SizeY() == 0)
|
||||
{
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
const Standard_Size aRowSize = theImage.SizeRowBytes();
|
||||
if (myTmp.Size() < aRowSize
|
||||
&& !myTmp.Allocate (aRowSize))
|
||||
{
|
||||
return Standard_False;
|
||||
}
|
||||
|
||||
// for odd height middle row should be left as is
|
||||
Standard_Size aNbRowsHalf = theImage.SizeY() / 2;
|
||||
for (Standard_Size aRowT = 0, aRowB = theImage.SizeY() - 1; aRowT < aNbRowsHalf; ++aRowT, --aRowB)
|
||||
{
|
||||
Standard_Byte* aTop = theImage.ChangeRow (aRowT);
|
||||
Standard_Byte* aBot = theImage.ChangeRow (aRowB);
|
||||
memcpy (myTmp.ChangeData(), aTop, aRowSize);
|
||||
memcpy (aTop, aBot, aRowSize);
|
||||
memcpy (aBot, myTmp.Data(), aRowSize);
|
||||
}
|
||||
return Standard_True;
|
||||
}
|
||||
|
||||
private:
|
||||
NCollection_Buffer myTmp;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
@ -6590,10 +6635,13 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
|
||||
Standard_Real aPlaySpeed = 1.0;
|
||||
Standard_Real aPlayStartTime = anAnimation->StartPts();
|
||||
Standard_Real aPlayDuration = anAnimation->Duration();
|
||||
Standard_Integer aFpsNum = 0;
|
||||
Standard_Integer aFpsDen = 1;
|
||||
Standard_Boolean isFreeCamera = Standard_False;
|
||||
Standard_Boolean isLockLoop = Standard_False;
|
||||
|
||||
// video recording parameters
|
||||
TCollection_AsciiString aRecFile;
|
||||
Image_VideoParams aRecParams;
|
||||
|
||||
Handle(V3d_View) aView = ViewerTest::CurrentView();
|
||||
for (; anArgIter < theArgNb; ++anArgIter)
|
||||
{
|
||||
@ -6679,6 +6727,37 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
|
||||
{
|
||||
isFreeCamera = Standard_True;
|
||||
}
|
||||
// video recodring options
|
||||
else if (anArg == "-rec"
|
||||
|| anArg == "-record")
|
||||
{
|
||||
if (++anArgIter >= theArgNb)
|
||||
{
|
||||
std::cout << "Syntax error at " << anArg << ".\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
aRecFile = theArgVec[anArgIter];
|
||||
if (aRecParams.FpsNum <= 0)
|
||||
{
|
||||
aRecParams.FpsNum = 24;
|
||||
}
|
||||
|
||||
if (anArgIter + 2 < theArgNb
|
||||
&& *theArgVec[anArgIter + 1] != '-'
|
||||
&& *theArgVec[anArgIter + 2] != '-')
|
||||
{
|
||||
TCollection_AsciiString aWidthArg (theArgVec[anArgIter + 1]);
|
||||
TCollection_AsciiString aHeightArg (theArgVec[anArgIter + 2]);
|
||||
if (aWidthArg .IsIntegerValue()
|
||||
&& aHeightArg.IsIntegerValue())
|
||||
{
|
||||
aRecParams.Width = aWidthArg .IntegerValue();
|
||||
aRecParams.Height = aHeightArg.IntegerValue();
|
||||
anArgIter += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (anArg == "-fps")
|
||||
{
|
||||
if (++anArgIter >= theArgNb)
|
||||
@ -6691,22 +6770,66 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
|
||||
Standard_Integer aSplitIndex = aFpsArg.FirstLocationInSet ("/", 1, aFpsArg.Length());
|
||||
if (aSplitIndex == 0)
|
||||
{
|
||||
aFpsNum = aFpsArg.IntegerValue();
|
||||
aRecParams.FpsNum = aFpsArg.IntegerValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
const TCollection_AsciiString aDenStr = aFpsArg.Split (aSplitIndex);
|
||||
aFpsArg.Split (aFpsArg.Length() - 1);
|
||||
const TCollection_AsciiString aNumStr = aFpsArg;
|
||||
aFpsNum = aNumStr.IntegerValue();
|
||||
aFpsDen = aDenStr.IntegerValue();
|
||||
if (aFpsDen < 1)
|
||||
aRecParams.FpsNum = aNumStr.IntegerValue();
|
||||
aRecParams.FpsDen = aDenStr.IntegerValue();
|
||||
if (aRecParams.FpsDen < 1)
|
||||
{
|
||||
std::cout << "Syntax error at " << anArg << ".\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (anArg == "-format")
|
||||
{
|
||||
if (++anArgIter >= theArgNb)
|
||||
{
|
||||
std::cout << "Syntax error at " << anArg << ".\n";
|
||||
return 1;
|
||||
}
|
||||
aRecParams.Format = theArgVec[anArgIter];
|
||||
}
|
||||
else if (anArg == "-pix_fmt"
|
||||
|| anArg == "-pixfmt"
|
||||
|| anArg == "-pixelformat")
|
||||
{
|
||||
if (++anArgIter >= theArgNb)
|
||||
{
|
||||
std::cout << "Syntax error at " << anArg << ".\n";
|
||||
return 1;
|
||||
}
|
||||
aRecParams.PixelFormat = theArgVec[anArgIter];
|
||||
}
|
||||
else if (anArg == "-codec"
|
||||
|| anArg == "-vcodec"
|
||||
|| anArg == "-videocodec")
|
||||
{
|
||||
if (++anArgIter >= theArgNb)
|
||||
{
|
||||
std::cout << "Syntax error at " << anArg << ".\n";
|
||||
return 1;
|
||||
}
|
||||
aRecParams.VideoCodec = theArgVec[anArgIter];
|
||||
}
|
||||
else if (anArg == "-crf"
|
||||
|| anArg == "-preset"
|
||||
|| anArg == "-qp")
|
||||
{
|
||||
const TCollection_AsciiString aParamName = anArg.SubString (2, anArg.Length());
|
||||
if (++anArgIter >= theArgNb)
|
||||
{
|
||||
std::cout << "Syntax error at " << anArg << ".\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
aRecParams.VideoCodecParams.Bind (aParamName, theArgVec[anArgIter]);
|
||||
}
|
||||
// animation definition options
|
||||
else if (anArg == "-start"
|
||||
|| anArg == "-starttime"
|
||||
@ -6958,7 +7081,7 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
|
||||
}
|
||||
}
|
||||
|
||||
if (!toPlay)
|
||||
if (!toPlay && aRecFile.IsEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -6974,7 +7097,7 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
|
||||
}
|
||||
|
||||
const Standard_Real anUpperPts = aPlayStartTime + aPlayDuration;
|
||||
if (aFpsNum <= 0)
|
||||
if (aRecParams.FpsNum <= 0)
|
||||
{
|
||||
while (!anAnimation->IsStopped())
|
||||
{
|
||||
@ -7028,12 +7151,35 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
|
||||
OSD_Timer aPerfTimer;
|
||||
aPerfTimer.Start();
|
||||
|
||||
Handle(Image_VideoRecorder) aRecorder;
|
||||
ImageFlipper aFlipper;
|
||||
Handle(Draw_ProgressIndicator) aProgress;
|
||||
if (!aRecFile.IsEmpty())
|
||||
{
|
||||
if (aRecParams.Width <= 0
|
||||
|| aRecParams.Height <= 0)
|
||||
{
|
||||
aView->Window()->Size (aRecParams.Width, aRecParams.Height);
|
||||
}
|
||||
|
||||
aRecorder = new Image_VideoRecorder();
|
||||
if (!aRecorder->Open (aRecFile.ToCString(), aRecParams))
|
||||
{
|
||||
std::cout << "Error: failed to open video file for recording\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
aProgress = new Draw_ProgressIndicator (theDI, 1);
|
||||
}
|
||||
|
||||
// Manage frame-rated animation here
|
||||
Standard_Real aPts = aPlayStartTime;
|
||||
int64_t aNbFrames = 0;
|
||||
for (; aPts <= anUpperPts;)
|
||||
Message_ProgressSentry aPSentry (aProgress, "Video recording, sec", 0, Max (1, Standard_Integer(aPlayDuration / aPlaySpeed)), 1);
|
||||
Standard_Integer aSecondsProgress = 0;
|
||||
for (; aPts <= anUpperPts && aPSentry.More();)
|
||||
{
|
||||
const Standard_Real aRecPts = aPlaySpeed * ((Standard_Real(aFpsDen) / Standard_Real(aFpsNum)) * Standard_Real(aNbFrames));
|
||||
const Standard_Real aRecPts = aPlaySpeed * ((Standard_Real(aRecParams.FpsDen) / Standard_Real(aRecParams.FpsNum)) * Standard_Real(aNbFrames));
|
||||
aPts = aPlayStartTime + aRecPts;
|
||||
++aNbFrames;
|
||||
if (!anAnimation->Update (aPts))
|
||||
@ -7041,9 +7187,37 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!aRecorder.IsNull())
|
||||
{
|
||||
V3d_ImageDumpOptions aDumpParams;
|
||||
aDumpParams.Width = aRecParams.Width;
|
||||
aDumpParams.Height = aRecParams.Height;
|
||||
aDumpParams.BufferType = Graphic3d_BT_RGBA;
|
||||
aDumpParams.StereoOptions = V3d_SDO_MONO;
|
||||
aDumpParams.ToAdjustAspect = Standard_True;
|
||||
if (!aView->ToPixMap (aRecorder->ChangeFrame(), aDumpParams))
|
||||
{
|
||||
std::cout << "Error: view dump is failed!\n";
|
||||
return 0;
|
||||
}
|
||||
aFlipper.FlipY (aRecorder->ChangeFrame());
|
||||
if (!aRecorder->PushFrame())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aView->Redraw();
|
||||
}
|
||||
|
||||
while (aSecondsProgress < Standard_Integer(aRecPts / aPlaySpeed))
|
||||
{
|
||||
aPSentry.Next();
|
||||
++aSecondsProgress;
|
||||
}
|
||||
}
|
||||
|
||||
aPerfTimer.Stop();
|
||||
anAnimation->Stop();
|
||||
const Standard_Real aRecFps = Standard_Real(aNbFrames) / aPerfTimer.ElapsedTime();
|
||||
@ -11172,6 +11346,17 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
|
||||
"\n\t\t: %Pts overall animation presentation timestamp"
|
||||
"\n\t\t: %LocalPts local animation timestamp"
|
||||
"\n\t\t: %Normalized local animation normalized value in range 0..1"
|
||||
"\n\t\t:"
|
||||
"\n\t\t: Video recording:"
|
||||
"\n\t\t: vanim name -record FileName [Width Height] [-fps FrameRate=24]"
|
||||
"\n\t\t: [-format Format] [-vcodec Codec] [-pix_fmt PixelFormat]"
|
||||
"\n\t\t: [-crf Value] [-preset Preset]"
|
||||
"\n\t\t: -fps video framerate"
|
||||
"\n\t\t: -format file format, container (matroska, etc.)"
|
||||
"\n\t\t: -vcodec video codec identifier (ffv1, mjpeg, etc.)"
|
||||
"\n\t\t: -pix_fmt image pixel format (yuv420p, rgb24, etc.)"
|
||||
"\n\t\t: -crf constant rate factor (specific to codec)"
|
||||
"\n\t\t: -preset codec parameters preset (specific to codec)"
|
||||
__FILE__, VAnimation, group);
|
||||
|
||||
theCommands.Add("vchangeselected",
|
||||
|
17
tests/v3d/anim/videorecorder
Normal file
17
tests/v3d/anim/videorecorder
Normal file
@ -0,0 +1,17 @@
|
||||
puts "================"
|
||||
puts "OCC25382"
|
||||
puts "================"
|
||||
puts ""
|
||||
puts "==============================================================="
|
||||
puts "Visualization, TKOpenGl - improved video recording capability"
|
||||
puts "==============================================================="
|
||||
|
||||
set aFile ${imagedir}/propeller.mkv
|
||||
file delete -force ${aFile}
|
||||
|
||||
source $env(CSF_OCCTTestsPath)/v3d/anim/propeller
|
||||
vraytrace 1
|
||||
vrenderparams -msaa 8
|
||||
vrenderparams -fsaa 1
|
||||
XProgress +g
|
||||
vanimation anim -play -record ${aFile} 1920 1080 -vcodec ffv1 -fps 30 -speed 0.5
|
Loading…
x
Reference in New Issue
Block a user