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.
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")
--- /dev/null
+# 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)
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")
# 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()
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")
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)")
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 ""
}
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@
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@"
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@"
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@"
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@"
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 -----
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
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.
| 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 |
# 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"
#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
Image_PixMap.cxx
Image_PixMap.hxx
Image_PixMapData.hxx
+Image_VideoRecorder.cxx
+Image_VideoRecorder.hxx
--- /dev/null
+// 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\9663, 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
+}
--- /dev/null
+// 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_
CSF_IOKit
CSF_FreeImagePlus
CSF_FREETYPE
+CSF_FFmpeg
#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>
#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>
#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>
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;
+ };
+
}
//=================================================================================================
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)
{
{
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)
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"
}
}
- if (!toPlay)
+ if (!toPlay && aRecFile.IsEmpty())
{
return 0;
}
}
const Standard_Real anUpperPts = aPlayStartTime + aPlayDuration;
- if (aFpsNum <= 0)
+ if (aRecParams.FpsNum <= 0)
{
while (!anAnimation->IsStopped())
{
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))
break;
}
- aView->Redraw();
+ 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();
"\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",
--- /dev/null
+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
\ No newline at end of file