diff options
75 files changed, 6832 insertions, 4006 deletions
@@ -36,5 +36,4 @@ # Other build/ -doc/doxygen_sqlite3.db -doc/html +docs/
\ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 89a45bf..2dc6271 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,59 +1,38 @@ language: c -sudo: false - -env: - global: - - secure: "p7KivdPu+pWYLI95Rmtu8FgOPWUq36TiXr9kH5rDyuqg2iz3DAt8unDYXWGfSn9p6OzfchBd2WsGMMvbvICfBNm+uOjrQwWwW6SBENLNyGDiGKQd4coAmUF247UXY+CslRQrSuciajrNSrBIPOL4EjvlMmV8N5kwDrFB8QktgxyLf35vbNAc8vkoUdFu7vUzt2MISIA1rrWcOUjDaM6vqovzosr/LzDwwjj3HX92ys2YZh2m1V7BOMLuNRLW7pou3ge4VmXdTVIyFcEEG1Nqk1apeX9ZEsw0IQ6xLfcOJbqg5CSIFrkGhdlM1L+u7ThBBa+EZU9EZr6qYnVvHMRU1xyCFEJvGMr9FhS9ZnzPxOxz0BG/qkDbtkQtPtkPobb/325b7/jpDNZFXj6/kErdLjsClP1jUnFmj3jkcOXYsGlz4Osl7tih4a52VLWhzL7Lz7XxmZ9KUOJPuofg2CT0eUa1w+OAtA/tB7molRo2AowmfuN+PvNcmE16cFtveFqAArnN0R7st91SkyGZNRTumDfb7rh9coAboFEZU7vKYnCk7Tt7Atp43HnqnX0ywvxqEmHY7yYQQE1Z/1Mw0+6JnHuEaFp0Q5aU/KJmGXae6v0HqvGqvr8mCjsm0LMAxtRb6tb5zbtGCKwEd3HaDA/i3B9xLDBebbIxjMBjDdvAn9Q=" - - CMAKE_PREFIX_PATH=$HOME/local - -cache: - directories: - - $HOME/local +sudo: required +dist: trusty compiler: - gcc -install: - - chmod +x ${TRAVIS_BUILD_DIR}/travis/sdl2.sh - - chmod +x ${TRAVIS_BUILD_DIR}/travis/ffmpeg.sh - - ${TRAVIS_BUILD_DIR}/travis/sdl2.sh - - ${TRAVIS_BUILD_DIR}/travis/ffmpeg.sh - -script: if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then cd $TRAVIS_BUILD_DIR && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/bin/gcc-5 -DBUILD_EXAMPLES=On -DBUILD_TESTS=Off . && make; fi +script: + - wget https://www.libsdl.org/release/SDL2-2.0.8.tar.gz -O - | tar -xz + - cd SDL2-2.0.8 && CC=gcc-7 ./configure --prefix=/usr && make -j2 && sudo make install && cd .. + - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_EXAMPLES=On -DUSE_DYNAMIC_LIBASS=On -DCMAKE_C_COMPILER=/usr/bin/gcc-7 . + - build-wrapper-linux-x86-64 --out-dir bw-output make && sonar-scanner notifications: email: false addons: - coverity_scan: - project: - name: "katajakasa/SDL_kitchensink" - description: "A Simple SDL2 / FFmpeg library for audio/video playback written in C99" - notification_email: katajakasa@gmail.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template && cmake -DCMAKE_C_COMPILER=/usr/bin/gcc-5 -DCMAKE_BUILD_TYPE=Debug ." - build_command: "make" - branch_pattern: coverity_scan + sonarcloud: + organization: "katajakasa-github" apt: sources: - - ubuntu-toolchain-r-test - - george-edison55-precise-backports + - sourceline: 'ppa:ubuntu-toolchain-r/test' + - sourceline: 'ppa:george-edison55/cmake-3.x' + - sourceline: 'ppa:jonathonf/ffmpeg-3' packages: - - libc6-dev - - yasm - - gcc-5 - - gettext - - libcunit1 - - libcunit1-dev - - libasound2-dev - - libpulse-dev - - libx11-dev - - libxext-dev - - libxrandr-dev - - libxi-dev - - libxxf86vm-dev - - libxss-dev - - libudev-dev - cmake - - cmake-data + - gcc-7 - libass-dev + - libavcodec-dev + - libavformat-dev + - libswresample-dev + - libswscale-dev + - libavutil-dev + +cache: + directories: + - '$HOME/.sonar/cache' diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ed58af..c89dbc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,11 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.7) project(SDL_kitchensink C) +include(GNUInstallDirs) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -set(KIT_VERSION_MAJOR "0") +set(KIT_VERSION_MAJOR "1") set(KIT_VERSION_MINOR "0") -set(KIT_VERSION_PATCH "7") +set(KIT_VERSION_PATCH "4") set(KIT_VERSION ${KIT_VERSION_MAJOR}.${KIT_VERSION_MINOR}.${KIT_VERSION_PATCH}) add_definitions( -DKIT_VERSION_MAJOR=${KIT_VERSION_MAJOR} @@ -12,76 +13,112 @@ add_definitions( -DKIT_VERSION_PATCH=${KIT_VERSION_PATCH} ) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=c99") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -pedantic -Werror -fno-omit-frame-pointer") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -Werror -fno-omit-frame-pointer -Wno-deprecated-declarations") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -ggdb -O2 -fno-omit-frame-pointer -DNDEBUG") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -DNDEBUG") set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -Os -DNDEBUG") option(BUILD_EXAMPLES "Build examples" OFF) -option(BUILD_TESTS "Build unittests" OFF) +option(USE_DYNAMIC_LIBASS "Use dynamically loaded libass" ON) +option(USE_ASAN "Use AddressSanitizer" OFF) -find_package(SDL2) -find_package(ass) +find_package(SDL2 REQUIRED) find_package(ffmpeg COMPONENTS avcodec avformat avutil swscale swresample) -if(BUILD_TESTS) - add_subdirectory(tests) -endif() - -include_directories( +set(LIBRARIES + ${SDL2_LIBRARIES} + ${FFMPEG_LIBRARIES} +) +set(INCLUDES include/ ${SDL2_INCLUDE_DIRS} ${FFMPEG_INCLUDE_DIRS} - ${ASS_INCLUDE_DIRS} ) -FILE(GLOB SOURCES "src/*.c") -FILE(GLOB HEADERS "include/kitchensink/*.h") +if(USE_DYNAMIC_LIBASS) + if(WIN32 OR MINGW OR MSYS) + set(DYNAMIC_LIBASS_NAME "\"libass-9.dll\"") + else() + set(DYNAMIC_LIBASS_NAME "\"libass.so\"") + endif() + add_definitions(-DUSE_DYNAMIC_LIBASS) + add_definitions(-DDYNAMIC_LIBASS_NAME=${DYNAMIC_LIBASS_NAME}) +else() + find_package(ass) + set(LIBRARIES ${LIBRARIES} ${ASS_LIBRARIES}) + set(INCLUDES ${INCLUDES} ${ASS_INCLUDE_DIRS}) +endif() + +FILE(GLOB_RECURSE SOURCES "src/*.c") +FILE(GLOB INSTALL_HEADERS "include/kitchensink/*.h") add_library(SDL_kitchensink SHARED ${SOURCES}) add_library(SDL_kitchensink_static STATIC ${SOURCES}) set_target_properties(SDL_kitchensink PROPERTIES VERSION ${KIT_VERSION}) set_target_properties(SDL_kitchensink PROPERTIES SOVERSION ${KIT_VERSION_MAJOR}) + set_target_properties(SDL_kitchensink PROPERTIES DEBUG_POSTFIX "d") set_target_properties(SDL_kitchensink_static PROPERTIES DEBUG_POSTFIX "d") + target_compile_definitions(SDL_kitchensink PRIVATE "KIT_DLL;KIT_DLL_EXPORTS") target_compile_options(SDL_kitchensink PRIVATE "-fvisibility=hidden") -target_link_libraries(SDL_kitchensink - ${SDL2_LIBRARIES} - ${FFMPEG_LIBRARIES} - ${ASS_LIBRARIES} +set_property(TARGET SDL_kitchensink PROPERTY C_STANDARD 99) +set_property(TARGET SDL_kitchensink_static PROPERTY C_STANDARD 99) + +if(USE_ASAN) + set(LIBRARIES asan ${LIBRARIES}) + target_compile_options(SDL_kitchensink PRIVATE "-fsanitize=address") + message(STATUS "DEVELOPMENT: AddressSanitizer enabled!") +endif() + +include_directories(${INCLUDES}) +target_link_libraries(SDL_kitchensink ${LIBRARIES}) + +set(PKG_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/SDL_kitchensink.pc") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/pkg-config.pc.in" + ${PKG_CONFIG_FILE} + @ONLY ) if(BUILD_EXAMPLES) - add_executable(exampleaudio examples/example_audio.c) - add_executable(examplevideo examples/example_video.c) + add_executable(audio examples/example_audio.c) + add_executable(complex examples/example_complex.c) + add_executable(simple examples/example_simple.c) + add_executable(custom examples/example_custom.c) + add_executable(rwops examples/example_rwops.c) if(MINGW) - target_link_libraries(exampleaudio mingw32) - target_link_libraries(examplevideo mingw32) + target_link_libraries(audio mingw32) + target_link_libraries(complex mingw32) + target_link_libraries(simple mingw32) + target_link_libraries(custom mingw32) + target_link_libraries(rwops mingw32) endif() - target_link_libraries(exampleaudio - SDL_kitchensink_static - ${SDL2_LIBRARIES} - ${FFMPEG_LIBRARIES} - ${ASS_LIBRARIES} - ) - target_link_libraries(examplevideo - SDL_kitchensink_static - ${SDL2_LIBRARIES} - ${FFMPEG_LIBRARIES} - ${ASS_LIBRARIES} - ) + set_property(TARGET audio PROPERTY C_STANDARD 99) + set_property(TARGET complex PROPERTY C_STANDARD 99) + set_property(TARGET simple PROPERTY C_STANDARD 99) + set_property(TARGET custom PROPERTY C_STANDARD 99) + set_property(TARGET rwops PROPERTY C_STANDARD 99) + + target_link_libraries(audio SDL_kitchensink_static ${LIBRARIES}) + target_link_libraries(complex SDL_kitchensink_static ${LIBRARIES}) + target_link_libraries(simple SDL_kitchensink_static ${LIBRARIES}) + target_link_libraries(custom SDL_kitchensink_static ${LIBRARIES}) + target_link_libraries(rwops SDL_kitchensink_static ${LIBRARIES}) endif() -# Installation -include(GNUInstallDirs) +# documentation target +add_custom_target(docs COMMAND doxygen WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) -INSTALL(FILES ${HEADERS} DESTINATION include/kitchensink/) +# Installation +install(FILES ${PKG_CONFIG_FILE} DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +install(FILES ${INSTALL_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/kitchensink) INSTALL(TARGETS SDL_kitchensink SDL_kitchensink_static RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..db85229 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,2430 @@ +# Doxyfile 1.8.13 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "SDL_kitchensink" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs/ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = include/kitchensink/ . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.h *.md + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = examples/ + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = README.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = doxygen-custom.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when enabling USE_PDFLATEX this option is only used for generating +# bitmaps for formulas in the HTML output, but not in the Makefile that is +# written to the output directory. +# The default file is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's config +# file, i.e. a series of assignments. You only have to provide replacements, +# missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's config file. A template extensions file can be generated +# using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sf.net) file that captures the +# structure of the code including all documentation. Note that this feature is +# still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Tuomas Virtanen +Copyright (c) 2018 Tuomas Virtanen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1,20 +1,24 @@ # SDL_kitchensink [![Build Status](https://travis-ci.org/katajakasa/SDL_kitchensink.svg?branch=master)](https://travis-ci.org/katajakasa/SDL_kitchensink) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/7585/badge.svg)](https://scan.coverity.com/projects/katajakasa-sdl_kitchensink) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=sdl_kitchensink&metric=alert_status)](https://sonarcloud.io/dashboard?id=sdl_kitchensink) FFmpeg and SDL2 based library for audio and video playback, written in C99. -This library is still very much todo, but it's slowly getting there. +Documentation is available at http://katajakasa.github.io/SDL_kitchensink/ Features: -* Decoding video & audio via FFmpeg -* Dumping video data on SDL_textures +* Decoding video, audio and subtitles via FFmpeg +* Dumping video and subtitle data on SDL_textures * Dumping audio data in the usual mono/stereo interleaved formats * Automatic audio and video conversion to SDL2 friendly formats * Synchronizing video & audio to clock * Seeking forwards and backwards -* Bitmap & libass subtitle support. No text (srt, sub) support yet. +* Bitmap, text and SSA/ASS subtitle support + +Note! Master branch is for the development of v1.0.0 series. v0 can be found in the +rel-kitchensink-0 branch. v0 is no longer in active development and only bug- and security-fixes +are accepted. ## 1. Library requirements @@ -23,9 +27,9 @@ Build requirements: * GCC (C99 support required) Library requirements: -* SDL2 (>=2.0.3) (Note! Examples require 2.0.4!) +* SDL2 (>=2.0.5) * FFmpeg (>=3.0) -* libass +* libass (optional, supports runtime linking via SDL_LoadSO) * CUnit (optional, for unittests) Note that Clang might work, but is not tested. Older SDL2 and FFmpeg library versions @@ -76,26 +80,56 @@ Change CMAKE_INSTALL_PREFIX as necessary to change the installation path. The fi Just add ```-DBUILD_EXAMPLES=1``` to cmake arguments and rebuild. -### 2.4. Building unittests - -Make sure CUnit is installed, then add ```-DBUILD_UNITTESTS=1``` to the cmake arguments and rebuild. - -You can run unittests by running ```make unittest```. +### 2.4. Building with AddressSanitizer -## 3. License +This is for development/debugging use only! -MIT. Please see ```LICENSE``` for details. +Make sure llvm is installed, then add ```-DUSE_ASAN=1``` to the cmake arguments and rebuild. Note that ASAN is not +supported on all OSes (eg. windows). -Note that FFmpeg has a rather complex license. Please take a look at [FFmpeg Legal page](http://ffmpeg.org/legal.html) -for details. +After building, you can run with the following (make sure to set correct llvm-symbolizer path): +``` +ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer ./examplevideo <my videofile> +``` -## 4. Why SDL_kitchensink +## 3. Why SDL_kitchensink Because pulling major blob of library code like ffmpeg feels like bringing in a whole house with its kitchensink and everything to the project. Also, it sounded funny. Also, SDL_ffmpeg is already reserved :( -## 5. Examples +## 4. Examples Please see examples directory. You can also take a look at unittests for some help. Note that examples are NOT meant for any kind of real life use; they are only meant to show simple use cases for the library. + +## 5. FFMPEG & licensing + +Note that FFmpeg has a rather complex license. Please take a look at +[FFmpeg Legal page](http://ffmpeg.org/legal.html) for details. + +## 6. License + +``` +The MIT License (MIT) + +Copyright (c) 2018 Tuomas Virtanen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake deleted file mode 100644 index 8102832..0000000 --- a/cmake/FindSDL2.cmake +++ /dev/null @@ -1,54 +0,0 @@ -# A Simple SDL2 Finder. -# (c) Tuomas Virtanen 2016 (Licensed under MIT license) -# Usage: -# find_package(SDL2) -# -# Declares: -# * SDL2_FOUND -# * SDL2_INCLUDE_DIRS -# * SDL2_LIBRARIES -# - -set(SDL2_SEARCH_PATHS - /usr/local/ - /usr/ - /opt -) - -find_path(SDL2_INCLUDE_DIR SDL2/SDL.h - HINTS - PATH_SUFFIXES include - PATHS ${SDL2_SEARCH_PATHS} -) - -find_library(SDL2_LIBRARY - NAMES SDL2 - HINTS - PATH_SUFFIXES lib - PATHS ${SDL2_SEARCH_PATHS} -) - -if(MINGW) - find_library(SDL2MAIN_LIBRARY - NAMES SDL2main - HINTS - PATH_SUFFIXES lib - PATHS ${SDL2_SEARCH_PATHS} - ) -else() - set(SDL2MAIN_LIBRARY "") -endif() - -if(SDL2_INCLUDE_DIR AND SDL2_LIBRARY) - set(SDL2_FOUND TRUE) -endif() - -if(SDL2_FOUND) - set(SDL2_LIBRARIES ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY}) - set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) - message(STATUS "Found SDL2: ${SDL2_LIBRARIES}") -else() - message(WARNING "Could not find SDL2") -endif() - -mark_as_advanced(SDL2MAIN_LIBRARY SDL2_LIBRARY SDL2_INCLUDE_DIR SDL2_SEARCH_PATHS) diff --git a/cmake/Findcunit.cmake b/cmake/Findcunit.cmake deleted file mode 100644 index ae81cf1..0000000 --- a/cmake/Findcunit.cmake +++ /dev/null @@ -1,41 +0,0 @@ -# A Simple CUnit Finder. -# (c) Tuomas Virtanen 2016 (Licensed under MIT license) -# Usage: -# find_package(cunit) -# -# Declares: -# * CUNIT_FOUND -# * CUNIT_INCLUDE_DIRS -# * CUNIT_LIBRARIES -# - -set(CUNIT_SEARCH_PATHS - /usr/local/ - /usr - /opt -) - -find_path(CUNIT_INCLUDE_DIR CUnit/CUnit.h - HINTS - PATH_SUFFIXES include - PATHS ${CUNIT_SEARCH_PATHS} -) -find_library(CUNIT_LIBRARY cunit - HINTS - PATH_SUFFIXES lib - PATHS ${CUNIT_SEARCH_PATHS} -) - -if(CUNIT_INCLUDE_DIR AND CUNIT_LIBRARY) - set(CUNIT_FOUND TRUE) -endif() - -if(CUNIT_FOUND) - set(CUNIT_LIBRARIES ${CUNIT_LIBRARY}) - set(CUNIT_INCLUDE_DIRS ${CUNIT_INCLUDE_DIR}) - message(STATUS "Found CUnit: ${CUNIT_LIBRARY}") -else() - message(WARNING "Could not find CUnit.") -endif() - -mark_as_advanced(CUNIT_LIBRARY CUNIT_INCLUDE_DIR) diff --git a/debian/.git-dpm b/debian/.git-dpm index cc8f7cb..9074f07 100644 --- a/debian/.git-dpm +++ b/debian/.git-dpm @@ -1,8 +1,8 @@ # see git-dpm(1) from git-dpm package -48c7daa8e91d96805548ef4d2fa9f4b925c26108 -48c7daa8e91d96805548ef4d2fa9f4b925c26108 -48c7daa8e91d96805548ef4d2fa9f4b925c26108 -48c7daa8e91d96805548ef4d2fa9f4b925c26108 -sdl-kitchensink_0.0.7.orig.tar.gz -8732427fdaf541cc768d9a26e426d371c74d3753 -3646266 +4c38cd19867186bb97c179eab4cc72a426b795df +4c38cd19867186bb97c179eab4cc72a426b795df +4c38cd19867186bb97c179eab4cc72a426b795df +4c38cd19867186bb97c179eab4cc72a426b795df +sdl-kitchensink_1.0.4.orig.tar.gz +e82045206e9ca1b9478a5488ea31d4e5016cc161 +67884 diff --git a/debian/changelog b/debian/changelog index 7dbdf2b..f358f5d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,26 @@ +sdl-kitchensink (1.0.4-2) unstable; urgency=medium + + * Upload to unstable + + -- Didier Raboud <odyx@debian.org> Mon, 17 Sep 2018 10:06:08 +0200 + +sdl-kitchensink (1.0.4-1) experimental; urgency=medium + + * New 1.0.4 upstream release + - Add Kit_CreateSourceFromRW symbol + * Bump S-V to 4.2.1 without changes needed + + -- Didier Raboud <odyx@debian.org> Mon, 03 Sep 2018 11:48:01 +0200 + +sdl-kitchensink (1.0.2-1) experimental; urgency=low + + * New 1.0.2 upstream release + * Bump S-V to 4.1.4 without changes needed + * Bump libsdl-kitchensink SOVERSION from 0 to 1, refresh symbols + * Add libsdl2-dev Depends to libsdl-kitchensink-dev + + -- Didier Raboud <odyx@debian.org> Mon, 02 Jul 2018 08:52:43 +0200 + sdl-kitchensink (0.0.7-3) unstable; urgency=medium * Cleanup d/control thanks to `cme update dpkg-control` diff --git a/debian/control b/debian/control index 74f5d73..8f51232 100644 --- a/debian/control +++ b/debian/control @@ -15,12 +15,12 @@ Build-Depends: debhelper (>= 10.2.5~), libswscale-dev, libpostproc-dev, libass-dev -Standards-Version: 4.1.3 +Standards-Version: 4.2.1 Vcs-Browser: https://salsa.debian.org/debian/sdl-kitchensink Vcs-Git: https://salsa.debian.org/debian/sdl-kitchensink.git Homepage: https://github.com/katajakasa/SDL_kitchensink -Package: libsdl-kitchensink0 +Package: libsdl-kitchensink1 Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, @@ -41,7 +41,8 @@ Package: libsdl-kitchensink-dev Architecture: any Multi-Arch: same Section: libdevel -Depends: libsdl-kitchensink0 (= ${binary:Version}), +Depends: libsdl-kitchensink1 (= ${binary:Version}), + libsdl2-dev, ${misc:Depends} Description: FFmpeg and SDL2 based library for audio and video playback - Development files It provides FFmpeg-based audio and video playback for SDL which features: diff --git a/debian/libsdl-kitchensink0.install b/debian/libsdl-kitchensink1.install index 79f436c..79f436c 100644 --- a/debian/libsdl-kitchensink0.install +++ b/debian/libsdl-kitchensink1.install diff --git a/debian/libsdl-kitchensink0.symbols b/debian/libsdl-kitchensink1.symbols index d0edaac..d5e54a0 100644 --- a/debian/libsdl-kitchensink0.symbols +++ b/debian/libsdl-kitchensink1.symbols @@ -1,25 +1,30 @@ -libSDL_kitchensink.so.0 libsdl-kitchensink0 #MINVER# +libSDL_kitchensink.so.1 libsdl-kitchensink1 #MINVER# Kit_ClearError@Base 0.0.6 Kit_ClosePlayer@Base 0.0.6 Kit_CloseSource@Base 0.0.6 Kit_CreatePlayer@Base 0.0.6 + Kit_CreateSourceFromCustom@Base 1.0.2 + Kit_CreateSourceFromRW@Base 1.0.4 Kit_CreateSourceFromUrl@Base 0.0.6 - Kit_GetAudioData@Base 0.0.6 Kit_GetBestSourceStream@Base 0.0.6 Kit_GetError@Base 0.0.6 + Kit_GetHint@Base 1.0.2 Kit_GetKitStreamTypeString@Base 0.0.6 + Kit_GetPlayerAudioData@Base 1.0.2 + Kit_GetPlayerAudioStream@Base 1.0.2 Kit_GetPlayerDuration@Base 0.0.6 Kit_GetPlayerInfo@Base 0.0.6 Kit_GetPlayerPosition@Base 0.0.6 Kit_GetPlayerState@Base 0.0.6 + Kit_GetPlayerSubtitleData@Base 1.0.2 + Kit_GetPlayerSubtitleStream@Base 1.0.2 + Kit_GetPlayerVideoData@Base 1.0.2 + Kit_GetPlayerVideoStream@Base 1.0.2 Kit_GetSDLAudioFormatString@Base 0.0.6 Kit_GetSDLPixelFormatString@Base 0.0.6 - Kit_GetSourceStream@Base 0.0.6 Kit_GetSourceStreamCount@Base 0.0.6 Kit_GetSourceStreamInfo@Base 0.0.6 - Kit_GetSubtitleData@Base 0.0.6 Kit_GetVersion@Base 0.0.6 - Kit_GetVideoData@Base 0.0.6 Kit_Init@Base 0.0.6 Kit_PlayerPause@Base 0.0.6 Kit_PlayerPlay@Base 0.0.6 @@ -27,4 +32,5 @@ libSDL_kitchensink.so.0 libsdl-kitchensink0 #MINVER# Kit_PlayerStop@Base 0.0.6 Kit_Quit@Base 0.0.6 Kit_SetError@Base 0.0.6 - Kit_SetSourceStream@Base 0.0.6 + Kit_SetHint@Base 1.0.2 + Kit_SetPlayerScreenSize@Base 1.0.2 diff --git a/doc/Doxyfile b/doc/Doxyfile deleted file mode 100644 index 7bb5ca0..0000000 --- a/doc/Doxyfile +++ /dev/null @@ -1,1892 +0,0 @@ -# Doxyfile 1.8.3.1 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or sequence of words) that should -# identify the project. Note that if you do not use Doxywizard you need -# to put quotes around the project name if it contains spaces. - -PROJECT_NAME = SDL_kitchensink - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "A Simple SDL2 / FFmpeg library for audio/video playback written in C99" - -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = . - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. Note that you specify absolute paths here, but also -# relative paths, which will be relative from the directory where doxygen is -# started. - -STRIP_FROM_PATH = ../.. - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding -# "class=itcl::class" will allow you to use the command class in the -# itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, -# and language is one of the parsers supported by doxygen: IDL, Java, -# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, -# C++. For instance to make doxygen treat .inc files as Fortran files (default -# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note -# that for custom extensions you also need to set FILE_PATTERNS otherwise the -# files are not read by doxygen. - -EXTENSION_MAPPING = - -# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all -# comments according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you -# can mix doxygen, HTML, and XML commands with Markdown formatting. -# Disable only in case of backward compatibilities issues. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented classes, -# or namespaces to their corresponding documentation. Such a link can be -# prevented in individual cases by by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES (the -# default) will make doxygen replace the get and set methods by a property in -# the documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or -# section (for LaTeX and RTF). - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and -# unions with only public data fields will be shown inline in the documentation -# of the scope in which they are defined (i.e. file, namespace, or group -# documentation), provided this scope is documented. If set to NO (the default), -# structs, classes, and unions are shown on a separate page (for HTML and Man -# pages) or section (for LaTeX and RTF). - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols. - -SYMBOL_CACHE_SIZE = 0 - -# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be -# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given -# their name and scope. Since this can be an expensive process and often the -# same symbol appear multiple times in the code, doxygen keeps a cache of -# pre-resolved symbols. If the cache is too small doxygen will become slower. -# If the cache is too large, memory is wasted. The cache size is given by this -# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = YES - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if section-label ... \endif -# and \cond section-label ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command <command> <input-file>, where <command> is the value of -# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files -# containing the references data. This must be a list of .bib files. The -# .bib extension is automatically appended if omitted. Using this command -# requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style -# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this -# feature you need bibtex and perl available in the search path. Do not use -# file names with spaces, bibtex cannot handle them. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = ../include/ FRONT.md - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.markdown \ - *.md \ - *.mm \ - *.dox \ - *.py \ - *.f90 \ - *.f \ - *.for \ - *.vhd \ - *.vhdl - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = */internal/* - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command <filter> <input-file>, where <filter> -# is the value of the INPUT_FILTER tag, and <input-file> is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page (index.html). -# This can be useful if you have a project on for instance GitHub and want reuse -# the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = FRONT.md - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C, C++ and Fortran comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is advised to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If left blank doxygen will -# generate a default style sheet. Note that it is recommended to use -# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this -# tag will in the future become obsolete. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional -# user-defined cascading style sheet that is included after the standard -# style sheets created by doxygen. Using this option one can overrule -# certain style aspects. This is preferred over using HTML_STYLESHEET -# since it does not replace the standard style sheet and is therefor more -# robust against future updates. Doxygen will copy the style sheet file to -# the output directory. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the style sheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of -# entries shown in the various tree structured indices initially; the user -# can expand and collapse entries dynamically later on. Doxygen will expand -# the tree to such a level that at most the specified number of entries are -# visible (unless a fully collapsed tree already exceeds this amount). -# So setting the number of entries 1 will produce a full collapsed tree by -# default. 0 is a special value representing an infinite number of entries -# and will result in a full expanded tree by default. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely -# identify the documentation publisher. This should be a reverse domain-name -# style string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> -# Qt Help Project / Custom Filters</a>. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> -# Qt Help Project / Filter Attributes</a>. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) -# at top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. Since the tabs have the same information as the -# navigation tree you can set this option to NO if you already set -# GENERATE_TREEVIEW to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. -# Since the tree basically has the same information as the tab index you -# could consider to set DISABLE_INDEX to NO when enabling this option. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you may also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and -# SVG. The default value is HTML-CSS, which is slower, but has the best -# compatibility. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to -# the MathJax Content Delivery Network so you can quickly see the result without -# installing MathJax. However, it is strongly recommended to install a local -# copy of MathJax from http://www.mathjax.org before deployment. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension -# names that should be enabled during MathJax rendering. - -MATHJAX_EXTENSIONS = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. -# There are two flavours of web server based search depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. -# See the manual for details. - -SERVER_BASED_SEARCH = NO - -# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP -# script for searching. Instead the search results are written to an XML file -# which needs to be processed by an external indexer. Doxygen will invoke an -# external search engine pointed to by the SEARCHENGINE_URL option to obtain -# the search results. Doxygen ships with an example indexer (doxyindexer) and -# search engine (doxysearch.cgi) which are based on the open source search engine -# library Xapian. See the manual for configuration details. - -EXTERNAL_SEARCH = NO - -# The SEARCHENGINE_URL should point to a search engine hosted by a web server -# which will returned the search results when EXTERNAL_SEARCH is enabled. -# Doxygen ships with an example search engine (doxysearch) which is based on -# the open source search engine library Xapian. See the manual for configuration -# details. - -SEARCHENGINE_URL = - -# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed -# search data is written to a file for indexing by an external tool. With the -# SEARCHDATA_FILE tag the name of this file can be specified. - -SEARCHDATA_FILE = searchdata.xml - -# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the -# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is -# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple -# projects and redirect the results back to the right project. - -EXTERNAL_SEARCH_ID = - -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen -# projects other than the one defined by this configuration file, but that are -# all added to the same external search index. Each project needs to have a -# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id -# of to a relative location where the documentation can be found. -# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... - -EXTRA_SEARCH_MAPPINGS = - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for -# the generated latex document. The footer should contain everything after -# the last chapter. If it is left blank doxygen will generate a -# standard footer. Notice: only use this tag if you know what you are doing! - -LATEX_FOOTER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -# The LATEX_BIB_STYLE tag can be used to specify the style to use for the -# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See -# http://en.wikipedia.org/wiki/BibTeX for more info. - -LATEX_BIB_STYLE = plain - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load style sheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# pointed to by INCLUDE_PATH will be searched when a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that -# overrules the definition found in the source code. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all references to function-like macros -# that are alone on a line, have an all uppercase name, and do not end with a -# semicolon, because these will confuse the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. For each -# tag file the location of the external documentation should be added. The -# format of a tag file without this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths -# or URLs. Note that each tag file must have a unique name (where the name does -# NOT include the path). If a tag file is not located in the directory in which -# doxygen is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to -# install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will use the Helvetica font for all dot files that -# doxygen generates. When you want a differently looking font you can specify -# the font name using DOT_FONTNAME. You need to make sure dot is able to find -# the font, which can be done by putting it in a standard location or by setting -# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the -# directory containing the font. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the Helvetica font. -# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to -# set the path where dot can find it. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If the UML_LOOK tag is enabled, the fields and methods are shown inside -# the class node. If there are many fields or methods and many nodes the -# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS -# threshold limits the number of items for each type to make the size more -# managable. Set this to 0 for no limit. Note that the threshold may be -# exceeded by 50% before the limit is enforced. - -UML_LIMIT_NUM_FIELDS = 10 - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will generate a graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are svg, png, jpg, or gif. -# If left blank png will be used. If you choose svg you need to set -# HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible in IE 9+ (other browsers do not have this requirement). - -DOT_IMAGE_FORMAT = png - -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. -# Note that this requires a modern browser other than Internet Explorer. -# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you -# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible. Older versions of IE do not have SVG support. - -INTERACTIVE_SVG = NO - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the -# \mscfile command). - -MSCFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/doc/FRONT.md b/doc/FRONT.md deleted file mode 100644 index 3bc6c4c..0000000 --- a/doc/FRONT.md +++ /dev/null @@ -1,43 +0,0 @@ -# SDL_kitchensink - -## Overview - -FFmpeg and SDL2 based library for audio and video playback, written in C99. -Please see [github.com/katajakasa/SDL_kitchensink](https://github.com/katajakasa/SDL_kitchensink) -for project details and source code. - -Features: -* Decoding video & audio via FFmpeg -* Dumping video data on SDL_textures -* Dumping audio data in the usual mono/stereo interleaved formats -* Automatic audio and video conversion to SDL2 friendly formats -* Synchronizing video & audio to clock -* Seeking forwards and backwards -* Bitmap & libass subtitle support. No text (srt, sub) support yet. - -Note that FFmpeg has a rather complex license. Please take a look at -[FFmpeg Legal page](http://ffmpeg.org/legal.html) for details. - -## License - -The MIT License (MIT) - -Copyright (c) 2016 Tuomas Virtanen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/doc/generate.sh b/doc/generate.sh deleted file mode 100644 index 6307434..0000000 --- a/doc/generate.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -doxygen Doxyfile
\ No newline at end of file diff --git a/doxygen-custom.css b/doxygen-custom.css new file mode 100644 index 0000000..cb0d91e --- /dev/null +++ b/doxygen-custom.css @@ -0,0 +1,3 @@ +.fragment { + padding: 0.5em !important; +} diff --git a/examples/example_audio.c b/examples/example_audio.c index 9780c26..20c8cdf 100644 --- a/examples/example_audio.c +++ b/examples/example_audio.c @@ -1,16 +1,14 @@ #include <kitchensink/kitchensink.h> -#include <SDL2/SDL.h> +#include <SDL.h> #include <stdio.h> #include <stdbool.h> /* -* Requires SDL2 2.0.4 ! -* * Note! This example does not do proper error handling etc. * It is for example use only! */ -#define AUDIOBUFFER_SIZE (16384) +#define AUDIOBUFFER_SIZE (32768) const char *stream_types[] = { "KIT_STREAMTYPE_UNKNOWN", @@ -39,7 +37,7 @@ int main(int argc, char *argv[]) { // Get filename to open if(argc != 2) { - fprintf(stderr, "Usage: exampleplay <filename>\n"); + fprintf(stderr, "Usage: audio <filename>\n"); return 0; } filename = argv[1]; @@ -51,7 +49,7 @@ int main(int argc, char *argv[]) { return 1; } - err = Kit_Init(KIT_INIT_FORMATS|KIT_INIT_NETWORK); + err = Kit_Init(KIT_INIT_NETWORK); if(err != 0) { fprintf(stderr, "Unable to initialize Kitchensink: %s", Kit_GetError()); return 1; @@ -64,13 +62,8 @@ int main(int argc, char *argv[]) { return 1; } - // Disable any video and subtitle streams. If we leave these enabled and then don't - // clear the buffers for these sometimes, decoding will block. - Kit_SetSourceStream(src, KIT_STREAMTYPE_SUBTITLE, -1); - Kit_SetSourceStream(src, KIT_STREAMTYPE_VIDEO, -1); - // Print stream information - Kit_StreamInfo sinfo; + Kit_SourceStreamInfo sinfo; fprintf(stderr, "Source streams:\n"); for(int i = 0; i < Kit_GetSourceStreamCount(src); i++) { err = Kit_GetSourceStreamInfo(src, &sinfo, i); @@ -81,8 +74,13 @@ int main(int argc, char *argv[]) { fprintf(stderr, " * Stream #%d: %s\n", i, stream_types[sinfo.type]); } - // Create the player - player = Kit_CreatePlayer(src); + // Create the player. No video, pick best audio stream, no subtitles, no screen + player = Kit_CreatePlayer( + src, + -1, + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO), + -1, + 0, 0); if(player == NULL) { fprintf(stderr, "Unable to create player: %s\n", Kit_GetError()); return 1; @@ -92,25 +90,26 @@ int main(int argc, char *argv[]) { Kit_PlayerInfo pinfo; Kit_GetPlayerInfo(player, &pinfo); - if(!pinfo.audio.is_enabled) { + // Make sure there is audio in the file to play first. + if(Kit_GetPlayerAudioStream(player) == -1) { fprintf(stderr, "File contains no audio!\n"); return 1; } fprintf(stderr, "Media information:\n"); fprintf(stderr, " * Audio: %s (%s), %dHz, %dch, %db, %s\n", - pinfo.acodec, - pinfo.acodec_name, - pinfo.audio.samplerate, - pinfo.audio.channels, - pinfo.audio.bytes, - pinfo.audio.is_signed ? "signed" : "unsigned"); + pinfo.audio.codec.name, + pinfo.audio.codec.description, + pinfo.audio.output.samplerate, + pinfo.audio.output.channels, + pinfo.audio.output.bytes, + pinfo.audio.output.is_signed ? "signed" : "unsigned"); // Init audio SDL_memset(&wanted_spec, 0, sizeof(wanted_spec)); - wanted_spec.freq = pinfo.audio.samplerate; - wanted_spec.format = pinfo.audio.format; - wanted_spec.channels = pinfo.audio.channels; + wanted_spec.freq = pinfo.audio.output.samplerate; + wanted_spec.format = pinfo.audio.output.format; + wanted_spec.channels = pinfo.audio.output.channels; audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &audio_spec, 0); SDL_PauseAudioDevice(audio_dev, 0); @@ -127,26 +126,22 @@ int main(int argc, char *argv[]) { } // Refresh audio - ret = SDL_GetQueuedAudioSize(audio_dev); - if(ret < AUDIOBUFFER_SIZE) { - ret = Kit_GetAudioData(player, (unsigned char*)audiobuf, AUDIOBUFFER_SIZE, 0); + int queued = SDL_GetQueuedAudioSize(audio_dev); + if(queued < AUDIOBUFFER_SIZE) { + ret = Kit_GetPlayerAudioData(player, (unsigned char*)audiobuf, AUDIOBUFFER_SIZE - queued); if(ret > 0) { - SDL_LockAudio(); SDL_QueueAudio(audio_dev, audiobuf, ret); - SDL_UnlockAudio(); - SDL_PauseAudioDevice(audio_dev, 0); } } SDL_Delay(1); } - SDL_CloseAudioDevice(audio_dev); - Kit_ClosePlayer(player); Kit_CloseSource(src); - Kit_Quit(); + + SDL_CloseAudioDevice(audio_dev); SDL_Quit(); return 0; } diff --git a/examples/example_video.c b/examples/example_complex.c index 1f411ca..b8dc6ee 100644 --- a/examples/example_video.c +++ b/examples/example_complex.c @@ -1,16 +1,17 @@ #include <kitchensink/kitchensink.h> -#include <SDL2/SDL.h> +#include <SDL.h> #include <stdio.h> #include <stdbool.h> /* -* Requires SDL2 2.0.4 ! -* * Note! This example does not do proper error handling etc. * It is for example use only! */ -#define AUDIOBUFFER_SIZE (32768) +#define AUDIOBUFFER_SIZE (1024 * 64) +#define ATLAS_WIDTH 4096 +#define ATLAS_HEIGHT 4096 +#define ATLAS_MAX 1024 void render_gui(SDL_Renderer *renderer, double percent) { @@ -44,6 +45,14 @@ void render_gui(SDL_Renderer *renderer, double percent) { SDL_RenderFillRect(renderer, &progress_top); } +void find_viewport_size(int sw, int sh, int vw, int vh, int *rw, int *rh) { + float r_x = (float)sw / (float)vw; + float r_y = (float)sh / (float)vh; + float r_t = r_x < r_y ? r_x : r_y; + *rw = vw * r_t; + *rh = vh * r_t; +} + int main(int argc, char *argv[]) { int err = 0, ret = 0; const char* filename = NULL; @@ -63,11 +72,10 @@ int main(int argc, char *argv[]) { // Audio playback SDL_AudioSpec wanted_spec, audio_spec; SDL_AudioDeviceID audio_dev; - char audiobuf[AUDIOBUFFER_SIZE]; // Get filename to open if(argc != 2) { - fprintf(stderr, "Usage: exampleplay <filename>\n"); + fprintf(stderr, "Usage: complex <filename>\n"); return 0; } filename = argv[1]; @@ -79,9 +87,6 @@ int main(int argc, char *argv[]) { return 1; } - // Attempt to acquire opengl driver context - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); - // Create a resizable window. window = SDL_CreateWindow("Example Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_RESIZABLE); if(window == NULL) { @@ -96,22 +101,17 @@ int main(int argc, char *argv[]) { return 1; } - // We want to alphablend textures, so switch that on - if(SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND) < 0) { - fprintf(stderr, "Unable to set blendmode!\n"); - return 1; - } - - // Ask for linear texture scaling (better quality) - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - - // Initialize Kitchensink with network support and all formats. - err = Kit_Init(KIT_INIT_FORMATS|KIT_INIT_NETWORK); + // Initialize Kitchensink with network and libass support. + err = Kit_Init(KIT_INIT_NETWORK|KIT_INIT_ASS); if(err != 0) { fprintf(stderr, "Unable to initialize Kitchensink: %s", Kit_GetError()); return 1; } + // Allow Kit to use more threads + Kit_SetHint(KIT_HINT_THREAD_COUNT, SDL_GetCPUCount() <= 8 ? SDL_GetCPUCount() : 8); + Kit_SetHint(KIT_HINT_FONT_HINTING, KIT_FONT_HINTING_LIGHT); + // Open up the sourcefile. // This can be a local file, network url, ... src = Kit_CreateSourceFromUrl(filename); @@ -121,7 +121,7 @@ int main(int argc, char *argv[]) { } // Print stream information - Kit_StreamInfo sinfo; + Kit_SourceStreamInfo sinfo; fprintf(stderr, "Source streams:\n"); for(int i = 0; i < Kit_GetSourceStreamCount(src); i++) { err = Kit_GetSourceStreamInfo(src, &sinfo, i); @@ -132,8 +132,14 @@ int main(int argc, char *argv[]) { fprintf(stderr, " * Stream #%d: %s\n", i, Kit_GetKitStreamTypeString(sinfo.type)); } - // Create the player - player = Kit_CreatePlayer(src); + // Create the player. Pick best video, audio and subtitle streams, and set subtitle + // rendering resolution to screen resolution. + player = Kit_CreatePlayer( + src, + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO), + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO), + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE), + 1280, 720); if(player == NULL) { fprintf(stderr, "Unable to create player: %s\n", Kit_GetError()); return 1; @@ -143,71 +149,107 @@ int main(int argc, char *argv[]) { Kit_PlayerInfo pinfo; Kit_GetPlayerInfo(player, &pinfo); - if(!pinfo.video.is_enabled) { + // Make sure there is video in the file to play first. + if(Kit_GetPlayerVideoStream(player) == -1) { fprintf(stderr, "File contains no video!\n"); return 1; } fprintf(stderr, "Media information:\n"); - if(pinfo.audio.is_enabled) { - fprintf(stderr, " * Audio: %s (%s), %dHz, %dch, %db, %s\n", - pinfo.acodec, - pinfo.acodec_name, - pinfo.audio.samplerate, - pinfo.audio.channels, - pinfo.audio.bytes, - pinfo.audio.is_signed ? "signed" : "unsigned"); + if(Kit_GetPlayerAudioStream(player) >= 0) { + fprintf(stderr, " * Audio: %s (%s), threads=%d, %dHz, %dch, %db, %s\n", + pinfo.audio.codec.name, + pinfo.audio.codec.description, + pinfo.video.codec.threads, + pinfo.audio.output.samplerate, + pinfo.audio.output.channels, + pinfo.audio.output.bytes, + pinfo.audio.output.is_signed ? "signed" : "unsigned"); } - fprintf(stderr, " * Video: %s (%s), %dx%d\n", - pinfo.vcodec, - pinfo.vcodec_name, - pinfo.video.width, - pinfo.video.height); - if(pinfo.subtitle.is_enabled) { - fprintf(stderr, " * Subtitle: %s (%s)\n", - pinfo.scodec, - pinfo.scodec_name); + if(Kit_GetPlayerVideoStream(player) >= 0) { + fprintf(stderr, " * Video: %s (%s), threads=%d, %dx%d\n", + pinfo.video.codec.name, + pinfo.video.codec.description, + pinfo.video.codec.threads, + pinfo.video.output.width, + pinfo.video.output.height); + } + if(Kit_GetPlayerSubtitleStream(player) >= 0) { + fprintf(stderr, " * Subtitle: %s (%s), threads=%d\n", + pinfo.subtitle.codec.name, + pinfo.subtitle.codec.description, + pinfo.video.codec.threads); } fprintf(stderr, "Duration: %f seconds\n", Kit_GetPlayerDuration(player)); // Init audio SDL_memset(&wanted_spec, 0, sizeof(wanted_spec)); - wanted_spec.freq = pinfo.audio.samplerate; - wanted_spec.format = pinfo.audio.format; - wanted_spec.channels = pinfo.audio.channels; + wanted_spec.freq = pinfo.audio.output.samplerate; + wanted_spec.format = pinfo.audio.output.format; + wanted_spec.channels = pinfo.audio.output.channels; audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &audio_spec, 0); SDL_PauseAudioDevice(audio_dev, 0); // Print some format info - fprintf(stderr, "Texture type: %s\n", Kit_GetSDLPixelFormatString(pinfo.video.format)); - fprintf(stderr, "Audio format: %s\n", Kit_GetSDLAudioFormatString(pinfo.audio.format)); + fprintf(stderr, "Texture type: %s\n", Kit_GetSDLPixelFormatString(pinfo.video.output.format)); + fprintf(stderr, "Audio format: %s\n", Kit_GetSDLAudioFormatString(pinfo.audio.output.format)); + fprintf(stderr, "Subtitle format: %s\n", Kit_GetSDLPixelFormatString(pinfo.subtitle.output.format)); + fflush(stderr); - // Initialize textures + // Initialize video texture. This will probably end up as YV12 most of the time. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); SDL_Texture *video_tex = SDL_CreateTexture( renderer, - pinfo.video.format, + pinfo.video.output.format, SDL_TEXTUREACCESS_STATIC, - pinfo.video.width, - pinfo.video.height); + pinfo.video.output.width, + pinfo.video.output.height); if(video_tex == NULL) { fprintf(stderr, "Error while attempting to create a video texture\n"); return 1; } - fflush(stderr); + // This is the subtitle texture atlas. This contains all the subtitle image fragments. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); // Always nearest for atlas operations + SDL_Texture *subtitle_tex = SDL_CreateTexture( + renderer, + pinfo.subtitle.output.format, + SDL_TEXTUREACCESS_STATIC, + ATLAS_WIDTH, ATLAS_HEIGHT); + if(subtitle_tex == NULL) { + fprintf(stderr, "Error while attempting to create a subtitle texture atlas\n"); + return 1; + } + + // Make sure subtitle texture is in correct blendmode + SDL_SetTextureBlendMode(subtitle_tex, SDL_BLENDMODE_BLEND); - // Set logical size for the renderer. This way when we scale, we keep aspect ratio. - SDL_RenderSetLogicalSize(renderer, pinfo.video.width, pinfo.video.height); + // Clear screen with black + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); // Start playback Kit_PlayerPlay(player); - // Get movie area size - int mouse_x = 0, mouse_y = 0; - int size_w = 0, size_h = 0; - SDL_RenderGetLogicalSize(renderer, &size_w, &size_h); - bool gui_enabled = false; + // Playback temporary data buffers + char audiobuf[AUDIOBUFFER_SIZE]; + SDL_Rect sources[ATLAS_MAX]; + SDL_Rect targets[ATLAS_MAX]; + int mouse_x = 0; + int mouse_y = 0; + int size_w = 0; + int size_h = 0; + int screen_w = 0; + int screen_h = 0; bool fullscreen = false; + + // Get movie area size + SDL_GetWindowSize(window, &screen_w, &screen_h); + find_viewport_size(screen_w, screen_h, pinfo.video.output.width, pinfo.video.output.height, &size_w, &size_h); + SDL_RenderSetLogicalSize(renderer, size_w, size_h); + Kit_SetPlayerScreenSize(player, size_w, size_h); + + // Run until playback is stopped while(run) { if(Kit_GetPlayerState(player) == KIT_STOPPED) { run = false; @@ -222,30 +264,6 @@ int main(int argc, char *argv[]) { if(event.key.keysym.sym == SDLK_ESCAPE) { run = false; } - if(event.key.keysym.sym == SDLK_q) { - // Start or unpause the video - Kit_PlayerPlay(player); - } - if(event.key.keysym.sym == SDLK_w) { - // Pause playback - Kit_PlayerPause(player); - } - if(event.key.keysym.sym == SDLK_e) { - // Stop playback (will close the window) - Kit_PlayerStop(player); - } - if(event.key.keysym.sym == SDLK_RIGHT) { - // Skip 10 seconds forwards or to the end of the file - if(Kit_PlayerSeek(player, 10.0) != 0) { - fprintf(stderr, "%s\n", Kit_GetError()); - } - } - if(event.key.keysym.sym == SDLK_LEFT) { - // Seek 10 seconds backwards or to the start of the file - if(Kit_PlayerSeek(player, -10.0) != 0) { - fprintf(stderr, "%s\n", Kit_GetError()); - } - } break; case SDL_KEYDOWN: @@ -266,14 +284,27 @@ int main(int argc, char *argv[]) { mouse_y = event.motion.y; break; + case SDL_WINDOWEVENT: + switch(event.window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + SDL_GetWindowSize(window, &screen_w, &screen_h); + find_viewport_size( + screen_w, screen_h, pinfo.video.output.width, pinfo.video.output.height, &size_w, &size_h); + SDL_RenderSetLogicalSize(renderer, size_w, size_h); + Kit_SetPlayerScreenSize(player, size_w, size_h); + break; + } + break; + case SDL_MOUSEBUTTONUP: // Handle user clicking the progress bar if(mouse_x >= 30 && mouse_x <= size_w-30 && mouse_y >= size_h - 60 && mouse_y <= size_h - 40) { double pos = ((double)mouse_x - 30) / ((double)size_w - 60); - double m_time = Kit_GetPlayerDuration(player) * pos - Kit_GetPlayerPosition(player); + double m_time = Kit_GetPlayerDuration(player) * pos; if(Kit_PlayerSeek(player, m_time) != 0) { fprintf(stderr, "%s\n", Kit_GetError()); } + SDL_ClearQueuedAudio(audio_dev); } else { // Handle pause if(Kit_GetPlayerState(player) == KIT_PAUSED) { @@ -290,17 +321,16 @@ int main(int argc, char *argv[]) { } } - // Enable GUI if mouse is hovering over the bottom third of the screen - int limit = (pinfo.video.height / 3) * 2; - gui_enabled = (mouse_y >= limit); - // Refresh audio - if(SDL_GetQueuedAudioSize(audio_dev) < AUDIOBUFFER_SIZE) { - int need = AUDIOBUFFER_SIZE - ret; + int queued = SDL_GetQueuedAudioSize(audio_dev); + if(queued < AUDIOBUFFER_SIZE) { + int need = AUDIOBUFFER_SIZE - queued; - SDL_LockAudio(); while(need > 0) { - ret = Kit_GetAudioData(player, (unsigned char*)audiobuf, AUDIOBUFFER_SIZE, (size_t)SDL_GetQueuedAudioSize(audio_dev)); + ret = Kit_GetPlayerAudioData( + player, + (unsigned char*)audiobuf, + AUDIOBUFFER_SIZE); need -= ret; if(ret > 0) { SDL_QueueAudio(audio_dev, audiobuf, ret); @@ -308,21 +338,25 @@ int main(int argc, char *argv[]) { break; } } - SDL_UnlockAudio(); - SDL_PauseAudioDevice(audio_dev, 0); + // If we now have data, start playback (again) + if(SDL_GetQueuedAudioSize(audio_dev) > 0) { + SDL_PauseAudioDevice(audio_dev, 0); + } } - // Clear screen with black - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderClear(renderer); - // Refresh videotexture and render it - Kit_GetVideoData(player, video_tex); + Kit_GetPlayerVideoData(player, video_tex); SDL_RenderCopy(renderer, video_tex, NULL, NULL); - Kit_GetSubtitleData(player, renderer); - // Render GUI - if(gui_enabled) { + // Refresh subtitle texture atlas and render subtitle frames from it + // For subtitles, use screen size instead of video size for best quality + int got = Kit_GetPlayerSubtitleData(player, subtitle_tex, sources, targets, ATLAS_MAX); + for(int i = 0; i < got; i++) { + SDL_RenderCopy(renderer, subtitle_tex, &sources[i], &targets[i]); + } + + // Enable GUI if mouse is hovering over the bottom third of the screen + if(mouse_y >= ((size_h / 3) * 2)) { double percent = Kit_GetPlayerPosition(player) / Kit_GetPlayerDuration(player); render_gui(renderer, percent); } @@ -331,13 +365,14 @@ int main(int argc, char *argv[]) { SDL_RenderPresent(renderer); } - SDL_DestroyTexture(video_tex); - SDL_CloseAudioDevice(audio_dev); - Kit_ClosePlayer(player); Kit_CloseSource(src); - Kit_Quit(); + + SDL_DestroyTexture(subtitle_tex); + SDL_DestroyTexture(video_tex); + SDL_CloseAudioDevice(audio_dev); + SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/examples/example_custom.c b/examples/example_custom.c new file mode 100644 index 0000000..f99c94e --- /dev/null +++ b/examples/example_custom.c @@ -0,0 +1,230 @@ +#include <kitchensink/kitchensink.h> +#include <SDL.h> +#include <stdio.h> +#include <stdbool.h> + +/* +* Note! This example does not do proper error handling etc. +* It is for example use only! +*/ + +#define AUDIOBUFFER_SIZE (1024 * 64) +#define ATLAS_WIDTH 4096 +#define ATLAS_HEIGHT 4096 +#define ATLAS_MAX 1024 + + +int read_callback(void *userdata, uint8_t *buf, int buf_size) { + FILE *fd = (FILE*)userdata; + if(!feof(fd)) { + return fread(buf, 1, buf_size, fd); + } + return -1; +} + +int main(int argc, char *argv[]) { + int err = 0, ret = 0; + const char* filename = NULL; + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; + bool run = true; + Kit_Source *src = NULL; + Kit_Player *player = NULL; + SDL_AudioSpec wanted_spec, audio_spec; + SDL_AudioDeviceID audio_dev; + + // Get filename to open + if(argc != 2) { + fprintf(stderr, "Usage: custom <filename>\n"); + return 0; + } + filename = argv[1]; + + // Init SDL + err = SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO); + if(err != 0) { + fprintf(stderr, "Unable to initialize SDL2!\n"); + return 1; + } + + // Create a resizable window. + window = SDL_CreateWindow("Example Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_RESIZABLE); + if(window == NULL) { + fprintf(stderr, "Unable to create a new window!\n"); + return 1; + } + + // Create an accelerated renderer. Enable vsync, so we don't need to play around with SDL_Delay. + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC); + if(window == NULL) { + fprintf(stderr, "Unable to create a renderer!\n"); + return 1; + } + + // Initialize Kitchensink with network and libass support. + err = Kit_Init(KIT_INIT_NETWORK|KIT_INIT_ASS); + if(err != 0) { + fprintf(stderr, "Unable to initialize Kitchensink: %s", Kit_GetError()); + return 1; + } + + // Open file with fopen. We then proceed to read this with our custom file handlers. + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) { + fprintf(stderr, "Unable to open file '%s' for reading\n", filename); + return 1; + } + + // Open up the custom source. Declare read callback, and transport FD in userdata. + src = Kit_CreateSourceFromCustom(read_callback, NULL, fd); + if(src == NULL) { + fprintf(stderr, "Unable to load file '%s': %s\n", filename, Kit_GetError()); + return 1; + } + + // Create the player. Pick best video, audio and subtitle streams, and set subtitle + // rendering resolution to screen resolution. + player = Kit_CreatePlayer( + src, + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO), + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO), + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE), + 1280, 720); + if(player == NULL) { + fprintf(stderr, "Unable to create player: %s\n", Kit_GetError()); + return 1; + } + + // Print some information + Kit_PlayerInfo pinfo; + Kit_GetPlayerInfo(player, &pinfo); + + // Make sure there is video in the file to play first. + if(Kit_GetPlayerVideoStream(player) == -1) { + fprintf(stderr, "File contains no video!\n"); + return 1; + } + + // Init audio + SDL_memset(&wanted_spec, 0, sizeof(wanted_spec)); + wanted_spec.freq = pinfo.audio.output.samplerate; + wanted_spec.format = pinfo.audio.output.format; + wanted_spec.channels = pinfo.audio.output.channels; + audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &audio_spec, 0); + SDL_PauseAudioDevice(audio_dev, 0); + + // Initialize video texture. This will probably end up as YV12 most of the time. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + SDL_Texture *video_tex = SDL_CreateTexture( + renderer, + pinfo.video.output.format, + SDL_TEXTUREACCESS_STATIC, + pinfo.video.output.width, + pinfo.video.output.height); + if(video_tex == NULL) { + fprintf(stderr, "Error while attempting to create a video texture\n"); + return 1; + } + + // This is the subtitle texture atlas. This contains all the subtitle image fragments. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); // Always nearest for atlas operations + SDL_Texture *subtitle_tex = SDL_CreateTexture( + renderer, + pinfo.subtitle.output.format, + SDL_TEXTUREACCESS_STATIC, + ATLAS_WIDTH, ATLAS_HEIGHT); + if(subtitle_tex == NULL) { + fprintf(stderr, "Error while attempting to create a subtitle texture atlas\n"); + return 1; + } + + // Make sure subtitle texture is in correct blendmode + SDL_SetTextureBlendMode(subtitle_tex, SDL_BLENDMODE_BLEND); + + // Clear screen with black + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + // Start playback + Kit_PlayerPlay(player); + + // Playback temporary data buffers + char audiobuf[AUDIOBUFFER_SIZE]; + SDL_Rect sources[ATLAS_MAX]; + SDL_Rect targets[ATLAS_MAX]; + + // Get movie area size + SDL_RenderSetLogicalSize(renderer, pinfo.video.output.width, pinfo.video.output.height); + while(run) { + if(Kit_GetPlayerState(player) == KIT_STOPPED) { + run = false; + continue; + } + + SDL_Event event; + while(SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + run = false; + break; + case SDL_KEYUP: + if(event.key.keysym.sym == SDLK_RIGHT) + Kit_PlayerSeek(player, Kit_GetPlayerPosition(player) + 10); + if(event.key.keysym.sym == SDLK_LEFT) + Kit_PlayerSeek(player, Kit_GetPlayerPosition(player) - 10); + break; + } + } + + // Refresh audio + int queued = SDL_GetQueuedAudioSize(audio_dev); + if(queued < AUDIOBUFFER_SIZE) { + int need = AUDIOBUFFER_SIZE - queued; + + while(need > 0) { + ret = Kit_GetPlayerAudioData( + player, + (unsigned char*)audiobuf, + AUDIOBUFFER_SIZE); + need -= ret; + if(ret > 0) { + SDL_QueueAudio(audio_dev, audiobuf, ret); + } else { + break; + } + } + // If we now have data, start playback (again) + if(SDL_GetQueuedAudioSize(audio_dev) > 0) { + SDL_PauseAudioDevice(audio_dev, 0); + } + } + + // Refresh videotexture and render it + Kit_GetPlayerVideoData(player, video_tex); + SDL_RenderCopy(renderer, video_tex, NULL, NULL); + + // Refresh subtitle texture atlas and render subtitle frames from it + // For subtitles, use screen size instead of video size for best quality + int got = Kit_GetPlayerSubtitleData(player, subtitle_tex, sources, targets, ATLAS_MAX); + for(int i = 0; i < got; i++) { + SDL_RenderCopy(renderer, subtitle_tex, &sources[i], &targets[i]); + } + + // Render to screen + wait for vsync + SDL_RenderPresent(renderer); + } + + Kit_ClosePlayer(player); + Kit_CloseSource(src); + fclose(fd); + Kit_Quit(); + + SDL_DestroyTexture(subtitle_tex); + SDL_DestroyTexture(video_tex); + SDL_CloseAudioDevice(audio_dev); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} diff --git a/examples/example_rwops.c b/examples/example_rwops.c new file mode 100644 index 0000000..84370f1 --- /dev/null +++ b/examples/example_rwops.c @@ -0,0 +1,223 @@ +#include <kitchensink/kitchensink.h> +#include <SDL.h> +#include <stdio.h> +#include <stdbool.h> + +/* +* Note! This example does not do proper error handling etc. +* It is for example use only! +*/ + +#define AUDIOBUFFER_SIZE (1024 * 64) +#define ATLAS_WIDTH 4096 +#define ATLAS_HEIGHT 4096 +#define ATLAS_MAX 1024 + + +int main(int argc, char *argv[]) { + int err = 0, ret = 0; + const char* filename = NULL; + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; + bool run = true; + Kit_Source *src = NULL; + Kit_Player *player = NULL; + SDL_AudioSpec wanted_spec, audio_spec; + SDL_AudioDeviceID audio_dev; + + // Get filename to open + if(argc != 2) { + fprintf(stderr, "Usage: custom <filename>\n"); + return 0; + } + filename = argv[1]; + + // Init SDL + err = SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO); + if(err != 0) { + fprintf(stderr, "Unable to initialize SDL2!\n"); + return 1; + } + + // Create a resizable window. + window = SDL_CreateWindow("Example Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_RESIZABLE); + if(window == NULL) { + fprintf(stderr, "Unable to create a new window!\n"); + return 1; + } + + // Create an accelerated renderer. Enable vsync, so we don't need to play around with SDL_Delay. + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC); + if(window == NULL) { + fprintf(stderr, "Unable to create a renderer!\n"); + return 1; + } + + // Initialize Kitchensink with network and libass support. + err = Kit_Init(KIT_INIT_NETWORK|KIT_INIT_ASS); + if(err != 0) { + fprintf(stderr, "Unable to initialize Kitchensink: %s", Kit_GetError()); + return 1; + } + + // Open file with fopen. We then proceed to read this with our custom file handlers. + SDL_RWops *rw_ops = SDL_RWFromFile(filename, "rb"); + if(rw_ops == NULL) { + fprintf(stderr, "Unable to open file '%s' for reading\n", filename); + return 1; + } + + // Open up the SDL RWops source + src = Kit_CreateSourceFromRW(rw_ops); + if(src == NULL) { + fprintf(stderr, "Unable to load file '%s': %s\n", filename, Kit_GetError()); + return 1; + } + + // Create the player. Pick best video, audio and subtitle streams, and set subtitle + // rendering resolution to screen resolution. + player = Kit_CreatePlayer( + src, + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO), + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO), + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE), + 1280, 720); + if(player == NULL) { + fprintf(stderr, "Unable to create player: %s\n", Kit_GetError()); + return 1; + } + + // Print some information + Kit_PlayerInfo pinfo; + Kit_GetPlayerInfo(player, &pinfo); + + // Make sure there is video in the file to play first. + if(Kit_GetPlayerVideoStream(player) == -1) { + fprintf(stderr, "File contains no video!\n"); + return 1; + } + + // Init audio + SDL_memset(&wanted_spec, 0, sizeof(wanted_spec)); + wanted_spec.freq = pinfo.audio.output.samplerate; + wanted_spec.format = pinfo.audio.output.format; + wanted_spec.channels = pinfo.audio.output.channels; + audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &audio_spec, 0); + SDL_PauseAudioDevice(audio_dev, 0); + + // Initialize video texture. This will probably end up as YV12 most of the time. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + SDL_Texture *video_tex = SDL_CreateTexture( + renderer, + pinfo.video.output.format, + SDL_TEXTUREACCESS_STATIC, + pinfo.video.output.width, + pinfo.video.output.height); + if(video_tex == NULL) { + fprintf(stderr, "Error while attempting to create a video texture\n"); + return 1; + } + + // This is the subtitle texture atlas. This contains all the subtitle image fragments. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); // Always nearest for atlas operations + SDL_Texture *subtitle_tex = SDL_CreateTexture( + renderer, + pinfo.subtitle.output.format, + SDL_TEXTUREACCESS_STATIC, + ATLAS_WIDTH, ATLAS_HEIGHT); + if(subtitle_tex == NULL) { + fprintf(stderr, "Error while attempting to create a subtitle texture atlas\n"); + return 1; + } + + // Make sure subtitle texture is in correct blendmode + SDL_SetTextureBlendMode(subtitle_tex, SDL_BLENDMODE_BLEND); + + // Clear screen with black + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + // Start playback + Kit_PlayerPlay(player); + + // Playback temporary data buffers + char audiobuf[AUDIOBUFFER_SIZE]; + SDL_Rect sources[ATLAS_MAX]; + SDL_Rect targets[ATLAS_MAX]; + + // Get movie area size + SDL_RenderSetLogicalSize(renderer, pinfo.video.output.width, pinfo.video.output.height); + while(run) { + if(Kit_GetPlayerState(player) == KIT_STOPPED) { + run = false; + continue; + } + + SDL_Event event; + while(SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + run = false; + break; + case SDL_KEYUP: + if(event.key.keysym.sym == SDLK_RIGHT) + Kit_PlayerSeek(player, Kit_GetPlayerPosition(player) + 10); + if(event.key.keysym.sym == SDLK_LEFT) + Kit_PlayerSeek(player, Kit_GetPlayerPosition(player) - 10); + break; + + } + } + + // Refresh audio + int queued = SDL_GetQueuedAudioSize(audio_dev); + if(queued < AUDIOBUFFER_SIZE) { + int need = AUDIOBUFFER_SIZE - queued; + + while(need > 0) { + ret = Kit_GetPlayerAudioData( + player, + (unsigned char*)audiobuf, + AUDIOBUFFER_SIZE); + need -= ret; + if(ret > 0) { + SDL_QueueAudio(audio_dev, audiobuf, ret); + } else { + break; + } + } + // If we now have data, start playback (again) + if(SDL_GetQueuedAudioSize(audio_dev) > 0) { + SDL_PauseAudioDevice(audio_dev, 0); + } + } + + // Refresh videotexture and render it + Kit_GetPlayerVideoData(player, video_tex); + SDL_RenderCopy(renderer, video_tex, NULL, NULL); + + // Refresh subtitle texture atlas and render subtitle frames from it + // For subtitles, use screen size instead of video size for best quality + int got = Kit_GetPlayerSubtitleData(player, subtitle_tex, sources, targets, ATLAS_MAX); + for(int i = 0; i < got; i++) { + SDL_RenderCopy(renderer, subtitle_tex, &sources[i], &targets[i]); + } + + // Render to screen + wait for vsync + SDL_RenderPresent(renderer); + } + + Kit_ClosePlayer(player); + Kit_CloseSource(src); + SDL_RWclose(rw_ops); + Kit_Quit(); + + SDL_DestroyTexture(subtitle_tex); + SDL_DestroyTexture(video_tex); + SDL_CloseAudioDevice(audio_dev); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} diff --git a/examples/example_simple.c b/examples/example_simple.c new file mode 100644 index 0000000..4c581c0 --- /dev/null +++ b/examples/example_simple.c @@ -0,0 +1,208 @@ +#include <kitchensink/kitchensink.h> +#include <SDL.h> +#include <stdio.h> +#include <stdbool.h> + +/* +* Note! This example does not do proper error handling etc. +* It is for example use only! +*/ + +#define AUDIOBUFFER_SIZE (1024 * 64) +#define ATLAS_WIDTH 4096 +#define ATLAS_HEIGHT 4096 +#define ATLAS_MAX 1024 + +int main(int argc, char *argv[]) { + int err = 0, ret = 0; + const char* filename = NULL; + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; + bool run = true; + Kit_Source *src = NULL; + Kit_Player *player = NULL; + SDL_AudioSpec wanted_spec, audio_spec; + SDL_AudioDeviceID audio_dev; + + // Get filename to open + if(argc != 2) { + fprintf(stderr, "Usage: simple <filename>\n"); + return 0; + } + filename = argv[1]; + + // Init SDL + err = SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO); + if(err != 0) { + fprintf(stderr, "Unable to initialize SDL2!\n"); + return 1; + } + + // Create a resizable window. + window = SDL_CreateWindow("Example Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_RESIZABLE); + if(window == NULL) { + fprintf(stderr, "Unable to create a new window!\n"); + return 1; + } + + // Create an accelerated renderer. Enable vsync, so we don't need to play around with SDL_Delay. + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC); + if(window == NULL) { + fprintf(stderr, "Unable to create a renderer!\n"); + return 1; + } + + // Initialize Kitchensink with network and libass support. + err = Kit_Init(KIT_INIT_NETWORK|KIT_INIT_ASS); + if(err != 0) { + fprintf(stderr, "Unable to initialize Kitchensink: %s", Kit_GetError()); + return 1; + } + + // Open up the sourcefile. + // This can be a local file, network url, ... + src = Kit_CreateSourceFromUrl(filename); + if(src == NULL) { + fprintf(stderr, "Unable to load file '%s': %s\n", filename, Kit_GetError()); + return 1; + } + + // Create the player. Pick best video, audio and subtitle streams, and set subtitle + // rendering resolution to screen resolution. + player = Kit_CreatePlayer( + src, + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO), + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO), + Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE), + 1280, 720); + if(player == NULL) { + fprintf(stderr, "Unable to create player: %s\n", Kit_GetError()); + return 1; + } + + // Print some information + Kit_PlayerInfo pinfo; + Kit_GetPlayerInfo(player, &pinfo); + + // Make sure there is video in the file to play first. + if(Kit_GetPlayerVideoStream(player) == -1) { + fprintf(stderr, "File contains no video!\n"); + return 1; + } + + // Init audio + SDL_memset(&wanted_spec, 0, sizeof(wanted_spec)); + wanted_spec.freq = pinfo.audio.output.samplerate; + wanted_spec.format = pinfo.audio.output.format; + wanted_spec.channels = pinfo.audio.output.channels; + audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &audio_spec, 0); + SDL_PauseAudioDevice(audio_dev, 0); + + // Initialize video texture. This will probably end up as YV12 most of the time. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + SDL_Texture *video_tex = SDL_CreateTexture( + renderer, + pinfo.video.output.format, + SDL_TEXTUREACCESS_STATIC, + pinfo.video.output.width, + pinfo.video.output.height); + if(video_tex == NULL) { + fprintf(stderr, "Error while attempting to create a video texture\n"); + return 1; + } + + // This is the subtitle texture atlas. This contains all the subtitle image fragments. + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); // Always nearest for atlas operations + SDL_Texture *subtitle_tex = SDL_CreateTexture( + renderer, + pinfo.subtitle.output.format, + SDL_TEXTUREACCESS_STATIC, + ATLAS_WIDTH, ATLAS_HEIGHT); + if(subtitle_tex == NULL) { + fprintf(stderr, "Error while attempting to create a subtitle texture atlas\n"); + return 1; + } + + // Make sure subtitle texture is in correct blendmode + SDL_SetTextureBlendMode(subtitle_tex, SDL_BLENDMODE_BLEND); + + // Clear screen with black + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + // Start playback + Kit_PlayerPlay(player); + + // Playback temporary data buffers + char audiobuf[AUDIOBUFFER_SIZE]; + SDL_Rect sources[ATLAS_MAX]; + SDL_Rect targets[ATLAS_MAX]; + + // Get movie area size + SDL_RenderSetLogicalSize(renderer, pinfo.video.output.width, pinfo.video.output.height); + while(run) { + if(Kit_GetPlayerState(player) == KIT_STOPPED) { + run = false; + continue; + } + + SDL_Event event; + while(SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + run = false; + break; + } + } + + // Refresh audio + int queued = SDL_GetQueuedAudioSize(audio_dev); + if(queued < AUDIOBUFFER_SIZE) { + int need = AUDIOBUFFER_SIZE - queued; + + while(need > 0) { + ret = Kit_GetPlayerAudioData( + player, + (unsigned char*)audiobuf, + AUDIOBUFFER_SIZE); + need -= ret; + if(ret > 0) { + SDL_QueueAudio(audio_dev, audiobuf, ret); + } else { + break; + } + } + // If we now have data, start playback (again) + if(SDL_GetQueuedAudioSize(audio_dev) > 0) { + SDL_PauseAudioDevice(audio_dev, 0); + } + } + + // Refresh videotexture and render it + Kit_GetPlayerVideoData(player, video_tex); + SDL_RenderCopy(renderer, video_tex, NULL, NULL); + + // Refresh subtitle texture atlas and render subtitle frames from it + // For subtitles, use screen size instead of video size for best quality + int got = Kit_GetPlayerSubtitleData(player, subtitle_tex, sources, targets, ATLAS_MAX); + for(int i = 0; i < got; i++) { + SDL_RenderCopy(renderer, subtitle_tex, &sources[i], &targets[i]); + } + + // Render to screen + wait for vsync + SDL_RenderPresent(renderer); + } + + Kit_ClosePlayer(player); + Kit_CloseSource(src); + Kit_Quit(); + + SDL_DestroyTexture(subtitle_tex); + SDL_DestroyTexture(video_tex); + SDL_CloseAudioDevice(audio_dev); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} diff --git a/include/kitchensink/internal/audio/kitaudio.h b/include/kitchensink/internal/audio/kitaudio.h new file mode 100644 index 0000000..e42770b --- /dev/null +++ b/include/kitchensink/internal/audio/kitaudio.h @@ -0,0 +1,11 @@ +#ifndef KITAUDIO_H
+#define KITAUDIO_H
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitsource.h"
+#include "kitchensink/internal/kitdecoder.h"
+
+KIT_LOCAL Kit_Decoder* Kit_CreateAudioDecoder(const Kit_Source *src, int stream_index);
+KIT_LOCAL int Kit_GetAudioDecoderData(Kit_Decoder *dec, unsigned char *buf, int len);
+
+#endif // KITAUDIO_H
diff --git a/include/kitchensink/internal/kitdecoder.h b/include/kitchensink/internal/kitdecoder.h new file mode 100644 index 0000000..ef3edb7 --- /dev/null +++ b/include/kitchensink/internal/kitdecoder.h @@ -0,0 +1,75 @@ +#ifndef KITDECODER_H
+#define KITDECODER_H
+
+#include <stdbool.h>
+
+#include <SDL_mutex.h>
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+
+#include "kitchensink/kitformat.h"
+#include "kitchensink/kitcodec.h"
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitsource.h"
+#include "kitchensink/internal/utils/kitbuffer.h"
+
+enum {
+ KIT_DEC_BUF_IN = 0,
+ KIT_DEC_BUF_OUT,
+ KIT_DEC_BUF_COUNT
+};
+
+typedef struct Kit_Decoder Kit_Decoder;
+
+typedef void (*dec_decode_cb)(Kit_Decoder *dec, AVPacket *in_packet);
+typedef void (*dec_close_cb)(Kit_Decoder *dec);
+typedef void (*dec_free_packet_cb)(void *packet);
+
+KIT_LOCAL struct Kit_Decoder {
+ int stream_index; ///< Source stream index for the current stream
+ double clock_sync; ///< Sync source for current stream
+ double clock_pos; ///< Current pts for the stream
+ Kit_OutputFormat output; ///< Output format for the decoder
+
+ AVCodecContext *codec_ctx; ///< FFMpeg internal: Codec context
+ AVFormatContext *format_ctx; ///< FFMpeg internal: Format context (owner: Kit_Source)
+
+ SDL_mutex *output_lock; ///< Threading lock for output buffer
+ Kit_Buffer *buffer[2]; ///< Buffers for incoming and decoded packets
+
+ void *userdata; ///< Decoder specific information (Audio, video, subtitle context)
+ dec_decode_cb dec_decode; ///< Decoder decoding function callback
+ dec_close_cb dec_close; ///< Decoder close function callback
+};
+
+KIT_LOCAL Kit_Decoder* Kit_CreateDecoder(const Kit_Source *src, int stream_index,
+ int out_b_size, dec_free_packet_cb free_out_cb,
+ int thread_count);
+KIT_LOCAL void Kit_CloseDecoder(Kit_Decoder *dec);
+
+KIT_LOCAL int Kit_GetDecoderStreamIndex(const Kit_Decoder *dec);
+KIT_LOCAL int Kit_GetDecoderCodecInfo(const Kit_Decoder *dec, Kit_Codec *codec);
+KIT_LOCAL int Kit_GetDecoderOutputFormat(const Kit_Decoder *dec, Kit_OutputFormat *output);
+
+KIT_LOCAL void Kit_SetDecoderClockSync(Kit_Decoder *dec, double sync);
+KIT_LOCAL void Kit_ChangeDecoderClockSync(Kit_Decoder *dec, double sync);
+
+KIT_LOCAL int Kit_RunDecoder(Kit_Decoder *dec);
+KIT_LOCAL void Kit_ClearDecoderBuffers(Kit_Decoder *dec);
+
+KIT_LOCAL bool Kit_CanWriteDecoderInput(Kit_Decoder *dec);
+KIT_LOCAL int Kit_WriteDecoderInput(Kit_Decoder *dec, AVPacket *packet);
+KIT_LOCAL AVPacket* Kit_ReadDecoderInput(Kit_Decoder *dec);
+KIT_LOCAL void Kit_ClearDecoderInput(Kit_Decoder *dec);
+
+KIT_LOCAL int Kit_WriteDecoderOutput(Kit_Decoder *dec, void *packet);
+KIT_LOCAL void* Kit_PeekDecoderOutput(Kit_Decoder *dec);
+KIT_LOCAL void* Kit_ReadDecoderOutput(Kit_Decoder *dec);
+KIT_LOCAL void Kit_AdvanceDecoderOutput(Kit_Decoder *dec);
+KIT_LOCAL void Kit_ForEachDecoderOutput(Kit_Decoder *dec, Kit_ForEachItemCallback foreach_cb, void *userdata);
+KIT_LOCAL int Kit_LockDecoderOutput(Kit_Decoder *dec);
+KIT_LOCAL void Kit_UnlockDecoderOutput(Kit_Decoder *dec);
+KIT_LOCAL void Kit_ClearDecoderOutput(Kit_Decoder *dec);
+
+
+#endif // KITDECODER_H
diff --git a/include/kitchensink/internal/kitlibstate.h b/include/kitchensink/internal/kitlibstate.h index e16a5c9..b92cebb 100644 --- a/include/kitchensink/internal/kitlibstate.h +++ b/include/kitchensink/internal/kitlibstate.h @@ -1,12 +1,18 @@ #ifndef KITLIBSTATE_H #define KITLIBSTATE_H -#include <ass/ass.h> +#include "kitchensink/internal/libass.h" #include "kitchensink/kitconfig.h" typedef struct Kit_LibraryState { unsigned int init_flags; + unsigned int thread_count; + unsigned int font_hinting; + unsigned int video_buf_frames; + unsigned int audio_buf_frames; + unsigned int subtitle_buf_frames; ASS_Library *libass_handle; + void *ass_so_handle; } Kit_LibraryState; KIT_LOCAL Kit_LibraryState* Kit_GetLibraryState(); diff --git a/include/kitchensink/internal/kitlist.h b/include/kitchensink/internal/kitlist.h deleted file mode 100644 index 85e3e3f..0000000 --- a/include/kitchensink/internal/kitlist.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef KITLIST_H -#define KITLIST_H - -#include "kitchensink/kitconfig.h" - -typedef struct Kit_List Kit_List; - -typedef void (*Kit_ListFreeCallback)(void*); - -struct Kit_List { - unsigned int size; - unsigned int length; - Kit_ListFreeCallback free_cb; - void **data; -}; - -KIT_LOCAL Kit_List* Kit_CreateList(unsigned int size, Kit_ListFreeCallback free_cb); -KIT_LOCAL void Kit_DestroyList(Kit_List *list); - -KIT_LOCAL void Kit_ClearList(Kit_List *list); -KIT_LOCAL void Kit_RemoveFromList(Kit_List *list, unsigned int iterator); -KIT_LOCAL void* Kit_IterateList(const Kit_List *list, unsigned int *iterator); -KIT_LOCAL int Kit_WriteList(Kit_List *list, void *ptr); -KIT_LOCAL int Kit_GetListLength(const Kit_List *list); - -#endif // KITLIST_H diff --git a/include/kitchensink/internal/libass.h b/include/kitchensink/internal/libass.h new file mode 100644 index 0000000..0f91d95 --- /dev/null +++ b/include/kitchensink/internal/libass.h @@ -0,0 +1,66 @@ +#ifndef KITLIBASS_H
+#define KITLIBASS_H
+
+#ifndef USE_DYNAMIC_LIBASS
+
+#include <ass/ass.h>
+
+#else // USE_DYNAMIC_LIBASS
+
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "kitchensink/kitconfig.h"
+
+typedef struct ass_library ASS_Library;
+typedef struct ass_renderer ASS_Renderer;
+typedef struct ass_track ASS_Track;
+
+typedef struct ass_image {
+ int w, h;
+ int stride;
+ unsigned char *bitmap;
+ uint32_t color;
+ int dst_x, dst_y;
+ struct ass_image *next;
+ enum {
+ IMAGE_TYPE_CHARACTER,
+ IMAGE_TYPE_OUTLINE,
+ IMAGE_TYPE_SHADOW
+ } type;
+} ASS_Image;
+
+typedef enum {
+ ASS_HINTING_NONE = 0,
+ ASS_HINTING_LIGHT,
+ ASS_HINTING_NORMAL,
+ ASS_HINTING_NATIVE
+} ASS_Hinting;
+
+KIT_LOCAL ASS_Library* (*ass_library_init)(void);
+KIT_LOCAL void (*ass_library_done)(ASS_Library *priv);
+KIT_LOCAL void (*ass_process_codec_private)(ASS_Track *track, char *data, int size);
+KIT_LOCAL void (*ass_set_message_cb)(ASS_Library *priv, void (*msg_cb)(int level, const char *fmt, va_list args, void *data), void *data);
+KIT_LOCAL ASS_Renderer* (*ass_renderer_init)(ASS_Library *);
+KIT_LOCAL void (*ass_renderer_done)(ASS_Renderer *priv);
+KIT_LOCAL void (*ass_set_frame_size)(ASS_Renderer *priv, int w, int h);
+KIT_LOCAL void (*ass_set_hinting)(ASS_Renderer *priv, ASS_Hinting ht);
+KIT_LOCAL void (*ass_set_fonts)(ASS_Renderer *priv, const char *default_font, const char *default_family, int dfp, const char *config, int update);
+KIT_LOCAL ASS_Image* (*ass_render_frame)(ASS_Renderer *priv, ASS_Track *track, long long now, int *detect_change);
+KIT_LOCAL ASS_Track* (*ass_new_track)(ASS_Library *);
+KIT_LOCAL void (*ass_free_track)(ASS_Track *track);
+KIT_LOCAL void (*ass_process_data)(ASS_Track *track, char *data, int size);
+KIT_LOCAL void (*ass_process_chunk)(ASS_Track *track, char *data, int size, long long timecode, long long duration);
+KIT_LOCAL void (*ass_add_font)(ASS_Library *library, char *name, char *data, int data_size);
+KIT_LOCAL void (*ass_set_storage_size)(ASS_Renderer *priv, int w, int h);
+
+KIT_LOCAL int load_libass(void *handle);
+
+#endif // USE_DYNAMIC_LIBASS
+
+// For compatibility
+#ifndef ASS_FONTPROVIDER_AUTODETECT
+#define ASS_FONTPROVIDER_AUTODETECT 1
+#endif
+
+#endif // KITLIBASS_H
diff --git a/include/kitchensink/internal/subtitle/kitatlas.h b/include/kitchensink/internal/subtitle/kitatlas.h new file mode 100644 index 0000000..d9207c1 --- /dev/null +++ b/include/kitchensink/internal/subtitle/kitatlas.h @@ -0,0 +1,40 @@ +#ifndef KITATLAS_H
+#define KITATLAS_H
+
+#include <stdbool.h>
+#include <SDL_rect.h>
+#include <SDL_render.h>
+
+#include "kitchensink/kitconfig.h"
+
+typedef struct Kit_TextureAtlasItem {
+ int cur_shelf; //< Current shelf number in cache
+ int cur_slot; //< Current slot on shelf in cache
+ SDL_Rect source; //< Source coordinates on cache surface
+ SDL_Rect target; //< Target coordinates on output surface
+} Kit_TextureAtlasItem;
+
+typedef struct Kit_Shelf {
+ uint16_t width;
+ uint16_t height;
+ uint16_t count;
+} Kit_Shelf;
+
+typedef struct Kit_TextureAtlas {
+ int cur_items; //< Current items count
+ int max_items; //< Maximum items count
+ int max_shelves; //< Maximum shelf count
+ int w; //< Current atlas width
+ int h; //< Current atlas height
+ Kit_TextureAtlasItem *items; //< Cached items
+ Kit_Shelf *shelves; //< Atlas shelves
+} Kit_TextureAtlas;
+
+KIT_LOCAL Kit_TextureAtlas* Kit_CreateAtlas();
+KIT_LOCAL void Kit_FreeAtlas(Kit_TextureAtlas *atlas);
+KIT_LOCAL void Kit_ClearAtlasContent(Kit_TextureAtlas *atlas);
+KIT_LOCAL void Kit_CheckAtlasTextureSize(Kit_TextureAtlas *atlas, SDL_Texture *texture);
+KIT_LOCAL int Kit_GetAtlasItems(const Kit_TextureAtlas *atlas, SDL_Rect *sources, SDL_Rect *targets, int limit);
+KIT_LOCAL int Kit_AddAtlasItem(Kit_TextureAtlas *atlas, SDL_Texture *texture, SDL_Surface *surface, const SDL_Rect *target);
+
+#endif // KITATLAS_H
diff --git a/include/kitchensink/internal/subtitle/kitsubtitle.h b/include/kitchensink/internal/subtitle/kitsubtitle.h new file mode 100644 index 0000000..8bb2a62 --- /dev/null +++ b/include/kitchensink/internal/subtitle/kitsubtitle.h @@ -0,0 +1,17 @@ +#ifndef KITSUBTITLE_H
+#define KITSUBTITLE_H
+
+#include <SDL_render.h>
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitsource.h"
+#include "kitchensink/internal/kitdecoder.h"
+
+KIT_LOCAL Kit_Decoder* Kit_CreateSubtitleDecoder(
+ const Kit_Source *src, int stream_index, int video_w, int video_h, int screen_w, int screen_h);
+KIT_LOCAL void Kit_GetSubtitleDecoderTexture(Kit_Decoder *dec, SDL_Texture *texture);
+KIT_LOCAL void Kit_SetSubtitleDecoderSize(Kit_Decoder *dec, int w, int h);
+KIT_LOCAL int Kit_GetSubtitleDecoderInfo(
+ Kit_Decoder *dec, SDL_Texture *texture, SDL_Rect *sources, SDL_Rect *targets, int limit);
+
+#endif // KITSUBTITLE_H
diff --git a/include/kitchensink/internal/subtitle/kitsubtitlepacket.h b/include/kitchensink/internal/subtitle/kitsubtitlepacket.h new file mode 100644 index 0000000..c560c74 --- /dev/null +++ b/include/kitchensink/internal/subtitle/kitsubtitlepacket.h @@ -0,0 +1,22 @@ +#ifndef KITSUBTITLEPACKET_H
+#define KITSUBTITLEPACKET_H
+
+#include <stdbool.h>
+#include <SDL_surface.h>
+
+#include "kitchensink/kitconfig.h"
+
+typedef struct Kit_SubtitlePacket {
+ double pts_start;
+ double pts_end;
+ int x;
+ int y;
+ bool clear;
+ SDL_Surface *surface;
+} Kit_SubtitlePacket;
+
+KIT_LOCAL Kit_SubtitlePacket* Kit_CreateSubtitlePacket(
+ bool clear, double pts_start, double pts_end, int pos_x, int pos_y, SDL_Surface *surface);
+KIT_LOCAL void Kit_FreeSubtitlePacket(Kit_SubtitlePacket *packet);
+
+#endif // KITSUBTITLEPACKET_H
diff --git a/include/kitchensink/internal/subtitle/renderers/kitsubass.h b/include/kitchensink/internal/subtitle/renderers/kitsubass.h new file mode 100644 index 0000000..6dff50c --- /dev/null +++ b/include/kitchensink/internal/subtitle/renderers/kitsubass.h @@ -0,0 +1,11 @@ +#ifndef KITSUBASS_H
+#define KITSUBASS_H
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/internal/kitdecoder.h"
+#include "kitchensink/internal/subtitle/renderers/kitsubrenderer.h"
+
+KIT_LOCAL Kit_SubtitleRenderer* Kit_CreateASSSubtitleRenderer(
+ Kit_Decoder *dec, int video_w, int video_h, int screen_w, int screen_h);
+
+#endif // KITSUBASS_H
diff --git a/include/kitchensink/internal/subtitle/renderers/kitsubimage.h b/include/kitchensink/internal/subtitle/renderers/kitsubimage.h new file mode 100644 index 0000000..883fde3 --- /dev/null +++ b/include/kitchensink/internal/subtitle/renderers/kitsubimage.h @@ -0,0 +1,11 @@ +#ifndef KITSUBIMAGE_H
+#define KITSUBIMAGE_H
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/internal/kitdecoder.h"
+#include "kitchensink/internal/subtitle/renderers/kitsubrenderer.h"
+
+KIT_LOCAL Kit_SubtitleRenderer* Kit_CreateImageSubtitleRenderer(
+ Kit_Decoder *dec, int video_w, int video_h, int screen_w, int screen_h);
+
+#endif // KITSUBIMAGE_H
diff --git a/include/kitchensink/internal/subtitle/renderers/kitsubrenderer.h b/include/kitchensink/internal/subtitle/renderers/kitsubrenderer.h new file mode 100644 index 0000000..3c37b00 --- /dev/null +++ b/include/kitchensink/internal/subtitle/renderers/kitsubrenderer.h @@ -0,0 +1,32 @@ +#ifndef KITSUBRENDERER_H
+#define KITSUBRENDERER_H
+
+#include <SDL_render.h>
+
+#include "kitchensink/kitsource.h"
+
+typedef struct Kit_SubtitleRenderer Kit_SubtitleRenderer;
+typedef struct Kit_TextureAtlas Kit_TextureAtlas;
+typedef struct Kit_Decoder Kit_Decoder;
+
+typedef void (*ren_render_cb)(Kit_SubtitleRenderer *ren, void *src, double start_pts, double end_pts);
+typedef int (*ren_get_data_cb)(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts);
+typedef void (*ren_set_size_cb)(Kit_SubtitleRenderer *ren, int w, int h);
+typedef void (*ren_close_cb)(Kit_SubtitleRenderer *ren);
+
+struct Kit_SubtitleRenderer {
+ Kit_Decoder *dec;
+ void *userdata;
+ ren_render_cb ren_render; ///< Subtitle rendering function callback
+ ren_get_data_cb ren_get_data; ///< Subtitle data getter function callback
+ ren_set_size_cb ren_set_size; ///< Screen size setter function callback
+ ren_close_cb ren_close; ///< Subtitle renderer close function callback
+};
+
+KIT_LOCAL Kit_SubtitleRenderer* Kit_CreateSubtitleRenderer(Kit_Decoder *dec);
+KIT_LOCAL void Kit_RunSubtitleRenderer(Kit_SubtitleRenderer *ren, void *src, double start_pts, double end_pts);
+KIT_LOCAL int Kit_GetSubtitleRendererData(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts);
+KIT_LOCAL void Kit_SetSubtitleRendererSize(Kit_SubtitleRenderer *ren, int w, int h);
+KIT_LOCAL void Kit_CloseSubtitleRenderer(Kit_SubtitleRenderer *ren);
+
+#endif // KITSUBRENDERER_H
diff --git a/include/kitchensink/internal/kitbuffer.h b/include/kitchensink/internal/utils/kitbuffer.h index 4d0f8cc..67d93c3 100644 --- a/include/kitchensink/internal/kitbuffer.h +++ b/include/kitchensink/internal/utils/kitbuffer.h @@ -6,6 +6,7 @@ typedef struct Kit_Buffer Kit_Buffer; typedef void (*Kit_BufferFreeCallback)(void*); +typedef void (*Kit_ForEachItemCallback)(void*, void *userdata); struct Kit_Buffer { unsigned int read_p; @@ -23,6 +24,7 @@ KIT_LOCAL void* Kit_ReadBuffer(Kit_Buffer *buffer); KIT_LOCAL void* Kit_PeekBuffer(const Kit_Buffer *buffer); KIT_LOCAL void Kit_AdvanceBuffer(Kit_Buffer *buffer); KIT_LOCAL int Kit_WriteBuffer(Kit_Buffer *buffer, void *ptr); +KIT_LOCAL void Kit_ForEachItemInBuffer(const Kit_Buffer *buffer, Kit_ForEachItemCallback cb, void *userdata); KIT_LOCAL int Kit_IsBufferFull(const Kit_Buffer *buffer); #endif // KITBUFFER_H diff --git a/include/kitchensink/internal/utils/kithelpers.h b/include/kitchensink/internal/utils/kithelpers.h new file mode 100644 index 0000000..5b94a7a --- /dev/null +++ b/include/kitchensink/internal/utils/kithelpers.h @@ -0,0 +1,11 @@ +#ifndef KITHELPERS_H
+#define KITHELPERS_H
+
+#include <stdbool.h>
+#include <libavformat/avformat.h>
+#include "kitchensink/kitconfig.h"
+
+KIT_LOCAL double _GetSystemTime();
+KIT_LOCAL bool attachment_is_font(AVStream *stream);
+
+#endif // KITHELPERS_H
diff --git a/include/kitchensink/internal/utils/kitlog.h b/include/kitchensink/internal/utils/kitlog.h new file mode 100644 index 0000000..d298592 --- /dev/null +++ b/include/kitchensink/internal/utils/kitlog.h @@ -0,0 +1,11 @@ +#ifndef KITLOG_H
+#define KITLOG_H
+
+#ifdef NDEBUG
+#define LOG(...)
+#else
+#include <stdio.h>
+#define LOG(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
+#endif
+
+#endif // KITLOG_H
diff --git a/include/kitchensink/internal/kitringbuffer.h b/include/kitchensink/internal/utils/kitringbuffer.h index 2f67520..153dfd4 100644 --- a/include/kitchensink/internal/kitringbuffer.h +++ b/include/kitchensink/internal/utils/kitringbuffer.h @@ -6,7 +6,8 @@ typedef struct Kit_RingBuffer { int size; int len; - int wpos, rpos; + int wpos; + int rpos; char* data; } Kit_RingBuffer; diff --git a/include/kitchensink/internal/video/kitvideo.h b/include/kitchensink/internal/video/kitvideo.h new file mode 100644 index 0000000..5c072ef --- /dev/null +++ b/include/kitchensink/internal/video/kitvideo.h @@ -0,0 +1,13 @@ +#ifndef KITVIDEO_H
+#define KITVIDEO_H
+
+#include <SDL_render.h>
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitsource.h"
+#include "kitchensink/internal/kitdecoder.h"
+
+KIT_LOCAL Kit_Decoder* Kit_CreateVideoDecoder(const Kit_Source *src, int stream_index);
+KIT_LOCAL int Kit_GetVideoDecoderData(Kit_Decoder *dec, SDL_Texture *texture);
+
+#endif // KITVIDEO_H
diff --git a/include/kitchensink/kitchensink.h b/include/kitchensink/kitchensink.h index be318a5..7fc08e6 100644 --- a/include/kitchensink/kitchensink.h +++ b/include/kitchensink/kitchensink.h @@ -1,8 +1,18 @@ #ifndef KITCHENSINK_H #define KITCHENSINK_H +/** + * @brief Header aggregator + * + * @file kitchensink.h + * @author Tuomas Virtanen + * @date 2018-06-27 + */ + #include "kitchensink/kitlib.h" #include "kitchensink/kiterror.h" +#include "kitchensink/kitformat.h" +#include "kitchensink/kitcodec.h" #include "kitchensink/kitsource.h" #include "kitchensink/kitplayer.h" #include "kitchensink/kitutils.h" diff --git a/include/kitchensink/kitcodec.h b/include/kitchensink/kitcodec.h new file mode 100644 index 0000000..589f84d --- /dev/null +++ b/include/kitchensink/kitcodec.h @@ -0,0 +1,32 @@ +#ifndef KITCODEC_H
+#define KITCODEC_H
+
+/**
+ * @brief Codec type
+ *
+ * @file kitcodec.h
+ * @author Tuomas Virtanen
+ * @date 2018-06-25
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KIT_CODEC_NAME_MAX 8
+#define KIT_CODEC_DESC_MAX 48
+
+/**
+ * @brief Contains information about the used codec for playback
+ */
+typedef struct Kit_Codec {
+ unsigned int threads; ///< Currently enabled threads (For all decoders)
+ char name[KIT_CODEC_NAME_MAX]; ///< Codec short name, eg. "ogg" or "webm"
+ char description[KIT_CODEC_DESC_MAX]; ///< Codec longer, more descriptive name
+} Kit_Codec;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // KITCODEC_H
diff --git a/include/kitchensink/kitconfig.h b/include/kitchensink/kitconfig.h index e8b2d01..8eda779 100644 --- a/include/kitchensink/kitconfig.h +++ b/include/kitchensink/kitconfig.h @@ -1,6 +1,14 @@ #ifndef KITCONFIG_H #define KITCONFIG_H +/** + * @brief Public API configurations + * + * @file kitconfig.h + * @author Tuomas Virtanen + * @date 2018-06-25 + */ + #if defined _WIN32 || defined __CYGWIN__ #define KIT_DLL_IMPORT __declspec(dllimport) #define KIT_DLL_EXPORT __declspec(dllexport) diff --git a/include/kitchensink/kiterror.h b/include/kitchensink/kiterror.h index 824ea8c..e6c2acd 100644 --- a/include/kitchensink/kiterror.h +++ b/include/kitchensink/kiterror.h @@ -1,14 +1,38 @@ #ifndef KITERROR_H #define KITERROR_H +/** + * @brief Error handling functions + * + * @file kiterror.h + * @author Tuomas Virtanen + * @date 2018-06-25 + */ + #include "kitchensink/kitconfig.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Returns the latest error. This is set by SDL_kitchensink library functions on error. + * + * @return Error message or NULL + */ KIT_API const char* Kit_GetError(); + +/** + * @brief Sets the error message. This should really only be used by the library. + * + * @param fmt Message format + * @param ... Message arguments + */ KIT_API void Kit_SetError(const char* fmt, ...); + +/** + * @brief Clears latest error message. After this, Kit_GetError() will return NULL. + */ KIT_API void Kit_ClearError(); #ifdef __cplusplus diff --git a/include/kitchensink/kitformat.h b/include/kitchensink/kitformat.h new file mode 100644 index 0000000..3e8df91 --- /dev/null +++ b/include/kitchensink/kitformat.h @@ -0,0 +1,33 @@ +#ifndef KITFORMAT_H
+#define KITFORMAT_H
+
+/**
+ * @brief Audio/video output format type
+ *
+ * @file kitformat.h
+ * @author Tuomas Virtanen
+ * @date 2018-06-25
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Contains information about the data format coming out from the player
+ */
+typedef struct Kit_OutputFormat {
+ unsigned int format; ///< SDL Format (SDL_PixelFormat if video/subtitle, SDL_AudioFormat if audio)
+ int is_signed; ///< Signedness, 1 = signed, 0 = unsigned (if audio)
+ int bytes; ///< Bytes per sample per channel (if audio)
+ int samplerate; ///< Sampling rate (if audio)
+ int channels; ///< Channels (if audio)
+ int width; ///< Width in pixels (if video)
+ int height; ///< Height in pixels (if video)
+} Kit_OutputFormat;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // KITFORMAT_H
diff --git a/include/kitchensink/kitlib.h b/include/kitchensink/kitlib.h index 9a827be..a5fe913 100644 --- a/include/kitchensink/kitlib.h +++ b/include/kitchensink/kitlib.h @@ -1,29 +1,118 @@ #ifndef KITLIB_H #define KITLIB_H -#include "kitchensink/kiterror.h" -#include "kitchensink/kitsource.h" -#include "kitchensink/kitplayer.h" -#include "kitchensink/kitutils.h" +/** + * @brief Library initialization and deinitialization functionality + * + * @file kitlib.h + * @author Tuomas Virtanen + * @date 2018-06-25 + */ + #include "kitchensink/kitconfig.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Font hinting options. Used as values for Kit_SetHint(KIT_HINT_FONT_HINTING, ...). + */ +enum { + KIT_FONT_HINTING_NONE = 0, ///< No hinting. This is recommended option + KIT_FONT_HINTING_LIGHT, ///< Light hinting. Use this if you need hinting + KIT_FONT_HINTING_NORMAL, ///< Not recommended, please see libass docs for details + KIT_FONT_HINTING_NATIVE, ///< Not recommended, please see libass docs for details + KIT_FONT_HINTING_COUNT +}; + +/** + * @brief SDL_kitchensink library version container + */ typedef struct Kit_Version { - unsigned char major; - unsigned char minor; - unsigned char patch; + unsigned char major; ///< Major version number, raising this signifies API breakage + unsigned char minor; ///< Minor version number, small/internal changes + unsigned char patch; ///< Patch version number, bugfixes etc. } Kit_Version; +/** + * @brief Library hint types. Used as keys for Kit_SetHint(). + * + * Note that all of these must be set *before* player initialization for them to take effect! + */ +typedef enum Kit_HintType { + KIT_HINT_FONT_HINTING, ///< Set font hinting mode (currently used for libass) + KIT_HINT_THREAD_COUNT, ///< Set thread count for ffmpeg (1 by default) + KIT_HINT_VIDEO_BUFFER_FRAMES, ///< Video output buffer frames (3 by default) + KIT_HINT_AUDIO_BUFFER_FRAMES, ///< Audio output buffers (64 by default) + KIT_HINT_SUBTITLE_BUFFER_FRAMES ///< Subtitle output buffers (64 by default, used by image subtitles) +} Kit_HintType; + +/** + * @brief Library initialization options, please see Kit_Init() + * + */ enum { - KIT_INIT_FORMATS = 0x1, - KIT_INIT_NETWORK = 0x2, + KIT_INIT_NETWORK = 0x1, ///< Initialise ffmpeg network support + KIT_INIT_ASS = 0x2 ///< Initialize libass support (library must be linked statically or loadable dynamically) }; +/** + * @brief Initialize SDL_kitchensink library. + * + * This MUST be run before doing anything. After you are done using the library, you should use Kit_Quit() to + * deinitialize everything. Otherwise there might be resource leaks. + * + * Following flags can be used to initialize subsystems: + * - `KIT_INIT_NETWORK` for ffmpeg network support (playback from the internet, for example) + * - `KIT_INIT_ASS` for libass subtitles (text and ass/ssa subtitle support) + * + * Note that if this function fails, the failure reason should be available via Kit_GetError(). + * + * For example: + * ``` + * if(Kit_Init(KIT_INIT_NETWORK|KIT_INIT_ASS) != 0) { + * fprintf(stderr, "Error: %s\n", Kit_GetError()); + * return 1; + * } + * ``` + * + * @param flags Library initialization flags + * @return Returns 0 on success, 1 on failure. + */ KIT_API int Kit_Init(unsigned int flags); + +/** + * @brief Deinitializes SDL_kitchensink + * + * Note that any calls to library functions after this will cause undefined behaviour! + */ KIT_API void Kit_Quit(); + +/** + * @brief Sets a librarywide hint + * + * This can be used to set hints on how the library should behave. See Kit_HintType + * for all the options. + * + * @param type Hint type (refer to Kit_HintType for options) + * @param value Value for the hint + */ +KIT_API void Kit_SetHint(Kit_HintType type, int value); + +/** + * @brief Gets a previously set or default hint value + * + * @param type Hint type (refer to Kit_HintType for options) + * @return Hint value + */ +KIT_API int Kit_GetHint(Kit_HintType type); + +/** + * @brief Can be used to fetch the version of the linked SDL_kitchensink library + * + * @param version Allocated Kit_Version + */ KIT_API void Kit_GetVersion(Kit_Version *version); #ifdef __cplusplus diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h index b9ea1c7..1c73020 100644 --- a/include/kitchensink/kitplayer.h +++ b/include/kitchensink/kitplayer.h @@ -1,124 +1,339 @@ #ifndef KITPLAYER_H #define KITPLAYER_H +/** + * @brief Video/audio player functions + * + * @file kitplayer.h + * @author Tuomas Virtanen + * @date 2018-06-27 + */ + #include "kitchensink/kitsource.h" #include "kitchensink/kitconfig.h" +#include "kitchensink/kitformat.h" +#include "kitchensink/kitcodec.h" -#include <SDL2/SDL_render.h> -#include <SDL2/SDL_thread.h> -#include <SDL2/SDL_surface.h> - -#include <stdbool.h> +#include <SDL_render.h> #ifdef __cplusplus extern "C" { #endif -#define KIT_CODECMAX 16 -#define KIT_CODECNAMEMAX 128 - +/** + * @brief Playback states + */ typedef enum Kit_PlayerState { KIT_STOPPED = 0, ///< Playback stopped or has not started yet. - KIT_PLAYING, ///< Playback started & player is actively decoding. - KIT_PAUSED, ///< Playback paused; player is actively decoding but no new data is given out. - KIT_CLOSED ///< Playback is stopped and player is closing. + KIT_PLAYING, ///< Playback started & player is actively decoding. + KIT_PAUSED, ///< Playback paused; player is actively decoding but no new data is given out. + KIT_CLOSED, ///< Playback is stopped and player is closing. } Kit_PlayerState; -typedef struct Kit_AudioFormat { - int stream_idx; ///< Stream index - bool is_enabled; ///< Is stream enabled - unsigned int format; ///< SDL Audio Format - bool is_signed; ///< Signedness - int bytes; ///< Bytes per sample per channel - int samplerate; ///< Sampling rate - int channels; ///< Channels -} Kit_AudioFormat; - -typedef struct Kit_VideoFormat { - int stream_idx; ///< Stream index - bool is_enabled; ///< Is stream enabled - unsigned int format; ///< SDL Pixel Format - int width; ///< Width in pixels - int height; ///< Height in pixels -} Kit_VideoFormat; - -typedef struct Kit_SubtitleFormat { - int stream_idx; ///< Stream index - bool is_enabled; ///< Is stream enabled -} Kit_SubtitleFormat; - +/** + * @brief Player state container + */ typedef struct Kit_Player { - // Local state - Kit_PlayerState state; ///< Playback state - Kit_VideoFormat vformat; ///< Video format information - Kit_AudioFormat aformat; ///< Audio format information - Kit_SubtitleFormat sformat; ///< Subtitle format information - - // Synchronization - double clock_sync; ///< Clock sync point - double pause_start; ///< Timestamp of pause beginning - double vclock_pos; ///< Video stream last pts - - // Threading - SDL_Thread *dec_thread; ///< Decoder thread - SDL_mutex *vmutex; ///< Video stream buffer lock - SDL_mutex *amutex; ///< Audio stream buffer lock - SDL_mutex *smutex; ///< Subtitle stream buffer lock - SDL_mutex *cmutex; ///< Control stream buffer lock - - // Buffers - void *abuffer; ///< Audio stream buffer - void *vbuffer; ///< Video stream buffer - void *sbuffer; ///< Subtitle stream buffer - void *cbuffer; ///< Control stream buffer - - // FFmpeg internal state - void *vcodec_ctx; ///< FFmpeg: Video codec context - void *acodec_ctx; ///< FFmpeg: Audio codec context - void *scodec_ctx; ///< FFmpeg: Subtitle codec context - void *tmp_vframe; ///< FFmpeg: Preallocated temporary video frame - void *tmp_aframe; ///< FFmpeg: Preallocated temporary audio frame - void *tmp_sframe; ///< FFmpeg: Preallocated temporary subtitle frame - void *swr; ///< FFmpeg: Audio resampler - void *sws; ///< FFmpeg: Video converter - - // libass - void *ass_renderer; - void *ass_track; - - // Other - uint8_t seek_flag; - const Kit_Source *src; ///< Reference to Audio/Video source + Kit_PlayerState state; ///< Playback state + void *decoders[3]; ///< Decoder contexts + void *dec_thread; ///< Decoder thread + void *dec_lock; ///< Decoder lock + const Kit_Source *src; ///< Reference to Audio/Video source + double pause_started; ///< Temporary flag for handling pauses } Kit_Player; +/** + * @brief Contains data about a stream selected for playback + */ +typedef struct Kit_PlayerStreamInfo { + Kit_Codec codec; ///< Decoder codec information + Kit_OutputFormat output; ///< Information about the output format +} Kit_PlayerStreamInfo; + +/** + * @brief Contains information about the streams selected for playback + * + */ typedef struct Kit_PlayerInfo { - char acodec[KIT_CODECMAX]; ///< Audio codec short name, eg "ogg", "mp3" - char acodec_name[KIT_CODECNAMEMAX]; ///< Audio codec long, more descriptive name - char vcodec[KIT_CODECMAX]; ///< Video codec short name, eg. "x264" - char vcodec_name[KIT_CODECNAMEMAX]; ///< Video codec long, more descriptive name - char scodec[KIT_CODECMAX]; ///< Subtitle codec short name, eg. "ass" - char scodec_name[KIT_CODECNAMEMAX]; ///< Subtitle codec long, more descriptive name - Kit_VideoFormat video; ///< Video format information - Kit_AudioFormat audio; ///< Audio format information - Kit_SubtitleFormat subtitle; ///< Subtitle format information + Kit_PlayerStreamInfo video; ///< Video stream data + Kit_PlayerStreamInfo audio; ///< Audio stream data + Kit_PlayerStreamInfo subtitle; ///< Subtitle stream data } Kit_PlayerInfo; -KIT_API Kit_Player* Kit_CreatePlayer(const Kit_Source *src); +/** + * @brief Creates a new player from a source. + * + * Creates a new player from the given source. The source must be previously succesfully + * initialized by calling either Kit_CreateSourceFromUrl() or Kit_CreateSourceFromCustom(), + * and it must not be used by any other player. Source must stay valid during the whole + * playback (as in, don't close it while stuff is playing). + * + * Screen width and height are used for subtitle positioning, scaling and rendering resolution. + * Ideally this should be precisely the size of your screen surface (in pixels). + * Higher resolution leads to higher resolution text rendering. This MUST be set precisely + * if you plan to use font hinting! If you don't care or don't have subtitles at all, + * set both to video surface size or 0. + * + * For streams, either video and/or audio stream MUST be set! Either set the stream indexes manually, + * or pick them automatically by using Kit_GetBestSourceStream(). + * + * On success, this will return an initialized Kit_Player which can later be freed by Kit_ClosePlayer(). + * On error, NULL is returned and a more detailed error is availably via Kit_GetError(). + * + * For example: + * ``` + * Kit_Player *player = Kit_CreatePlayer( + * src, + * Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO), + * Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO), + * Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE), + * 1280, 720); + * if(player == NULL) { + * fprintf(stderr, "Unable to create player: %s\n", Kit_GetError()); + * return 1; + * } + * ``` + * + * @param src Valid video/audio source + * @param video_stream_index Video stream index or -1 if not wanted + * @param audio_stream_index Audio stream index or -1 if not wanted + * @param subtitle_stream_index Subtitle stream index or -1 if not wanted + * @param screen_w Screen width in pixels + * @param screen_h Screen height in pixels + * @return Ínitialized Kit_Player or NULL + */ +KIT_API Kit_Player* Kit_CreatePlayer(const Kit_Source *src, + int video_stream_index, + int audio_stream_index, + int subtitle_stream_index, + int screen_w, + int screen_h); + +/** + * @brief Close previously initialized player + * + * Closes a previously initialized Kit_Player instance. Note that this does NOT free + * the linked Kit_Source -- you must free it manually. + * + * @param player Player instance + */ KIT_API void Kit_ClosePlayer(Kit_Player *player); -KIT_API int Kit_UpdatePlayer(Kit_Player *player); -KIT_API int Kit_GetVideoData(Kit_Player *player, SDL_Texture *texture); -KIT_API int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer); -KIT_API int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int cur_buf_len); +/** + * @brief Sets the current screen size in pixels + * + * Call this to change the subtitle font rendering resolution if eg. your + * video window size changes. + * + * This does nothing if subtitles are not in use or if subtitles are bitmaps. + * + * @param player Player instance + * @param w New width in pixels + * @param h New height in pixels + */ +KIT_API void Kit_SetPlayerScreenSize(Kit_Player *player, int w, int h); + +/** + * @brief Gets the current video stream index + * + * Returns the current video stream index or -1 if one is not selected. + * + * @param player Player instance + * @return Video stream index or -1 + */ +KIT_API int Kit_GetPlayerVideoStream(const Kit_Player *player); + +/** + * @brief Gets the current audio stream index + * + * Returns the current audio stream index or -1 if one is not selected. + * + * @param player Player instance + * @return Audio stream index or -1 + */ +KIT_API int Kit_GetPlayerAudioStream(const Kit_Player *player); + +/** + * @brief Gets the current subtitle stream index + * + * Returns the current subtitle stream index or -1 if one is not selected. + * + * @param player Player instance + * @return Subtitle stream index or -1 + */ +KIT_API int Kit_GetPlayerSubtitleStream(const Kit_Player *player); + +/** + * @brief Fetches a new video frame from the player + * + * Note that the output texture must be previously allocated and valid. + * + * It is important to select the correct texture format and size. If you pick a different + * texture format or size from what the decoder outputs, then the decoder will attempt to convert + * the frames to fit the texture. This will slow down the decoder a *lot* however, so if possible, + * pick the texture format from what Kit_GetPlayerInfo() outputs. + * + * Access flag for the texture *MUST* always be SDL_TEXTUREACCESS_STATIC! Anything else will lead to + * undefined behaviour. + * + * This function will do nothing if player playback has not been started. + * + * @param player Player instance + * @param texture A previously allocated texture + * @return 0 on success, 1 on error + */ +KIT_API int Kit_GetPlayerVideoData(Kit_Player *player, SDL_Texture *texture); + +/** + * @brief Fetches subtitle data from the player + * + * Output texture will be used as a texture atlas for the subtitle fragments. + * + * Note that the output texture must be previously allocated and valid. Make sure to have large + * enough a texture for the rendering resolution you picked! If your rendering resolution if 4k, + * then make sure to have texture sized 4096x4096 etc. This gives the texture room to handle the + * worst case subtitle textures. If your resolutions is too small, this function will return + * value -1. At that point you can replace your current texture with a bigger one on the fly. + * + * Note that the texture format for the atlas texture *MUST* be SDL_PIXELFORMAT_RGBA32 and + * the access flag *MUST* be set to SDL_TEXTUREACCESS_STATIC for correct rendering. + * Using any other format will lead to undefined behaviour. Also, make sure to set scaling quality + * to 0 or "nearest" before creating the texture -- otherwise you get artifacts + * (see SDL_HINT_RENDER_SCALE_QUALITY). + * + * This function will do nothing if player playback has not been started. + * + * For example: + * ``` + * SDL_Rect sources[256]; + * SDL_Rect targets[256]; + * int got = Kit_GetPlayerSubtitleData(player, subtitle_tex, sources, targets, 256); + * for(int i = 0; i < got; i++) { + * SDL_RenderCopy(renderer, subtitle_tex, &sources[i], &targets[i]); + * } + * ``` + * + * @param player Player instance + * @param texture A previously allocated texture + * @param sources List of source rectangles to copy fropm + * @param targets List of target rectangles to render + * @param limit Defines the maximum size of your rectangle lists + * @return Number of sources or <0 on error + */ +KIT_API int Kit_GetPlayerSubtitleData(Kit_Player *player, + SDL_Texture *texture, + SDL_Rect *sources, + SDL_Rect *targets, + int limit); + +/** + * @brief Fetches audio data from the player + * + * Note that the output buffer must be previously allocated. + * + * Outputted audio data will be precisely what is described by the output format struct given + * by Kit_GetPlayerInfo(). + * + * This function will attemt to read the maximum allowed amount of data allowed by the length + * argument. It is possible however that there is not enough data available, at which point + * this function will read less and return value may differ from maximum allowed value. + * Return value 0 should be taken as a hint that there is nothing available. + * + * This function will do nothing if player playback has not been started. + * + * @param player Player instance + * @param buffer Buffer to read into + * @param length Maximum length of the buffer + * @return Amount of data that was read, <0 on error. + */ +KIT_API int Kit_GetPlayerAudioData(Kit_Player *player, unsigned char *buffer, int length); + +/** + * @brief Fetches information about the currently selected streams + * + * This function should be used to fetch codec information and output format data from the player + * before creating textures and setting up audio outputs. + * + * @param player Player instance + * @param info A previously allocated Kit_PlayerInfo instance + */ KIT_API void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info); +/** + * @brief Returns the current state of the player + * + * @param player Player instance + * @return Current state of the player, see Kit_PlayerState + */ KIT_API Kit_PlayerState Kit_GetPlayerState(const Kit_Player *player); + +/** + * @brief Starts playback + * + * State shifts: + * - If player is already playing, will do nothing. + * - If player is paused, will resume playback. + * - If player is stopped, will begin playback (and background decoding). + * + * @param player Player instance + */ KIT_API void Kit_PlayerPlay(Kit_Player *player); + +/** + * @brief Stops playback + * + * State shifts: + * - If player is already stopped, will do nothing. + * - If player is paused, will stop playback. + * - If player is started, will stop playback (and background decoding). + * + * @param player Player instance + */ KIT_API void Kit_PlayerStop(Kit_Player *player); + +/** + * @brief Pauses playback + * + * State shifts: + * - If player is already paused, will do nothing. + * - If player is stopped, will do nothing. + * - If player is started, will pause playback (and background decoding). + * + * @param player Player instance + */ KIT_API void Kit_PlayerPause(Kit_Player *player); +/** + * @brief Seek to timestamp + * + * Rewinds or forwards video/audio playback to the given timestamp (in seconds). + * + * This may not work for network or custom sources! + * + * @param player Player instance + * @param time Timestamp to seek to in seconds + * @return 0 on success, 1 on failure. + */ KIT_API int Kit_PlayerSeek(Kit_Player *player, double time); + +/** + * @brief Get the duration of the source + * + * Returns the duration of the source in seconds + * + * @param player Player instance + * @return Duration + */ KIT_API double Kit_GetPlayerDuration(const Kit_Player *player); + +/** + * @brief Get the current position of the playback + * + * Returns the position of the playback in seconds + * + * @param player Player instance + * @return Position + */ KIT_API double Kit_GetPlayerPosition(const Kit_Player *player); #ifdef __cplusplus diff --git a/include/kitchensink/kitsource.h b/include/kitchensink/kitsource.h index ed1711f..9b744ca 100644 --- a/include/kitchensink/kitsource.h +++ b/include/kitchensink/kitsource.h @@ -1,15 +1,27 @@ #ifndef KITSOURCE_H #define KITSOURCE_H +/** + * @brief Video/Audio source file handling + * + * @file kitsource.h + * @author Tuomas Virtanen + * @date 2018-06-27 + */ + +#include <inttypes.h> +#include <SDL_rwops.h> #include "kitchensink/kitconfig.h" #ifdef __cplusplus extern "C" { #endif -#define KIT_CODECNAMESIZE 32 -#define KIT_CODECLONGNAMESIZE 128 - +/** + * @brief Type of the stream. + * + * This is used by Kit_SourceStreamInfo and Kit_GetSourceStreamInfo(). + */ typedef enum Kit_StreamType { KIT_STREAMTYPE_UNKNOWN, ///< Unknown stream type KIT_STREAMTYPE_VIDEO, ///< Video stream @@ -19,26 +31,202 @@ typedef enum Kit_StreamType { KIT_STREAMTYPE_ATTACHMENT ///< Attachment stream (images, etc) } Kit_StreamType; +/** + * @brief Audio/video source. + * + * Should be created using Kit_CreateSourceFromUrl() or Kit_CreateSourceFromCustom(), and + * closed with Kit_CloseSource(). + * + * Source must exist for the whole duration of using a player. You must take care of closing the source + * yourself after you are done with it! + */ typedef struct Kit_Source { - int astream_idx; ///< Audio stream index - int vstream_idx; ///< Video stream index - int sstream_idx; ///< Subtitle stream index void *format_ctx; ///< FFmpeg: Videostream format context + void *avio_ctx; ///< FFmpeg: AVIO context } Kit_Source; -typedef struct Kit_Stream { +/** + * @brief Information for a source stream. + * + * Fetch information by using Kit_GetSourceStreamInfo(). + */ +typedef struct Kit_SourceStreamInfo { int index; ///< Stream index Kit_StreamType type; ///< Stream type -} Kit_StreamInfo; +} Kit_SourceStreamInfo; + +/** + * @brief Callback function type for reading data stream + * + * Used by Kit_CreateSourceFromCustom() for reading data from user defined source. + * + * A custom reader function must accept three arguments: + * - userdata, this is the same data as set as last argument for Kit_CreateSourceFromCustom + * - buf, a buffer the data must be copied into + * - size, how much data you are expected to provide at maximum. + * + * The function must return the amount of bytes copied to the buffer or <0 on error. + * + * Note that this callback is passed directly to ffmpeg avio, so please refer to ffmpeg documentation + * for any further details. + */ +typedef int (*Kit_ReadCallback)(void *userdata, uint8_t *buf, int size); + +/** + * @brief Callback function type for seeking data strema + * + * Used by Kit_CreateSourceFromCustom() for seeking a user defined source. + * + * A custom seeking function must accept three arguments: + * - userdata, this is the same data as set as last argument for Kit_CreateSourceFromCustom + * - offset, an seeking offset in bytes + * - whence, reference position for the offset. + * + * Whence parameter can be one of the standard fseek values or optionally AVSEEK_SIZE. + * - SEEK_SET: Reference position is beginning of file + * - SEEK_CUR: Reference position is the current position of the file pointer + * - SEEK_END: Reference position is the end of the file + * - AVSEEK_SIZE: Optional. Does not seek, instead finds the size of the source file. + * - AVSEEK_FORCE: Optional. Suggests that seeking should be done at any cost. May be passed alongside + * any of the SEEK_* flags, eg. SEEK_SET|AVSEEK_FORCE. + * + * The function must return the position (in bytes) we seeked to or <0 on error or on unsupported operation. + * + * Note that this callback is passed directly to ffmpeg avio, so please refer to ffmpeg documentation + * for any further details. + */ +typedef int64_t (*Kit_SeekCallback)(void *userdata, int64_t offset, int whence); + +/** + * @brief Create a new source from a given url + * + * This can be used to load video/audio from a file or network resource. If you wish to + * use network resources, make sure the library has been initialized using KIT_INIT_NETWORK flag. + * + * This function will return an initialized Kit_Source on success. Note that you need to manually + * free the source when it's no longer needed by calling Kit_CloseSource(). + * + * On failure, this function will return NULL, and further error data is available via Kit_GetError(). + * + * For example: + * ``` + * if(Kit_CreateSourceFromUrl(filename) == NULL) { + * fprintf(stderr, "Error: %s\n", Kit_GetError()); + * return 1; + * } + * ``` + * + * @param url File path or URL to a video/audio resource + * @return Returns an initialized Kit_Source* on success or NULL on failure + */ +KIT_API Kit_Source* Kit_CreateSourceFromUrl(const char *url); -KIT_API Kit_Source* Kit_CreateSourceFromUrl(const char *path); +/** + * @brief Create a new source from custom data + * + * This can be used to load data from any resource via the given read and seek functions. + * + * This function will return an initialized Kit_Source on success. Note that you need to manually + * free the source when it's no longer needed by calling Kit_CloseSource(). + * + * On failure, this function will return NULL, and further error data is available via Kit_GetError(). + * + * For example: + * ``` + * if(Kit_CreateSourceFromCustom(read_fn, seek_fn, fp) == NULL) { + * fprintf(stderr, "Error: %s\n", Kit_GetError()); + * return 1; + * } + * ``` + * + * @param read_cb Read function callback + * @param seek_cb Seek function callback + * @param userdata Any data (or NULL). Will be passed to read_cb and/or seek_cb functions as-is. + * @return Returns an initialized Kit_Source* on success or NULL on failure + */ +KIT_API Kit_Source* Kit_CreateSourceFromCustom(Kit_ReadCallback read_cb, Kit_SeekCallback seek_cb, void *userdata); + +/** + * @brief Create a new source from SDL RWops struct + * + * Can be used to read data from SDL compatible sources. + * + * This function will return an initialized Kit_Source on success. Note that you need to manually + * free the source when it's no longer needed by calling Kit_CloseSource(). + * + * On failure, this function will return NULL, and further error data is available via Kit_GetError(). + * + * Note that the RWops struct must exist during the whole lifetime of the source, and you must take + * care of freeing the rwops after it's no longer needed. + * + * For example: + * ``` + * SDL_RWops *rw = SDL_RWFromFile("myvideo.mkv", "rb"); + * if(Kit_CreateSourceFromRW(rw) == NULL) { + * fprintf(stderr, "Error: %s\n", Kit_GetError()); + * return 1; + * } + * ``` + * + * @param rw_ops Initialized RWOps + * @return KIT_API* Kit_CreateSourceFromRW + */ +KIT_API Kit_Source* Kit_CreateSourceFromRW(SDL_RWops *rw_ops); + +/** + * @brief Closes a previously initialized source + * + * Closes a Kit_Source that was previously created by Kit_CreateSourceFromUrl() or Kit_CreateSourceFromCustom() + * and frees up all memory and resources used by it. Using the source for anything after this will + * lead to undefined behaviour. + * + * Passing NULL as argument is valid, and will do nothing. + * + * @param src Previously initialized Kit_Source to close + */ KIT_API void Kit_CloseSource(Kit_Source *src); -KIT_API int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_StreamInfo *info, int index); +/** + * @brief Fetches stream information for a given stream index + * + * Sets fields for given Kit_SourceStreamInfo with information about the stream. + * + * For example: + * ``` + * Kit_SourceStreamInfo stream; + * if(Kit_GetSourceStreamInfo(source, &stream, 0) == 1) { + * fprintf(stderr, "Error: %s\n", Kit_GetError()); + * return 1; + * } + * fprintf(stderr, "Stream type: %s\n", Kit_GetKitStreamTypeString(stream.type)) + * ``` + * + * @param src Source to query from + * @param info A previously allocated Kit_SourceStreamInfo to fill out + * @param index Stream index (starting from 0) + * @return 0 on success, 1 on error. + */ +KIT_API int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_SourceStreamInfo *info, int index); + +/** + * @brief Gets the amount of streams in source + * + * @param src Source to query from + * @return Number of streams in the source + */ KIT_API int Kit_GetSourceStreamCount(const Kit_Source *src); + +/** + * @brief Gets the best stream index for a given stream type. + * + * Find the best stream index for a given stream type, if one exists. If there is no + * stream for the wanted type, will return -1. + * + * @param src Source to query from + * @param type Stream type + * @return Index number on success (>=0), -1 on error. + */ KIT_API int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_StreamType type); -KIT_API int Kit_SetSourceStream(Kit_Source *src, const Kit_StreamType type, int index); -KIT_API int Kit_GetSourceStream(const Kit_Source *src, const Kit_StreamType type); #ifdef __cplusplus } diff --git a/include/kitchensink/kitutils.h b/include/kitchensink/kitutils.h index af3307c..af0e14b 100644 --- a/include/kitchensink/kitutils.h +++ b/include/kitchensink/kitutils.h @@ -1,14 +1,42 @@ #ifndef KITUTILS_H #define KITUTILS_H +/** + * @brief Helpful utilities + * + * @file kitutils.h + * @author Tuomas Virtanen + * @date 2018-06-25 + */ + #include "kitchensink/kitconfig.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Returns a descriptive string for SDL audio format types + * + * @param type SDL_AudioFormat + * @return Format string, eg. "AUDIO_S8". + */ KIT_API const char* Kit_GetSDLAudioFormatString(unsigned int type); + +/** + * @brief Returns a descriptive string for SDL pixel format types + * + * @param type SDL_PixelFormat + * @return Format string, eg. "SDL_PIXELFORMAT_YV12" + */ KIT_API const char* Kit_GetSDLPixelFormatString(unsigned int type); + +/** + * @brief Returns a descriptibe string for Kitchensink stream types + * + * @param type Kit_StreamType + * @return Format string, eg. "KIT_STREAMTYPE_VIDEO" + */ KIT_API const char* Kit_GetKitStreamTypeString(unsigned int type); #ifdef __cplusplus diff --git a/pkg-config.pc.in b/pkg-config.pc.in new file mode 100644 index 0000000..53e3a53 --- /dev/null +++ b/pkg-config.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+
+Name: SDL_kitchensink
+Description: SDL2/ffmpeg video playback library
+Version: @KIT_VERSION@
+URL: https://github.com/katajakasa/SDL_kitchensink
+
+Libs: -L${libdir} -l@PROJECT_NAME@
+Cflags: -I${includedir}
diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..3edb3a5 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.projectKey=sdl_kitchensink
+sonar.projectName=SDL_Kitchensink
+sonar.projectVersion=1.0
+sonar.sourceEncoding=UTF-8
+sonar.sources=src,include
+sonar.language=c
+sonar.cfamily.build-wrapper-output=bw-output
diff --git a/src/internal/audio/kitaudio.c b/src/internal/audio/kitaudio.c new file mode 100644 index 0000000..b1a0fdc --- /dev/null +++ b/src/internal/audio/kitaudio.c @@ -0,0 +1,321 @@ +#include <assert.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <libavformat/avformat.h>
+#include <libavutil/samplefmt.h>
+#include <libswresample/swresample.h>
+#include <SDL.h>
+
+#include "kitchensink/kiterror.h"
+#include "kitchensink/internal/kitlibstate.h"
+#include "kitchensink/internal/utils/kithelpers.h"
+#include "kitchensink/internal/utils/kitbuffer.h"
+#include "kitchensink/internal/audio/kitaudio.h"
+#include "kitchensink/internal/utils/kitringbuffer.h"
+#include "kitchensink/internal/utils/kitlog.h"
+
+#define AUDIO_SYNC_THRESHOLD 0.05
+
+typedef struct Kit_AudioDecoder {
+ SwrContext *swr;
+ AVFrame *scratch_frame;
+} Kit_AudioDecoder;
+
+typedef struct Kit_AudioPacket {
+ double pts;
+ size_t original_size;
+ Kit_RingBuffer *rb;
+} Kit_AudioPacket;
+
+
+Kit_AudioPacket* _CreateAudioPacket(const char* data, size_t len, double pts) {
+ Kit_AudioPacket *p = calloc(1, sizeof(Kit_AudioPacket));
+ p->rb = Kit_CreateRingBuffer(len);
+ Kit_WriteRingBuffer(p->rb, data, len);
+ p->pts = pts;
+ return p;
+}
+
+enum AVSampleFormat _FindAVSampleFormat(int format) {
+ switch(format) {
+ case AUDIO_U8: return AV_SAMPLE_FMT_U8;
+ case AUDIO_S16SYS: return AV_SAMPLE_FMT_S16;
+ case AUDIO_S32SYS: return AV_SAMPLE_FMT_S32;
+ default: return AV_SAMPLE_FMT_NONE;
+ }
+}
+
+int64_t _FindAVChannelLayout(int channels) {
+ switch(channels) {
+ case 1: return AV_CH_LAYOUT_MONO;
+ case 2: return AV_CH_LAYOUT_STEREO;
+ default: return AV_CH_LAYOUT_STEREO_DOWNMIX;
+ }
+}
+
+int _FindChannelLayout(uint64_t channel_layout) {
+ switch(channel_layout) {
+ case AV_CH_LAYOUT_MONO: return 1;
+ case AV_CH_LAYOUT_STEREO: return 2;
+ default: return 2;
+ }
+}
+
+int _FindBytes(enum AVSampleFormat fmt) {
+ switch(fmt) {
+ case AV_SAMPLE_FMT_U8P:
+ case AV_SAMPLE_FMT_U8:
+ return 1;
+ case AV_SAMPLE_FMT_S32P:
+ case AV_SAMPLE_FMT_S32:
+ return 4;
+ default:
+ return 2;
+ }
+}
+
+int _FindSDLSampleFormat(enum AVSampleFormat fmt) {
+ switch(fmt) {
+ case AV_SAMPLE_FMT_U8P:
+ case AV_SAMPLE_FMT_U8:
+ return AUDIO_U8;
+ case AV_SAMPLE_FMT_S32P:
+ case AV_SAMPLE_FMT_S32:
+ return AUDIO_S32SYS;
+ default:
+ return AUDIO_S16SYS;
+ }
+}
+
+int _FindSignedness(enum AVSampleFormat fmt) {
+ switch(fmt) {
+ case AV_SAMPLE_FMT_U8P:
+ case AV_SAMPLE_FMT_U8:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static void free_out_audio_packet_cb(void *packet) {
+ Kit_AudioPacket *p = packet;
+ Kit_DestroyRingBuffer(p->rb);
+ free(p);
+}
+
+static void dec_decode_audio_cb(Kit_Decoder *dec, AVPacket *in_packet) {
+ assert(dec != NULL);
+ assert(in_packet != NULL);
+
+ Kit_AudioDecoder *audio_dec = dec->userdata;
+ int frame_finished;
+ int len;
+ int len2;
+ int dst_linesize;
+ int dst_nb_samples;
+ int dst_bufsize;
+ double pts;
+ unsigned char **dst_data;
+ Kit_AudioPacket *out_packet;
+
+ // Decode as long as there is data
+ while(in_packet->size > 0) {
+ len = avcodec_decode_audio4(dec->codec_ctx, audio_dec->scratch_frame, &frame_finished, in_packet);
+ if(len < 0) {
+ return;
+ }
+
+ if(frame_finished) {
+ dst_nb_samples = av_rescale_rnd(
+ audio_dec->scratch_frame->nb_samples,
+ dec->output.samplerate, // Target samplerate
+ dec->codec_ctx->sample_rate, // Source samplerate
+ AV_ROUND_UP);
+
+ av_samples_alloc_array_and_samples(
+ &dst_data,
+ &dst_linesize,
+ dec->output.channels,
+ dst_nb_samples,
+ _FindAVSampleFormat(dec->output.format),
+ 0);
+
+ len2 = swr_convert(
+ audio_dec->swr,
+ dst_data,
+ audio_dec->scratch_frame->nb_samples,
+ (const unsigned char **)audio_dec->scratch_frame->extended_data,
+ audio_dec->scratch_frame->nb_samples);
+
+ dst_bufsize = av_samples_get_buffer_size(
+ &dst_linesize,
+ dec->output.channels,
+ len2,
+ _FindAVSampleFormat(dec->output.format), 1);
+
+ // Get presentation timestamp
+#ifndef FF_API_FRAME_GET_SET
+ pts = av_frame_get_best_effort_timestamp(audio_dec->scratch_frame);
+#else
+ pts = audio_dec->scratch_frame->best_effort_timestamp;
+#endif
+ pts *= av_q2d(dec->format_ctx->streams[dec->stream_index]->time_base);
+
+ // Lock, write to audio buffer, unlock
+ out_packet = _CreateAudioPacket(
+ (char*)dst_data[0], (size_t)dst_bufsize, pts);
+ Kit_WriteDecoderOutput(dec, out_packet);
+
+ // Free temps
+ av_freep(&dst_data[0]);
+ av_freep(&dst_data);
+ }
+
+ in_packet->size -= len;
+ in_packet->data += len;
+ }
+}
+
+static void dec_close_audio_cb(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+
+ Kit_AudioDecoder *audio_dec = dec->userdata;
+ if(audio_dec->scratch_frame != NULL) {
+ av_frame_free(&audio_dec->scratch_frame);
+ }
+ if(audio_dec->swr != NULL) {
+ swr_free(&audio_dec->swr);
+ }
+ free(audio_dec);
+}
+
+Kit_Decoder* Kit_CreateAudioDecoder(const Kit_Source *src, int stream_index) {
+ assert(src != NULL);
+ if(stream_index < 0) {
+ return NULL;
+ }
+
+ Kit_LibraryState *state = Kit_GetLibraryState();
+
+ // First the generic decoder component ...
+ Kit_Decoder *dec = Kit_CreateDecoder(
+ src,
+ stream_index,
+ state->audio_buf_frames,
+ free_out_audio_packet_cb,
+ state->thread_count);
+ if(dec == NULL) {
+ goto exit_0;
+ }
+
+ // ... then allocate the audio decoder
+ Kit_AudioDecoder *audio_dec = calloc(1, sizeof(Kit_AudioDecoder));
+ if(audio_dec == NULL) {
+ goto exit_1;
+ }
+
+ // Create temporary audio frame
+ audio_dec->scratch_frame = av_frame_alloc();
+ if(audio_dec->scratch_frame == NULL) {
+ Kit_SetError("Unable to initialize temporary audio frame");
+ goto exit_2;
+ }
+
+ // Set format configs
+ Kit_OutputFormat output;
+ memset(&output, 0, sizeof(Kit_OutputFormat));
+ output.samplerate = dec->codec_ctx->sample_rate;
+ output.channels = _FindChannelLayout(dec->codec_ctx->channel_layout);
+ output.bytes = _FindBytes(dec->codec_ctx->sample_fmt);
+ output.is_signed = _FindSignedness(dec->codec_ctx->sample_fmt);
+ output.format = _FindSDLSampleFormat(dec->codec_ctx->sample_fmt);
+
+ // Create resampler
+ audio_dec->swr = swr_alloc_set_opts(
+ NULL,
+ _FindAVChannelLayout(output.channels), // Target channel layout
+ _FindAVSampleFormat(output.format), // Target fmt
+ output.samplerate, // Target samplerate
+ dec->codec_ctx->channel_layout, // Source channel layout
+ dec->codec_ctx->sample_fmt, // Source fmt
+ dec->codec_ctx->sample_rate, // Source samplerate
+ 0, NULL);
+
+ if(swr_init(audio_dec->swr) != 0) {
+ Kit_SetError("Unable to initialize audio resampler context");
+ goto exit_3;
+ }
+
+ // Set callbacks and userdata, and we're go
+ dec->dec_decode = dec_decode_audio_cb;
+ dec->dec_close = dec_close_audio_cb;
+ dec->userdata = audio_dec;
+ dec->output = output;
+ return dec;
+
+exit_3:
+ av_frame_free(&audio_dec->scratch_frame);
+exit_2:
+ free(audio_dec);
+exit_1:
+ Kit_CloseDecoder(dec);
+exit_0:
+ return NULL;
+}
+
+int Kit_GetAudioDecoderData(Kit_Decoder *dec, unsigned char *buf, int len) {
+ assert(dec != NULL);
+
+ Kit_AudioPacket *packet = Kit_PeekDecoderOutput(dec);
+ if(packet == NULL) {
+ return 0;
+ }
+
+ int ret = 0;
+ int bytes_per_sample = dec->output.bytes * dec->output.channels;
+ double bytes_per_second = bytes_per_sample * dec->output.samplerate;
+ double sync_ts = _GetSystemTime() - dec->clock_sync;
+
+ if(packet->pts > sync_ts + AUDIO_SYNC_THRESHOLD) {
+ return 0;
+ } else if(packet->pts < sync_ts - AUDIO_SYNC_THRESHOLD) {
+ // Audio is lagging, skip until good pts is found
+ while(1) {
+ Kit_AdvanceDecoderOutput(dec);
+ free_out_audio_packet_cb(packet);
+ packet = Kit_PeekDecoderOutput(dec);
+ if(packet == NULL) {
+ break;
+ } else {
+ dec->clock_pos = packet->pts;
+ }
+ if(packet->pts > sync_ts - AUDIO_SYNC_THRESHOLD) {
+ break;
+ }
+ }
+ }
+
+ // If we have no viable packet, just skip
+ if(packet == NULL) {
+ return 0;
+ }
+
+ // Read data from packet ringbuffer
+ if(len > 0) {
+ ret = Kit_ReadRingBuffer(packet->rb, (char*)buf, len);
+ }
+
+ // If ringbuffer is cleared, kill packet and advance buffer.
+ // Otherwise forward the pts value for the current packet.
+ if(Kit_GetRingBufferLength(packet->rb) == 0) {
+ Kit_AdvanceDecoderOutput(dec);
+ dec->clock_pos = packet->pts;
+ free_out_audio_packet_cb(packet);
+ } else {
+ packet->pts += ((double)ret) / bytes_per_second;
+ dec->clock_pos = packet->pts;
+ }
+
+ return ret;
+}
diff --git a/src/internal/kitdecoder.c b/src/internal/kitdecoder.c new file mode 100644 index 0000000..0855eee --- /dev/null +++ b/src/internal/kitdecoder.c @@ -0,0 +1,282 @@ +#include <stdlib.h>
+#include <assert.h>
+
+#include <libavformat/avformat.h>
+
+#include "kitchensink/internal/kitdecoder.h"
+#include "kitchensink/kiterror.h"
+
+#define BUFFER_IN_SIZE 256
+
+static void free_in_video_packet_cb(void *packet) {
+ av_packet_free((AVPacket**)&packet);
+}
+
+Kit_Decoder* Kit_CreateDecoder(const Kit_Source *src, int stream_index,
+ int out_b_size, dec_free_packet_cb free_out_cb,
+ int thread_count) {
+ assert(src != NULL);
+ assert(out_b_size > 0);
+ assert(thread_count > 0);
+
+ AVCodecContext *codec_ctx = NULL;
+ AVCodec *codec = NULL;
+ AVFormatContext *format_ctx = src->format_ctx;
+ int bsizes[2] = {BUFFER_IN_SIZE, out_b_size};
+ dec_free_packet_cb free_hooks[2] = {free_in_video_packet_cb, free_out_cb};
+
+ // Make sure index seems correct
+ if(stream_index >= (int)format_ctx->nb_streams || stream_index < 0) {
+ Kit_SetError("Invalid stream %d", stream_index);
+ goto exit_0;
+ }
+
+ // Allocate decoder and make sure allocation was a success
+ Kit_Decoder *dec = calloc(1, sizeof(Kit_Decoder));
+ if(dec == NULL) {
+ Kit_SetError("Unable to allocate kit decoder for stream %d", stream_index);
+ goto exit_0;
+ }
+
+ // Find audio decoder
+ codec = avcodec_find_decoder(format_ctx->streams[stream_index]->codec->codec_id);
+ if(!codec) {
+ Kit_SetError("No suitable decoder found for stream %d", stream_index);
+ goto exit_1;
+ }
+
+ // Allocate a context for the codec
+ codec_ctx = avcodec_alloc_context3(codec);
+ if(codec_ctx == NULL) {
+ Kit_SetError("Unable to allocate codec context for stream %d", stream_index);
+ goto exit_1;
+ }
+
+ // Copy context from stream to target codec context
+ if(avcodec_copy_context(codec_ctx, format_ctx->streams[stream_index]->codec) != 0) {
+ Kit_SetError("Unable to copy codec context for stream %d", stream_index);
+ goto exit_2;
+ }
+
+ // Set thread count
+ codec_ctx->thread_count = thread_count;
+ codec_ctx->thread_type = FF_THREAD_SLICE;
+
+ // Open the stream
+ if(avcodec_open2(codec_ctx, codec, NULL) < 0) {
+ Kit_SetError("Unable to open codec for stream %d", stream_index);
+ goto exit_2;
+ }
+
+ // Set index and codec
+ dec->stream_index = stream_index;
+ dec->codec_ctx = codec_ctx;
+ dec->format_ctx = format_ctx;
+
+ // Allocate input/output ringbuffers
+ for(int i = 0; i < 2; i++) {
+ dec->buffer[i] = Kit_CreateBuffer(bsizes[i], free_hooks[i]);
+ if(dec->buffer[i] == NULL) {
+ Kit_SetError("Unable to allocate buffer for stream %d: %s", stream_index, SDL_GetError());
+ goto exit_3;
+ }
+ }
+
+ // Create a lock for output buffer synchronization
+ dec->output_lock = SDL_CreateMutex();
+ if(dec->output_lock == NULL) {
+ Kit_SetError("Unable to allocate mutex for stream %d: %s", stream_index, SDL_GetError());
+ goto exit_3;
+ }
+
+ // That's that
+ return dec;
+
+exit_3:
+ for(int i = 0; i < KIT_DEC_BUF_COUNT; i++) {
+ Kit_DestroyBuffer(dec->buffer[i]);
+ }
+ avcodec_close(codec_ctx);
+exit_2:
+ avcodec_free_context(&codec_ctx);
+exit_1:
+ free(dec);
+exit_0:
+ return NULL;
+}
+
+void Kit_CloseDecoder(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+ if(dec->dec_close) {
+ dec->dec_close(dec);
+ }
+ for(int i = 0; i < KIT_DEC_BUF_COUNT; i++) {
+ Kit_DestroyBuffer(dec->buffer[i]);
+ }
+ SDL_DestroyMutex(dec->output_lock);
+ avcodec_close(dec->codec_ctx);
+ avcodec_free_context(&dec->codec_ctx);
+ free(dec);
+}
+
+int Kit_RunDecoder(Kit_Decoder *dec) {
+ if(dec == NULL) return 0;
+
+ AVPacket *in_packet;
+ int is_output_full = 1;
+
+ // First, check if there is room in output buffer
+ if(SDL_LockMutex(dec->output_lock) == 0) {
+ is_output_full = Kit_IsBufferFull(dec->buffer[KIT_DEC_BUF_OUT]);
+ SDL_UnlockMutex(dec->output_lock);
+ }
+ if(is_output_full) {
+ return 0;
+ }
+
+ // Then, see if we have incoming data
+ in_packet = Kit_ReadDecoderInput(dec);
+ if(in_packet == NULL) {
+ return 0;
+ }
+
+ // Run decoder with incoming packet
+ dec->dec_decode(dec, in_packet);
+
+ // Free raw packet before returning
+ av_packet_free(&in_packet);
+ return 1;
+}
+
+// ---- Information API ----
+
+int Kit_GetDecoderCodecInfo(const Kit_Decoder *dec, Kit_Codec *codec) {
+ if(dec == NULL) {
+ memset(codec, 0, sizeof(Kit_Codec));
+ return 1;
+ }
+ codec->threads = dec->codec_ctx->thread_count;
+ snprintf(codec->name, KIT_CODEC_NAME_MAX, "%s", dec->codec_ctx->codec->name);
+ snprintf(codec->description, KIT_CODEC_DESC_MAX, "%s", dec->codec_ctx->codec->long_name);
+ return 0;
+}
+
+int Kit_GetDecoderOutputFormat(const Kit_Decoder *dec, Kit_OutputFormat *output) {
+ if(dec == NULL) {
+ memset(output, 0, sizeof(Kit_OutputFormat));
+ return 1;
+ }
+ memcpy(output, &dec->output, sizeof(Kit_OutputFormat));
+ return 0;
+}
+
+int Kit_GetDecoderStreamIndex(const Kit_Decoder *dec) {
+ if(dec == NULL)
+ return -1;
+ return dec->stream_index;
+}
+
+// ---- Clock handling ----
+
+void Kit_SetDecoderClockSync(Kit_Decoder *dec, double sync) {
+ if(dec == NULL)
+ return;
+ dec->clock_sync = sync;
+}
+
+void Kit_ChangeDecoderClockSync(Kit_Decoder *dec, double sync) {
+ if(dec == NULL)
+ return;
+ dec->clock_sync += sync;
+}
+
+// ---- Input buffer handling ----
+
+int Kit_WriteDecoderInput(Kit_Decoder *dec, AVPacket *packet) {
+ assert(dec != NULL);
+ return Kit_WriteBuffer(dec->buffer[KIT_DEC_BUF_IN], packet);
+}
+
+bool Kit_CanWriteDecoderInput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ return !Kit_IsBufferFull(dec->buffer[KIT_DEC_BUF_IN]);
+}
+
+AVPacket* Kit_ReadDecoderInput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ return Kit_ReadBuffer(dec->buffer[KIT_DEC_BUF_IN]);
+}
+
+void Kit_ClearDecoderInput(Kit_Decoder *dec) {
+ Kit_ClearBuffer(dec->buffer[KIT_DEC_BUF_IN]);
+}
+
+// ---- Output buffer handling ----
+
+int Kit_WriteDecoderOutput(Kit_Decoder *dec, void *packet) {
+ assert(dec != NULL);
+ int ret = 1;
+ if(SDL_LockMutex(dec->output_lock) == 0) {
+ ret = Kit_WriteBuffer(dec->buffer[KIT_DEC_BUF_OUT], packet);
+ SDL_UnlockMutex(dec->output_lock);
+ }
+ return ret;
+}
+
+void Kit_ClearDecoderOutput(Kit_Decoder *dec) {
+ if(SDL_LockMutex(dec->output_lock) == 0) {
+ Kit_ClearBuffer(dec->buffer[KIT_DEC_BUF_OUT]);
+ SDL_UnlockMutex(dec->output_lock);
+ }
+}
+
+void* Kit_PeekDecoderOutput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ void *ret = NULL;
+ if(SDL_LockMutex(dec->output_lock) == 0) {
+ ret = Kit_PeekBuffer(dec->buffer[KIT_DEC_BUF_OUT]);
+ SDL_UnlockMutex(dec->output_lock);
+ }
+ return ret;
+}
+
+void* Kit_ReadDecoderOutput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ void *ret = NULL;
+ if(SDL_LockMutex(dec->output_lock) == 0) {
+ ret = Kit_ReadBuffer(dec->buffer[KIT_DEC_BUF_OUT]);
+ SDL_UnlockMutex(dec->output_lock);
+ }
+ return ret;
+}
+
+void Kit_ForEachDecoderOutput(Kit_Decoder *dec, Kit_ForEachItemCallback cb, void *userdata) {
+ assert(dec != NULL);
+ if(SDL_LockMutex(dec->output_lock) == 0) {
+ Kit_ForEachItemInBuffer(dec->buffer[KIT_DEC_BUF_OUT], cb, userdata);
+ SDL_UnlockMutex(dec->output_lock);
+ }
+}
+
+void Kit_AdvanceDecoderOutput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ if(SDL_LockMutex(dec->output_lock) == 0) {
+ Kit_AdvanceBuffer(dec->buffer[KIT_DEC_BUF_OUT]);
+ SDL_UnlockMutex(dec->output_lock);
+ }
+}
+
+void Kit_ClearDecoderBuffers(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+ Kit_ClearDecoderInput(dec);
+ Kit_ClearDecoderOutput(dec);
+ avcodec_flush_buffers(dec->codec_ctx);
+}
+
+int Kit_LockDecoderOutput(Kit_Decoder *dec) {
+ return SDL_LockMutex(dec->output_lock);
+}
+
+void Kit_UnlockDecoderOutput(Kit_Decoder *dec) {
+ SDL_UnlockMutex(dec->output_lock);
+}
diff --git a/src/kitlibstate.c b/src/internal/kitlibstate.c index 0dd4e6b..4482ee9 100644 --- a/src/kitlibstate.c +++ b/src/internal/kitlibstate.c @@ -1,6 +1,7 @@ +#include <stdlib.h> #include "kitchensink/internal/kitlibstate.h" -static Kit_LibraryState _librarystate = {0, NULL}; +static Kit_LibraryState _librarystate = {0, 1, 0, 3, 64, 64, NULL, NULL}; Kit_LibraryState* Kit_GetLibraryState() { return &_librarystate; diff --git a/src/internal/libass.c b/src/internal/libass.c new file mode 100644 index 0000000..4a43e86 --- /dev/null +++ b/src/internal/libass.c @@ -0,0 +1,26 @@ +#ifdef USE_DYNAMIC_LIBASS
+
+#include <SDL_loadso.h>
+#include "kitchensink/internal/libass.h"
+
+int load_libass(void *handle) {
+ ass_library_init = SDL_LoadFunction(handle, "ass_library_init");
+ ass_library_done = SDL_LoadFunction(handle, "ass_library_done");
+ ass_set_message_cb = SDL_LoadFunction(handle, "ass_set_message_cb");
+ ass_renderer_init = SDL_LoadFunction(handle, "ass_renderer_init");
+ ass_renderer_done = SDL_LoadFunction(handle, "ass_renderer_done");
+ ass_set_frame_size = SDL_LoadFunction(handle, "ass_set_frame_size");
+ ass_set_hinting = SDL_LoadFunction(handle, "ass_set_hinting");
+ ass_set_fonts = SDL_LoadFunction(handle, "ass_set_fonts");
+ ass_render_frame = SDL_LoadFunction(handle, "ass_render_frame");
+ ass_new_track = SDL_LoadFunction(handle, "ass_new_track");
+ ass_free_track = SDL_LoadFunction(handle, "ass_free_track");
+ ass_process_data = SDL_LoadFunction(handle, "ass_process_data");
+ ass_add_font = SDL_LoadFunction(handle, "ass_add_font");
+ ass_process_codec_private = SDL_LoadFunction(handle, "ass_process_codec_private");
+ ass_process_chunk = SDL_LoadFunction(handle, "ass_process_chunk");
+ ass_set_storage_size = SDL_LoadFunction(handle, "ass_set_storage_size");
+ return 0;
+}
+
+#endif // USE_DYNAMIC_LIBASS
diff --git a/src/internal/subtitle/kitatlas.c b/src/internal/subtitle/kitatlas.c new file mode 100644 index 0000000..e7bb654 --- /dev/null +++ b/src/internal/subtitle/kitatlas.c @@ -0,0 +1,187 @@ +#include <assert.h>
+
+#include "kitchensink/internal/subtitle/kitatlas.h"
+#include "kitchensink/internal/utils/kitlog.h"
+
+static int min(int a, int b) {
+ if(a < b)
+ return a;
+ return b;
+}
+
+
+Kit_TextureAtlas* Kit_CreateAtlas() {
+ Kit_TextureAtlas *atlas = calloc(1, sizeof(Kit_TextureAtlas));
+ if(atlas == NULL) {
+ goto exit_0;
+ }
+ atlas->cur_items = 0;
+ atlas->max_items = 1024;
+ atlas->max_shelves = 256;
+ atlas->w = 0;
+ atlas->h = 0;
+
+ // Allocate items. These hold the surfaces that should be in atlas
+ atlas->items = calloc(atlas->max_items, sizeof(Kit_TextureAtlasItem));
+ if(atlas->items == NULL) {
+ goto exit_1;
+ }
+
+ // Allocate shelves. These describe the used space of the atlas
+ atlas->shelves = calloc(atlas->max_shelves, sizeof(Kit_Shelf));
+ if(atlas->shelves == NULL) {
+ goto exit_2;
+ }
+
+ return atlas;
+
+exit_2:
+ free(atlas->items);
+exit_1:
+ free(atlas);
+exit_0:
+ return NULL;
+}
+
+void Kit_ClearAtlasContent(Kit_TextureAtlas *atlas) {
+ atlas->cur_items = 0;
+ memset(atlas->items, 0, atlas->max_items * sizeof(Kit_TextureAtlasItem));
+ memset(atlas->shelves, 0, atlas->max_shelves * sizeof(Kit_Shelf));
+}
+
+void Kit_FreeAtlas(Kit_TextureAtlas *atlas) {
+ assert(atlas != NULL);
+ free(atlas->items);
+ free(atlas->shelves);
+ free(atlas);
+}
+
+void Kit_SetItemAllocation(Kit_TextureAtlasItem *item, SDL_Surface *surface, int shelf, int slot, int x, int y) {
+ assert(item != NULL);
+
+ item->cur_shelf = shelf;
+ item->cur_slot = slot;
+ item->source.x = x;
+ item->source.y = y;
+ item->source.w = surface->w;
+ item->source.h = surface->h;
+}
+
+int Kit_FindFreeAtlasSlot(Kit_TextureAtlas *atlas, SDL_Surface *surface, Kit_TextureAtlasItem *item) {
+ assert(atlas != NULL);
+ assert(item != NULL);
+
+ int shelf_w;
+ int shelf_h;
+ int total_remaining_h = atlas->h;
+ int total_reserved_h = 0;
+
+ // First, try to look for a good, existing shelf
+ int best_shelf_idx = -1;
+ int best_shelf_h = atlas->h;
+ int best_shelf_y = 0;
+
+ // Try to find a good shelf to put this item in
+ int shelf_idx;
+ for(shelf_idx = 0; shelf_idx < atlas->max_shelves; shelf_idx++) {
+ shelf_w = atlas->shelves[shelf_idx].width;
+ shelf_h = atlas->shelves[shelf_idx].height;
+ if(shelf_h == 0) {
+ break;
+ }
+ total_remaining_h -= shelf_h;
+ total_reserved_h += shelf_h;
+
+ // If the item fits, check if the space is better than previous one
+ if(surface->w <= (atlas->w - shelf_w) && surface->h <= shelf_h && shelf_h < best_shelf_h) {
+ best_shelf_h = shelf_h;
+ best_shelf_idx = shelf_idx;
+ best_shelf_y = total_reserved_h - shelf_h;
+ }
+ }
+
+ // If existing shelf found, put the item there. Otherwise create a new shelf.
+ if(best_shelf_idx != -1) {
+ Kit_SetItemAllocation(
+ item,
+ surface,
+ best_shelf_idx,
+ atlas->shelves[best_shelf_idx].count,
+ atlas->shelves[best_shelf_idx].width,
+ best_shelf_y);
+ atlas->shelves[best_shelf_idx].width += surface->w;
+ atlas->shelves[best_shelf_idx].count += 1;
+ return 0;
+ } else if(total_remaining_h >= surface->h) {
+ atlas->shelves[shelf_idx].width = surface->w;
+ atlas->shelves[shelf_idx].height = surface->h;
+ atlas->shelves[shelf_idx].count = 1;
+ Kit_SetItemAllocation(
+ item,
+ surface,
+ shelf_idx,
+ 0,
+ 0,
+ total_reserved_h);
+ return 0;
+ }
+
+ return 1; // Can't fit!
+}
+
+void Kit_CheckAtlasTextureSize(Kit_TextureAtlas *atlas, SDL_Texture *texture) {
+ assert(atlas != NULL);
+ assert(texture != NULL);
+
+ // Check if texture size has changed, and clear content if it has.
+ int texture_w;
+ int texture_h;
+ if(SDL_QueryTexture(texture, NULL, NULL, &texture_w, &texture_h) == 0) {
+ atlas->w = texture_w;
+ atlas->h = texture_h;
+ }
+}
+
+int Kit_GetAtlasItems(const Kit_TextureAtlas *atlas, SDL_Rect *sources, SDL_Rect *targets, int limit) {
+ assert(atlas != NULL);
+ assert(limit >= 0);
+
+ int max_count = min(atlas->cur_items, limit);
+ for(int i = 0; i < max_count; i++) {
+ Kit_TextureAtlasItem *item = &atlas->items[i];
+ if(sources != NULL)
+ memcpy(&sources[i], &item->source, sizeof(SDL_Rect));
+ if(targets != NULL)
+ memcpy(&targets[i], &item->target, sizeof(SDL_Rect));
+ }
+ return max_count;
+}
+
+int Kit_AddAtlasItem(Kit_TextureAtlas *atlas, SDL_Texture *texture, SDL_Surface *surface, const SDL_Rect *target) {
+ assert(atlas != NULL);
+ assert(surface != NULL);
+ assert(target != NULL);
+
+ // Make sure there is still room
+ if(atlas->cur_items >= atlas->max_items)
+ return -1;
+
+ // Create a new item
+ Kit_TextureAtlasItem item;
+ memset(&item, 0, sizeof(Kit_TextureAtlasItem));
+ memcpy(&item.target, target, sizeof(SDL_Rect));
+ item.cur_shelf = -1;
+ item.cur_slot = -1;
+
+ // Allocate space for the new item
+ if(Kit_FindFreeAtlasSlot(atlas, surface, &item) != 0) {
+ return -1;
+ }
+
+ // And update texture with the surface
+ SDL_UpdateTexture(texture, &item.source, surface->pixels, surface->pitch);
+
+ // Room found, add item to the atlas
+ memcpy(&atlas->items[atlas->cur_items++], &item, sizeof(Kit_TextureAtlasItem));
+ return 0;
+}
diff --git a/src/internal/subtitle/kitsubtitle.c b/src/internal/subtitle/kitsubtitle.c new file mode 100644 index 0000000..d6bed5d --- /dev/null +++ b/src/internal/subtitle/kitsubtitle.c @@ -0,0 +1,190 @@ +#include <assert.h>
+
+#include <SDL.h>
+#include <libavformat/avformat.h>
+
+#include "kitchensink/internal/utils/kitlog.h"
+
+#include "kitchensink/kiterror.h"
+#include "kitchensink/kitlib.h"
+#include "kitchensink/internal/utils/kitlog.h"
+#include "kitchensink/internal/kitlibstate.h"
+#include "kitchensink/internal/subtitle/kitsubtitlepacket.h"
+#include "kitchensink/internal/subtitle/kitsubtitle.h"
+#include "kitchensink/internal/subtitle/kitatlas.h"
+#include "kitchensink/internal/subtitle/renderers/kitsubimage.h"
+#include "kitchensink/internal/subtitle/renderers/kitsubass.h"
+#include "kitchensink/internal/subtitle/renderers/kitsubrenderer.h"
+#include "kitchensink/internal/utils/kithelpers.h"
+
+
+typedef struct Kit_SubtitleDecoder {
+ Kit_SubtitleRenderer *renderer;
+ AVSubtitle scratch_frame;
+ Kit_TextureAtlas *atlas;
+} Kit_SubtitleDecoder;
+
+
+static void free_out_subtitle_packet_cb(void *packet) {
+ Kit_FreeSubtitlePacket((Kit_SubtitlePacket*)packet);
+}
+
+static void dec_decode_subtitle_cb(Kit_Decoder *dec, AVPacket *in_packet) {
+ assert(dec != NULL);
+ assert(in_packet != NULL);
+
+ Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
+ double pts;
+ double start;
+ double end;
+ int frame_finished;
+ int len;
+
+ if(in_packet->size > 0) {
+ len = avcodec_decode_subtitle2(dec->codec_ctx, &subtitle_dec->scratch_frame, &frame_finished, in_packet);
+ if(len < 0) {
+ return;
+ }
+
+ if(frame_finished) {
+ // Start and end presentation timestamps for subtitle frame
+ pts = 0;
+ if(in_packet->pts != AV_NOPTS_VALUE) {
+ pts = in_packet->pts;
+ pts *= av_q2d(dec->format_ctx->streams[dec->stream_index]->time_base);
+ }
+
+ // If subtitle has no ending time, we set some safety value.
+ if(subtitle_dec->scratch_frame.end_display_time == UINT_MAX) {
+ subtitle_dec->scratch_frame.end_display_time = 30000;
+ }
+
+ start = pts + subtitle_dec->scratch_frame.start_display_time / 1000.0F;
+ end = pts + subtitle_dec->scratch_frame.end_display_time / 1000.0F;
+
+ // Create a packet. This should be filled by renderer.
+ Kit_RunSubtitleRenderer(
+ subtitle_dec->renderer, &subtitle_dec->scratch_frame, start, end);
+
+ // Free subtitle since it has now been handled
+ avsubtitle_free(&subtitle_dec->scratch_frame);
+ }
+ }
+}
+
+static void dec_close_subtitle_cb(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+ Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
+ Kit_FreeAtlas(subtitle_dec->atlas);
+ Kit_CloseSubtitleRenderer(subtitle_dec->renderer);
+ free(subtitle_dec);
+}
+
+Kit_Decoder* Kit_CreateSubtitleDecoder(const Kit_Source *src, int stream_index, int video_w, int video_h, int screen_w, int screen_h) {
+ assert(src != NULL);
+ assert(video_w >= 0);
+ assert(video_h >= 0);
+ assert(screen_w >= 0);
+ assert(screen_h >= 0);
+
+ if(stream_index < 0) {
+ return NULL;
+ }
+
+ Kit_LibraryState *state = Kit_GetLibraryState();
+
+ // First the generic decoder component
+ Kit_Decoder *dec = Kit_CreateDecoder(
+ src,
+ stream_index,
+ state->subtitle_buf_frames,
+ free_out_subtitle_packet_cb,
+ state->thread_count);
+ if(dec == NULL) {
+ Kit_SetError("Unable to allocate subtitle decoder");
+ goto exit_0;
+ }
+
+ // ... then allocate the subtitle decoder
+ Kit_SubtitleDecoder *subtitle_dec = calloc(1, sizeof(Kit_SubtitleDecoder));
+ if(subtitle_dec == NULL) {
+ Kit_SetError("Unable to allocate subtitle decoder");
+ goto exit_1;
+ }
+
+ // Set format. Note that is_enabled may be changed below ...
+ Kit_OutputFormat output;
+ memset(&output, 0, sizeof(Kit_OutputFormat));
+ output.format = SDL_PIXELFORMAT_RGBA32; // Always this
+
+ // For subtitles, we need a renderer for the stream. Pick one based on codec ID.
+ switch(dec->codec_ctx->codec_id) {
+ case AV_CODEC_ID_TEXT:
+ case AV_CODEC_ID_HDMV_TEXT_SUBTITLE:
+ case AV_CODEC_ID_SRT:
+ case AV_CODEC_ID_SUBRIP:
+ case AV_CODEC_ID_SSA:
+ case AV_CODEC_ID_ASS:
+ if(state->init_flags & KIT_INIT_ASS) {
+ subtitle_dec->renderer = Kit_CreateASSSubtitleRenderer(dec, video_w, video_h, screen_w, screen_h);
+ }
+ break;
+ case AV_CODEC_ID_DVD_SUBTITLE:
+ case AV_CODEC_ID_DVB_SUBTITLE:
+ case AV_CODEC_ID_HDMV_PGS_SUBTITLE:
+ case AV_CODEC_ID_XSUB:
+ subtitle_dec->renderer = Kit_CreateImageSubtitleRenderer(dec, video_w, video_h, screen_w, screen_h);
+ break;
+ default:
+ Kit_SetError("Unrecognized subtitle format");
+ break;
+ }
+ if(subtitle_dec->renderer == NULL) {
+ goto exit_2;
+ }
+
+ // Allocate texture atlas for subtitle rectangles
+ subtitle_dec->atlas = Kit_CreateAtlas();
+ if(subtitle_dec->atlas == NULL) {
+ Kit_SetError("Unable to allocate subtitle texture atlas");
+ goto exit_3;
+ }
+
+ // Set callbacks and userdata, and we're go
+ dec->dec_decode = dec_decode_subtitle_cb;
+ dec->dec_close = dec_close_subtitle_cb;
+ dec->userdata = subtitle_dec;
+ dec->output = output;
+ return dec;
+
+exit_3:
+ Kit_CloseSubtitleRenderer(subtitle_dec->renderer);
+exit_2:
+ free(subtitle_dec);
+exit_1:
+ Kit_CloseDecoder(dec);
+exit_0:
+ return NULL;
+}
+
+void Kit_SetSubtitleDecoderSize(Kit_Decoder *dec, int screen_w, int screen_h) {
+ assert(dec != NULL);
+ Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
+ Kit_SetSubtitleRendererSize(subtitle_dec->renderer, screen_w, screen_h);
+}
+
+void Kit_GetSubtitleDecoderTexture(Kit_Decoder *dec, SDL_Texture *texture) {
+ assert(dec != NULL);
+ assert(texture != NULL);
+
+ Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
+ double sync_ts = _GetSystemTime() - dec->clock_sync;
+
+ // Tell the renderer to render content to atlas
+ Kit_GetSubtitleRendererData(subtitle_dec->renderer, subtitle_dec->atlas, texture, sync_ts);
+}
+
+int Kit_GetSubtitleDecoderInfo(Kit_Decoder *dec, SDL_Texture *texture, SDL_Rect *sources, SDL_Rect *targets, int limit) {
+ Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
+ return Kit_GetAtlasItems(subtitle_dec->atlas, sources, targets, limit);
+}
diff --git a/src/internal/subtitle/kitsubtitlepacket.c b/src/internal/subtitle/kitsubtitlepacket.c new file mode 100644 index 0000000..f44eee6 --- /dev/null +++ b/src/internal/subtitle/kitsubtitlepacket.c @@ -0,0 +1,23 @@ +#include "kitchensink/internal/subtitle/kitsubtitlepacket.h"
+
+
+Kit_SubtitlePacket* Kit_CreateSubtitlePacket(
+ bool clear, double pts_start, double pts_end, int pos_x, int pos_y, SDL_Surface *surface)
+{
+ Kit_SubtitlePacket *p = calloc(1, sizeof(Kit_SubtitlePacket));
+ p->pts_start = pts_start;
+ p->pts_end = pts_end;
+ p->x = pos_x;
+ p->y = pos_y;
+ p->surface = surface;
+ if(p->surface != NULL) {
+ p->surface->refcount++; // We dont want to needlessly copy; instead increase refcount.
+ }
+ p->clear = clear;
+ return p;
+}
+
+void Kit_FreeSubtitlePacket(Kit_SubtitlePacket *p) {
+ SDL_FreeSurface(p->surface);
+ free(p);
+}
diff --git a/src/internal/subtitle/renderers/kitsubass.c b/src/internal/subtitle/renderers/kitsubass.c new file mode 100644 index 0000000..7db72dd --- /dev/null +++ b/src/internal/subtitle/renderers/kitsubass.c @@ -0,0 +1,212 @@ +#include <assert.h>
+#include <stdlib.h>
+
+#include <SDL_surface.h>
+
+#include "kitchensink/kiterror.h"
+#include "kitchensink/internal/utils/kitlog.h"
+#include "kitchensink/internal/kitlibstate.h"
+#include "kitchensink/internal/subtitle/kitsubtitlepacket.h"
+#include "kitchensink/internal/subtitle/kitatlas.h"
+#include "kitchensink/internal/utils/kithelpers.h"
+#include "kitchensink/internal/subtitle/renderers/kitsubass.h"
+
+typedef struct Kit_ASSSubtitleRenderer {
+ ASS_Renderer *renderer;
+ ASS_Track *track;
+} Kit_ASSSubtitleRenderer;
+
+static void Kit_ProcessAssImage(SDL_Surface *surface, const ASS_Image *img) {
+ unsigned char r = ((img->color) >> 24) & 0xFF;
+ unsigned char g = ((img->color) >> 16) & 0xFF;
+ unsigned char b = ((img->color) >> 8) & 0xFF;
+ unsigned char a = 0xFF - ((img->color) & 0xFF);
+ unsigned char *src = img->bitmap;
+ unsigned char *dst = surface->pixels;
+ unsigned int x;
+ unsigned int y;
+ unsigned int rx;
+
+ for(y = 0; y < img->h; y++) {
+ for(x = 0; x < img->w; x++) {
+ rx = x * 4;
+ dst[rx + 0] = r;
+ dst[rx + 1] = g;
+ dst[rx + 2] = b;
+ dst[rx + 3] = (a * src[x]) >> 8;
+ }
+ src += img->stride;
+ dst += surface->pitch;
+ }
+}
+
+static void ren_render_ass_cb(Kit_SubtitleRenderer *ren, void *src, double start_pts, double end_pts) {
+ assert(ren != NULL);
+ assert(src != NULL);
+
+ Kit_ASSSubtitleRenderer *ass_ren = ren->userdata;
+ AVSubtitle *sub = src;
+
+ // Read incoming subtitle packets to libASS
+ if(Kit_LockDecoderOutput(ren->dec) == 0) {
+ for(int r = 0; r < sub->num_rects; r++) {
+ if(sub->rects[r]->ass == NULL)
+ continue;
+ ass_process_data(ass_ren->track, sub->rects[r]->ass, strlen(sub->rects[r]->ass));
+ }
+ Kit_UnlockDecoderOutput(ren->dec);
+ }
+}
+
+static void ren_close_ass_cb(Kit_SubtitleRenderer *ren) {
+ if(ren == NULL) return;
+
+ Kit_ASSSubtitleRenderer *ass_ren = ren->userdata;
+ ass_free_track(ass_ren->track);
+ ass_renderer_done(ass_ren->renderer);
+ free(ass_ren);
+}
+
+static int ren_get_ass_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts) {
+ Kit_ASSSubtitleRenderer *ass_ren = ren->userdata;
+ SDL_Surface *dst = NULL;
+ ASS_Image *src = NULL;
+ int change = 0;
+ unsigned int now = current_pts * 1000;
+
+ if(Kit_LockDecoderOutput(ren->dec) == 0) {
+ // Tell ASS to render some images
+ src = ass_render_frame(ass_ren->renderer, ass_ren->track, now, &change);
+
+ // If there was no change, stop here
+ if(change == 0) {
+ Kit_UnlockDecoderOutput(ren->dec);
+ return 0;
+ }
+
+ // There was some change, process images and add them to atlas
+ Kit_ClearAtlasContent(atlas);
+ Kit_CheckAtlasTextureSize(atlas, texture);
+ for(; src; src = src->next) {
+ if(src->w == 0 || src->h == 0)
+ continue;
+ dst = SDL_CreateRGBSurfaceWithFormat(0, src->w, src->h, 32, SDL_PIXELFORMAT_RGBA32);
+ Kit_ProcessAssImage(dst, src);
+ SDL_Rect target;
+ target.x = src->dst_x;
+ target.y = src->dst_y;
+ target.w = dst->w;
+ target.h = dst->h;
+ Kit_AddAtlasItem(atlas, texture, dst, &target);
+ SDL_FreeSurface(dst);
+ }
+
+ Kit_UnlockDecoderOutput(ren->dec);
+ }
+
+ ren->dec->clock_pos = current_pts;
+ return 0;
+}
+
+static void ren_set_ass_size_cb(Kit_SubtitleRenderer *ren, int w, int h) {
+ Kit_ASSSubtitleRenderer *ass_ren = ren->userdata;
+ ass_set_frame_size(ass_ren->renderer, w, h);
+}
+
+Kit_SubtitleRenderer* Kit_CreateASSSubtitleRenderer(Kit_Decoder *dec, int video_w, int video_h, int screen_w, int screen_h) {
+ assert(dec != NULL);
+ assert(video_w >= 0);
+ assert(video_h >= 0);
+ assert(screen_w >= 0);
+ assert(screen_h >= 0);
+
+ // Make sure that libass library has been initialized + get handle
+ Kit_LibraryState *state = Kit_GetLibraryState();
+ if(state->libass_handle == NULL) {
+ Kit_SetError("Libass library has not been initialized");
+ return NULL;
+ }
+
+ // First allocate the generic decoder component
+ Kit_SubtitleRenderer *ren = Kit_CreateSubtitleRenderer(dec);
+ if(ren == NULL) {
+ goto exit_0;
+ }
+
+ // Next, allocate ASS subtitle renderer context.
+ Kit_ASSSubtitleRenderer *ass_ren = calloc(1, sizeof(Kit_ASSSubtitleRenderer));
+ if(ass_ren == NULL) {
+ Kit_SetError("Unable to allocate ass subtitle renderer");
+ goto exit_1;
+ }
+
+ // Initialize libass renderer
+ ASS_Renderer *ass_renderer = ass_renderer_init(state->libass_handle);
+ if(ass_renderer == NULL) {
+ Kit_SetError("Unable to initialize libass renderer");
+ goto exit_2;
+ }
+
+ // Read fonts from attachment streams and give them to libass
+ for(int j = 0; j < dec->format_ctx->nb_streams; j++) {
+ AVStream *st = dec->format_ctx->streams[j];
+ if(st->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT && attachment_is_font(st)) {
+ const AVDictionaryEntry *tag = av_dict_get(
+ st->metadata,
+ "filename",
+ NULL,
+ AV_DICT_MATCH_CASE);
+ if(tag) {
+ ass_add_font(
+ state->libass_handle,
+ tag->value,
+ (char*)st->codec->extradata,
+ st->codec->extradata_size);
+ }
+ }
+ }
+
+ // Init libass fonts and window frame size
+ ass_set_fonts(
+ ass_renderer,
+ NULL, "sans-serif",
+ ASS_FONTPROVIDER_AUTODETECT,
+ NULL, 1);
+ ass_set_storage_size(ass_renderer, video_w, video_h);
+ ass_set_frame_size(ass_renderer, screen_w, screen_h);
+ ass_set_hinting(ass_renderer, state->font_hinting);
+
+ // Initialize libass track
+ ASS_Track *ass_track = ass_new_track(state->libass_handle);
+ if(ass_track == NULL) {
+ Kit_SetError("Unable to initialize libass track");
+ goto exit_3;
+ }
+
+ // Set up libass track headers (ffmpeg provides these)
+ if(dec->codec_ctx->subtitle_header) {
+ ass_process_codec_private(
+ ass_track,
+ (char*)dec->codec_ctx->subtitle_header,
+ dec->codec_ctx->subtitle_header_size);
+ }
+
+ // Set callbacks and userdata, and we're go
+ ass_ren->renderer = ass_renderer;
+ ass_ren->track = ass_track;
+ ren->ren_render = ren_render_ass_cb;
+ ren->ren_close = ren_close_ass_cb;
+ ren->ren_get_data = ren_get_ass_data_cb;
+ ren->ren_set_size = ren_set_ass_size_cb;
+ ren->userdata = ass_ren;
+ return ren;
+
+exit_3:
+ ass_renderer_done(ass_renderer);
+exit_2:
+ free(ass_ren);
+exit_1:
+ Kit_CloseSubtitleRenderer(ren);
+exit_0:
+ return NULL;
+}
diff --git a/src/internal/subtitle/renderers/kitsubimage.c b/src/internal/subtitle/renderers/kitsubimage.c new file mode 100644 index 0000000..0cd0cce --- /dev/null +++ b/src/internal/subtitle/renderers/kitsubimage.c @@ -0,0 +1,144 @@ +#include <assert.h>
+#include <stdlib.h>
+
+#include <SDL_surface.h>
+
+#include "kitchensink/kiterror.h"
+#include "kitchensink/internal/utils/kitlog.h"
+#include "kitchensink/internal/subtitle/kitatlas.h"
+#include "kitchensink/internal/subtitle/kitsubtitlepacket.h"
+#include "kitchensink/internal/subtitle/renderers/kitsubimage.h"
+
+
+typedef struct Kit_ImageSubtitleRenderer {
+ int video_w;
+ int video_h;
+ float scale_x;
+ float scale_y;
+} Kit_ImageSubtitleRenderer;
+
+static void ren_render_image_cb(Kit_SubtitleRenderer *ren, void *sub_src, double start_pts, double end_pts) {
+ assert(ren != NULL);
+ assert(sub_src != NULL);
+
+ AVSubtitle *sub = sub_src;
+ SDL_Surface *dst = NULL;
+ SDL_Surface *src = NULL;
+
+ // If this subtitle has no rects, we still need to clear screen from old subs
+ if(sub->num_rects == 0) {
+ Kit_WriteDecoderOutput(
+ ren->dec, Kit_CreateSubtitlePacket(true, start_pts, end_pts, 0, 0, NULL));
+ return;
+ }
+
+ // Convert subtitle images from paletted to RGBA8888
+ for(int n = 0; n < sub->num_rects; n++) {
+ AVSubtitleRect *r = sub->rects[n];
+ if(r->type != SUBTITLE_BITMAP)
+ continue;
+
+ src = SDL_CreateRGBSurfaceWithFormatFrom(
+ r->data[0], r->w, r->h, 8, r->linesize[0], SDL_PIXELFORMAT_INDEX8);
+ SDL_SetPaletteColors(src->format->palette, (SDL_Color*)r->data[1], 0, 256);
+ dst = SDL_CreateRGBSurfaceWithFormat(
+ 0, r->w, r->h, 32, SDL_PIXELFORMAT_RGBA32);
+
+ // Blit source to target and free source surface.
+ SDL_BlitSurface(src, NULL, dst, NULL);
+
+ // Create a new packet and write it to output buffer
+ Kit_WriteDecoderOutput(
+ ren->dec, Kit_CreateSubtitlePacket(false, start_pts, end_pts, r->x, r->y, dst));
+
+ // Free surfaces
+ SDL_FreeSurface(src);
+ SDL_FreeSurface(dst);
+ }
+}
+
+static int ren_get_img_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts) {
+ Kit_ImageSubtitleRenderer *img_ren = ren->userdata;
+ Kit_SubtitlePacket *packet = NULL;
+
+ Kit_CheckAtlasTextureSize(atlas, texture);
+ while((packet = Kit_PeekDecoderOutput(ren->dec)) != NULL) {
+ // Clear dead packets
+ if(packet->pts_end < current_pts) {
+ Kit_AdvanceDecoderOutput(ren->dec);
+ Kit_FreeSubtitlePacket(packet);
+ continue;
+ }
+
+ // Show visible ones
+ if(packet->pts_start < current_pts) {
+ if(packet->clear) {
+ Kit_ClearAtlasContent(atlas);
+ }
+ if(packet->surface != NULL) {
+ SDL_Rect target;
+ target.x = packet->x * img_ren->scale_x;
+ target.y = packet->y * img_ren->scale_y;
+ target.w = packet->surface->w * img_ren->scale_x;
+ target.h = packet->surface->h * img_ren->scale_y;
+ Kit_AddAtlasItem(atlas, texture, packet->surface, &target);
+ }
+ Kit_AdvanceDecoderOutput(ren->dec);
+ Kit_FreeSubtitlePacket(packet);
+ ren->dec->clock_pos = current_pts;
+ continue;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void ren_set_img_size_cb(Kit_SubtitleRenderer *ren, int w, int h) {
+ Kit_ImageSubtitleRenderer *img_ren = ren->userdata;
+ img_ren->scale_x = (float)w / (float)img_ren->video_w;
+ img_ren->scale_y = (float)h / (float)img_ren->video_h;
+}
+
+static void ren_close_ass_cb(Kit_SubtitleRenderer *ren) {
+ if(ren == NULL) return;
+ free(ren->userdata);
+}
+
+Kit_SubtitleRenderer* Kit_CreateImageSubtitleRenderer(Kit_Decoder *dec, int video_w, int video_h, int screen_w, int screen_h) {
+ assert(dec != NULL);
+ assert(video_w >= 0);
+ assert(video_h >= 0);
+ assert(screen_w >= 0);
+ assert(screen_h >= 0);
+
+ // Allocate a new renderer
+ Kit_SubtitleRenderer *ren = Kit_CreateSubtitleRenderer(dec);
+ if(ren == NULL) {
+ goto exit_0;
+ }
+
+ // Allocate image renderer internal context
+ Kit_ImageSubtitleRenderer *img_ren = calloc(1, sizeof(Kit_ImageSubtitleRenderer));
+ if(img_ren == NULL) {
+ Kit_SetError("Unable to allocate image subtitle renderer");
+ goto exit_1;
+ }
+
+ // Only renderer required, no other data.
+ img_ren->video_w = video_w;
+ img_ren->video_h = video_h;
+ img_ren->scale_x = (float)screen_w / (float)video_w;
+ img_ren->scale_y = (float)screen_h / (float)video_h;
+ ren->ren_render = ren_render_image_cb;
+ ren->ren_get_data = ren_get_img_data_cb;
+ ren->ren_set_size = ren_set_img_size_cb;
+ ren->ren_close = ren_close_ass_cb;
+ ren->userdata = img_ren;
+ return ren;
+
+exit_1:
+ Kit_CloseSubtitleRenderer(ren);
+exit_0:
+ return NULL;
+}
diff --git a/src/internal/subtitle/renderers/kitsubrenderer.c b/src/internal/subtitle/renderers/kitsubrenderer.c new file mode 100644 index 0000000..77ad201 --- /dev/null +++ b/src/internal/subtitle/renderers/kitsubrenderer.c @@ -0,0 +1,48 @@ +#include <stdlib.h>
+#include <assert.h>
+
+#include "kitchensink/kiterror.h"
+#include "kitchensink/internal/subtitle/kitsubtitlepacket.h"
+#include "kitchensink/internal/subtitle/renderers/kitsubrenderer.h"
+
+
+Kit_SubtitleRenderer* Kit_CreateSubtitleRenderer(Kit_Decoder *dec) {
+ // Allocate renderer and make sure allocation was a success
+ Kit_SubtitleRenderer *ren = calloc(1, sizeof(Kit_SubtitleRenderer));
+ if(ren == NULL) {
+ Kit_SetError("Unable to allocate kit subtitle renderer");
+ return NULL;
+ }
+ ren->dec = dec;
+ return ren;
+}
+
+void Kit_RunSubtitleRenderer(Kit_SubtitleRenderer *ren, void *src, double start_pts, double end_pts) {
+ if(ren == NULL)
+ return;
+ if(ren->ren_render != NULL)
+ ren->ren_render(ren, src, start_pts, end_pts);
+}
+
+int Kit_GetSubtitleRendererData(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts) {
+ if(ren == NULL)
+ return 0;
+ if(ren->ren_get_data != NULL)
+ return ren->ren_get_data(ren, atlas, texture, current_pts);
+ return 0;
+}
+
+void Kit_SetSubtitleRendererSize(Kit_SubtitleRenderer *ren, int w, int h) {
+ if(ren == NULL)
+ return;
+ if(ren->ren_set_size != NULL)
+ ren->ren_set_size(ren, w, h);
+}
+
+void Kit_CloseSubtitleRenderer(Kit_SubtitleRenderer *ren) {
+ if(ren == NULL)
+ return;
+ if(ren->ren_close != NULL)
+ ren->ren_close(ren);
+ free(ren);
+}
diff --git a/src/kitbuffer.c b/src/internal/utils/kitbuffer.c index 5c492ea..0133154 100644 --- a/src/kitbuffer.c +++ b/src/internal/utils/kitbuffer.c @@ -1,8 +1,8 @@ -#include "kitchensink/internal/kitbuffer.h" - #include <stdlib.h> #include <assert.h> +#include "kitchensink/internal/utils/kitbuffer.h" + Kit_Buffer* Kit_CreateBuffer(unsigned int size, Kit_BufferFreeCallback free_cb) { Kit_Buffer *b = calloc(1, sizeof(Kit_Buffer)); if(b == NULL) { @@ -27,6 +27,8 @@ void Kit_DestroyBuffer(Kit_Buffer *buffer) { void Kit_ClearBuffer(Kit_Buffer *buffer) { void *data; + if(buffer->free_cb == NULL) + return; while((data = Kit_ReadBuffer(buffer)) != NULL) { buffer->free_cb(data); } @@ -47,12 +49,15 @@ void* Kit_ReadBuffer(Kit_Buffer *buffer) { return NULL; } -KIT_LOCAL void* Kit_PeekBuffer(const Kit_Buffer *buffer) { +void* Kit_PeekBuffer(const Kit_Buffer *buffer) { assert(buffer != NULL); - return buffer->data[buffer->read_p % buffer->size]; + if(buffer->read_p < buffer->write_p) { + return buffer->data[buffer->read_p % buffer->size]; + } + return NULL; } -KIT_LOCAL void Kit_AdvanceBuffer(Kit_Buffer *buffer) { +void Kit_AdvanceBuffer(Kit_Buffer *buffer) { assert(buffer != NULL); if(buffer->read_p < buffer->write_p) { buffer->data[buffer->read_p % buffer->size] = NULL; @@ -64,6 +69,18 @@ KIT_LOCAL void Kit_AdvanceBuffer(Kit_Buffer *buffer) { } } +void Kit_ForEachItemInBuffer(const Kit_Buffer *buffer, Kit_ForEachItemCallback cb, void *userdata) { + unsigned int read_p = buffer->read_p; + unsigned int write_p = buffer->write_p; + while(read_p < write_p) { + cb(buffer->data[read_p++ % buffer->size], userdata); + if(read_p >= buffer->size) { + read_p = read_p % buffer->size; + write_p = write_p % buffer->size; + } + } +} + int Kit_WriteBuffer(Kit_Buffer *buffer, void *ptr) { assert(buffer != NULL); assert(ptr != NULL); diff --git a/src/internal/utils/kithelpers.c b/src/internal/utils/kithelpers.c new file mode 100644 index 0000000..c68f1c7 --- /dev/null +++ b/src/internal/utils/kithelpers.c @@ -0,0 +1,30 @@ +#include <libavutil/time.h>
+#include <libavutil/avstring.h>
+
+#include "kitchensink/internal/utils/kithelpers.h"
+
+static const char * const font_mime[] = {
+ "application/x-font-ttf",
+ "application/x-font-truetype",
+ "application/x-truetype-font",
+ "application/x-font-opentype",
+ "application/vnd.ms-opentype",
+ "application/font-sfnt",
+ NULL
+};
+
+double _GetSystemTime() {
+ return (double)av_gettime() / 1000000.0;
+}
+
+bool attachment_is_font(AVStream *stream) {
+ AVDictionaryEntry *tag = av_dict_get(stream->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE);
+ if(tag) {
+ for(int n = 0; font_mime[n]; n++) {
+ if(av_strcasecmp(font_mime[n], tag->value) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
diff --git a/src/kitringbuffer.c b/src/internal/utils/kitringbuffer.c index 1711d52..70fd24f 100644 --- a/src/kitringbuffer.c +++ b/src/internal/utils/kitringbuffer.c @@ -1,17 +1,17 @@ /* * Ringbuffer * - * Copyright (c) 2016, Tuomas Virtanen + * Copyright (c) 2017, Tuomas Virtanen * license: MIT; see LICENSE for details. */ -#include "kitchensink/internal/kitringbuffer.h" - #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> +#include "kitchensink/internal/utils/kitringbuffer.h" + /** * Creates a new ringbuffer with the given size. * @param size Size for the new ringbuffer diff --git a/src/internal/video/kitvideo.c b/src/internal/video/kitvideo.c new file mode 100644 index 0000000..1629b05 --- /dev/null +++ b/src/internal/video/kitvideo.c @@ -0,0 +1,301 @@ +#include <assert.h>
+
+#include <libavformat/avformat.h>
+#include <libavutil/imgutils.h>
+#include <libswscale/swscale.h>
+
+#include "kitchensink/kiterror.h"
+#include "kitchensink/internal/kitlibstate.h"
+#include "kitchensink/internal/kitdecoder.h"
+#include "kitchensink/internal/utils/kithelpers.h"
+#include "kitchensink/internal/utils/kitbuffer.h"
+#include "kitchensink/internal/video/kitvideo.h"
+#include "kitchensink/internal/utils/kitlog.h"
+
+#define KIT_VIDEO_SYNC_THRESHOLD 0.01
+
+enum AVPixelFormat supported_list[] = {
+ AV_PIX_FMT_YUV420P,
+ AV_PIX_FMT_YUYV422,
+ AV_PIX_FMT_UYVY422,
+ AV_PIX_FMT_NV12,
+ AV_PIX_FMT_NV21,
+ AV_PIX_FMT_RGB24,
+ AV_PIX_FMT_BGR24,
+ AV_PIX_FMT_RGB555,
+ AV_PIX_FMT_BGR555,
+ AV_PIX_FMT_RGB565,
+ AV_PIX_FMT_BGR565,
+ AV_PIX_FMT_BGRA,
+ AV_PIX_FMT_RGBA,
+ AV_PIX_FMT_NONE
+};
+
+typedef struct Kit_VideoDecoder {
+ struct SwsContext *sws;
+ AVFrame *scratch_frame;
+} Kit_VideoDecoder;
+
+typedef struct Kit_VideoPacket {
+ double pts;
+ AVFrame *frame;
+} Kit_VideoPacket;
+
+
+static Kit_VideoPacket* _CreateVideoPacket(AVFrame *frame, double pts) {
+ Kit_VideoPacket *p = calloc(1, sizeof(Kit_VideoPacket));
+ p->frame = frame;
+ p->pts = pts;
+ return p;
+}
+
+static unsigned int _FindSDLPixelFormat(enum AVPixelFormat fmt) {
+ switch(fmt) {
+ case AV_PIX_FMT_YUV420P:
+ return SDL_PIXELFORMAT_YV12;
+ case AV_PIX_FMT_YUYV422:
+ return SDL_PIXELFORMAT_YUY2;
+ case AV_PIX_FMT_UYVY422:
+ return SDL_PIXELFORMAT_UYVY;
+ case AV_PIX_FMT_NV12:
+ return SDL_PIXELFORMAT_NV12;
+ case AV_PIX_FMT_NV21:
+ return SDL_PIXELFORMAT_NV21;
+ default:
+ return SDL_PIXELFORMAT_RGBA32;
+ }
+}
+
+static enum AVPixelFormat _FindAVPixelFormat(unsigned int fmt) {
+ switch(fmt) {
+ case SDL_PIXELFORMAT_YV12: return AV_PIX_FMT_YUV420P;
+ case SDL_PIXELFORMAT_YUY2: return AV_PIX_FMT_YUYV422;
+ case SDL_PIXELFORMAT_UYVY: return AV_PIX_FMT_UYVY422;
+ case SDL_PIXELFORMAT_NV12: return AV_PIX_FMT_NV12;
+ case SDL_PIXELFORMAT_NV21: return AV_PIX_FMT_NV21;
+ case SDL_PIXELFORMAT_ARGB32: return AV_PIX_FMT_BGRA;
+ case SDL_PIXELFORMAT_RGBA32: return AV_PIX_FMT_RGBA;
+ case SDL_PIXELFORMAT_BGR24: return AV_PIX_FMT_BGR24;
+ case SDL_PIXELFORMAT_RGB24: return AV_PIX_FMT_RGB24;
+ case SDL_PIXELFORMAT_RGB555: return AV_PIX_FMT_RGB555;
+ case SDL_PIXELFORMAT_BGR555: return AV_PIX_FMT_BGR555;
+ case SDL_PIXELFORMAT_RGB565: return AV_PIX_FMT_RGB565;
+ case SDL_PIXELFORMAT_BGR565: return AV_PIX_FMT_BGR565;
+ default:
+ return AV_PIX_FMT_NONE;
+ }
+}
+
+static void free_out_video_packet_cb(void *packet) {
+ Kit_VideoPacket *p = packet;
+ av_freep(&p->frame->data[0]);
+ av_frame_free(&p->frame);
+ free(p);
+}
+
+static void dec_decode_video_cb(Kit_Decoder *dec, AVPacket *in_packet) {
+ assert(dec != NULL);
+ assert(in_packet != NULL);
+
+ Kit_VideoDecoder *video_dec = dec->userdata;
+ AVFrame *out_frame;
+ int frame_finished;
+ int len;
+ double pts;
+ Kit_VideoPacket *out_packet;
+
+ while(in_packet->size > 0) {
+ len = avcodec_decode_video2(dec->codec_ctx, video_dec->scratch_frame, &frame_finished, in_packet);
+ if(len < 0) {
+ return;
+ }
+
+ if(frame_finished) {
+ // Target frame
+ out_frame = av_frame_alloc();
+ av_image_alloc(
+ out_frame->data,
+ out_frame->linesize,
+ dec->codec_ctx->width,
+ dec->codec_ctx->height,
+ _FindAVPixelFormat(dec->output.format),
+ 1);
+
+ // Scale from source format to target format, don't touch the size
+ sws_scale(
+ video_dec->sws,
+ (const unsigned char * const *)video_dec->scratch_frame->data,
+ video_dec->scratch_frame->linesize,
+ 0,
+ dec->codec_ctx->height,
+ out_frame->data,
+ out_frame->linesize);
+
+ // Get presentation timestamp
+#ifndef FF_API_FRAME_GET_SET
+ pts = av_frame_get_best_effort_timestamp(video_dec->scratch_frame);
+#else
+ pts = video_dec->scratch_frame->best_effort_timestamp;
+#endif
+ pts *= av_q2d(dec->format_ctx->streams[dec->stream_index]->time_base);
+
+ // Lock, write to audio buffer, unlock
+ out_packet = _CreateVideoPacket(out_frame, pts);
+ Kit_WriteDecoderOutput(dec, out_packet);
+ }
+ in_packet->size -= len;
+ in_packet->data += len;
+ }
+}
+
+static void dec_close_video_cb(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+
+ Kit_VideoDecoder *video_dec = dec->userdata;
+ if(video_dec->scratch_frame != NULL) {
+ av_frame_free(&video_dec->scratch_frame);
+ }
+ if(video_dec->sws != NULL) {
+ sws_freeContext(video_dec->sws);
+ }
+ free(video_dec);
+}
+
+Kit_Decoder* Kit_CreateVideoDecoder(const Kit_Source *src, int stream_index) {
+ assert(src != NULL);
+ if(stream_index < 0) {
+ return NULL;
+ }
+
+ Kit_LibraryState *state = Kit_GetLibraryState();
+
+ // First the generic decoder component ...
+ Kit_Decoder *dec = Kit_CreateDecoder(
+ src,
+ stream_index,
+ state->video_buf_frames,
+ free_out_video_packet_cb,
+ state->thread_count);
+ if(dec == NULL) {
+ goto exit_0;
+ }
+
+ // ... then allocate the video decoder
+ Kit_VideoDecoder *video_dec = calloc(1, sizeof(Kit_VideoDecoder));
+ if(video_dec == NULL) {
+ goto exit_1;
+ }
+
+ // Create temporary video frame
+ video_dec->scratch_frame = av_frame_alloc();
+ if(video_dec->scratch_frame == NULL) {
+ Kit_SetError("Unable to initialize temporary video frame");
+ goto exit_2;
+ }
+
+ // Find best output format for us
+ enum AVPixelFormat output_format = avcodec_find_best_pix_fmt_of_list(
+ supported_list, dec->codec_ctx->pix_fmt, 1, NULL);
+
+ // Set format configs
+ Kit_OutputFormat output;
+ memset(&output, 0, sizeof(Kit_OutputFormat));
+ output.width = dec->codec_ctx->width;
+ output.height = dec->codec_ctx->height;
+ output.format = _FindSDLPixelFormat(output_format);
+
+ // Create scaler for handling format changes
+ video_dec->sws = sws_getContext(
+ dec->codec_ctx->width, // Source w
+ dec->codec_ctx->height, // Source h
+ dec->codec_ctx->pix_fmt, // Source fmt
+ dec->codec_ctx->width, // Target w
+ dec->codec_ctx->height, // Target h
+ _FindAVPixelFormat(output.format), // Target fmt
+ SWS_BILINEAR,
+ NULL, NULL, NULL);
+ if(video_dec->sws == NULL) {
+ Kit_SetError("Unable to initialize video converter context");
+ goto exit_3;
+ }
+
+ // Set callbacks and userdata, and we're go
+ dec->dec_decode = dec_decode_video_cb;
+ dec->dec_close = dec_close_video_cb;
+ dec->userdata = video_dec;
+ dec->output = output;
+ return dec;
+
+exit_3:
+ av_frame_free(&video_dec->scratch_frame);
+exit_2:
+ free(video_dec);
+exit_1:
+ Kit_CloseDecoder(dec);
+exit_0:
+ return NULL;
+}
+
+int Kit_GetVideoDecoderData(Kit_Decoder *dec, SDL_Texture *texture) {
+ assert(dec != NULL);
+ assert(texture != NULL);
+
+ Kit_VideoPacket *packet = Kit_PeekDecoderOutput(dec);
+ if(packet == NULL) {
+ return 0;
+ }
+
+ double sync_ts = _GetSystemTime() - dec->clock_sync;
+
+ // Check if we want the packet
+ if(packet->pts > sync_ts + KIT_VIDEO_SYNC_THRESHOLD) {
+ // Video is ahead, don't show yet.
+ return 0;
+ } else if(packet->pts < sync_ts - KIT_VIDEO_SYNC_THRESHOLD) {
+ // Video is lagging, skip until we find a good PTS to continue from.
+ while(packet != NULL) {
+ Kit_AdvanceDecoderOutput(dec);
+ free_out_video_packet_cb(packet);
+ packet = Kit_PeekDecoderOutput(dec);
+ if(packet == NULL) {
+ break;
+ } else {
+ dec->clock_pos = packet->pts;
+ }
+ if(packet->pts > sync_ts - KIT_VIDEO_SYNC_THRESHOLD) {
+ break;
+ }
+ }
+ }
+
+ // If we have no viable packet, just skip
+ if(packet == NULL) {
+ return 0;
+ }
+
+ // Update output texture with current video data.
+ // Take formats into account.
+ switch(dec->output.format) {
+ case SDL_PIXELFORMAT_YV12:
+ case SDL_PIXELFORMAT_IYUV:
+ SDL_UpdateYUVTexture(
+ texture, NULL,
+ packet->frame->data[0], packet->frame->linesize[0],
+ packet->frame->data[1], packet->frame->linesize[1],
+ packet->frame->data[2], packet->frame->linesize[2]);
+ break;
+ default:
+ SDL_UpdateTexture(
+ texture, NULL,
+ packet->frame->data[0],
+ packet->frame->linesize[0]);
+ break;
+ }
+
+ // Advance buffer, and free the decoded frame.
+ Kit_AdvanceDecoderOutput(dec);
+ dec->clock_pos = packet->pts;
+ free_out_video_packet_cb(packet);
+
+ return 0;
+}
diff --git a/src/kiterror.c b/src/kiterror.c index 2c87414..9ed0081 100644 --- a/src/kiterror.c +++ b/src/kiterror.c @@ -1,11 +1,11 @@ -#include "kitchensink/kitchensink.h" - #include <stdlib.h> #include <stdarg.h> #include <stdio.h> #include <stdbool.h> #include <assert.h> +#include "kitchensink/kitchensink.h" + #define KIT_ERRBUFSIZE 1024 static char _error_available = false; diff --git a/src/kitlib.c b/src/kitlib.c index e5f1277..9cfd13e 100644 --- a/src/kitlib.c +++ b/src/kitlib.c @@ -1,35 +1,75 @@ +#include <assert.h> +#include <SDL_loadso.h> + +#include <libavformat/avformat.h> + +#include "kitchensink/internal/utils/kitlog.h" #include "kitchensink/kitchensink.h" #include "kitchensink/internal/kitlibstate.h" -#include <libavformat/avformat.h> -#include <ass/ass.h> -#include <assert.h> -// No-op -void _libass_msg_callback(int level, const char *fmt, va_list va, void *data) {} +static void _libass_msg_callback(int level, const char *fmt, va_list va, void *data) {} + +static int max(int a, int b) { return a > b ? a : b; } +static int min(int a, int b) { return a < b ? a : b; } + +int Kit_InitASS(Kit_LibraryState *state) { +#ifdef USE_DYNAMIC_LIBASS + state->ass_so_handle = SDL_LoadObject(DYNAMIC_LIBASS_NAME); + if(state->ass_so_handle == NULL) { + Kit_SetError("Unable to load ASS library"); + return 1; + } + load_libass(state->ass_so_handle); +#endif + state->libass_handle = ass_library_init(); + state->thread_count = 1; + state->font_hinting = KIT_FONT_HINTING_NONE; + state->video_buf_frames = 3; + state->audio_buf_frames = 64; + state->subtitle_buf_frames = 64; + ass_set_message_cb(state->libass_handle, _libass_msg_callback, NULL); + return 0; +} + +void Kit_CloseASS(Kit_LibraryState *state) { + ass_library_done(state->libass_handle); + state->libass_handle = NULL; +#ifdef USE_DYNAMIC_LIBASS + SDL_UnloadObject(state->ass_so_handle); + state->ass_so_handle = NULL; +#endif +} int Kit_Init(unsigned int flags) { Kit_LibraryState *state = Kit_GetLibraryState(); if(state->init_flags != 0) { - Kit_SetError("Kitchensink is already initialized."); - return 1; + Kit_SetError("SDL_kitchensink is already initialized"); + goto exit_0; } + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) + av_register_all(); +#endif + if(flags & KIT_INIT_NETWORK) { avformat_network_init(); } - if(flags & KIT_INIT_FORMATS) { - av_register_all(); + if(flags & KIT_INIT_ASS) { + if(Kit_InitASS(state) != 0) { + Kit_SetError("Failed to initialize libass"); + goto exit_1; + } } state->init_flags = flags; + return 0; - // Init libass - state->libass_handle = ass_library_init(); +exit_1: + avformat_network_deinit(); - // Make libass message spam go away - ass_set_message_cb(state->libass_handle, _libass_msg_callback, NULL); - - return 0; +exit_0: + return 1; } void Kit_Quit() { @@ -38,9 +78,49 @@ void Kit_Quit() { if(state->init_flags & KIT_INIT_NETWORK) { avformat_network_deinit(); } + if(state->init_flags & KIT_INIT_ASS) { + Kit_CloseASS(state); + } state->init_flags = 0; +} - ass_library_done(state->libass_handle); +void Kit_SetHint(Kit_HintType type, int value) { + Kit_LibraryState *state = Kit_GetLibraryState(); + switch(type) { + case KIT_HINT_THREAD_COUNT: + state->thread_count = max(value, 1); + break; + case KIT_HINT_FONT_HINTING: + state->font_hinting = max(min(value, KIT_FONT_HINTING_COUNT), 0); + break; + case KIT_HINT_VIDEO_BUFFER_FRAMES: + state->video_buf_frames = min(value, 1); + break; + case KIT_HINT_AUDIO_BUFFER_FRAMES: + state->audio_buf_frames = min(value, 1); + break; + case KIT_HINT_SUBTITLE_BUFFER_FRAMES: + state->subtitle_buf_frames = min(value, 1); + break; + } +} + +int Kit_GetHint(Kit_HintType type) { + Kit_LibraryState *state = Kit_GetLibraryState(); + switch(type) { + case KIT_HINT_THREAD_COUNT: + return state->thread_count; + case KIT_HINT_FONT_HINTING: + return state->font_hinting; + case KIT_HINT_VIDEO_BUFFER_FRAMES: + return state->video_buf_frames; + case KIT_HINT_AUDIO_BUFFER_FRAMES: + return state->audio_buf_frames; + case KIT_HINT_SUBTITLE_BUFFER_FRAMES: + return state->subtitle_buf_frames; + default: + return 0; + } } void Kit_GetVersion(Kit_Version *version) { diff --git a/src/kitlist.c b/src/kitlist.c deleted file mode 100644 index b6d861c..0000000 --- a/src/kitlist.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "kitchensink/internal/kitlist.h" - -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> - -Kit_List* Kit_CreateList(unsigned int size, Kit_ListFreeCallback free_cb) { - Kit_List *m = calloc(1, sizeof(Kit_List)); - if(m == NULL) { - return NULL; - } - m->size = size; - m->free_cb = free_cb; - m->data = calloc(size, sizeof(void*)); - if(m->data == NULL) { - free(m); - return NULL; - } - return m; -} - -void Kit_DestroyList(Kit_List *list) { - if(list == NULL) return; - Kit_ClearList(list); - free(list->data); - free(list); -} - -void Kit_ClearList(Kit_List *list) { - assert(list != NULL); - for(unsigned int i = 0; i < list->size; i++) { - if(list->data[i] != NULL) { - list->free_cb(list->data[i]); - list->data[i] = NULL; - } - } - list->length = 0; -} - -void Kit_RemoveFromList(Kit_List *list, unsigned int iterator) { - assert(list != NULL); - list->free_cb(list->data[iterator-1]); - list->data[iterator-1] = NULL; - list->length--; -} - -void* Kit_IterateList(const Kit_List *list, unsigned int *iterator) { - assert(list != NULL); - assert(iterator != NULL); - while((*iterator) < list->size) { - void *ptr = list->data[(*iterator)]; - *iterator += 1; - if(ptr != NULL) { - return ptr; - } - } - return NULL; -} - -int Kit_WriteList(Kit_List *list, void *ptr) { - assert(list != NULL); - assert(ptr != NULL); - if(list->length >= list->size) { - return 1; - } - for(unsigned int i = 0; i < list->size; i++) { - if(list->data[i] == NULL) { - list->data[i] = ptr; - list->length++; - return 0; - } - } - return 1; -} - -int Kit_GetListLength(const Kit_List *list) { - assert(list != NULL); - return list->length; -} diff --git a/src/kitplayer.c b/src/kitplayer.c index 9f8a963..5220945 100644 --- a/src/kitplayer.c +++ b/src/kitplayer.c @@ -1,829 +1,104 @@ -#include "kitchensink/kitplayer.h" -#include "kitchensink/kiterror.h" -#include "kitchensink/internal/kitbuffer.h" -#include "kitchensink/internal/kitringbuffer.h" -#include "kitchensink/internal/kitlist.h" -#include "kitchensink/internal/kitlibstate.h" - -#include <libavcodec/avcodec.h> -#include <libavformat/avformat.h> -#include <libswscale/swscale.h> -#include <libswresample/swresample.h> -#include <libavutil/pixfmt.h> -#include <libavutil/time.h> -#include <libavutil/samplefmt.h> -#include <libavutil/avstring.h> -#include <libavutil/imgutils.h> - -#include <SDL2/SDL.h> -#include <ass/ass.h> - -#include <stdlib.h> -#include <string.h> #include <assert.h> -#include <math.h> -#define __STDC_FORMAT_MACROS -#include <inttypes.h> - -// For compatibility -#ifndef ASS_FONTPROVIDER_AUTODETECT -#define ASS_FONTPROVIDER_AUTODETECT 1 -#endif - -// Threshold is in seconds -#define VIDEO_SYNC_THRESHOLD 0.01 -#define AUDIO_SYNC_THRESHOLD 0.05 - -// Buffersizes -#define KIT_VBUFFERSIZE 3 -#define KIT_ABUFFERSIZE 64 -#define KIT_CBUFFERSIZE 8 -#define KIT_SBUFFERSIZE 512 - -typedef enum Kit_ControlPacketType { - KIT_CONTROL_SEEK, - KIT_CONTROL_FLUSH -} Kit_ControlPacketType; - -typedef struct Kit_VideoPacket { - double pts; - AVFrame *frame; -} Kit_VideoPacket; - -typedef struct Kit_AudioPacket { - double pts; - size_t original_size; - Kit_RingBuffer *rb; -} Kit_AudioPacket; - -typedef struct Kit_ControlPacket { - Kit_ControlPacketType type; - double value1; -} Kit_ControlPacket; - -typedef struct Kit_SubtitlePacket { - double pts_start; - double pts_end; - SDL_Rect *rect; - SDL_Surface *surface; - SDL_Texture *texture; -} Kit_SubtitlePacket; - -static int _InitCodecs(Kit_Player *player, const Kit_Source *src) { - assert(player != NULL); - assert(src != NULL); - AVCodecContext *acodec_ctx = NULL; - AVCodecContext *vcodec_ctx = NULL; - AVCodecContext *scodec_ctx = NULL; - AVCodec *acodec = NULL; - AVCodec *vcodec = NULL; - AVCodec *scodec = NULL; - AVFormatContext *format_ctx = (AVFormatContext *)src->format_ctx; - - // Make sure index seems correct - if(src->astream_idx >= (int)format_ctx->nb_streams) { - Kit_SetError("Invalid audio stream index: %d", src->astream_idx); - goto exit_0; - } else if(src->astream_idx >= 0) { - // Find audio decoder - acodec = avcodec_find_decoder(format_ctx->streams[src->astream_idx]->codec->codec_id); - if(!acodec) { - Kit_SetError("No suitable audio decoder found"); - goto exit_0; - } - - // Copy the original audio codec context - acodec_ctx = avcodec_alloc_context3(acodec); - if(avcodec_copy_context(acodec_ctx, format_ctx->streams[src->astream_idx]->codec) != 0) { - Kit_SetError("Unable to copy audio codec context"); - goto exit_0; - } - - // Create an audio decoder context - if(avcodec_open2(acodec_ctx, acodec, NULL) < 0) { - Kit_SetError("Unable to allocate audio codec context"); - goto exit_1; - } - } - - // Make sure index seems correct - if(src->vstream_idx >= (int)format_ctx->nb_streams) { - Kit_SetError("Invalid video stream index: %d", src->vstream_idx); - goto exit_2; - } else if(src->vstream_idx >= 0) { - // Find video decoder - vcodec = avcodec_find_decoder(format_ctx->streams[src->vstream_idx]->codec->codec_id); - if(!vcodec) { - Kit_SetError("No suitable video decoder found"); - goto exit_2; - } - - // Copy the original video codec context - vcodec_ctx = avcodec_alloc_context3(vcodec); - if(avcodec_copy_context(vcodec_ctx, format_ctx->streams[src->vstream_idx]->codec) != 0) { - Kit_SetError("Unable to copy video codec context"); - goto exit_2; - } - - // Create a video decoder context - if(avcodec_open2(vcodec_ctx, vcodec, NULL) < 0) { - Kit_SetError("Unable to allocate video codec context"); - goto exit_3; - } - } - - if(src->sstream_idx >= (int)format_ctx->nb_streams) { - Kit_SetError("Invalid subtitle stream index: %d", src->sstream_idx); - goto exit_2; - } else if(src->sstream_idx >= 0) { - // Find subtitle decoder - scodec = avcodec_find_decoder(format_ctx->streams[src->sstream_idx]->codec->codec_id); - if(!scodec) { - Kit_SetError("No suitable subtitle decoder found"); - goto exit_4; - } - - // Copy the original subtitle codec context - scodec_ctx = avcodec_alloc_context3(scodec); - if(avcodec_copy_context(scodec_ctx, format_ctx->streams[src->sstream_idx]->codec) != 0) { - Kit_SetError("Unable to copy subtitle codec context"); - goto exit_4; - } - - // Create a subtitle decoder context - if(avcodec_open2(scodec_ctx, scodec, NULL) < 0) { - Kit_SetError("Unable to allocate subtitle codec context"); - goto exit_5; - } - } - - player->acodec_ctx = acodec_ctx; - player->vcodec_ctx = vcodec_ctx; - player->scodec_ctx = scodec_ctx; - player->src = src; - return 0; - -exit_5: - avcodec_free_context(&scodec_ctx); -exit_4: - avcodec_close(vcodec_ctx); -exit_3: - avcodec_free_context(&vcodec_ctx); -exit_2: - avcodec_close(acodec_ctx); -exit_1: - avcodec_free_context(&acodec_ctx); -exit_0: - return 1; -} - -static int reset_libass_track(Kit_Player *player) { - AVCodecContext *scodec_ctx = player->scodec_ctx; - - if(scodec_ctx == NULL) { - return 0; - } - - // Flush libass track events - ass_flush_events(player->ass_track); - return 0; -} +#include <SDL.h> -static void _FindPixelFormat(enum AVPixelFormat fmt, unsigned int *out_fmt) { - switch(fmt) { - case AV_PIX_FMT_YUV420P9: - case AV_PIX_FMT_YUV420P10: - case AV_PIX_FMT_YUV420P12: - case AV_PIX_FMT_YUV420P14: - case AV_PIX_FMT_YUV420P16: - case AV_PIX_FMT_YUV420P: - *out_fmt = SDL_PIXELFORMAT_YV12; - break; - case AV_PIX_FMT_YUYV422: - *out_fmt = SDL_PIXELFORMAT_YUY2; - break; - case AV_PIX_FMT_UYVY422: - *out_fmt = SDL_PIXELFORMAT_UYVY; - break; - default: - *out_fmt = SDL_PIXELFORMAT_ABGR8888; - break; - } -} - -static void _FindAudioFormat(enum AVSampleFormat fmt, int *bytes, bool *is_signed, unsigned int *format) { - switch(fmt) { - case AV_SAMPLE_FMT_U8: - *bytes = 1; - *is_signed = false; - *format = AUDIO_U8; - break; - case AV_SAMPLE_FMT_S16: - *bytes = 2; - *is_signed = true; - *format = AUDIO_S16SYS; - break; - case AV_SAMPLE_FMT_S32: - *bytes = 4; - *is_signed = true; - *format = AUDIO_S32SYS; - break; - default: - *bytes = 2; - *is_signed = true; - *format = AUDIO_S16SYS; - break; - } -} - -static enum AVPixelFormat _FindAVPixelFormat(unsigned int fmt) { - switch(fmt) { - case SDL_PIXELFORMAT_IYUV: return AV_PIX_FMT_YUV420P; - case SDL_PIXELFORMAT_YV12: return AV_PIX_FMT_YUV420P; - case SDL_PIXELFORMAT_YUY2: return AV_PIX_FMT_YUYV422; - case SDL_PIXELFORMAT_UYVY: return AV_PIX_FMT_UYVY422; - case SDL_PIXELFORMAT_ARGB8888: return AV_PIX_FMT_BGRA; - case SDL_PIXELFORMAT_ABGR8888: return AV_PIX_FMT_RGBA; - default: - return AV_PIX_FMT_NONE; - } -} - -static enum AVSampleFormat _FindAVSampleFormat(int format) { - switch(format) { - case AUDIO_U8: return AV_SAMPLE_FMT_U8; - case AUDIO_S16SYS: return AV_SAMPLE_FMT_S16; - case AUDIO_S32SYS: return AV_SAMPLE_FMT_S32; - default: - return AV_SAMPLE_FMT_NONE; - } -} - -static unsigned int _FindAVChannelLayout(int channels) { - switch(channels) { - case 1: return AV_CH_LAYOUT_MONO; - case 2: return AV_CH_LAYOUT_STEREO; - case 4: return AV_CH_LAYOUT_QUAD; - case 6: return AV_CH_LAYOUT_5POINT1; - default: return AV_CH_LAYOUT_STEREO_DOWNMIX; - } -} - -static Kit_VideoPacket* _CreateVideoPacket(AVFrame *frame, double pts) { - Kit_VideoPacket *p = calloc(1, sizeof(Kit_VideoPacket)); - p->frame = frame; - p->pts = pts; - return p; -} - -static void _FreeVideoPacket(void *ptr) { - Kit_VideoPacket *packet = ptr; - av_freep(&packet->frame->data[0]); - av_frame_free(&packet->frame); - free(packet); -} - -static Kit_AudioPacket* _CreateAudioPacket(const char* data, size_t len, double pts) { - Kit_AudioPacket *p = calloc(1, sizeof(Kit_AudioPacket)); - p->rb = Kit_CreateRingBuffer(len); - Kit_WriteRingBuffer(p->rb, data, len); - p->pts = pts; - return p; -} - -static void _FreeAudioPacket(void *ptr) { - Kit_AudioPacket *packet = ptr; - Kit_DestroyRingBuffer(packet->rb); - free(packet); -} - -static Kit_ControlPacket* _CreateControlPacket(Kit_ControlPacketType type, double value1) { - Kit_ControlPacket *p = calloc(1, sizeof(Kit_ControlPacket)); - p->type = type; - p->value1 = value1; - return p; -} - -static void _FreeControlPacket(void *ptr) { - Kit_ControlPacket *packet = ptr; - free(packet); -} - - -static Kit_SubtitlePacket* _CreateSubtitlePacket(double pts_start, double pts_end, SDL_Rect *rect, SDL_Surface *surface) { - Kit_SubtitlePacket *p = calloc(1, sizeof(Kit_SubtitlePacket)); - p->pts_start = pts_start; - p->pts_end = pts_end; - p->surface = surface; - p->rect = rect; - p->texture = NULL; // Cached texture - return p; -} - -static void _FreeSubtitlePacket(void *ptr) { - Kit_SubtitlePacket *packet = ptr; - SDL_FreeSurface(packet->surface); - if(packet->texture) { - SDL_DestroyTexture(packet->texture); - } - free(packet->rect); - free(packet); -} - -static double _GetSystemTime() { - return (double)av_gettime() / 1000000.0; -} - -static void _HandleVideoPacket(Kit_Player *player, AVPacket *packet) { - assert(player != NULL); - assert(packet != NULL); - - int frame_finished; - AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx; - AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx; - AVFrame *iframe = player->tmp_vframe; - - while(packet->size > 0) { - int len = avcodec_decode_video2(vcodec_ctx, player->tmp_vframe, &frame_finished, packet); - if(len < 0) { - return; - } - - if(frame_finished) { - // Target frame - AVFrame *oframe = av_frame_alloc(); - av_image_alloc( - oframe->data, - oframe->linesize, - vcodec_ctx->width, - vcodec_ctx->height, - _FindAVPixelFormat(player->vformat.format), - 1); - - // Scale from source format to target format, don't touch the size - sws_scale( - (struct SwsContext *)player->sws, - (const unsigned char * const *)iframe->data, - iframe->linesize, - 0, - vcodec_ctx->height, - oframe->data, - oframe->linesize); - - // Get pts - double pts = 0; - if(packet->dts != AV_NOPTS_VALUE) { - pts = av_frame_get_best_effort_timestamp(player->tmp_vframe); - pts *= av_q2d(fmt_ctx->streams[player->src->vstream_idx]->time_base); - } - - // Just seeked, set sync clock & pos. - if(player->seek_flag == 1) { - player->vclock_pos = pts; - player->clock_sync = _GetSystemTime() - pts; - player->seek_flag = 0; - } - - // Lock, write to audio buffer, unlock - Kit_VideoPacket *vpacket = _CreateVideoPacket(oframe, pts); - bool done = false; - if(SDL_LockMutex(player->vmutex) == 0) { - if(Kit_WriteBuffer((Kit_Buffer*)player->vbuffer, vpacket) == 0) { - done = true; - } - SDL_UnlockMutex(player->vmutex); - } - - // Unable to write packet, free it. - if(!done) { - _FreeVideoPacket(vpacket); - } - } - packet->size -= len; - packet->data += len; - } -} +#include "kitchensink/kitplayer.h" +#include "kitchensink/kiterror.h" +#include "kitchensink/internal/kitlibstate.h" +#include "kitchensink/internal/video/kitvideo.h" +#include "kitchensink/internal/audio/kitaudio.h" +#include "kitchensink/internal/subtitle/kitsubtitle.h" +#include "kitchensink/internal/utils/kithelpers.h" +#include "kitchensink/internal/utils/kitlog.h" + +enum DecoderIndex { + KIT_VIDEO_DEC = 0, + KIT_AUDIO_DEC, + KIT_SUBTITLE_DEC, + KIT_DEC_COUNT +}; -static void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) { +// Return 0 if stream is good but nothing else to do for now +// Return -1 if there may still work to be done +// Return 1 if there was an error or stream end +static int _DemuxStream(const Kit_Player *player) { assert(player != NULL); - assert(packet != NULL); - - int frame_finished; - int len, len2; - int dst_linesize; - int dst_nb_samples, dst_bufsize; - unsigned char **dst_data; - AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx; - AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx; - struct SwrContext *swr = (struct SwrContext *)player->swr; - AVFrame *aframe = (AVFrame*)player->tmp_aframe; - - while(packet->size > 0) { - len = avcodec_decode_audio4(acodec_ctx, aframe, &frame_finished, packet); - if(len < 0) { - return; - } - - if(frame_finished) { - dst_nb_samples = av_rescale_rnd( - aframe->nb_samples, - player->aformat.samplerate, - acodec_ctx->sample_rate, - AV_ROUND_UP); - - av_samples_alloc_array_and_samples( - &dst_data, - &dst_linesize, - player->aformat.channels, - dst_nb_samples, - _FindAVSampleFormat(player->aformat.format), - 0); - - len2 = swr_convert( - swr, - dst_data, - aframe->nb_samples, - (const unsigned char **)aframe->extended_data, - aframe->nb_samples); - - dst_bufsize = av_samples_get_buffer_size( - &dst_linesize, - player->aformat.channels, - len2, - _FindAVSampleFormat(player->aformat.format), 1); - - // Get pts - double pts = 0; - if(packet->dts != AV_NOPTS_VALUE) { - pts = av_frame_get_best_effort_timestamp(player->tmp_aframe); - pts *= av_q2d(fmt_ctx->streams[player->src->astream_idx]->time_base); - } - - // Just seeked, set sync clock & pos. - if(player->seek_flag == 1) { - player->vclock_pos = pts; - player->clock_sync = _GetSystemTime() - pts; - player->seek_flag = 0; - } - - // Lock, write to audio buffer, unlock - Kit_AudioPacket *apacket = _CreateAudioPacket((char*)dst_data[0], (size_t)dst_bufsize, pts); - bool done = false; - if(SDL_LockMutex(player->amutex) == 0) { - if(Kit_WriteBuffer((Kit_Buffer*)player->abuffer, apacket) == 0) { - done = true; - } - SDL_UnlockMutex(player->amutex); - } - - // Couldn't write packet, free memory - if(!done) { - _FreeAudioPacket(apacket); - } - - av_freep(&dst_data[0]); - av_freep(&dst_data); - } + AVFormatContext *format_ctx = player->src->format_ctx; - packet->size -= len; - packet->data += len; - } -} - -static void _HandleBitmapSubtitle(Kit_SubtitlePacket** spackets, int *n, Kit_Player *player, double pts, AVSubtitle *sub, AVSubtitleRect *rect) { - if(rect->nb_colors == 256) { - // Paletted image based subtitles. Convert and set palette. - SDL_Surface *s = SDL_CreateRGBSurfaceFrom( - rect->data[0], - rect->w, rect->h, 8, - rect->linesize[0], - 0, 0, 0, 0); - - SDL_SetPaletteColors(s->format->palette, (SDL_Color*)rect->data[1], 0, 256); - - Uint32 rmask, gmask, bmask, amask; - #if SDL_BYTEORDER == SDL_BIG_ENDIAN - rmask = 0xff000000; - gmask = 0x00ff0000; - bmask = 0x0000ff00; - amask = 0x000000ff; - #else - rmask = 0x000000ff; - gmask = 0x0000ff00; - bmask = 0x00ff0000; - amask = 0xff000000; - #endif - SDL_Surface *tmp = SDL_CreateRGBSurface( - 0, rect->w, rect->h, 32, - rmask, gmask, bmask, amask); - SDL_BlitSurface(s, NULL, tmp, NULL); - SDL_FreeSurface(s); - - SDL_Rect *dst_rect = malloc(sizeof(SDL_Rect)); - dst_rect->x = rect->x; - dst_rect->y = rect->y; - dst_rect->w = rect->w; - dst_rect->h = rect->h; - - double start = pts + (sub->start_display_time / 1000.0f); - double end = -1; - if(sub->end_display_time < UINT_MAX) { - end = pts + (sub->end_display_time / 1000.0f); - } - - spackets[(*n)++] = _CreateSubtitlePacket(start, end, dst_rect, tmp); - } -} - -static void _ProcessAssSubtitleRect(Kit_Player *player, AVSubtitleRect *rect) { - ass_process_data((ASS_Track*)player->ass_track, rect->ass, strlen(rect->ass)); -} - -static void _ProcessAssImage(SDL_Surface *surface, const ASS_Image *img) { - int x, y; - // libass headers claim img->color is RGBA, but the alpha is 0. - unsigned char r = ((img->color) >> 24) & 0xFF; - unsigned char g = ((img->color) >> 16) & 0xFF; - unsigned char b = ((img->color) >> 8) & 0xFF; - unsigned char *src = img->bitmap; - unsigned char *dst = (unsigned char*)surface->pixels; - - for(y = 0; y < img->h; y++) { - for(x = 0; x < img->w; x++) { - dst[x * 4 + 0] = r; - dst[x * 4 + 1] = g; - dst[x * 4 + 2] = b; - dst[x * 4 + 3] = src[x]; - } - src += img->stride; - dst += surface->pitch; - } -} - -static void _HandleAssSubtitle(Kit_SubtitlePacket** spackets, int *n, Kit_Player *player, double pts, AVSubtitle *sub) { - double start = pts + (sub->start_display_time / 1000.0f); - double end = pts + (sub->end_display_time / 1000.0f); - - // Process current chunk of data - unsigned int now = start * 1000; - int change = 0; - ASS_Image *images = ass_render_frame((ASS_Renderer*)player->ass_renderer, (ASS_Track*)player->ass_track, now, &change); - - // Convert to SDL_Surfaces - if(change > 0) { - ASS_Image *now = images; - if(now != NULL) { - do { - Uint32 rmask, gmask, bmask, amask; - #if SDL_BYTEORDER == SDL_BIG_ENDIAN - rmask = 0xff000000; - gmask = 0x00ff0000; - bmask = 0x0000ff00; - amask = 0x000000ff; - #else - rmask = 0x000000ff; - gmask = 0x0000ff00; - bmask = 0x00ff0000; - amask = 0xff000000; - #endif - SDL_Surface *tmp = SDL_CreateRGBSurface( - 0, now->w, now->h, 32, - rmask, gmask, bmask, amask); - - _ProcessAssImage(tmp, now); - - SDL_Rect *dst_rect = malloc(sizeof(SDL_Rect)); - dst_rect->x = now->dst_x; - dst_rect->y = now->dst_y; - dst_rect->w = now->w; - dst_rect->h = now->h; - - spackets[(*n)++] = _CreateSubtitlePacket(start, end, dst_rect, tmp); - } while((now = now->next) != NULL); - } + // If any buffer is full, just stop here for now. + // Since we don't know what kind of data is going to come out of av_read_frame, we really + // want to make sure we are prepared for everything :) + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_Decoder *dec = player->decoders[i]; + if(dec == NULL) + continue; + if(!Kit_CanWriteDecoderInput(dec)) + return 0; } -} - -static void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) { - assert(player != NULL); - assert(packet != NULL); - - int frame_finished; - int len; - AVCodecContext *scodec_ctx = (AVCodecContext*)player->scodec_ctx; - AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx; - Kit_SubtitlePacket *tmp = NULL; - unsigned int it; - AVSubtitle sub; - memset(&sub, 0, sizeof(AVSubtitle)); - - if(packet->size > 0) { - len = avcodec_decode_subtitle2(scodec_ctx, &sub, &frame_finished, packet); - if(len < 0) { - return; - } - - if(frame_finished) { - // Get pts - double pts = 0; - if(packet->dts != AV_NOPTS_VALUE) { - pts = packet->pts; - pts *= av_q2d(fmt_ctx->streams[player->src->sstream_idx]->time_base); - } - // Convert subtitles to SDL_Surface and create a packet - Kit_SubtitlePacket *spackets[KIT_SBUFFERSIZE]; - memset(spackets, 0, sizeof(Kit_SubtitlePacket*) * KIT_SBUFFERSIZE); - - int n = 0; - bool has_ass = false; - for(int r = 0; r < sub.num_rects; r++) { - switch(sub.rects[r]->type) { - case SUBTITLE_BITMAP: - _HandleBitmapSubtitle(spackets, &n, player, pts, &sub, sub.rects[r]); - break; - case SUBTITLE_ASS: - _ProcessAssSubtitleRect(player, sub.rects[r]); - has_ass = true; - break; - case SUBTITLE_TEXT: - break; - case SUBTITLE_NONE: - break; - } - } - - // Process libass content - if(has_ass) { - _HandleAssSubtitle(spackets, &n, player, pts, &sub); - } - - // Lock, write to subtitle buffer, unlock - if(SDL_LockMutex(player->smutex) == 0) { - if(has_ass) { - Kit_ClearList((Kit_List*)player->sbuffer); - } else { - // Clear out old subtitles that should only be valid until next (this) subtitle - it = 0; - while((tmp = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) { - if(tmp->pts_end < 0) { - Kit_RemoveFromList((Kit_List*)player->sbuffer, it); - } - } - } - - // Add new subtitle - for(int i = 0; i < KIT_SBUFFERSIZE; i++) { - Kit_SubtitlePacket *spacket = spackets[i]; - if(spacket != NULL) { - if(Kit_WriteList((Kit_List*)player->sbuffer, spacket) == 0) { - spackets[i] = NULL; - } - } - } - - // Unlock subtitle buffer - SDL_UnlockMutex(player->smutex); - } - - // Couldn't write packet, free memory - for(int i = 0; i < KIT_SBUFFERSIZE; i++) { - if(spackets[i] != NULL) { - _FreeSubtitlePacket(spackets[i]); - } - } - } + // Attempt to read frame. Just return here if it fails. + AVPacket *packet = av_packet_alloc(); + if(av_read_frame(format_ctx, packet) < 0) { + av_packet_free(&packet); + return 1; } -} -static void _HandlePacket(Kit_Player *player, AVPacket *packet) { // Check if this is a packet we need to handle and pass it on - if(player->vcodec_ctx != NULL && packet->stream_index == player->src->vstream_idx) { - _HandleVideoPacket(player, packet); - } - else if(player->acodec_ctx != NULL && packet->stream_index == player->src->astream_idx) { - _HandleAudioPacket(player, packet); - } - else if(player->scodec_ctx != NULL && packet->stream_index == player->src->sstream_idx) { - _HandleSubtitlePacket(player, packet); - } -} - -static void _HandleFlushCommand(Kit_Player *player, Kit_ControlPacket *packet) { - if(player->abuffer != NULL) { - if(SDL_LockMutex(player->amutex) == 0) { - Kit_ClearBuffer((Kit_Buffer*)player->abuffer); - SDL_UnlockMutex(player->amutex); - } - } - if(player->vbuffer != NULL) { - if(SDL_LockMutex(player->vmutex) == 0) { - Kit_ClearBuffer((Kit_Buffer*)player->vbuffer); - SDL_UnlockMutex(player->vmutex); - } - } - if(player->sbuffer != NULL) { - if(SDL_LockMutex(player->smutex) == 0) { - Kit_ClearList((Kit_List*)player->sbuffer); - SDL_UnlockMutex(player->smutex); + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_Decoder *dec = player->decoders[i]; + if(dec == NULL) + continue; + if(dec->stream_index == packet->stream_index) { + Kit_WriteDecoderInput(player->decoders[i], packet); + return -1; } } - reset_libass_track(player); -} -static void _HandleSeekCommand(Kit_Player *player, Kit_ControlPacket *packet) { - AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx; - - // Find and limit absolute position - double seek = packet->value1; - double duration = Kit_GetPlayerDuration(player); - if(player->vclock_pos + seek <= 0) { - seek = -player->vclock_pos; - } - if(player->vclock_pos + seek >= duration) { - seek = duration - player->vclock_pos; - } - double absolute_pos = player->vclock_pos + seek; - int64_t seek_target = absolute_pos * AV_TIME_BASE; - - // Seek to timestamp. - avformat_seek_file(fmt_ctx, -1, INT64_MIN, seek_target, INT64_MAX, 0); - if(player->vcodec_ctx != NULL) - avcodec_flush_buffers(player->vcodec_ctx); - if(player->acodec_ctx != NULL) - avcodec_flush_buffers(player->acodec_ctx); - - // On first packet, set clock and current position - player->seek_flag = 1; + // We only get here if packet was not written to a decoder. IF that is the case, + // disregard and free the packet. + av_packet_free(&packet); + return -1; } -static void _HandleControlPacket(Kit_Player *player, Kit_ControlPacket *packet) { - switch(packet->type) { - case KIT_CONTROL_FLUSH: - _HandleFlushCommand(player, packet); - break; - case KIT_CONTROL_SEEK: - _HandleSeekCommand(player, packet); - break; +static bool _IsOutputEmpty(const Kit_Player *player) { + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_Decoder *dec = player->decoders[i]; + if(dec == NULL) + continue; + if(Kit_PeekDecoderOutput(dec)) + return false; } + return true; } -// Return 0 if stream is good but nothing else to do for now -// Return -1 if there is still work to be done -// Return 1 if there was an error or stream end -static int _UpdatePlayer(Kit_Player *player) { - assert(player != NULL); - - AVFormatContext *format_ctx = (AVFormatContext*)player->src->format_ctx; +static int _RunDecoder(Kit_Player *player) { + int got; + int ret = 0; - // Handle control queue - if(SDL_LockMutex(player->cmutex) == 0) { - Kit_ControlPacket *cpacket; - while((cpacket = (Kit_ControlPacket*)Kit_ReadBuffer(player->cbuffer)) != NULL) { - _HandleControlPacket(player, cpacket); - _FreeControlPacket(cpacket); - } - SDL_UnlockMutex(player->cmutex); + if(SDL_LockMutex(player->dec_lock) != 0) { + return ret; } - // If either buffer is full, just stop here for now. - // Since we don't know what kind of data is going to come out of av_read_frame, we really - // want to make sure we are prepared for everything :) - if(player->vcodec_ctx != NULL) { - if(SDL_LockMutex(player->vmutex) == 0) { - int ret = Kit_IsBufferFull(player->vbuffer); - SDL_UnlockMutex(player->vmutex); - if(ret == 1) { - return 0; - } - } - } - if(player->acodec_ctx != NULL) { - if(SDL_LockMutex(player->amutex) == 0) { - int ret = Kit_IsBufferFull(player->abuffer); - SDL_UnlockMutex(player->amutex); - if(ret == 1) { - return 0; - } - } + while((got = _DemuxStream(player)) == -1); + if(got == 1 && _IsOutputEmpty(player)) { + ret = 1; + goto exit; } - // Attempt to read frame. Just return here if it fails. - AVPacket packet; - if(av_read_frame(format_ctx, &packet) < 0) { - return 1; + // Run decoders for a bit + for(int i = 0; i < KIT_DEC_COUNT; i++) { + while(Kit_RunDecoder(player->decoders[i]) == 1); } - _HandlePacket(player, &packet); - av_packet_unref(&packet); - return -1; + +exit: + SDL_UnlockMutex(player->dec_lock); + return ret; } static int _DecoderThread(void *ptr) { - Kit_Player *player = (Kit_Player*)ptr; + Kit_Player *player = ptr; bool is_running = true; bool is_playing = true; - int ret; while(is_running) { if(player->state == KIT_CLOSED) { @@ -842,424 +117,142 @@ static int _DecoderThread(void *ptr) { is_playing = false; continue; } - - // Get more data from demuxer, decode. Wait a bit if there's no more work for now. - ret = _UpdatePlayer(player); - if(ret == 1) { + if(_RunDecoder(player) == 1) { player->state = KIT_STOPPED; - } else if(ret == 0) { - SDL_Delay(1); + continue; } + SDL_Delay(2); } // Just idle while waiting for work. - SDL_Delay(10); + SDL_Delay(25); } return 0; } -static const char * const font_mime[] = { - "application/x-font-ttf", - "application/x-font-truetype", - "application/x-truetype-font", - "application/x-font-opentype", - "application/vnd.ms-opentype", - "application/font-sfnt", - NULL -}; - -static bool attachment_is_font(AVStream *stream) { - AVDictionaryEntry *tag = av_dict_get(stream->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE); - if(tag) { - for(int n = 0; font_mime[n]; n++) { - if(av_strcasecmp(font_mime[n], tag->value) == 0) { - return true; - } - } - } - return false; -} - -Kit_Player* Kit_CreatePlayer(const Kit_Source *src) { +Kit_Player* Kit_CreatePlayer(const Kit_Source *src, + int video_stream_index, + int audio_stream_index, + int subtitle_stream_index, + int screen_w, + int screen_h) { assert(src != NULL); + assert(screen_w >= 0); + assert(screen_h >= 0); + + if(video_stream_index < 0 && subtitle_stream_index >= 0) { + Kit_SetError("Subtitle stream selected without video stream"); + goto exit_0; + } Kit_Player *player = calloc(1, sizeof(Kit_Player)); if(player == NULL) { Kit_SetError("Unable to allocate player"); - return NULL; - } - - AVCodecContext *acodec_ctx = NULL; - AVCodecContext *vcodec_ctx = NULL; - AVCodecContext *scodec_ctx = NULL; - - // Initialize codecs - if(_InitCodecs(player, src) != 0) { - goto error; - } - - // Init audio codec information if audio codec is initialized - acodec_ctx = (AVCodecContext*)player->acodec_ctx; - if(acodec_ctx != NULL) { - player->aformat.samplerate = acodec_ctx->sample_rate; - player->aformat.channels = acodec_ctx->channels > 2 ? 2 : acodec_ctx->channels; - player->aformat.is_enabled = true; - player->aformat.stream_idx = src->astream_idx; - _FindAudioFormat(acodec_ctx->sample_fmt, &player->aformat.bytes, &player->aformat.is_signed, &player->aformat.format); - - player->swr = swr_alloc_set_opts( - NULL, - _FindAVChannelLayout(player->aformat.channels), // Target channel layout - _FindAVSampleFormat(player->aformat.format), // Target fmt - player->aformat.samplerate, // Target samplerate - acodec_ctx->channel_layout, // Source channel layout - acodec_ctx->sample_fmt, // Source fmt - acodec_ctx->sample_rate, // Source samplerate - 0, NULL); - if(swr_init((struct SwrContext *)player->swr) != 0) { - Kit_SetError("Unable to initialize audio converter context"); - goto error; - } - - player->abuffer = Kit_CreateBuffer(KIT_ABUFFERSIZE, _FreeAudioPacket); - if(player->abuffer == NULL) { - Kit_SetError("Unable to initialize audio ringbuffer"); - goto error; - } - - player->tmp_aframe = av_frame_alloc(); - if(player->tmp_aframe == NULL) { - Kit_SetError("Unable to initialize temporary audio frame"); - goto error; - } - } - - // Initialize video codec information is initialized - vcodec_ctx = (AVCodecContext*)player->vcodec_ctx; - if(vcodec_ctx != NULL) { - player->vformat.is_enabled = true; - player->vformat.width = vcodec_ctx->width; - player->vformat.height = vcodec_ctx->height; - player->vformat.stream_idx = src->vstream_idx; - _FindPixelFormat(vcodec_ctx->pix_fmt, &player->vformat.format); - - player->sws = sws_getContext( - vcodec_ctx->width, // Source w - vcodec_ctx->height, // Source h - vcodec_ctx->pix_fmt, // Source fmt - vcodec_ctx->width, // Target w - vcodec_ctx->height, // Target h - _FindAVPixelFormat(player->vformat.format), // Target fmt - SWS_BICUBIC, - NULL, NULL, NULL); - if((struct SwsContext *)player->sws == NULL) { - Kit_SetError("Unable to initialize video converter context"); - goto error; - } - - player->vbuffer = Kit_CreateBuffer(KIT_VBUFFERSIZE, _FreeVideoPacket); - if(player->vbuffer == NULL) { - Kit_SetError("Unable to initialize video ringbuffer"); - goto error; - } - - player->tmp_vframe = av_frame_alloc(); - if(player->tmp_vframe == NULL) { - Kit_SetError("Unable to initialize temporary video frame"); - goto error; - } - } - - // Initialize subtitle codec - scodec_ctx = (AVCodecContext*)player->scodec_ctx; - if(scodec_ctx != NULL) { - player->sformat.is_enabled = true; - player->sformat.stream_idx = src->sstream_idx; - - // subtitle packet buffer - player->sbuffer = Kit_CreateList(KIT_SBUFFERSIZE, _FreeSubtitlePacket); - if(player->sbuffer == NULL) { - Kit_SetError("Unable to initialize active subtitle list"); - goto error; - } - - // Initialize libass renderer - Kit_LibraryState *state = Kit_GetLibraryState(); - player->ass_renderer = ass_renderer_init(state->libass_handle); - if(player->ass_renderer == NULL) { - Kit_SetError("Unable to initialize libass renderer"); - goto error; - } - - // Read fonts from attachment streams and give them to libass - AVFormatContext *format_ctx = player->src->format_ctx; - for (int j = 0; j < format_ctx->nb_streams; j++) { - AVStream *st = format_ctx->streams[j]; - if(st->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT && attachment_is_font(st)) { - const AVDictionaryEntry *tag = av_dict_get( - st->metadata, - "filename", - NULL, - AV_DICT_MATCH_CASE); - if(tag) { - ass_add_font( - state->libass_handle, - tag->value, - (char*)st->codec->extradata, - st->codec->extradata_size); - } - } - } - - // Init libass fonts and window frame size - ass_set_fonts(player->ass_renderer, NULL, "sans-serif", ASS_FONTPROVIDER_AUTODETECT, NULL, 1); - ass_set_frame_size(player->ass_renderer, vcodec_ctx->width, vcodec_ctx->height); - ass_set_hinting(player->ass_renderer, ASS_HINTING_NONE); - - // Initialize libass track - player->ass_track = ass_new_track(state->libass_handle); - if(player->ass_track == NULL) { - Kit_SetError("Unable to initialize libass track"); - goto error; - } - - // Set up libass track headers (ffmpeg provides these) - if(scodec_ctx->subtitle_header) { - ass_process_codec_private( - (ASS_Track*)player->ass_track, - (char*)scodec_ctx->subtitle_header, - scodec_ctx->subtitle_header_size); - } - } - - player->cbuffer = Kit_CreateBuffer(KIT_CBUFFERSIZE, _FreeControlPacket); - if(player->cbuffer == NULL) { - Kit_SetError("Unable to initialize control ringbuffer"); - goto error; + goto exit_0; } - player->vmutex = SDL_CreateMutex(); - if(player->vmutex == NULL) { - Kit_SetError("Unable to allocate video mutex"); - goto error; + // Initialize audio decoder + player->decoders[KIT_AUDIO_DEC] = Kit_CreateAudioDecoder(src, audio_stream_index); + if(player->decoders[KIT_AUDIO_DEC] == NULL && audio_stream_index >= 0) { + goto exit_1; } - player->amutex = SDL_CreateMutex(); - if(player->amutex == NULL) { - Kit_SetError("Unable to allocate audio mutex"); - goto error; + // Initialize video decoder + player->decoders[KIT_VIDEO_DEC] = Kit_CreateVideoDecoder(src, video_stream_index); + if(player->decoders[KIT_VIDEO_DEC] == NULL && video_stream_index >= 0) { + goto exit_2; } - player->cmutex = SDL_CreateMutex(); - if(player->cmutex == NULL) { - Kit_SetError("Unable to allocate control buffer mutex"); - goto error; + // Initialize subtitle decoder. + Kit_OutputFormat output; + Kit_GetDecoderOutputFormat(player->decoders[KIT_VIDEO_DEC], &output); + player->decoders[KIT_SUBTITLE_DEC] = Kit_CreateSubtitleDecoder( + src, subtitle_stream_index, output.width, output.height, screen_w, screen_h); + if(player->decoders[KIT_SUBTITLE_DEC] == NULL && subtitle_stream_index >= 0) { + goto exit_2; } - player->smutex = SDL_CreateMutex(); - if(player->smutex == NULL) { - Kit_SetError("Unable to allocate subtitle buffer mutex"); - goto error; + // Decoder thread lock + player->dec_lock = SDL_CreateMutex(); + if(player->dec_lock == NULL) { + Kit_SetError("Unable to create a decoder thread lock mutex: %s", SDL_GetError()); + goto exit_2; } + // Decoder thread player->dec_thread = SDL_CreateThread(_DecoderThread, "Kit Decoder Thread", player); if(player->dec_thread == NULL) { Kit_SetError("Unable to create a decoder thread: %s", SDL_GetError()); - goto error; + goto exit_3; } + player->src = src; return player; -error: - if(player->amutex != NULL) { - SDL_DestroyMutex(player->amutex); - } - if(player->vmutex != NULL) { - SDL_DestroyMutex(player->vmutex); - } - if(player->cmutex != NULL) { - SDL_DestroyMutex(player->cmutex); - } - if(player->smutex != NULL) { - SDL_DestroyMutex(player->smutex); - } - if(player->tmp_aframe != NULL) { - av_frame_free((AVFrame**)&player->tmp_aframe); - } - if(player->tmp_vframe != NULL) { - av_frame_free((AVFrame**)&player->tmp_vframe); - } - - Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer); - Kit_DestroyBuffer((Kit_Buffer*)player->abuffer); - Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer); - Kit_DestroyList((Kit_List*)player->sbuffer); - - if(player->sws != NULL) { - sws_freeContext((struct SwsContext *)player->sws); - } - if(player->swr != NULL) { - swr_free((struct SwrContext **)player->swr); - } - - if(player->ass_track != NULL) { - ass_free_track((ASS_Track*)player->ass_track); - } - if(player->ass_renderer != NULL) { - ass_renderer_done((ASS_Renderer *)player->ass_renderer); - } - if(player != NULL) { - free(player); +exit_3: + SDL_DestroyMutex(player->dec_lock); +exit_2: + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_CloseDecoder(player->decoders[i]); } +exit_1: + free(player); +exit_0: return NULL; } void Kit_ClosePlayer(Kit_Player *player) { if(player == NULL) return; - // Kill the decoder thread - player->state = KIT_CLOSED; - SDL_WaitThread(player->dec_thread, NULL); - SDL_DestroyMutex(player->vmutex); - SDL_DestroyMutex(player->amutex); - SDL_DestroyMutex(player->cmutex); - SDL_DestroyMutex(player->smutex); - - // Free up converters - if(player->sws != NULL) { - sws_freeContext((struct SwsContext *)player->sws); - } - if(player->swr != NULL) { - swr_free((struct SwrContext **)&player->swr); - } - - // Free temporary frames - if(player->tmp_vframe != NULL) { - av_frame_free((AVFrame**)&player->tmp_vframe); - } - if(player->tmp_aframe != NULL) { - av_frame_free((AVFrame**)&player->tmp_aframe); + // Kill the decoder thread and mutex + if(SDL_LockMutex(player->dec_lock) == 0) { + player->state = KIT_CLOSED; + SDL_UnlockMutex(player->dec_lock); } + SDL_WaitThread(player->dec_thread, NULL); + SDL_DestroyMutex(player->dec_lock); - // Free contexts - avcodec_close((AVCodecContext*)player->acodec_ctx); - avcodec_close((AVCodecContext*)player->vcodec_ctx); - avcodec_close((AVCodecContext*)player->scodec_ctx); - avcodec_free_context((AVCodecContext**)&player->acodec_ctx); - avcodec_free_context((AVCodecContext**)&player->vcodec_ctx); - avcodec_free_context((AVCodecContext**)&player->scodec_ctx); - - // Free local audio buffers - Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer); - Kit_DestroyBuffer((Kit_Buffer*)player->abuffer); - Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer); - Kit_DestroyList((Kit_List*)player->sbuffer); - - // Free libass context - if(player->ass_track != NULL) { - ass_free_track((ASS_Track*)player->ass_track); - } - if(player->ass_renderer != NULL) { - ass_renderer_done((ASS_Renderer *)player->ass_renderer); + // Shutdown decoders + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_CloseDecoder(player->decoders[i]); } // Free the player structure itself free(player); } -int Kit_GetVideoData(Kit_Player *player, SDL_Texture *texture) { +void Kit_SetPlayerScreenSize(Kit_Player *player, int w, int h) { assert(player != NULL); + Kit_Decoder *dec = player->decoders[KIT_SUBTITLE_DEC]; + if(dec == NULL) + return; + Kit_SetSubtitleDecoderSize(dec, w, h); +} - if(player->src->vstream_idx == -1) { - return 0; - } - - assert(texture != NULL); - - // If paused or stopped, do nothing - if(player->state == KIT_PAUSED) { - return 0; - } - if(player->state == KIT_STOPPED) { - return 0; - } - - // Read a packet from buffer, if one exists. Stop here if not. - Kit_VideoPacket *packet = NULL; - Kit_VideoPacket *n_packet = NULL; - if(SDL_LockMutex(player->vmutex) == 0) { - packet = (Kit_VideoPacket*)Kit_PeekBuffer((Kit_Buffer*)player->vbuffer); - if(packet == NULL) { - SDL_UnlockMutex(player->vmutex); - return 0; - } - - // Print some data - double cur_video_ts = _GetSystemTime() - player->clock_sync; - - // Check if we want the packet - if(packet->pts > cur_video_ts + VIDEO_SYNC_THRESHOLD) { - // Video is ahead, don't show yet. - SDL_UnlockMutex(player->vmutex); - return 0; - } else if(packet->pts < cur_video_ts - VIDEO_SYNC_THRESHOLD) { - // Video is lagging, skip until we find a good PTS to continue from. - while(packet != NULL) { - Kit_AdvanceBuffer((Kit_Buffer*)player->vbuffer); - n_packet = (Kit_VideoPacket*)Kit_PeekBuffer((Kit_Buffer*)player->vbuffer); - if(n_packet == NULL) { - break; - } - _FreeVideoPacket(packet); - packet = n_packet; - if(packet->pts > cur_video_ts - VIDEO_SYNC_THRESHOLD) { - break; - } - } - } - - // Advance buffer one frame forwards - Kit_AdvanceBuffer((Kit_Buffer*)player->vbuffer); - player->vclock_pos = packet->pts; - - // Update textures as required. Handle UYV frames separately. - if(player->vformat.format == SDL_PIXELFORMAT_YV12 - || player->vformat.format == SDL_PIXELFORMAT_IYUV) - { - SDL_UpdateYUVTexture( - texture, NULL, - packet->frame->data[0], packet->frame->linesize[0], - packet->frame->data[1], packet->frame->linesize[1], - packet->frame->data[2], packet->frame->linesize[2]); - } - else { - SDL_UpdateTexture( - texture, NULL, - packet->frame->data[0], - packet->frame->linesize[0]); - } +int Kit_GetPlayerVideoStream(const Kit_Player *player) { + assert(player != NULL); + return Kit_GetDecoderStreamIndex(player->decoders[KIT_VIDEO_DEC]); +} - _FreeVideoPacket(packet); - SDL_UnlockMutex(player->vmutex); - } else { - Kit_SetError("Unable to lock video buffer mutex"); - return 1; - } +int Kit_GetPlayerAudioStream(const Kit_Player *player) { + assert(player != NULL); + return Kit_GetDecoderStreamIndex(player->decoders[KIT_AUDIO_DEC]); +} - return 0; +int Kit_GetPlayerSubtitleStream(const Kit_Player *player) { + assert(player != NULL); + return Kit_GetDecoderStreamIndex(player->decoders[KIT_SUBTITLE_DEC]); } -int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer) { +int Kit_GetPlayerVideoData(Kit_Player *player, SDL_Texture *texture) { assert(player != NULL); - // If there is no audio stream, don't bother. - if(player->src->sstream_idx == -1) { + Kit_Decoder *dec = player->decoders[KIT_VIDEO_DEC]; + if(dec == NULL) { return 0; } - assert(renderer != NULL); - // If paused or stopped, do nothing if(player->state == KIT_PAUSED) { return 0; @@ -1268,47 +261,15 @@ int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer) { return 0; } - unsigned int it; - Kit_SubtitlePacket *packet = NULL; - - // Current sync timestamp - double cur_subtitle_ts = _GetSystemTime() - player->clock_sync; - - // Read a packet from buffer, if one exists. Stop here if not. - if(SDL_LockMutex(player->smutex) == 0) { - // Check if refresh is required and remove old subtitles - it = 0; - while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) { - if(packet->pts_end >= 0 && packet->pts_end < cur_subtitle_ts) { - Kit_RemoveFromList((Kit_List*)player->sbuffer, it); - } - } - - // Render subtitle bitmaps - it = 0; - while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) { - if(packet->texture == NULL) { - packet->texture = SDL_CreateTextureFromSurface(renderer, packet->surface); - SDL_SetTextureBlendMode(packet->texture, SDL_BLENDMODE_BLEND); - } - SDL_RenderCopy(renderer, packet->texture, NULL, packet->rect); - } - - // Unlock subtitle buffer mutex. - SDL_UnlockMutex(player->smutex); - } else { - Kit_SetError("Unable to lock subtitle buffer mutex"); - return 0; - } - - return 0; + return Kit_GetVideoDecoderData(dec, texture); } -int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int cur_buf_len) { +int Kit_GetPlayerAudioData(Kit_Player *player, unsigned char *buffer, int length) { assert(player != NULL); + assert(buffer != NULL); - // If there is no audio stream, don't bother. - if(player->src->astream_idx == -1) { + Kit_Decoder *dec = player->decoders[KIT_AUDIO_DEC]; + if(dec == NULL) { return 0; } @@ -1317,8 +278,6 @@ int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int return 0; } - assert(buffer != NULL); - // If paused or stopped, do nothing if(player->state == KIT_PAUSED) { return 0; @@ -1327,158 +286,147 @@ int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int return 0; } - // Read a packet from buffer, if one exists. Stop here if not. - int ret = 0; - Kit_AudioPacket *packet = NULL; - Kit_AudioPacket *n_packet = NULL; - if(SDL_LockMutex(player->amutex) == 0) { - packet = (Kit_AudioPacket*)Kit_PeekBuffer((Kit_Buffer*)player->abuffer); - if(packet == NULL) { - SDL_UnlockMutex(player->amutex); - return 0; - } + return Kit_GetAudioDecoderData(dec, buffer, length); +} - int bytes_per_sample = player->aformat.bytes * player->aformat.channels; - double bps = bytes_per_sample * player->aformat.samplerate; - double cur_audio_ts = _GetSystemTime() - player->clock_sync + ((double)cur_buf_len / bps); - double diff = cur_audio_ts - packet->pts; - int diff_samples = fabs(diff) * player->aformat.samplerate; - - if(packet->pts > cur_audio_ts + AUDIO_SYNC_THRESHOLD) { - // Audio is ahead, fill buffer with some silence - int max_diff_samples = length / bytes_per_sample; - int max_samples = (max_diff_samples < diff_samples) ? max_diff_samples : diff_samples; - - av_samples_set_silence( - &buffer, - 0, // Offset - max_samples, - player->aformat.channels, - _FindAVSampleFormat(player->aformat.format)); - - int diff_bytes = max_samples * bytes_per_sample; - - SDL_UnlockMutex(player->amutex); - return diff_bytes; - - } else if(packet->pts < cur_audio_ts - AUDIO_SYNC_THRESHOLD) { - // Audio is lagging, skip until good pts is found - - while(1) { - Kit_AdvanceBuffer((Kit_Buffer*)player->abuffer); - n_packet = (Kit_AudioPacket*)Kit_PeekBuffer((Kit_Buffer*)player->abuffer); - if(n_packet != NULL) { - packet = n_packet; - } else { - break; - } - if(packet->pts > cur_audio_ts - AUDIO_SYNC_THRESHOLD) { - break; - } - } - } +int Kit_GetPlayerSubtitleData(Kit_Player *player, SDL_Texture *texture, SDL_Rect *sources, SDL_Rect *targets, int limit) { + assert(player != NULL); + assert(texture != NULL); + assert(sources != NULL); + assert(targets != NULL); + assert(limit >= 0); - if(length > 0) { - ret = Kit_ReadRingBuffer(packet->rb, (char*)buffer, length); - } + Kit_Decoder *dec = player->decoders[KIT_SUBTITLE_DEC]; + if(dec == NULL) { + return 0; + } - if(Kit_GetRingBufferLength(packet->rb) == 0) { - Kit_AdvanceBuffer((Kit_Buffer*)player->abuffer); - _FreeAudioPacket(packet); - } else { - double adjust = (double)ret / bps; - packet->pts += adjust; - } + // If paused, just return the current items + if(player->state == KIT_PAUSED) { + return Kit_GetSubtitleDecoderInfo(dec, texture, sources, targets, limit); + } - SDL_UnlockMutex(player->amutex); - } else { - Kit_SetError("Unable to lock audio buffer mutex"); + // If stopped, do nothing. + if(player->state == KIT_STOPPED) { return 0; } - return ret; + // Refresh texture, then refresh rects and return number of items in the texture. + Kit_GetSubtitleDecoderTexture(dec, texture); + return Kit_GetSubtitleDecoderInfo(dec, texture, sources, targets, limit); } void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) { assert(player != NULL); assert(info != NULL); - AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx; - AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx; - AVCodecContext *scodec_ctx = (AVCodecContext*)player->scodec_ctx; - - // Reset everything to 0. We might not fill all fields. - memset(info, 0, sizeof(Kit_PlayerInfo)); - - if(acodec_ctx != NULL) { - strncpy(info->acodec, acodec_ctx->codec->name, KIT_CODECMAX-1); - strncpy(info->acodec_name, acodec_ctx->codec->long_name, KIT_CODECNAMEMAX-1); - memcpy(&info->audio, &player->aformat, sizeof(Kit_AudioFormat)); + void *streams[] = {&info->video, &info->audio, &info->subtitle}; + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_Decoder *dec = player->decoders[i]; + Kit_PlayerStreamInfo *stream = streams[i]; + Kit_GetDecoderCodecInfo(dec, &stream->codec); + Kit_GetDecoderOutputFormat(dec, &stream->output); } - if(vcodec_ctx != NULL) { - strncpy(info->vcodec, vcodec_ctx->codec->name, KIT_CODECMAX-1); - strncpy(info->vcodec_name, vcodec_ctx->codec->long_name, KIT_CODECNAMEMAX-1); - memcpy(&info->video, &player->vformat, sizeof(Kit_VideoFormat)); +} + +static void _SetClockSync(Kit_Player *player) { + double sync = _GetSystemTime(); + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_SetDecoderClockSync(player->decoders[i], sync); } - if(scodec_ctx != NULL) { - strncpy(info->scodec, scodec_ctx->codec->name, KIT_CODECMAX-1); - strncpy(info->scodec_name, scodec_ctx->codec->long_name, KIT_CODECNAMEMAX-1); - memcpy(&info->subtitle, &player->sformat, sizeof(Kit_SubtitleFormat)); +} + +static void _ChangeClockSync(Kit_Player *player, double delta) { + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_ChangeDecoderClockSync(player->decoders[i], delta); } } Kit_PlayerState Kit_GetPlayerState(const Kit_Player *player) { assert(player != NULL); - return player->state; } void Kit_PlayerPlay(Kit_Player *player) { assert(player != NULL); - - if(player->state == KIT_PLAYING) { - return; - } - if(player->state == KIT_STOPPED) { - player->clock_sync = _GetSystemTime(); + double tmp; + if(SDL_LockMutex(player->dec_lock) == 0) { + switch(player->state) { + case KIT_PLAYING: + case KIT_CLOSED: + break; + case KIT_PAUSED: + tmp = _GetSystemTime() - player->pause_started; + _ChangeClockSync(player, tmp); + player->state = KIT_PLAYING; + break; + case KIT_STOPPED: + _SetClockSync(player); + player->state = KIT_PLAYING; + break; + } + SDL_UnlockMutex(player->dec_lock); } - if(player->state == KIT_PAUSED) { - player->clock_sync += _GetSystemTime() - player->pause_start; - } - player->state = KIT_PLAYING; } void Kit_PlayerStop(Kit_Player *player) { assert(player != NULL); - - if(player->state == KIT_STOPPED) { - return; + if(SDL_LockMutex(player->dec_lock) == 0) { + switch(player->state) { + case KIT_STOPPED: + case KIT_CLOSED: + break; + case KIT_PLAYING: + case KIT_PAUSED: + player->state = KIT_STOPPED; + break; + } + SDL_UnlockMutex(player->dec_lock); } - player->state = KIT_STOPPED; } void Kit_PlayerPause(Kit_Player *player) { assert(player != NULL); - - if(player->state != KIT_PLAYING) { - return; - } - player->pause_start = _GetSystemTime(); player->state = KIT_PAUSED; + player->pause_started = _GetSystemTime(); } -int Kit_PlayerSeek(Kit_Player *player, double m_time) { +int Kit_PlayerSeek(Kit_Player *player, double seek_set) { assert(player != NULL); + double position; + double duration; + int64_t seek_target; + int flags = AVSEEK_FLAG_ANY; - // Send packets to control stream - if(SDL_LockMutex(player->cmutex) == 0) { - // Flush audio and video buffers, then set seek, then unlock control queue mutex. - Kit_WriteBuffer((Kit_Buffer*)player->cbuffer, _CreateControlPacket(KIT_CONTROL_FLUSH, 0)); - Kit_WriteBuffer((Kit_Buffer*)player->cbuffer, _CreateControlPacket(KIT_CONTROL_SEEK, m_time)); - SDL_UnlockMutex(player->cmutex); - } else { - Kit_SetError("Unable to lock control queue mutex"); - return 1; + if(SDL_LockMutex(player->dec_lock) == 0) { + duration = Kit_GetPlayerDuration(player); + position = Kit_GetPlayerPosition(player); + if(seek_set <= 0) { + seek_set = 0; + } + if(seek_set >= duration) { + seek_set = duration; + } + + // Set source to timestamp + AVFormatContext *format_ctx = player->src->format_ctx; + seek_target = seek_set * AV_TIME_BASE; + if(seek_set < position) { + flags |= AVSEEK_FLAG_BACKWARD; + } + if(avformat_seek_file(format_ctx, -1, 0, seek_target, seek_target, flags) < 0) { + Kit_SetError("Unable to seek source"); + SDL_UnlockMutex(player->dec_lock); + return 1; + } else { + _ChangeClockSync(player, position - seek_set); + for(int i = 0; i < KIT_DEC_COUNT; i++) { + Kit_ClearDecoderBuffers(player->decoders[i]); + } + } + + // That's it. Unlock and continue. + SDL_UnlockMutex(player->dec_lock); } return 0; @@ -1487,12 +435,18 @@ int Kit_PlayerSeek(Kit_Player *player, double m_time) { double Kit_GetPlayerDuration(const Kit_Player *player) { assert(player != NULL); - AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx; + AVFormatContext *fmt_ctx = player->src->format_ctx; return (fmt_ctx->duration / AV_TIME_BASE); } double Kit_GetPlayerPosition(const Kit_Player *player) { assert(player != NULL); - return player->vclock_pos; + if(player->decoders[KIT_VIDEO_DEC]) { + return ((Kit_Decoder*)player->decoders[KIT_VIDEO_DEC])->clock_pos; + } + if(player->decoders[KIT_AUDIO_DEC]) { + return ((Kit_Decoder*)player->decoders[KIT_AUDIO_DEC])->clock_pos; + } + return 0; } diff --git a/src/kitsource.c b/src/kitsource.c index 075bcbd..588f829 100644 --- a/src/kitsource.c +++ b/src/kitsource.c @@ -1,13 +1,26 @@ -#include "kitchensink/kitsource.h" -#include "kitchensink/kiterror.h" +#include <stdlib.h> +#include <string.h> +#include <assert.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> +#include "kitchensink/kitsource.h" +#include "kitchensink/kiterror.h" +#include "kitchensink/internal/utils/kitlog.h" + +#define AVIO_BUF_SIZE 32768 + +static int _ScanSource(AVFormatContext *format_ctx) { + av_opt_set_int(format_ctx, "probesize", INT_MAX, 0); + av_opt_set_int(format_ctx, "analyzeduration", INT_MAX, 0); + if(avformat_find_stream_info(format_ctx, NULL) < 0) { + Kit_SetError("Unable to fetch source information"); + return 1; + } + return 0; +} Kit_Source* Kit_CreateSourceFromUrl(const char *url) { assert(url != NULL); @@ -24,19 +37,11 @@ Kit_Source* Kit_CreateSourceFromUrl(const char *url) { goto exit_0; } - av_opt_set_int(src->format_ctx, "probesize", INT_MAX, 0); - av_opt_set_int(src->format_ctx, "analyzeduration", INT_MAX, 0); - - // Fetch stream information. This may potentially take a while. - if(avformat_find_stream_info((AVFormatContext *)src->format_ctx, NULL) < 0) { - Kit_SetError("Unable to fetch source information"); + // Scan source information (may seek forwards) + if(_ScanSource(src->format_ctx)) { goto exit_1; } - // Find best streams for defaults - src->astream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO); - src->vstream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO); - src->sstream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE); return src; exit_1: @@ -46,13 +51,122 @@ exit_0: return NULL; } +Kit_Source* Kit_CreateSourceFromCustom(Kit_ReadCallback read_cb, Kit_SeekCallback seek_cb, void *userdata) { + assert(read_cb != NULL); + + Kit_Source *src = calloc(1, sizeof(Kit_Source)); + if(src == NULL) { + Kit_SetError("Unable to allocate source"); + return NULL; + } + + uint8_t *avio_buf = av_malloc(AVIO_BUF_SIZE); + if(avio_buf == NULL) { + Kit_SetError("Unable to allocate avio buffer"); + goto exit_0; + } + + AVFormatContext *format_ctx = avformat_alloc_context(); + if(format_ctx == NULL) { + Kit_SetError("Unable to allocate format context"); + goto exit_1; + } + + AVIOContext *avio_ctx = avio_alloc_context( + avio_buf, AVIO_BUF_SIZE, 0, userdata, read_cb, 0, seek_cb); + if(avio_ctx == NULL) { + Kit_SetError("Unable to allocate avio context"); + goto exit_2; + } + + // Set the format as AVIO format + format_ctx->pb = avio_ctx; + + // Attempt to open source + if(avformat_open_input(&format_ctx, "", NULL, NULL) < 0) { + Kit_SetError("Unable to open custom source"); + goto exit_3; + } + + // Scan source information (may seek forwards) + if(_ScanSource(format_ctx)) { + goto exit_4; + } + + // Set internals + src->format_ctx = format_ctx; + src->avio_ctx = avio_ctx; + return src; + +exit_4: + avformat_close_input(&format_ctx); +exit_3: + av_freep(&avio_ctx); +exit_2: + avformat_free_context(format_ctx); +exit_1: + av_freep(&avio_buf); +exit_0: + free(src); + return NULL; +} + +static int _RWReadCallback(void *userdata, uint8_t *buf, int size) { + return SDL_RWread((SDL_RWops*)userdata, buf, 1, size); +} + +static int64_t _RWGetSize(SDL_RWops *rw_ops) { + int64_t current_pos; + int64_t max_pos; + + // First, see if tell works at all, and fail with -1 if it doesn't. + current_pos = SDL_RWtell(rw_ops); + if(current_pos < 0) { + return -1; + } + + // Seek to end, get pos (this is the size), then return. + if(SDL_RWseek(rw_ops, 0, RW_SEEK_END) < 0) { + return -1; // Seek failed, never mind then + } + max_pos = SDL_RWtell(rw_ops); + SDL_RWseek(rw_ops, current_pos, RW_SEEK_SET); + return max_pos; +} + +static int64_t _RWSeekCallback(void *userdata, int64_t offset, int whence) { + int rw_whence = 0; + if(whence & AVSEEK_SIZE) + return _RWGetSize(userdata); + + if((whence & ~AVSEEK_FORCE) == SEEK_CUR) + rw_whence = RW_SEEK_CUR; + else if((whence & ~AVSEEK_FORCE) == SEEK_SET) + rw_whence = RW_SEEK_SET; + else if((whence & ~AVSEEK_FORCE) == SEEK_END) + rw_whence = RW_SEEK_END; + + return SDL_RWseek((SDL_RWops*)userdata, offset, rw_whence); +} + + +Kit_Source* Kit_CreateSourceFromRW(SDL_RWops *rw_ops) { + return Kit_CreateSourceFromCustom(_RWReadCallback, _RWSeekCallback, rw_ops); +} + void Kit_CloseSource(Kit_Source *src) { assert(src != NULL); - avformat_close_input((AVFormatContext **)&src->format_ctx); + AVFormatContext *format_ctx = src->format_ctx; + AVIOContext *avio_ctx = src->avio_ctx; + avformat_close_input(&format_ctx); + if(avio_ctx) { + av_freep(&avio_ctx->buffer); + av_freep(&avio_ctx); + } free(src); } -int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_StreamInfo *info, int index) { +int Kit_GetSourceStreamInfo(const Kit_Source *src, Kit_SourceStreamInfo *info, int index) { assert(src != NULL); assert(info != NULL); @@ -99,31 +213,6 @@ int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_StreamType type) { return ret; } -int Kit_SetSourceStream(Kit_Source *src, const Kit_StreamType type, int index) { - assert(src != NULL); - switch(type) { - case KIT_STREAMTYPE_AUDIO: src->astream_idx = index; break; - case KIT_STREAMTYPE_VIDEO: src->vstream_idx = index; break; - case KIT_STREAMTYPE_SUBTITLE: src->sstream_idx = index; break; - default: - Kit_SetError("Invalid stream type"); - return 1; - } - return 0; -} - -int Kit_GetSourceStream(const Kit_Source *src, const Kit_StreamType type) { - assert(src != NULL); - switch(type) { - case KIT_STREAMTYPE_AUDIO: return src->astream_idx; - case KIT_STREAMTYPE_VIDEO: return src->vstream_idx; - case KIT_STREAMTYPE_SUBTITLE: return src->sstream_idx; - default: - break; - } - return -1; -} - int Kit_GetSourceStreamCount(const Kit_Source *src) { assert(src != NULL); return ((AVFormatContext *)src->format_ctx)->nb_streams; diff --git a/src/kitutils.c b/src/kitutils.c index b618c07..8cf7261 100644 --- a/src/kitutils.c +++ b/src/kitutils.c @@ -1,8 +1,8 @@ +#include <SDL.h> + #include "kitchensink/kitutils.h" #include "kitchensink/kitsource.h" -#include <SDL2/SDL.h> - const char* Kit_GetSDLAudioFormatString(unsigned int type) { switch(type) { case AUDIO_S8: return "AUDIO_S8"; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index ddfe4f9..0000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -find_package(CUnit) - -add_executable(test_lib - test_lib.c - test_source.c -) - -include_directories(${CUNIT_INCLUDE_DIR} . ../include/) -if(MINGW) - target_link_libraries(test_lib mingw32) -endif() -target_link_libraries(test_lib - SDL_kitchensink_static - ${CUNIT_LIBRARIES} - ${SDL2_LIBRARIES} - ${FFMPEG_LIBRARIES} -) -add_custom_target(unittest test_lib) diff --git a/tests/data/CEP140_512kb.mp4 b/tests/data/CEP140_512kb.mp4 Binary files differdeleted file mode 100644 index 4341032..0000000 --- a/tests/data/CEP140_512kb.mp4 +++ /dev/null diff --git a/tests/test_lib.c b/tests/test_lib.c deleted file mode 100644 index 92eb058..0000000 --- a/tests/test_lib.c +++ /dev/null @@ -1,28 +0,0 @@ -#include <CUnit/CUnit.h> -#include <CUnit/Basic.h> -#include "kitchensink/kitchensink.h" - -void source_test_suite(CU_pSuite suite); - -int main(int argc, char **argv) { - CU_pSuite suite = NULL; - - Kit_Init(KIT_INIT_NETWORK|KIT_INIT_FORMATS); - - if(CU_initialize_registry() != CUE_SUCCESS) { - return CU_get_error(); - } - - suite = CU_add_suite("Source functions", NULL, NULL); - if(suite == NULL) goto end; - source_test_suite(suite); - - // Run tests - CU_basic_set_mode(CU_BRM_VERBOSE); - CU_basic_run_tests(); - -end: - CU_cleanup_registry(); - Kit_Quit(); - return CU_get_error(); -} diff --git a/tests/test_source.c b/tests/test_source.c deleted file mode 100644 index 0f70635..0000000 --- a/tests/test_source.c +++ /dev/null @@ -1,48 +0,0 @@ -#include <CUnit/CUnit.h> -#include <CUnit/Basic.h> -#include <kitchensink/kitchensink.h> - -Kit_Source *src = NULL; - -void test_Kit_CreateSourceFromUrl(void) { - CU_ASSERT_PTR_NULL(Kit_CreateSourceFromUrl("nonexistent")); - src = Kit_CreateSourceFromUrl("../../tests/data/CEP140_512kb.mp4"); - CU_ASSERT_PTR_NOT_NULL(src); -} - -void test_Kit_GetBestSourceStream(void) { - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO) == 0); - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO) == 1); - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_UNKNOWN) == -1); - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_DATA) == -1); - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_ATTACHMENT) == -1); - CU_ASSERT(Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE) == -1); -} - -void test_Kit_GetSourceStreamCount(void) { - CU_ASSERT(Kit_GetSourceStreamCount(src) == 2); -} - -void test_Kit_SetSourceStream(void) { - CU_ASSERT(Kit_SetSourceStream(src, KIT_STREAMTYPE_VIDEO, 0) == 0); - CU_ASSERT(Kit_SetSourceStream(src, KIT_STREAMTYPE_UNKNOWN, 0) == 1); -} - -void test_Kit_GetSourceStream(void) { - CU_ASSERT(Kit_GetSourceStream(src, KIT_STREAMTYPE_VIDEO) == 0); - CU_ASSERT(Kit_GetSourceStream(src, KIT_STREAMTYPE_AUDIO) == 1); - CU_ASSERT(Kit_GetSourceStream(src, KIT_STREAMTYPE_UNKNOWN) == -1); -} - -void test_Kit_CloseSource(void) { - Kit_CloseSource(src); -} - -void source_test_suite(CU_pSuite suite) { - if(CU_add_test(suite, "Kit_CreateSourceFromUrl", test_Kit_CreateSourceFromUrl) == NULL) { return; } - if(CU_add_test(suite, "Kit_GetBestSourceStream", test_Kit_GetBestSourceStream) == NULL) { return; } - if(CU_add_test(suite, "Kit_GetSourceStreamCount", test_Kit_GetSourceStreamCount) == NULL) { return; } - if(CU_add_test(suite, "Kit_SetSourceStream", test_Kit_SetSourceStream) == NULL) { return; } - if(CU_add_test(suite, "Kit_GetSourceStream", test_Kit_GetSourceStream) == NULL) { return; } - if(CU_add_test(suite, "Kit_CloseSource", test_Kit_CloseSource) == NULL) { return; } -} diff --git a/travis/ffmpeg.sh b/travis/ffmpeg.sh deleted file mode 100644 index d27c136..0000000 --- a/travis/ffmpeg.sh +++ /dev/null @@ -1,12 +0,0 @@ - -if [ ! -e "$HOME/local/lib/libavcodec.so" ]; then - wget https://www.ffmpeg.org/releases/ffmpeg-3.0.4.tar.gz -O ~/ffmpeg.tar.gz - tar xzf ~/ffmpeg.tar.gz -C ~/ - cd ~/ffmpeg-3.0.4 - export CC=gcc-5 - ./configure --prefix=$HOME/local --disable-static --enable-shared --disable-doc - make - make install -else - echo 'Using cached FFmpeg build directory.'; -fi diff --git a/travis/sdl2.sh b/travis/sdl2.sh deleted file mode 100644 index 8b914fb..0000000 --- a/travis/sdl2.sh +++ /dev/null @@ -1,13 +0,0 @@ - -if [ ! -e "$HOME/local/lib/libSDL2.so" ]; then - wget https://www.libsdl.org/release/SDL2-2.0.4.tar.gz -O ~/SDL2.tar.gz - tar -xzvf ~/SDL2.tar.gz -C ~/ - mkdir ~/sdl-build - cd ~/sdl-build - export CC=gcc-5 - ~/SDL2-2.0.4/configure --prefix=$HOME/local - make -j2 - make install -else - echo 'Using cached SDL2 build directory.'; -fi |