├── DecoderPluginVLC ├── libvvcdecoder_plugin.dll ├── vvc_nal.h ├── README.md ├── startcode_helper.h ├── CMakeLists.txt ├── packetizer_helper.h ├── vvc_demux.cpp ├── vvc_packetizer.cpp └── libVVCDecoder_plugin.cpp ├── TsDemuxPluginVLC ├── libvvctsdemux_plugin.dll ├── config.h ├── README.md └── CMakeLists.txt ├── README.md └── LICENSE /DecoderPluginVLC/libvvcdecoder_plugin.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InterDigitalInc/VTMDecoder_VLCPlugin/HEAD/DecoderPluginVLC/libvvcdecoder_plugin.dll -------------------------------------------------------------------------------- /TsDemuxPluginVLC/libvvctsdemux_plugin.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InterDigitalInc/VTMDecoder_VLCPlugin/HEAD/TsDemuxPluginVLC/libvvctsdemux_plugin.dll -------------------------------------------------------------------------------- /TsDemuxPluginVLC/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _DVBPSI_CONFIG_H_ 2 | #define _DVBPSI_CONFIG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined(_MSC_VER) 9 | #define NOMINMAX 10 | #include 11 | #include 12 | typedef SSIZE_T ssize_t; 13 | #define strcasecmp(s1, s2) _stricmp(s1, s2) 14 | #define strncasecmp(s1, s2, s3) _strnicmp(s1, s2, s3) 15 | #define asprintf(...) false // TODO 16 | #define timegm( x ) 4 // TODO 17 | #define __attribute__(x) 18 | static char* strndup(char const* s, size_t n) 19 | { 20 | size_t len = strnlen(s, n); 21 | char* newChar = (char*)malloc(len + 1); 22 | if (!newChar) 23 | return 0; 24 | newChar[len] = '\0'; 25 | return (char*) memcpy(newChar, s, len); 26 | } 27 | #endif 28 | #define N_(x) x // TODO 29 | #define _(x) x // TODO 30 | 31 | #endif -------------------------------------------------------------------------------- /TsDemuxPluginVLC/README.md: -------------------------------------------------------------------------------- 1 | TS demux with a new es vvc stream format ID 2 | 3 | Modified tsDemux plugin to include the "h266" FOURCC for the VVC stream in a ts stream. 4 | 5 | ------------------- 6 | Alternative 7 | ------------------- 8 | An alternative to building a ts Demux is explicitly specifying a FOURCC "h266" for the VVC stream: 9 | use VLS with the following parameters (either command line, or in the options dialog: 10 | --ts-extra-pmt=2:7=pid:video=h266 --program=7 (with pid=pid of the VVC stream) 11 | 12 | 13 | ------------------- 14 | On WINDOWS 15 | ------------------- 16 | use CMake to build the dll 17 | 18 | Prerequisites: 19 | 20 | - VLC SDK 21 | You can download the binary release with sdk on videolan site 22 | For example 3.0.9.2 version 23 | - download and decompress for example on c:/. 24 | - modify \sdk\include\vlc\plugins\vlc_threads.h file 25 | Unless you need it (and you'll have to figure out how to compile with it) 26 | you should comment the definition of vlc_poll function 27 | 28 | - VLC sources, corresponding to the version of the SDK 29 | 30 | - DVBPSI sources 31 | you can get DVBPSI from the videolan website 32 | for example version 1.3.2. 33 | 34 | set CMake variables: 35 | - VLC_PROGRAM_DIR: ("C:/VLC") path where the dll will be copied (vlc plugin directory) 36 | - VLC_INCLUDE_DIR: ("${VLC_PROGRAM_DIR}/sdk/include/vlc/plugins") path to VLC core source files 37 | - VLC_LIB_DIR: ("${VLC_PROGRAM_DIR}/sdk/lib") path to VLC core lib files 38 | - VLC_SRC_DIR: ("vlc/src") path to VLC src dir 39 | - DVBPSI_DIR: ("dvbpsi") path to dvbpsi source dir (ex: version 1.3.2) 40 | 41 | Generate and build ! 42 | 43 | ------------------- 44 | On Linux 45 | ------------------- 46 | use the patch to add a ES format for VVC when building VLC -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | /************* VLC PLUGINS FOR THE MULTITHREADED VVC DECODER BASED ON VTM ************/ 2 | 3 | See also the README.md files inside the following subfolders for build instructions. 4 | 5 | ./DecoderPluginVLC VVC video decoder plugin making use of the VTM decoder lib 6 | ./TsDemuxPluginVLC TS demux with a new es vvc stream format ID 7 | 8 | /************* Installation and usage ************/ 9 | 10 | Tested with VLC 3.0.9.2 and 3.0.12 64-bit versions (Windows) 11 | 12 | 1. Install VLC 13 | 14 | - download VLC media player. 15 | - unpack in (for example) C:\VLC 16 | note: in order to use windows compiled binaries (which are 64-bits), the 64-bits version of VLC is required 17 | 18 | 2. Install plugin and decoder dlls 19 | 20 | - copy decoder plugin and VTM decoder dll into plugin directory: 21 | ....VLC\plugins\codec\ 22 | - to play ts files, add the ts-demux plugin libvvctsdemux_plugin.dll to: 23 | ....VLC\plugins\demux\ 24 | 25 | 3. Try to play a VVC binary file 26 | 27 | It tries to follow frame rate from HRD information if coded, otherwise targets 50 Hz. 28 | If playing a ts, it uses ts timestamps and find fps with consecutive timestamps. 29 | 30 | Additional parameters control the behaviour of the decoder, and can be set either 31 | from the command line of from the configuration dialog: go to Tools / Preferences, 32 | select "show allow parameters" on the bottom-left, then look for Input/codecs / 33 | Video codecs / vvcdec. 34 | 35 | Decoder parameters description 36 | 37 | nb-threads integer (default 0), number of threads for decoding in the range [1-32]; 0: automatic detection of cores 38 | nb-threads-parsing integer (default -1), Maximum number of threads for CABAC parsing (from same pool as decoding threads) [1-32]; -1: auto; 0: sequantial parsing and decoding 39 | target-layer-set integer (default -1), Target output layer set (for multi-layer streams) 40 | vvc-enable-hurry-mode bool (default true), hurry-up mode: skip decoding pictures if late 41 | vvc-fps float (default 0), Frames per Second; 0: try automatic, default 50Hz 42 | -------------------------------------------------------------------------------- /DecoderPluginVLC/vvc_nal.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * vvc_nal.h: h.266/vvc nal types description 3 | ***************************************************************************** 4 | * Copyright (C) 2021 interdigital 5 | * 6 | 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU Lesser General Public License as published by 9 | * the Free Software Foundation; either version 2.1 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 20 | *****************************************************************************/ 21 | 22 | #ifndef __VVC_NAL_H__ 23 | #define __VVC_NAL_H__ 24 | 25 | enum vvc_nal_unit_type_e 26 | { 27 | VVC_NAL_CODED_SLICE_TRAIL = 0, // 0 28 | VVC_NAL_CODED_SLICE_STSA, // 1 29 | VVC_NAL_CODED_SLICE_RADL, // 2 30 | VVC_NAL_CODED_SLICE_RASL, // 3 31 | 32 | VVC_NAL_RESERVED_VCL_4, 33 | VVC_NAL_RESERVED_VCL_5, 34 | VVC_NAL_RESERVED_VCL_6, 35 | 36 | VVC_NAL_CODED_SLICE_IDR_W_RADL, // 7 37 | VVC_NAL_CODED_SLICE_IDR_N_LP, // 8 38 | VVC_NAL_CODED_SLICE_CRA, // 9 39 | VVC_NAL_CODED_SLICE_GDR, // 10 40 | 41 | VVC_NAL_RESERVED_IRAP_VCL_11, 42 | VVC_NAL_OPI, // 12 43 | VVC_NAL_DCI, // 13 44 | VVC_NAL_VPS, // 14 45 | VVC_NAL_SPS, // 15 46 | VVC_NAL_PPS, // 16 47 | VVC_NAL_PREFIX_APS, // 17 48 | VVC_NAL_SUFFIX_APS, // 18 49 | VVC_NAL_PH, // 19 50 | VVC_NAL_ACCESS_UNIT_DELIMITER, // 20 51 | VVC_NAL_EOS, // 21 52 | VVC_NAL_EOB, // 22 53 | VVC_NAL_PREFIX_SEI, // 23 54 | VVC_NAL_SUFFIX_SEI, // 24 55 | VVC_NAL_FD, // 25 56 | 57 | VVC_NAL_RESERVED_NVCL_26, 58 | VVC_NAL_RESERVED_NVCL_27, 59 | 60 | VVC_NAL_UNSPECIFIED_28, 61 | VVC_NAL_UNSPECIFIED_29, 62 | VVC_NAL_UNSPECIFIED_30, 63 | VVC_NAL_UNSPECIFIED_31, 64 | VVC_NAL_INVALID 65 | }; 66 | 67 | #endif // __VVC_NAL_H__ 68 | -------------------------------------------------------------------------------- /DecoderPluginVLC/README.md: -------------------------------------------------------------------------------- 1 | /************* HOW TO BUILD THE VVC VLC PLUGIN ************/ 2 | 3 | Prerequisite: 4 | 5 | VVC decoder library libvtmdec 6 | - currently VTM-22.2 version can be found here: 7 | https://vcgit.hhi.fraunhofer.de/delagrangep/VVCSoftware_VTM/-/tree/VTM-22.2-MT 8 | - currently VTM-21.2 version can be found here: 9 | https://vcgit.hhi.fraunhofer.de/delagrangep/VVCSoftware_VTM/-/tree/VTM-21.2-MT 10 | - currently VTM-20.2 version can be found here: 11 | https://vcgit.hhi.fraunhofer.de/delagrangep/VVCSoftware_VTM/-/tree/VTM-20.2-MT 12 | - VTM-18.2 version can be found here: 13 | https://vcgit.hhi.fraunhofer.de/delagrangep/VVCSoftware_VTM/-/tree/VTM-18.2-MT 14 | - VTM-17.2 version can be found here: 15 | https://vcgit.hhi.fraunhofer.de/delagrangep/VVCSoftware_VTM/-/tree/VTM-17.2-MT 16 | - for Windows, a precompiled .dll is available 17 | - otherwise, get the sources and build libvtmdec decoder library (enable option "BUILD_LIBVTMDEC" in CMake). 18 | 19 | 20 | VLC Pugin: 21 | ------------------- 22 | On WINDOWS 23 | ------------------- 24 | 25 | Prerequisite: 26 | - VLC SDK 27 | You can download the binary release with sdk on videolan site: https://get.videolan.org/vlc/ 28 | For example 3.0.20 version: https://get.videolan.org/vlc/3.0.20/, choose the 7z package 29 | - download and decompress for example on c:/. 30 | - modify \sdk\include\vlc\plugins\vlc_threads.h file 31 | Unless you need it (and you'll have to figure out how to compile with it) 32 | you should comment the definition of vlc_poll function 33 | 34 | Build the VTM plugin using CMake: 35 | - set libvtmdec related variables: 36 | VTM_DIR path to VTM root directory with libvtmdec built 37 | VTMDEC_LIB_NAME dll libray file name (default name should be OK) 38 | 39 | - set directories for vlc sdk: 40 | VLC_PROGRAM_DIR VLC binary directory - used to copy output library in the appropriate plugin directory 41 | VLC_INCLUDE_DIR =c:/vlc-3.0.20/sdk/include/vlc/plugins 42 | VLC_LIB_DIR =c:/vlc-3.0.20/sdk/lib 43 | 44 | - generate the solution and compile 45 | The dll will be placed in the correct vlc plugin directory (VLC_OUTPUT_PLUGIN_DIR = c:/vlc-3.0.20/plugins) 46 | 47 | ------------------- 48 | On Linux 49 | ------------------- 50 | 51 | Prerequisite: 52 | - VLC SDK 53 | You can download the binaries release with sdk on videolan site: https://get.videolan.org/vlc/ 54 | For example 3.0.20 version: https://get.videolan.org/vlc/3.0.20/ 55 | - download and decompress the sources 56 | - build using configure and make 57 | get missing dependencies, or disable them 58 | 59 | Build the VTM plugin using CMake: 60 | - set libvtmdec related variables: 61 | VTM_DIR path to VTM root directory with libvtmdec built 62 | VTMDEC_LIB_NAME dll libray file name (default name should be OK) 63 | 64 | - set directories for vlc sdk: 65 | VLC_PROGRAM_DIR VLC binary directory - used to copy output library in the appropriate plugin directory 66 | VLC_INCLUDE_DIR =c:/vlc-3.0.20/sdk/include/vlc/plugins 67 | VLC_LIB_DIR =c:/vlc-3.0.20/sdk/lib 68 | 69 | - generate the solution and compile 70 | The dll will be placed in the source/bin directory and need to be placed in vlc plugin directory /vlc/modules/.libs/ 71 | -------------------------------------------------------------------------------- /DecoderPluginVLC/startcode_helper.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * startcode_helper.h: Startcodes helpers 3 | ***************************************************************************** 4 | * Copyright (C) 2016 VideoLAN Authors 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program; if not, write to the Free Software Foundation, 18 | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 19 | *****************************************************************************/ 20 | #ifndef VLC_STARTCODE_HELPER_H_ 21 | #define VLC_STARTCODE_HELPER_H_ 22 | 23 | #include 24 | 25 | #if !defined(CAN_COMPILE_SSE2) && defined(HAVE_SSE2_INTRINSICS) 26 | #include 27 | #endif 28 | 29 | /* Looks up efficiently for an AnnexB startcode 0x00 0x00 0x01 30 | * by using a 4 times faster trick than single byte lookup. */ 31 | 32 | #define TRY_MATCH(p,a) {\ 33 | if (p[a+1] == 0) {\ 34 | if (p[a+0] == 0 && p[a+2] == 1)\ 35 | return a+p;\ 36 | if (p[a+2] == 0 && p[a+3] == 1)\ 37 | return a+p+1;\ 38 | }\ 39 | if (p[a+3] == 0) {\ 40 | if (p[a+2] == 0 && p[a+4] == 1)\ 41 | return a+p+2;\ 42 | if (p[a+4] == 0 && p[a+5] == 1)\ 43 | return a+p+3;\ 44 | }\ 45 | } 46 | 47 | #if defined(CAN_COMPILE_SSE2) || defined(HAVE_SSE2_INTRINSICS) 48 | 49 | __attribute__ ((__target__ ("sse2"))) 50 | static inline const uint8_t * startcode_FindAnnexB_SSE2( const uint8_t *p, const uint8_t *end ) 51 | { 52 | /* First align to 16 */ 53 | /* Skipping this step and doing unaligned loads isn't faster */ 54 | const uint8_t *alignedend = p + 16 - ((intptr_t)p & 15); 55 | for (end -= 3; p < alignedend && p < end; p++) { 56 | if (p[0] == 0 && p[1] == 0 && p[2] == 1) 57 | return p; 58 | } 59 | 60 | if( p == end ) 61 | return NULL; 62 | 63 | alignedend = end - ((intptr_t) end & 15); 64 | if( alignedend > p ) 65 | { 66 | #ifdef CAN_COMPILE_SSE2 67 | asm volatile( 68 | "pxor %%xmm1, %%xmm1\n" 69 | ::: "xmm1" 70 | ); 71 | #else 72 | __m128i zeros = _mm_set1_epi8( 0x00 ); 73 | #endif 74 | for( ; p < alignedend; p += 16) 75 | { 76 | uint32_t match; 77 | #ifdef CAN_COMPILE_SSE2 78 | asm volatile( 79 | "movdqa 0(%[v]), %%xmm0\n" 80 | "pcmpeqb %%xmm1, %%xmm0\n" 81 | "pmovmskb %%xmm0, %[match]\n" 82 | : [match]"=r"(match) 83 | : [v]"r"(p) 84 | : "xmm0" 85 | ); 86 | #else 87 | __m128i v = _mm_load_si128((__m128i*)p); 88 | __m128i res = _mm_cmpeq_epi8( zeros, v ); 89 | match = _mm_movemask_epi8( res ); /* mask will be in reversed match order */ 90 | #endif 91 | if( match & 0x000F ) 92 | TRY_MATCH(p, 0); 93 | if( match & 0x00F0 ) 94 | TRY_MATCH(p, 4); 95 | if( match & 0x0F00 ) 96 | TRY_MATCH(p, 8); 97 | if( match & 0xF000 ) 98 | TRY_MATCH(p, 12); 99 | } 100 | } 101 | 102 | for (; p < end; p++) { 103 | if (p[0] == 0 && p[1] == 0 && p[2] == 1) 104 | return p; 105 | } 106 | 107 | return NULL; 108 | } 109 | 110 | #endif 111 | 112 | /* That code is adapted from libav's ff_avc_find_startcode_internal 113 | * and i believe the trick originated from 114 | * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord 115 | */ 116 | static inline const uint8_t * startcode_FindAnnexB( const uint8_t *p, const uint8_t *end ) 117 | { 118 | #if defined(CAN_COMPILE_SSE2) || defined(HAVE_SSE2_INTRINSICS) 119 | if (vlc_CPU_SSE2()) 120 | return startcode_FindAnnexB_SSE2(p, end); 121 | #endif 122 | const uint8_t *a = p + 4 - ((intptr_t)p & 3); 123 | 124 | for (end -= 3; p < a && p < end; p++) { 125 | if (p[0] == 0 && p[1] == 0 && p[2] == 1) 126 | return p; 127 | } 128 | 129 | for (end -= 3; p < end; p += 4) { 130 | uint32_t x = *(const uint32_t*)p; 131 | if ((x - 0x01010101) & (~x) & 0x80808080) 132 | { 133 | /* matching DW isn't faster */ 134 | TRY_MATCH(p, 0); 135 | } 136 | } 137 | 138 | for (end += 3; p < end; p++) { 139 | if (p[0] == 0 && p[1] == 0 && p[2] == 1) 140 | return p; 141 | } 142 | 143 | return NULL; 144 | } 145 | 146 | /* Special variation to return on prefix only and no data */ 147 | static inline const uint8_t * startcode_FindAnyAnnexB( const uint8_t *p, const uint8_t *end ) 148 | { 149 | size_t i_size = end - p; 150 | if( i_size <= 4 ) 151 | { 152 | if( i_size == 4 ) 153 | { 154 | TRY_MATCH(p, 0); 155 | } 156 | else if ( i_size == 3 && p[0] == 0 && p[1] == 0 && p[2] == 1 ) 157 | return p; 158 | return NULL; 159 | } 160 | else return startcode_FindAnnexB( p, end ); 161 | } 162 | 163 | #undef TRY_MATCH 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /DecoderPluginVLC/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(VVCdecoder_plugin) 2 | # minimum required cmake version 3 | cmake_minimum_required( VERSION 3.5 FATAL_ERROR ) 4 | 5 | # executable 6 | IF(WIN32) 7 | set( LIB_NAME libvvcdecoder_plugin ) 8 | else() 9 | set( LIB_NAME vvcdecoder_plugin ) 10 | endif() 11 | 12 | # set default CMAKE_BUILD_TYPE to Release if not set 13 | if( NOT CMAKE_BUILD_TYPE ) 14 | set( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release." FORCE ) 15 | endif() 16 | 17 | if( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) 18 | if( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" ) 19 | set( USE_ADDRESS_SANITIZER OFF CACHE BOOL "Compiles with -sanitize=address and links to libasan" ) 20 | endif() 21 | endif() 22 | 23 | if( CMAKE_COMPILER_IS_GNUCC ) 24 | set( BUILD_STATIC OFF CACHE BOOL "Build static executables" ) 25 | endif() 26 | 27 | # set c++11 28 | set( CMAKE_CXX_STANDARD 11 ) 29 | set( CMAKE_CXX_STANDARD_REQUIRED ON ) 30 | 31 | # compile everything position independent (even static libraries) 32 | set( CMAKE_POSITION_INDEPENDENT_CODE TRUE ) 33 | 34 | # enable sse4.1 build for all source files for gcc and clang 35 | if( UNIX OR MINGW ) 36 | add_compile_options( "-msse4.1" ) 37 | endif() 38 | 39 | # enable parallel build for Visual Studio 40 | if( MSVC ) 41 | add_compile_options( "/MP" ) 42 | add_compile_options( "/EHsc" ) 43 | set(CMAKE_CXX_STANDARD_LIBRARIES "") 44 | # ---------------------------------------------------------------------------- 45 | # Use statically or dynamically linked CRT? 46 | # Default: static 47 | # ---------------------------------------------------------------------------- 48 | if(WIN32) 49 | OPTION (BUILD_WITH_STATIC_CRT "Enables use of staticaly linked CRT" ON) 50 | if(BUILD_WITH_STATIC_CRT) 51 | foreach(flag_var 52 | CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE 53 | CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO 54 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 55 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 56 | if(${flag_var} MATCHES "/MD") 57 | string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") 58 | endif() 59 | if(${flag_var} MATCHES "/MDd") 60 | string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") 61 | endif() 62 | endforeach(flag_var) 63 | 64 | else(BUILD_WITH_STATIC_CRT) 65 | foreach(flag_var 66 | CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE 67 | CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO 68 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 69 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 70 | if(${flag_var} MATCHES "/MT") 71 | string(REGEX REPLACE "/MT" "/MD" ${flag_var} "${${flag_var}}") 72 | endif() 73 | if(${flag_var} MATCHES "/MTd") 74 | string(REGEX REPLACE "/MTd" "/MDd" ${flag_var} "${${flag_var}}") 75 | endif() 76 | endforeach(flag_var) 77 | endif(BUILD_WITH_STATIC_CRT) 78 | endif() 79 | endif() 80 | 81 | # set address sanitizer compiler arguments 82 | if( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" ) 83 | if( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) 84 | if( USE_ADDRESS_SANITIZER ) 85 | # add compile options 86 | add_compile_options( "-fsanitize=address" ) 87 | add_link_options( "-fsanitize=address" ) 88 | endif() 89 | endif() 90 | 91 | if( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0 ) 92 | add_compile_options( "-fabi-version=6" ) 93 | endif() 94 | endif() 95 | 96 | # get source files 97 | file( GLOB SRC_FILES "*.cpp" ) 98 | 99 | set( VTM_DIR "VVCSoftware_VTM/" CACHE PATH "path to VTM lib include directory and import lib for windows" ) 100 | 101 | set( VLC_PROGRAM_DIR "C:/VLC" CACHE PATH "will output dll in plugin dir of VLC" ) 102 | set( VLC_INCLUDE_DIR "${VLC_PROGRAM_DIR}/sdk/include/vlc/plugins" CACHE PATH "path to VLC core source files" ) 103 | set( VLC_LIB_DIR "${VLC_PROGRAM_DIR}/sdk/lib" CACHE PATH "path to VLC core lib files" ) 104 | IF(WIN32) 105 | set( VTMDEC_LIB_NAME "libvtmdec" CACHE PATH "name of the VTM decoder lib" ) 106 | set( VLC_LIBs "${VLC_LIB_DIR}/libvlccore.lib" ) 107 | ELSE() 108 | set( VTMDEC_LIB_NAME "libvtmdec.so" CACHE PATH "name of the VTM decoder lib" ) 109 | set( VLC_LIBs "${VLC_LIB_DIR}/libvlccore.so" ) 110 | ENDIF() 111 | 112 | # add executable 113 | add_library( ${LIB_NAME} SHARED ${SRC_FILES} ${INC_FILES} ${NATVIS_FILES} ) 114 | 115 | target_include_directories( ${LIB_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" "${VLC_INCLUDE_DIR}" "${VTM_DIR}/source/App/LibVTMDec") 116 | 117 | file( GLOB SRC_FILES "*.cpp" ) 118 | 119 | target_link_directories( ${LIB_NAME} PRIVATE "${VTM_DIR}/lib") 120 | target_link_libraries( ${LIB_NAME} PRIVATE ${VLC_LIBs} ${VTMDEC_LIB_NAME} ) 121 | 122 | # set the folder where to place the projects 123 | IF(WIN32) 124 | set_target_properties( ${LIB_NAME} PROPERTIES 125 | RUNTIME_OUTPUT_DIRECTORY "${VLC_PROGRAM_DIR}/plugins/codec" 126 | RUNTIME_OUTPUT_DIRECTORY_DEBUG "${VLC_PROGRAM_DIR}/plugins/codec" 127 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${VLC_PROGRAM_DIR}/plugins/codec" 128 | ) 129 | SET_TARGET_PROPERTIES(${LIB_NAME} PROPERTIES LINK_FLAGS "/DELAYLOAD:${VTMDEC_LIB_NAME}.dll") 130 | target_compile_definitions( ${LIB_NAME} PRIVATE VTM_LIB_NAME="${VTMDEC_LIB_NAME}.dll" ) 131 | ELSEIF( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) 132 | add_custom_command( TARGET ${LIB_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 133 | ${CMAKE_BINARY_DIR}/lib${LIB_NAME}.so 134 | ${VLC_PROGRAM_DIR}/modules/.libs/lib${LIB_NAME}.so) 135 | add_custom_command( TARGET ${LIB_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 136 | ${VTM_DIR}/lib/${VTMDEC_LIB_NAME} 137 | ${VLC_PROGRAM_DIR}/modules/.libs/${VTMDEC_LIB_NAME}) 138 | ENDIF() 139 | 140 | target_compile_definitions( ${LIB_NAME} PRIVATE MODULE_STRING="vvcdecoder" __PLUGIN__ ) 141 | 142 | if( MSVC ) 143 | target_compile_options( ${LIB_NAME} PRIVATE "-WX-" ) 144 | else() 145 | target_compile_options( ${LIB_NAME} PRIVATE "-Wno-error" ) 146 | endif() 147 | 148 | # Install 149 | #INSTALL(FILES ${LIB_NAME} 150 | # DESTINATION bin) 151 | #INSTALL(TARGETS ${LIB_NAME} RUNTIME DESTINATION "/") 152 | # RUNTIME DESTINATION bin 153 | # LIBRARY DESTINATION lib 154 | # ARCHIVE DESTINATION lib) -------------------------------------------------------------------------------- /DecoderPluginVLC/packetizer_helper.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * packetizer_helper.h: Packetizer helpers 3 | ***************************************************************************** 4 | * Copyright (C) 2021 interdigital 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU Lesser General Public License as published by 9 | * the Free Software Foundation; either version 2.1 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 20 | *****************************************************************************/ 21 | 22 | #ifndef VLC_PACKETIZER_HELPER_H_ 23 | #define VLC_PACKETIZER_HELPER_H_ 24 | 25 | #include 26 | 27 | #define BLOCK_FLAG_DROP (1 << BLOCK_FLAG_PRIVATE_SHIFT) 28 | 29 | enum 30 | { 31 | STATE_NOSYNC, 32 | STATE_SYNC, 33 | STATE_HEADER, 34 | STATE_NEXT_SYNC, 35 | STATE_GET_DATA, 36 | STATE_SEND_DATA 37 | }; 38 | 39 | typedef void (*packetizer_reset_t)( void *p_private, bool b_broken ); 40 | typedef block_t *(*packetizer_parse_t)( void *p_private, bool *pb_ts_used, block_t * ); 41 | typedef int (*packetizer_validate_t)( void *p_private, block_t * ); 42 | 43 | typedef struct 44 | { 45 | int i_state; 46 | block_bytestream_t bytestream; 47 | size_t i_offset; 48 | 49 | int i_startcode; 50 | const uint8_t *p_startcode; 51 | block_startcode_helper_t pf_startcode_helper; 52 | 53 | int i_au_prepend; 54 | const uint8_t *p_au_prepend; 55 | 56 | unsigned i_au_min_size; 57 | 58 | void *p_private; 59 | packetizer_reset_t pf_reset; 60 | packetizer_parse_t pf_parse; 61 | packetizer_validate_t pf_validate; 62 | 63 | } packetizer_t; 64 | 65 | static inline void packetizer_Init( packetizer_t *p_pack, 66 | const uint8_t *p_startcode, int i_startcode, 67 | block_startcode_helper_t pf_start_helper, 68 | const uint8_t *p_au_prepend, int i_au_prepend, 69 | unsigned i_au_min_size, 70 | packetizer_reset_t pf_reset, 71 | packetizer_parse_t pf_parse, 72 | packetizer_validate_t pf_validate, 73 | void *p_private ) 74 | { 75 | p_pack->i_state = STATE_NOSYNC; 76 | block_BytestreamInit( &p_pack->bytestream ); 77 | p_pack->i_offset = 0; 78 | 79 | p_pack->i_au_prepend = i_au_prepend; 80 | p_pack->p_au_prepend = p_au_prepend; 81 | p_pack->i_au_min_size = i_au_min_size; 82 | 83 | p_pack->i_startcode = i_startcode; 84 | p_pack->p_startcode = p_startcode; 85 | p_pack->pf_startcode_helper = pf_start_helper; 86 | p_pack->pf_reset = pf_reset; 87 | p_pack->pf_parse = pf_parse; 88 | p_pack->pf_validate = pf_validate; 89 | p_pack->p_private = p_private; 90 | } 91 | 92 | static inline void packetizer_Clean( packetizer_t *p_pack ) 93 | { 94 | block_BytestreamRelease( &p_pack->bytestream ); 95 | } 96 | 97 | static inline void packetizer_Flush( packetizer_t *p_pack ) 98 | { 99 | p_pack->i_state = STATE_NOSYNC; 100 | block_BytestreamEmpty( &p_pack->bytestream ); 101 | p_pack->i_offset = 0; 102 | p_pack->pf_reset( p_pack->p_private, true ); 103 | } 104 | 105 | static inline block_t *packetizer_Packetize( packetizer_t *p_pack, block_t **pp_block ) 106 | { 107 | block_t *p_block = ( pp_block ) ? *pp_block : NULL; 108 | 109 | if( p_block == NULL && p_pack->bytestream.p_block == NULL ) 110 | return NULL; /* nothing to do */ 111 | 112 | if( p_block && unlikely( p_block->i_flags&(BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) ) 113 | { 114 | block_t *p_drained = packetizer_Packetize( p_pack, NULL ); 115 | if( p_drained ) 116 | return p_drained; 117 | 118 | const bool b_broken = !!( p_block->i_flags&BLOCK_FLAG_CORRUPTED ); 119 | p_pack->i_state = STATE_NOSYNC; 120 | block_BytestreamEmpty( &p_pack->bytestream ); 121 | p_pack->i_offset = 0; 122 | p_pack->pf_reset( p_pack->p_private, b_broken ); 123 | if( b_broken ) 124 | { 125 | block_Release( p_block ); 126 | return NULL; 127 | } 128 | } 129 | 130 | if( p_block ) 131 | block_BytestreamPush( &p_pack->bytestream, p_block ); 132 | 133 | for( ;; ) 134 | { 135 | bool b_used_ts; 136 | block_t *p_pic; 137 | 138 | switch( p_pack->i_state ) 139 | { 140 | case STATE_NOSYNC: 141 | /* Find a startcode */ 142 | if( !block_FindStartcodeFromOffset( &p_pack->bytestream, &p_pack->i_offset, 143 | p_pack->p_startcode, p_pack->i_startcode, 144 | p_pack->pf_startcode_helper, NULL ) ) 145 | p_pack->i_state = STATE_NEXT_SYNC; 146 | 147 | if( p_pack->i_offset ) 148 | { 149 | block_SkipBytes( &p_pack->bytestream, p_pack->i_offset ); 150 | p_pack->i_offset = 0; 151 | block_BytestreamFlush( &p_pack->bytestream ); 152 | } 153 | 154 | if( p_pack->i_state != STATE_NEXT_SYNC ) 155 | return NULL; /* Need more data */ 156 | 157 | p_pack->i_offset = 1; /* To find next startcode */ 158 | /* fallthrough */ 159 | 160 | case STATE_NEXT_SYNC: 161 | /* Find the next startcode */ 162 | if( block_FindStartcodeFromOffset( &p_pack->bytestream, &p_pack->i_offset, 163 | p_pack->p_startcode, p_pack->i_startcode, 164 | p_pack->pf_startcode_helper, NULL ) ) 165 | { 166 | if( pp_block /* not flushing */ || !p_pack->bytestream.p_chain ) 167 | return NULL; /* Need more data */ 168 | 169 | /* When flusing and we don't find a startcode, suppose that 170 | * the data extend up to the end */ 171 | block_ChainProperties( p_pack->bytestream.p_block, 172 | NULL, &p_pack->i_offset, NULL ); 173 | p_pack->i_offset -= p_pack->bytestream.i_block_offset; 174 | 175 | if( p_pack->i_offset <= (size_t)p_pack->i_startcode ) 176 | return NULL; 177 | } 178 | 179 | block_BytestreamFlush( &p_pack->bytestream ); 180 | 181 | /* Get the new fragment and set the pts/dts */ 182 | block_t *p_block_bytestream = p_pack->bytestream.p_block; 183 | 184 | p_pic = block_Alloc( p_pack->i_offset + p_pack->i_au_prepend ); 185 | p_pic->i_pts = p_block_bytestream->i_pts; 186 | p_pic->i_dts = p_block_bytestream->i_dts; 187 | 188 | block_GetBytes( &p_pack->bytestream, &p_pic->p_buffer[p_pack->i_au_prepend], 189 | p_pic->i_buffer - p_pack->i_au_prepend ); 190 | if( p_pack->i_au_prepend > 0 ) 191 | memcpy( p_pic->p_buffer, p_pack->p_au_prepend, p_pack->i_au_prepend ); 192 | 193 | p_pack->i_offset = 0; 194 | 195 | /* Parse the NAL */ 196 | if( p_pic->i_buffer < p_pack->i_au_min_size ) 197 | { 198 | block_Release( p_pic ); 199 | p_pic = NULL; 200 | } 201 | else 202 | { 203 | p_pic = p_pack->pf_parse( p_pack->p_private, &b_used_ts, p_pic ); 204 | if( b_used_ts ) 205 | { 206 | p_block_bytestream->i_dts = VLC_TS_INVALID; 207 | p_block_bytestream->i_pts = VLC_TS_INVALID; 208 | } 209 | } 210 | 211 | if( !p_pic ) 212 | { 213 | p_pack->i_state = STATE_NOSYNC; 214 | break; 215 | } 216 | if( p_pack->pf_validate( p_pack->p_private, p_pic ) ) 217 | { 218 | p_pack->i_state = STATE_NOSYNC; 219 | block_Release( p_pic ); 220 | break; 221 | } 222 | 223 | /* So p_block doesn't get re-added several times */ 224 | block_t* dummyOut; 225 | block_t** dest = pp_block ? pp_block: &dummyOut; 226 | *dest = block_BytestreamPop( &p_pack->bytestream ); 227 | 228 | p_pack->i_state = STATE_NOSYNC; 229 | 230 | return p_pic; 231 | } 232 | } 233 | } 234 | 235 | static inline void packetizer_Header( packetizer_t *p_pack, 236 | const uint8_t *p_header, int i_header ) 237 | { 238 | block_t *p_init = block_Alloc( i_header ); 239 | if( !p_init ) 240 | return; 241 | 242 | memcpy( p_init->p_buffer, p_header, i_header ); 243 | 244 | block_t *p_pic; 245 | while( ( p_pic = packetizer_Packetize( p_pack, &p_init ) ) ) 246 | block_Release( p_pic ); /* Should not happen (only sequence header) */ 247 | while( ( p_pic = packetizer_Packetize( p_pack, NULL ) ) ) 248 | block_Release( p_pic ); 249 | 250 | p_pack->i_state = STATE_NOSYNC; 251 | block_BytestreamEmpty( &p_pack->bytestream ); 252 | p_pack->i_offset = 0; 253 | } 254 | 255 | #endif 256 | 257 | -------------------------------------------------------------------------------- /TsDemuxPluginVLC/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(TsDemuxVVC_plugin) 2 | # minimum required cmake version 3 | cmake_minimum_required( VERSION 3.5 FATAL_ERROR ) 4 | # executable 5 | IF(WIN32) 6 | set( LIB_NAME libvvctsdemux_plugin ) 7 | else() 8 | set( LIB_NAME vctsdemux_plugin ) 9 | endif() 10 | 11 | # set default CMAKE_BUILD_TYPE to Release if not set 12 | if( NOT CMAKE_BUILD_TYPE ) 13 | set( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release." FORCE ) 14 | endif() 15 | 16 | if( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) 17 | if( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" ) 18 | set( USE_ADDRESS_SANITIZER OFF CACHE BOOL "Compiles with -sanitize=address and links to libasan" ) 19 | endif() 20 | endif() 21 | 22 | if( CMAKE_COMPILER_IS_GNUCC ) 23 | set( BUILD_STATIC OFF CACHE BOOL "Build static executables" ) 24 | endif() 25 | 26 | # set c++11 27 | set( CMAKE_C_STANDARD 11 ) 28 | set( C_STANDARD_REQUIRED 11 ) 29 | 30 | # compile everything position independent (even static libraries) 31 | set( CMAKE_POSITION_INDEPENDENT_CODE TRUE ) 32 | 33 | # enable sse4.1 build for all source files for gcc and clang 34 | if( UNIX OR MINGW ) 35 | add_compile_options( "-msse4.1" ) 36 | endif() 37 | 38 | # enable parallel build for Visual Studio 39 | if( MSVC ) 40 | add_compile_options( "/MP" ) 41 | add_compile_options( "/EHsc" ) 42 | set(CMAKE_CXX_STANDARD_LIBRARIES "") 43 | # ---------------------------------------------------------------------------- 44 | # Use statically or dynamically linked CRT? 45 | # Default: static 46 | # ---------------------------------------------------------------------------- 47 | if(WIN32) 48 | OPTION (BUILD_WITH_STATIC_CRT "Enables use of staticaly linked CRT" ON) 49 | if(BUILD_WITH_STATIC_CRT) 50 | foreach(flag_var 51 | CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE 52 | CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO 53 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 54 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 55 | if(${flag_var} MATCHES "/MD") 56 | string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") 57 | endif() 58 | if(${flag_var} MATCHES "/MDd") 59 | string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") 60 | endif() 61 | endforeach(flag_var) 62 | 63 | else(BUILD_WITH_STATIC_CRT) 64 | foreach(flag_var 65 | CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE 66 | CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO 67 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 68 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 69 | if(${flag_var} MATCHES "/MT") 70 | string(REGEX REPLACE "/MT" "/MD" ${flag_var} "${${flag_var}}") 71 | endif() 72 | if(${flag_var} MATCHES "/MTd") 73 | string(REGEX REPLACE "/MTd" "/MDd" ${flag_var} "${${flag_var}}") 74 | endif() 75 | endforeach(flag_var) 76 | endif(BUILD_WITH_STATIC_CRT) 77 | endif() 78 | endif() 79 | 80 | # set address sanitizer compiler arguments 81 | if( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" ) 82 | if( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) 83 | if( USE_ADDRESS_SANITIZER ) 84 | # add compile options 85 | add_compile_options( "-fsanitize=address" ) 86 | add_link_options( "-fsanitize=address" ) 87 | endif() 88 | endif() 89 | 90 | if( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0 ) 91 | add_compile_options( "-fabi-version=6" ) 92 | endif() 93 | endif() 94 | 95 | # get source files 96 | file( GLOB SRC_FILES "*.cpp" "*.c" ) 97 | # get all source files 98 | 99 | set( VLC_PROGRAM_DIR "C:/VLC" CACHE PATH "will output dll in plugin dir of VLC" ) 100 | set( VLC_INCLUDE_DIR "${VLC_PROGRAM_DIR}/sdk/include/vlc/plugins" CACHE PATH "path to VLC core source files" ) 101 | set( VLC_LIB_DIR "${VLC_PROGRAM_DIR}/sdk/lib" CACHE PATH "path to VLC core lib files" ) 102 | set( VLC_SRC_DIR "vlc/src" CACHE PATH "path to VLC src dir" ) 103 | set( DVBPSI_DIR "dvbpsi" CACHE PATH "path to dvbpsi dir (ex: version 1.3.2)" ) 104 | 105 | set( SRC_FILES "${SRC_FILES}" 106 | "${VLC_SRC_DIR}/modules/demux/mpeg/ts.h;\ 107 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_pid.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_pid_fwd.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_pid.c;\ 108 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_psi.h;\ 109 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_si.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_si.c;\ 110 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_psip.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_psip.c;\ 111 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_psip_dvbpsi_fixes.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_psip_dvbpsi_fixes.c;\ 112 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_decoders.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_decoders.c;\ 113 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_streams.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_streams.c;\ 114 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_scte.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_scte.c;\ 115 | ${VLC_SRC_DIR}/modules/demux/mpeg/sections.c;${VLC_SRC_DIR}/modules/demux/mpeg/sections.h;\ 116 | ${VLC_SRC_DIR}/modules/demux/mpeg/mpeg4_iod.c;${VLC_SRC_DIR}/modules/demux/mpeg/mpeg4_iod.h;\ 117 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_arib.c;${VLC_SRC_DIR}/modules/demux/mpeg/ts_arib.h;\ 118 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_sl.c;${VLC_SRC_DIR}/modules/demux/mpeg/ts_sl.h;\ 119 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_metadata.c;${VLC_SRC_DIR}/modules/demux/mpeg/ts_metadata.h;\ 120 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_hotfixes.c;${VLC_SRC_DIR}/modules/demux/mpeg/ts_hotfixes.h;\ 121 | ${VLC_SRC_DIR}/modules/demux/mpeg/ts_strings.h;${VLC_SRC_DIR}/modules/demux/mpeg/ts_streams_private.h;\ 122 | ${VLC_SRC_DIR}/modules/demux/mpeg/pes.h;\ 123 | ${VLC_SRC_DIR}/modules/demux/mpeg/timestamps.h;\ 124 | ${VLC_SRC_DIR}/modules/demux/dvb-text.h;\ 125 | ${VLC_SRC_DIR}/modules/demux/opus.h;\ 126 | ${VLC_SRC_DIR}/modules/mux/mpeg/csa.c;\ 127 | ${VLC_SRC_DIR}/modules/mux/mpeg/dvbpsi_compat.h;\ 128 | ${VLC_SRC_DIR}/modules/mux/mpeg/streams.h;\ 129 | ${VLC_SRC_DIR}/modules/mux/mpeg/tables.c;${VLC_SRC_DIR}/modules/mux/mpeg/tables.h;\ 130 | ${VLC_SRC_DIR}/modules/mux/mpeg/tsutil.c;${VLC_SRC_DIR}/modules/mux/mpeg/tsutil.h;\ 131 | ${VLC_SRC_DIR}/modules/access/dtv/en50221_capmt.h;\ 132 | ${VLC_SRC_DIR}/modules/codec/jpeg2000.h;${VLC_SRC_DIR}/modules/codec/scte18.h;\ 133 | ${VLC_SRC_DIR}/modules/codec/atsc_a65.c;${VLC_SRC_DIR}/modules/codec/atsc_a65.h;\ 134 | ${VLC_SRC_DIR}/modules/codec/opus_header.c" ) 135 | 136 | file( GLOB DVBPSI_SRC_FILES "${DVBPSI_DIR}/src/*.c" "${DVBPSI_DIR}/src/tables/*.c" "${DVBPSI_DIR}/src/descriptors/*.c" "${DVBPSI_DIR}/src/descriptors/types/*.c" ) 137 | 138 | 139 | IF(WIN32) 140 | set( VLC_OUTPUT_PLUGIN_DIR "C:/VLC/plugins/" CACHE PATH "path to VLC plugin dir for output binary" ) 141 | ENDIF() 142 | set(VLC_LIBs "${VLC_LIB_DIR}/libvlccore.lib" ) 143 | 144 | # add executable 145 | add_library( dvbpsi ${DVBPSI_SRC_FILES} ) 146 | add_library( ${LIB_NAME} SHARED ${SRC_FILES} ) 147 | 148 | target_include_directories( ${LIB_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" "${VLC_SRC_DIR}/modules/demux" "${VLC_INCLUDE_DIR}" "./" "${DVBPSI_DIR}/src/descriptors/") 149 | target_include_directories( ${LIB_NAME} PRIVATE "${VLC_SRC_DIR}/modules/demux/mpeg/") 150 | 151 | target_include_directories( dvbpsi PRIVATE "./") 152 | 153 | 154 | target_link_libraries( ${LIB_NAME} ${VLC_LIBs} dvbpsi ) 155 | 156 | 157 | if( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) 158 | add_custom_command( TARGET ${LIB_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 159 | $<$:${CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG}/lib${LIB_NAME}.so> 160 | $<$:${CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE}/lib${LIB_NAME}.so> 161 | $<$:${VLC_PROGRAM_DIR}/modules/.libs/lib${LIB_NAME}.so> 162 | $<$:${VLC_PROGRAM_DIR}/modules/.libs/lib${LIB_NAME}.so>) 163 | endif() 164 | 165 | 166 | # set the folder where to place the projects 167 | IF(WIN32) 168 | set_target_properties( ${LIB_NAME} PROPERTIES 169 | FOLDER app 170 | RUNTIME_OUTPUT_DIRECTORY "${VLC_PROGRAM_DIR}/plugins/demux" 171 | RUNTIME_OUTPUT_DIRECTORY_DEBUG "${VLC_PROGRAM_DIR}/plugins/demux" 172 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${VLC_PROGRAM_DIR}/plugins/demux" 173 | ) 174 | ENDIF() 175 | 176 | target_compile_definitions( ${LIB_NAME} PRIVATE MODULE_STRING="vvctsdemux" __PLUGIN__ ) 177 | target_compile_definitions( ${LIB_NAME} PRIVATE HAVE_CONFIG_H ) 178 | 179 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 180 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 181 | ${DVBPSI_DIR}/src/dvbpsi.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 182 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 183 | ${DVBPSI_DIR}/src/demux.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 184 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 185 | ${DVBPSI_DIR}/src/descriptor.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 186 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 187 | ${DVBPSI_DIR}/src/psi.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 188 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 189 | ${DVBPSI_DIR}/src/tables/pat.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 190 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 191 | ${DVBPSI_DIR}/src/tables/sdt.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 192 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 193 | ${DVBPSI_DIR}/src/tables/pmt.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 194 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 195 | ${DVBPSI_DIR}/src/tables/eit.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 196 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 197 | ${DVBPSI_DIR}/src/tables/tot.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 198 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 199 | ${DVBPSI_DIR}/src/tables/atsc_stt.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 200 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 201 | ${DVBPSI_DIR}/src/tables/atsc_mgt.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 202 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 203 | ${DVBPSI_DIR}/src/tables/atsc_vct.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 204 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 205 | ${DVBPSI_DIR}/src/tables/atsc_eit.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 206 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 207 | ${DVBPSI_DIR}/src/tables/atsc_ett.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 208 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 209 | ${DVBPSI_DIR}/src/descriptors/dr.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 210 | add_custom_command( TARGET dvbpsi POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different 211 | ${DVBPSI_DIR}/src/descriptors/dr_a0.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}dvbpsi/) 212 | 213 | SET_SOURCE_FILES_PROPERTIES("${DVBPSI_DIR}/src/tables/sis.c" PROPERTIES LANGUAGE CXX ) 214 | if( MSVC ) 215 | target_compile_options( ${LIB_NAME} PRIVATE "-WX-" ) 216 | else() 217 | target_compile_options( ${LIB_NAME} PRIVATE "-Wno-error" ) 218 | endif() 219 | 220 | # Install 221 | #INSTALL(FILES ${LIB_NAME} 222 | # DESTINATION bin) 223 | #INSTALL(TARGETS ${LIB_NAME} RUNTIME DESTINATION "/") 224 | # RUNTIME DESTINATION bin 225 | # LIBRARY DESTINATION lib 226 | # ARCHIVE DESTINATION lib) 227 | 228 | -------------------------------------------------------------------------------- /DecoderPluginVLC/vvc_demux.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * vvc_demux.cpp: h.266/vvc demuxer 3 | ***************************************************************************** 4 | * Copyright (C) 2021 interdigital 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program; if not, write to the Free Software Foundation, 18 | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 19 | *****************************************************************************/ 20 | 21 | /***************************************************************************** 22 | * Preamble 23 | *****************************************************************************/ 24 | #if defined(_MSC_VER) 25 | #define NOMINMAX 26 | #include 27 | typedef SSIZE_T ssize_t; 28 | #define strcasecmp(s1, s2) _stricmp(s1, s2) 29 | #endif 30 | 31 | #ifdef HAVE_CONFIG_H 32 | # include "config.h" 33 | #endif 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "vvc_nal.h" 41 | 42 | struct demux_sys_t 43 | { 44 | int frame_size; 45 | 46 | es_format_t fmt_video; 47 | 48 | date_t pcr; 49 | 50 | bool b_vvc; 51 | 52 | es_out_id_t *p_es; 53 | 54 | date_t dts; 55 | unsigned frame_rate_num; 56 | unsigned frame_rate_den; 57 | int baseLayerID; 58 | 59 | decoder_t *p_packetizer; 60 | }; 61 | 62 | typedef struct 63 | { 64 | bool b_sps; 65 | bool b_pps; 66 | bool b_vps; 67 | } vvc_probe_ctx_t; 68 | 69 | #define H26X_PACKET_SIZE 2048 70 | #define H26X_PEEK_CHUNK (H26X_PACKET_SIZE * 4) 71 | #define H26X_MIN_PEEK (4 + 7 + 10) 72 | #define H26X_MAX_PEEK (H26X_PEEK_CHUNK * 8) /* max data to check */ 73 | static const int H26X_MAX_NAL_SIZE = (H26X_PACKET_SIZE * 16); 74 | #define H26X_NAL_COUNT 16 /* max # or NAL to check */ 75 | #define VLC_CODEC_VVC VLC_FOURCC('h','2','6','6') 76 | 77 | /**************************************************************************** 78 | * Local prototypes 79 | ****************************************************************************/ 80 | namespace VvcDecoder 81 | { 82 | int OpenDemux(vlc_object_t*); 83 | void CloseDemux(vlc_object_t*); 84 | } 85 | 86 | static int Demux(demux_t*); 87 | static int Control(demux_t*, int i_query, va_list args); 88 | 89 | static inline bool check_Property(demux_t* p_demux, const char** pp_psz, 90 | bool(*pf_check)(demux_t*, const char*)); 91 | /** 92 | * initialization for decoder 93 | */ 94 | 95 | static int ProbeVVC(const uint8_t* p_peek, size_t i_peek, vvc_probe_ctx_t* p_ctx) 96 | { 97 | 98 | if (i_peek < 2) 99 | return -1; 100 | 101 | if (p_peek[0] & 0x80) 102 | return -1; 103 | 104 | // get next NAL unit type 105 | int firstByte = 0; 106 | if (p_peek[0] == 0 && p_peek[1] == 0 && p_peek[2] == 0 && p_peek[3] == 1) 107 | { 108 | firstByte = 4; 109 | } 110 | else if(p_peek[0] == 0 && p_peek[1] == 0 && p_peek[2] ==1) 111 | { 112 | firstByte = 3; 113 | } 114 | int nuhLayerId = (firstByte > 0) ? ((p_peek[firstByte]) & 0x3f) : 0; 115 | vvc_nal_unit_type_e i_nal_type = (firstByte > 0) ? (vvc_nal_unit_type_e)((p_peek[firstByte + 1] >> 3) & 0x1f) : VVC_NAL_INVALID; 116 | int ret = 0; // Probe more 117 | switch (i_nal_type) 118 | { 119 | case VVC_NAL_CODED_SLICE_IDR_W_RADL: 120 | case VVC_NAL_CODED_SLICE_IDR_N_LP: 121 | case VVC_NAL_CODED_SLICE_CRA: 122 | case VVC_NAL_CODED_SLICE_GDR: 123 | if (p_ctx->b_sps && p_ctx->b_pps && nuhLayerId == 0) 124 | ret = 1; 125 | break; 126 | case VVC_NAL_OPI: 127 | case VVC_NAL_DCI: 128 | break; 129 | case VVC_NAL_PH: 130 | break; 131 | case VVC_NAL_VPS: /* VPS */ 132 | if (nuhLayerId != 0 || i_peek < 7 || 133 | ((p_peek[firstByte + 2] >> 4) & 0x0F) == 0) // Check setVPSId 134 | ret = -1; 135 | p_ctx->b_vps = true; 136 | break; 137 | case VVC_NAL_SPS: /* SPS */ 138 | if (nuhLayerId != 0) 139 | ret = -1; 140 | p_ctx->b_sps = true; 141 | break; 142 | case VVC_NAL_PPS: /* PPS */ 143 | if (nuhLayerId != 0) 144 | ret = -1; 145 | p_ctx->b_pps = true; 146 | break; 147 | case VVC_NAL_PREFIX_APS: /* Prefix SEI */ 148 | if (p_ctx->b_sps && p_ctx->b_pps && nuhLayerId == 0) 149 | ret = 1; 150 | break; 151 | case VVC_NAL_ACCESS_UNIT_DELIMITER: /* AU */ 152 | if (i_peek < H26X_MIN_PEEK - 4 || 153 | p_peek[firstByte + 3] != 0 || p_peek[firstByte + 4] != 0) /* Must prefix another NAL */ 154 | ret = -1; 155 | break; 156 | case VVC_NAL_PREFIX_SEI: /* Prefix SEI */ 157 | if (p_peek[2] == 0xFF) /* empty SEI */ 158 | ret = -1; 159 | break; 160 | default: 161 | ret = -1; 162 | } 163 | 164 | return ret; 165 | } 166 | 167 | int VvcDecoder::OpenDemux(vlc_object_t* p_this) 168 | { 169 | vvc_probe_ctx_t ctx = { 0, 0, 0 }; 170 | const char* rgi_psz_ext[] = { ".h266", ".266", ".vvc", ".bin", ".bit", ".raw", NULL }; 171 | const char* rgi_psz_mime[] = { "video/H266", "video/h266", "video/vvc", "video/vvc1", NULL }; 172 | 173 | demux_sys_t* p_sys; 174 | const uint8_t* p_peek; 175 | es_format_t fmt; 176 | uint8_t annexb_startcode[] = { 0,0,0,1 }; 177 | int i_ret = 0; 178 | 179 | // Restrict by type first 180 | demux_t* p_demux = (demux_t*)p_this; 181 | if (!p_demux->obj.force && 182 | !check_Property(p_demux, rgi_psz_ext, demux_IsPathExtension) && 183 | !check_Property(p_demux, rgi_psz_mime, demux_IsContentType)) 184 | { 185 | return VLC_EGENERIC; 186 | } 187 | 188 | // First check for first AnnexB header 189 | if (vlc_stream_Peek(p_demux->s, &p_peek, H26X_MIN_PEEK) == H26X_MIN_PEEK && 190 | !memcmp(p_peek, annexb_startcode, 4)) 191 | { 192 | i_ret = 1; 193 | } 194 | 195 | /* First check for first AnnexB header */ 196 | int i_probe_offset_correct = 4; 197 | if (vlc_stream_Peek(p_demux->s, &p_peek, H26X_MIN_PEEK) == H26X_MIN_PEEK && 198 | !memcmp(p_peek, annexb_startcode, 4)) 199 | { 200 | size_t i_peek = H26X_MIN_PEEK; 201 | size_t i_peek_target = H26X_MIN_PEEK; 202 | size_t i_probe_offset = 4; 203 | const uint8_t* p_probe = p_peek; 204 | bool b_synced = true; 205 | unsigned i_bitflow = 0; 206 | 207 | for (unsigned i = 0; i < H26X_NAL_COUNT; i++) 208 | { 209 | while (!b_synced) 210 | { 211 | if (i_probe_offset + H26X_MIN_PEEK >= i_peek && 212 | i_peek_target + H26X_PEEK_CHUNK <= H26X_MAX_PEEK) 213 | { 214 | i_peek_target += H26X_PEEK_CHUNK; 215 | i_peek = vlc_stream_Peek(p_demux->s, &p_peek, i_peek_target); 216 | } 217 | if (i_probe_offset + H26X_MIN_PEEK >= i_peek) 218 | break; 219 | 220 | p_probe = &p_peek[i_probe_offset]; 221 | i_bitflow = (i_bitflow << 1) | (!p_probe[0]); 222 | /* Check for annexB */ 223 | if ((p_probe[0] == 0x01 && ((i_bitflow & 0x06) == 0x06)) 224 | || (p_probe[0] == 0x00 && p_probe[-1] == 0x00 && p_probe[-2] == 0x00) 225 | ) 226 | { 227 | b_synced = true; 228 | i_probe_offset_correct = 3; 229 | i_bitflow = 0; 230 | } 231 | 232 | i_probe_offset++; 233 | } 234 | 235 | 236 | if (b_synced) 237 | { 238 | p_probe = &p_peek[i_probe_offset - i_probe_offset_correct]; 239 | i_ret = ProbeVVC(p_probe, i_peek - i_probe_offset, &ctx); 240 | } 241 | 242 | if (i_ret != 0) 243 | break; 244 | 245 | i_probe_offset += 4; 246 | b_synced = false; 247 | } 248 | } 249 | 250 | if (i_ret < 1) 251 | { 252 | if (!p_demux->obj.force) 253 | { 254 | msg_Warn(p_demux, "vvc module discarded (no startcode)"); 255 | return VLC_EGENERIC; 256 | } 257 | msg_Err(p_demux, "this doesn't look like a vvc ES stream ", "continuing anyway"); 258 | } 259 | 260 | p_demux->pf_demux = Demux; 261 | p_demux->pf_control = Control; 262 | p_demux->p_sys = p_sys = new demux_sys_t; 263 | p_sys->p_es = NULL; 264 | p_sys->frame_rate_num = 0; 265 | p_sys->frame_rate_den = 0; 266 | p_sys->baseLayerID = -1; 267 | 268 | double fps = 0; 269 | char psz_fpsvar[10]; 270 | if (sprintf(psz_fpsvar, "vvc-fps")) 271 | { 272 | fps = var_CreateGetFloat(p_demux, psz_fpsvar); 273 | } 274 | 275 | std::string filename(p_demux->psz_location); 276 | int pos = (int)filename.find_last_of("/\\"); 277 | filename = filename.substr(pos+1); 278 | 279 | if (!fps) 280 | { 281 | if (filename.size() > 3) 282 | { 283 | int pos = (int)filename.find("Hz"); 284 | if (pos == std::string::npos) pos = (int)filename.find("HZ"); 285 | if (pos == std::string::npos) pos = (int)filename.find("hz"); 286 | if (pos == std::string::npos) pos = (int)filename.find("fps"); 287 | if (pos == std::string::npos) pos = (int)filename.find("FPS"); 288 | if (pos != std::string::npos && pos > 0) 289 | { 290 | int length = 1; 291 | while (pos - length >= 0 && filename[pos - length] >= '0' && filename[pos - length] <= '9') 292 | { 293 | length++; 294 | } 295 | length--; 296 | if (length > 0) 297 | { 298 | std::string val = filename.substr(std::max(0, pos - length), length); 299 | fps = atof(val.c_str()); 300 | if (fps) 301 | { 302 | msg_Dbg(p_demux, "found frame rate in path %.2f fps", fps); 303 | } 304 | } 305 | } 306 | } 307 | } 308 | 309 | if (fps) 310 | { 311 | if (fps < 0.001f) fps = 0.001f; 312 | p_sys->frame_rate_den = 1000; 313 | p_sys->frame_rate_num = (int) (1000 * fps); 314 | date_Init(&p_sys->dts, p_sys->frame_rate_num, p_sys->frame_rate_den); 315 | } 316 | else 317 | { 318 | date_Init(&p_sys->dts, 50000, 1000); 319 | } 320 | date_Set(&p_sys->dts, VLC_TS_0); 321 | 322 | // Load the mpegvideo packetizer 323 | es_format_Init(&fmt, VIDEO_ES, VLC_CODEC_VVC); 324 | fmt.video.i_frame_rate = p_sys->dts.i_divider_num; 325 | fmt.video.i_frame_rate_base = p_sys->dts.i_divider_den; 326 | 327 | if (filename.size() > 4) 328 | { 329 | int pos = (int)filename.find("HLG_"); 330 | if (pos == std::string::npos) pos = (int)filename.find("Hlg_"); 331 | if (pos == std::string::npos) pos = (int)filename.find("hlg_"); 332 | if (pos == std::string::npos) pos = (int)filename.find("_HLG"); 333 | if (pos == std::string::npos) pos = (int)filename.find("_Hlg"); 334 | if (pos == std::string::npos) pos = (int)filename.find("_hlg"); 335 | if (pos != std::string::npos && pos > 0) 336 | { 337 | fmt.video.primaries = COLOR_PRIMARIES_BT2020; 338 | fmt.video.transfer = TRANSFER_FUNC_HLG; 339 | fmt.video.space = COLOR_SPACE_BT2020; 340 | } 341 | 342 | pos = (int)filename.find("PQ_"); 343 | if (pos == std::string::npos) pos = (int)filename.find("Pq_"); 344 | if (pos == std::string::npos) pos = (int)filename.find("pq_"); 345 | if (pos == std::string::npos) pos = (int)filename.find("_PQ"); 346 | if (pos == std::string::npos) pos = (int)filename.find("_Pq"); 347 | if (pos == std::string::npos) pos = (int)filename.find("_pq"); 348 | if (pos != std::string::npos && pos > 0) 349 | { 350 | fmt.video.primaries = COLOR_PRIMARIES_BT2020; 351 | fmt.video.transfer = TRANSFER_FUNC_SMPTE_ST2084; 352 | fmt.video.space = COLOR_SPACE_BT2020; 353 | } 354 | 355 | pos = (int)filename.find("P3_"); 356 | if (pos == std::string::npos) pos = (int)filename.find("p3_"); 357 | if (pos == std::string::npos) pos = (int)filename.find("_P3"); 358 | if (pos == std::string::npos) pos = (int)filename.find("_p3"); 359 | if (pos != std::string::npos && pos > 0) 360 | { 361 | fmt.video.primaries = COLOR_PRIMARIES_DCI_P3; 362 | fmt.video.space = COLOR_SPACE_UNDEF; 363 | } 364 | 365 | pos = (int)filename.find("_HDR10"); 366 | if (pos == std::string::npos) pos = (int)filename.find("_Hdr10"); 367 | if (pos == std::string::npos) pos = (int)filename.find("_hdr10"); 368 | if (pos != std::string::npos && pos > 0) 369 | { 370 | fmt.video.primaries = COLOR_PRIMARIES_BT2020; 371 | fmt.video.transfer = TRANSFER_FUNC_SMPTE_ST2084; 372 | fmt.video.space = COLOR_SPACE_BT2020; 373 | } 374 | } 375 | 376 | p_sys->p_packetizer = demux_PacketizerNew(p_demux, &fmt, "vvc"); 377 | 378 | if (!p_sys->p_packetizer) 379 | { 380 | free(p_sys); 381 | return VLC_EGENERIC; 382 | } 383 | 384 | return VLC_SUCCESS; 385 | } 386 | 387 | /***************************************************************************** 388 | * Demux: reads and demuxes data packets 389 | ***************************************************************************** 390 | * Returns -1 in case of error, 0 in case of EOF, 1 otherwise 391 | *****************************************************************************/ 392 | 393 | static int Demux(demux_t* p_demux) 394 | { 395 | demux_sys_t* p_sys = p_demux->p_sys; 396 | block_t* p_block_in, * p_block_out; 397 | bool b_eof = false; 398 | 399 | p_block_in = vlc_stream_Block(p_demux->s, H26X_PACKET_SIZE); 400 | if (p_block_in == NULL) 401 | { 402 | b_eof = true; 403 | } 404 | else 405 | { 406 | p_block_in->i_dts = date_Get(&p_sys->dts); 407 | } 408 | while ((p_block_out = p_sys->p_packetizer->pf_packetize(p_sys->p_packetizer, 409 | p_block_in ? &p_block_in : NULL))) 410 | { 411 | while (p_block_out) 412 | { 413 | block_t* p_next = p_block_out->p_next; 414 | 415 | p_block_out->p_next = NULL; 416 | 417 | 418 | if (p_sys->p_es == NULL) 419 | { 420 | p_sys->p_packetizer->fmt_out.b_packetized = true; 421 | p_sys->p_es = es_out_Add(p_demux->out, &p_sys->p_packetizer->fmt_out); 422 | if (!p_sys->p_es) 423 | { 424 | block_ChainRelease(p_block_out); 425 | return VLC_DEMUXER_EOF; 426 | } 427 | } 428 | 429 | bool frame = p_block_out->i_flags & BLOCK_FLAG_TYPE_MASK; 430 | const mtime_t i_frame_dts = p_block_out->i_dts; 431 | const mtime_t i_frame_length = p_block_out->i_length; 432 | uint32_t nuhLayerId = 0; 433 | if (frame) 434 | { 435 | int firstByte = 4; 436 | if (p_block_out->p_buffer[0] == 0 && p_block_out->p_buffer[1] == 0 && p_block_out->p_buffer[2] == 0 && p_block_out->p_buffer[3] == 1) 437 | { 438 | firstByte = 4; 439 | } 440 | else if (p_block_out->p_buffer[0] == 0 && p_block_out->p_buffer[1] == 0 && p_block_out->p_buffer[2] == 1) 441 | { 442 | firstByte = 3; 443 | } 444 | nuhLayerId = ((p_block_out->p_buffer[firstByte]) & 0x3f); 445 | } 446 | es_out_Send(p_demux->out, p_sys->p_es, p_block_out); 447 | if (frame) 448 | { 449 | if (!p_sys->frame_rate_den) 450 | { 451 | // Use packetizer's one 452 | if (p_sys->p_packetizer->fmt_out.video.i_frame_rate_base && 453 | p_sys->p_packetizer->fmt_out.video.i_frame_rate) 454 | { 455 | p_sys->frame_rate_num = p_sys->p_packetizer->fmt_out.video.i_frame_rate; 456 | p_sys->frame_rate_den = p_sys->p_packetizer->fmt_out.video.i_frame_rate_base; 457 | } 458 | else 459 | { 460 | p_sys->frame_rate_num = 50000; 461 | p_sys->frame_rate_den = 1000; 462 | } 463 | date_Init(&p_sys->dts, p_sys->frame_rate_num, p_sys->frame_rate_den); 464 | date_Set(&p_sys->dts, VLC_TS_0); 465 | msg_Dbg(p_demux, "using %.2f fps", (double)p_sys->frame_rate_num / p_sys->frame_rate_den); 466 | } 467 | 468 | es_out_SetPCR(p_demux->out, date_Get(&p_sys->dts)); 469 | unsigned i_nb_frames; 470 | if (p_sys->baseLayerID < 0 || (int)nuhLayerId < p_sys->baseLayerID) 471 | { 472 | p_sys->baseLayerID = nuhLayerId; 473 | } 474 | if (i_frame_length > 0) 475 | { 476 | i_nb_frames = (unsigned int)(i_frame_length * p_sys->frame_rate_num / 477 | (p_sys->frame_rate_den * CLOCK_FREQ)); 478 | } 479 | else i_nb_frames = 1; 480 | if (i_nb_frames <= 3) 481 | { 482 | if (nuhLayerId == p_sys->baseLayerID) 483 | { 484 | date_Increment(&p_sys->dts, i_nb_frames); 485 | } 486 | } 487 | else // Somehow some discontinuity 488 | { 489 | date_Set(&p_sys->dts, i_frame_dts); 490 | } 491 | } 492 | 493 | if (p_block_in) 494 | { 495 | p_block_in->i_dts = date_Get(&p_sys->dts); 496 | p_block_in->i_pts = VLC_TS_INVALID; 497 | } 498 | p_block_out = p_next; 499 | } 500 | } 501 | return (b_eof) ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS; 502 | } 503 | 504 | /***************************************************************************** 505 | * Control: 506 | *****************************************************************************/ 507 | static int Control(demux_t* p_demux, int i_query, va_list args) 508 | { 509 | if (i_query == DEMUX_SET_TIME) 510 | return VLC_EGENERIC; 511 | else 512 | return demux_vaControlHelper(p_demux->s, 513 | 0, 0, 514 | 0, 0, i_query, args); 515 | } 516 | 517 | static inline bool check_Property(demux_t* p_demux, const char** pp_psz, 518 | bool(*pf_check)(demux_t*, const char*)) 519 | { 520 | while (*pp_psz) 521 | { 522 | if (pf_check(p_demux, *pp_psz)) 523 | return true; 524 | pp_psz++; 525 | } 526 | return false; 527 | } 528 | 529 | /** 530 | * Common deinitialization 531 | */ 532 | 533 | void VvcDecoder::CloseDemux(vlc_object_t* p_this) 534 | { 535 | demux_t* p_demux = (demux_t*)p_this; 536 | demux_sys_t* p_sys = p_demux->p_sys; 537 | 538 | demux_PacketizerDestroy(p_sys->p_packetizer); 539 | delete p_sys; 540 | } 541 | -------------------------------------------------------------------------------- /DecoderPluginVLC/vvc_packetizer.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * vvc_packetizer.cpp: h.266/vvc video packetizer 3 | ***************************************************************************** 4 | * Copyright (C) 2021 interdigital 5 | * 6 | 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU Lesser General Public License as published by 9 | * the Free Software Foundation; either version 2.1 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 20 | *****************************************************************************/ 21 | 22 | /***************************************************************************** 23 | * Preamble 24 | *****************************************************************************/ 25 | #if defined(_MSC_VER) 26 | #define NOMINMAX 27 | #include 28 | typedef SSIZE_T ssize_t; 29 | #define strcasecmp(s1, s2) _stricmp(s1, s2) 30 | #endif 31 | 32 | #ifdef HAVE_CONFIG_H 33 | # include "config.h" 34 | #endif 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include "packetizer_helper.h" 44 | #include "startcode_helper.h" 45 | 46 | #include 47 | #include "vvc_nal.h" 48 | 49 | /***************************************************************************** 50 | * Module descriptor 51 | *****************************************************************************/ 52 | namespace VvcDecoder 53 | { 54 | static const uint8_t* startcode_FindAnnexB(const uint8_t* p, const uint8_t* end); 55 | int OpenPack(vlc_object_t*); 56 | void ClosePack(vlc_object_t*); 57 | } 58 | 59 | /**************************************************************************** 60 | * Local prototypes 61 | ****************************************************************************/ 62 | static block_t *PacketizeAnnexB(decoder_t *, block_t **); 63 | static void PacketizeFlush( decoder_t * ); 64 | static void PacketizeReset(void *p_private, bool b_broken); 65 | static block_t *PacketizeParse(void *p_private, bool *pb_ts_used, block_t *); 66 | static block_t *ParseNALBlock(decoder_t *, bool *pb_ts_used, block_t *); 67 | static int PacketizeValidate(void *p_private, block_t *); 68 | static block_t* GatherAndValidateChain(block_t* p_outputchain); 69 | 70 | struct decoder_sys_t 71 | { 72 | /* */ 73 | packetizer_t packetizer; 74 | 75 | struct 76 | { 77 | block_t *p_chain; 78 | block_t **pp_chain_last; 79 | } frame, frame2; 80 | 81 | //uint8_t i_nal_length_size; 82 | bool b_init_sequence_complete; 83 | int i_nb_frames; 84 | 85 | date_t dts; 86 | mtime_t pts; 87 | int lastTid; 88 | bool b_need_ts; 89 | bool sliceInPicture; 90 | bool gotSps; 91 | bool gotPps; 92 | int baseLayerID; 93 | }; 94 | 95 | static const uint8_t p_vcc_startcode[3] = { 0x00, 0x00, 0x01}; 96 | /**************************************************************************** 97 | * Helpers 98 | ****************************************************************************/ 99 | static inline void InitQueue( block_t **pp_head, block_t ***ppp_tail ) 100 | { 101 | *pp_head = NULL; 102 | *ppp_tail = pp_head; 103 | } 104 | #define INITQ(name) InitQueue(&p_sys->name.p_chain, &p_sys->name.pp_chain_last) 105 | 106 | static block_t * OutputQueues(decoder_sys_t *p_sys, bool b_valid) 107 | { 108 | block_t *p_output = NULL; 109 | block_t **pp_output_last = &p_output; 110 | uint32_t i_flags = 0; /* Because block_ChainGather does not merge flags or times */ 111 | 112 | if (p_sys->frame.p_chain) 113 | { 114 | i_flags |= p_sys->frame.p_chain->i_flags; 115 | block_ChainLastAppend(&pp_output_last, p_sys->frame.p_chain); 116 | 117 | mtime_t dts = VLC_TS_INVALID, pts = VLC_TS_INVALID; 118 | block_t* lastBlock = p_sys->frame.p_chain; 119 | while (lastBlock) 120 | { 121 | if (lastBlock->i_dts > VLC_TS_INVALID) 122 | { 123 | dts = lastBlock->i_dts; 124 | } 125 | if (lastBlock->i_pts > VLC_TS_INVALID) 126 | { 127 | pts = lastBlock->i_pts; 128 | } 129 | lastBlock = lastBlock->p_next; 130 | } 131 | 132 | if (dts > VLC_TS_INVALID) 133 | { 134 | p_output->i_dts = dts; 135 | } 136 | else 137 | { 138 | p_output->i_dts = date_Get(&p_sys->dts); 139 | } 140 | if (pts > VLC_TS_INVALID) 141 | { 142 | p_output->i_pts = pts; 143 | } 144 | else 145 | { 146 | p_output->i_pts = p_sys->pts; 147 | } 148 | INITQ(frame); 149 | } 150 | 151 | if(p_output) 152 | { 153 | p_output->i_flags |= i_flags; 154 | if(!b_valid) 155 | p_output->i_flags |= BLOCK_FLAG_DROP; 156 | } 157 | 158 | return p_output; 159 | } 160 | 161 | const uint8_t* VvcDecoder::startcode_FindAnnexB(const uint8_t* p, const uint8_t* end) 162 | { 163 | for (end -= sizeof(p_vcc_startcode) - 1; p < end; p++) 164 | { 165 | bool match = true; 166 | for (int i=0; ifmt_in.i_codec != VLC_FOURCC('h', '2', '6', '6') 184 | && p_dec->fmt_in.i_codec != VLC_FOURCC('v', 'v', 'c', '1')) 185 | return VLC_EGENERIC; 186 | 187 | p_dec->p_sys = p_sys = (decoder_sys_t *)calloc(1, sizeof(decoder_sys_t)); 188 | if (!p_dec->p_sys) 189 | return VLC_ENOMEM; 190 | 191 | INITQ(frame); 192 | INITQ(frame2); 193 | p_sys->sliceInPicture = false; 194 | p_sys->lastTid = 0; 195 | p_sys->gotPps = false; 196 | p_sys->gotSps = false; 197 | p_sys->b_init_sequence_complete = false; 198 | p_sys->i_nb_frames = 0; 199 | p_sys->baseLayerID = -1; 200 | 201 | packetizer_t* p_pack = &p_dec->p_sys->packetizer; 202 | p_pack->i_state = STATE_NOSYNC; 203 | block_BytestreamInit(&p_pack->bytestream); 204 | p_pack->i_offset = 0; 205 | 206 | packetizer_Init(&p_dec->p_sys->packetizer, 207 | p_vcc_startcode, sizeof(p_vcc_startcode), VvcDecoder::startcode_FindAnnexB, 208 | p_vcc_startcode, 1, 5, 209 | PacketizeReset, PacketizeParse, PacketizeValidate, p_dec); 210 | 211 | /* Copy properties */ 212 | es_format_Copy(&p_dec->fmt_out, &p_dec->fmt_in); 213 | p_dec->fmt_out.b_packetized = true; 214 | 215 | /* Init timings */ 216 | if( p_dec->fmt_in.video.i_frame_rate_base && 217 | p_dec->fmt_in.video.i_frame_rate && 218 | p_dec->fmt_in.video.i_frame_rate <= UINT_MAX ) 219 | date_Init( &p_sys->dts, p_dec->fmt_in.video.i_frame_rate, 220 | p_dec->fmt_in.video.i_frame_rate_base ); 221 | else 222 | date_Init( &p_sys->dts, 50000, 1000 ); 223 | date_Set( &p_sys->dts, VLC_TS_INVALID ); 224 | p_sys->pts = VLC_TS_INVALID; 225 | p_sys->b_need_ts = true; 226 | 227 | /* Set callbacks */ 228 | const uint8_t *p_extra = (uint8_t *)p_dec->fmt_in.p_extra; 229 | const size_t i_extra = p_dec->fmt_in.i_extra; 230 | 231 | p_dec->pf_packetize = PacketizeAnnexB; 232 | p_dec->pf_flush = PacketizeFlush; 233 | 234 | if(p_dec->fmt_out.i_extra) 235 | { 236 | /* Feed with AnnexB VPS/SPS/PPS/SEI extradata */ 237 | packetizer_Header(&p_sys->packetizer, 238 | (uint8_t*)p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra); 239 | } 240 | 241 | return VLC_SUCCESS; 242 | } 243 | 244 | /***************************************************************************** 245 | * Close 246 | *****************************************************************************/ 247 | void VvcDecoder::ClosePack(vlc_object_t *p_this) 248 | { 249 | decoder_t *p_dec = (decoder_t*)p_this; 250 | decoder_sys_t *p_sys = p_dec->p_sys; 251 | 252 | block_t *b = p_sys->frame.p_chain; 253 | while (b) 254 | { 255 | msg_Warn(p_dec, "close packetizer with chain %d ", b->i_buffer); 256 | b = b->p_next; 257 | } 258 | b = p_sys->packetizer.bytestream.p_chain; 259 | while (b) 260 | { 261 | msg_Warn(p_dec, "close packetizer with bytestream %d - offset %d ", b->i_buffer, p_sys->packetizer.bytestream.i_block_offset); 262 | b = b->p_next; 263 | } 264 | msg_Warn(p_dec, "close packetizer - packetized %d frames ", p_sys->i_nb_frames); 265 | 266 | 267 | packetizer_Clean(&p_sys->packetizer); 268 | 269 | block_ChainRelease(p_sys->frame.p_chain); 270 | 271 | free(p_sys); 272 | } 273 | 274 | /**************************************************************************** 275 | * Packetize 276 | ****************************************************************************/ 277 | static block_t *PacketizeAnnexB(decoder_t *p_dec, block_t **pp_block) 278 | { 279 | decoder_sys_t *p_sys = p_dec->p_sys; 280 | block_t *output = packetizer_Packetize(&p_sys->packetizer, pp_block); 281 | if (!output && !pp_block) 282 | { 283 | if (p_sys->frame2.p_chain) 284 | { 285 | block_ChainLastAppend(&p_sys->frame.pp_chain_last, p_sys->frame2.p_chain); 286 | INITQ(frame2); 287 | } 288 | block_t* flushOutput = OutputQueues(p_sys, p_sys->b_init_sequence_complete); 289 | block_ChainAppend(&output, flushOutput); 290 | 291 | output = GatherAndValidateChain(output); 292 | } 293 | if (output) 294 | { 295 | p_sys->i_nb_frames++; 296 | } 297 | 298 | return output; 299 | } 300 | 301 | static void PacketizeFlush( decoder_t *p_dec ) 302 | { 303 | decoder_sys_t *p_sys = p_dec->p_sys; 304 | msg_Warn(p_dec, "packetizer flush called at pts: %d ", p_sys->pts); 305 | packetizer_Flush( &p_sys->packetizer ); 306 | } 307 | 308 | /**************************************************************************** 309 | * Packetizer Helpers 310 | ****************************************************************************/ 311 | static void PacketizeReset(void *p_private, bool b_broken) 312 | { 313 | VLC_UNUSED(b_broken); 314 | 315 | decoder_t *p_dec = (decoder_t *)p_private; 316 | decoder_sys_t *p_sys = p_dec->p_sys; 317 | 318 | msg_Warn(p_dec, "packetizer reset called at pts: %d ", p_sys->pts); 319 | if (p_sys->frame2.p_chain) 320 | { 321 | block_ChainLastAppend(&p_sys->frame.pp_chain_last, p_sys->frame2.p_chain); 322 | INITQ(frame2); 323 | } 324 | 325 | block_t *p_out = OutputQueues(p_sys, false); 326 | if(p_out) 327 | block_ChainRelease(p_out); 328 | 329 | INITQ(frame); 330 | INITQ(frame2); 331 | p_sys->b_init_sequence_complete = false; 332 | p_sys->sliceInPicture = false; 333 | p_sys->lastTid = 0; 334 | p_sys->gotPps = false; 335 | p_sys->gotSps = false; 336 | p_sys->i_nb_frames = 0; 337 | date_Set(&p_sys->dts, VLC_TS_INVALID); 338 | p_sys->pts = VLC_TS_INVALID; 339 | p_sys->b_need_ts = true; 340 | } 341 | 342 | 343 | static block_t *GatherAndValidateChain(block_t *p_outputchain) 344 | { 345 | block_t *p_output = NULL; 346 | 347 | if(p_outputchain) 348 | { 349 | if(p_outputchain->i_flags & BLOCK_FLAG_DROP) 350 | p_output = p_outputchain; /* Avoid useless gather */ 351 | else 352 | p_output = block_ChainGather(p_outputchain); 353 | } 354 | 355 | if(p_output && (p_output->i_flags & BLOCK_FLAG_DROP)) 356 | { 357 | block_ChainRelease(p_output); /* Chain! see above */ 358 | p_output = NULL; 359 | } 360 | 361 | return p_output; 362 | } 363 | 364 | /*static void SetOutputBlockProperties(decoder_t *p_dec, block_t *p_output) 365 | { 366 | decoder_sys_t *p_sys = p_dec->p_sys; 367 | // Set frame duration 368 | if(p_sys->p_active_sps) 369 | { 370 | uint8_t i_num_clock_ts = hevc_get_num_clock_ts(p_sys->p_active_sps, 371 | p_sys->p_timing); 372 | const mtime_t i_start = date_Get(&p_sys->dts); 373 | if( i_start != VLC_TS_INVALID ) 374 | { 375 | date_Increment(&p_sys->dts, i_num_clock_ts); 376 | p_output->i_length = date_Get(&p_sys->dts) - i_start; 377 | } 378 | p_sys->pts = VLC_TS_INVALID; 379 | } 380 | hevc_release_sei_pic_timing(p_sys->p_timing); 381 | p_sys->p_timing = NULL; 382 | }*/ 383 | 384 | /***************************************************************************** 385 | * ParseNALBlock: parses annexB type NALs 386 | * All p_frag blocks are required to start with 0 0 0 1 4-byte startcode 387 | *****************************************************************************/ 388 | static block_t *ParseNALBlock(decoder_t *p_dec, bool *pb_ts_used, block_t *p_frag) 389 | { 390 | decoder_sys_t *p_sys = p_dec->p_sys; 391 | *pb_ts_used = false; 392 | 393 | if(p_sys->b_need_ts) 394 | { 395 | if(p_frag->i_dts > VLC_TS_INVALID) 396 | date_Set(&p_sys->dts, p_frag->i_dts); 397 | p_sys->pts = p_frag->i_pts; 398 | if(date_Get( &p_sys->dts ) != VLC_TS_INVALID) 399 | p_sys->b_need_ts = false; 400 | *pb_ts_used = true; 401 | } 402 | 403 | if(unlikely(p_frag->i_buffer < 5)) 404 | { 405 | msg_Warn(p_dec,"NAL too small"); 406 | block_Release(p_frag); 407 | return NULL; 408 | } 409 | 410 | if(p_frag->p_buffer[4] & 0x80) 411 | { 412 | msg_Warn(p_dec,"Forbidden zero bit not null, corrupted NAL"); 413 | block_Release(p_frag); 414 | return GatherAndValidateChain(OutputQueues(p_sys, false)); // will drop 415 | } 416 | 417 | // get next NAL unit type 418 | int firstByte = 4; 419 | if (p_frag->p_buffer[0] == 0 && p_frag->p_buffer[1] == 0 && p_frag->p_buffer[2] == 0 && p_frag->p_buffer[3] == 1) 420 | { 421 | firstByte = 4; 422 | } 423 | else if (p_frag->p_buffer[0] == 0 && p_frag->p_buffer[1] == 0 && p_frag->p_buffer[2] == 1) 424 | { 425 | firstByte = 3; 426 | } 427 | uint32_t nuhLayerId = ((p_frag->p_buffer[firstByte]) & 0x3f); 428 | vvc_nal_unit_type_e i_nal_type = (vvc_nal_unit_type_e) ((p_frag->p_buffer[firstByte+1] >> 3) & 0x1f); 429 | int i_nal_temporal_ID = ((p_frag->p_buffer[firstByte+1]) & 0x07) - 1; 430 | const mtime_t dts = p_frag->i_dts, pts = p_frag->i_pts; 431 | block_t * p_output = NULL; 432 | 433 | bool isNewPicture = false; 434 | bool currentIsFirstSlice = false; 435 | bool maybeNew = false; 436 | switch (i_nal_type)//nalu.m_nalUnitType) 437 | { 438 | // NUT that indicate the start of a new picture 439 | case VVC_NAL_ACCESS_UNIT_DELIMITER: 440 | case VVC_NAL_OPI: 441 | case VVC_NAL_DCI: 442 | case VVC_NAL_VPS: 443 | case VVC_NAL_SPS: 444 | case VVC_NAL_PPS: 445 | case VVC_NAL_PH: 446 | isNewPicture = p_sys->sliceInPicture; 447 | break; 448 | 449 | // NUT that may be the start of a new picture - check first bit in slice header 450 | case VVC_NAL_CODED_SLICE_TRAIL: 451 | case VVC_NAL_CODED_SLICE_STSA: 452 | case VVC_NAL_CODED_SLICE_RASL: 453 | case VVC_NAL_CODED_SLICE_RADL: 454 | case VVC_NAL_CODED_SLICE_IDR_W_RADL: 455 | case VVC_NAL_CODED_SLICE_IDR_N_LP: 456 | case VVC_NAL_CODED_SLICE_CRA: 457 | case VVC_NAL_CODED_SLICE_GDR: 458 | case VVC_NAL_RESERVED_VCL_4: 459 | case VVC_NAL_RESERVED_VCL_5: 460 | case VVC_NAL_RESERVED_VCL_6: 461 | case VVC_NAL_RESERVED_IRAP_VCL_11: 462 | p_frag->i_flags |= BLOCK_FLAG_TYPE_P; 463 | // checkPictureHeaderInSliceHeaderFlag 464 | currentIsFirstSlice = ((p_frag->p_buffer[6] >> 7) & 0x1) || !p_sys->sliceInPicture; 465 | isNewPicture = p_sys->sliceInPicture && currentIsFirstSlice; 466 | p_sys->sliceInPicture = true; 467 | break; 468 | 469 | case VVC_NAL_EOS: 470 | case VVC_NAL_EOB: 471 | isNewPicture = true; 472 | break; 473 | case VVC_NAL_SUFFIX_SEI: 474 | case VVC_NAL_SUFFIX_APS: 475 | break; 476 | default: 477 | maybeNew = p_sys->sliceInPicture; 478 | break; 479 | } 480 | 481 | if (!p_sys->gotPps && i_nal_type == VVC_NAL_PPS) 482 | { 483 | p_sys->gotPps = true; 484 | } 485 | if (!p_sys->gotSps && i_nal_type == VVC_NAL_SPS) 486 | { 487 | p_sys->gotSps = true; 488 | } 489 | if (isNewPicture) 490 | { 491 | if (!p_sys->b_init_sequence_complete && p_sys->gotPps && p_sys->gotSps) 492 | { 493 | p_sys->b_init_sequence_complete = true; 494 | } 495 | if (p_sys->frame.p_chain) 496 | { 497 | p_sys->frame.p_chain->i_flags |= (p_sys->lastTid < 2 )? BLOCK_FLAG_TYPE_P: BLOCK_FLAG_TYPE_B; 498 | // Starting new frame: return previous frame data for output 499 | if (p_sys->baseLayerID < 0 || (int)nuhLayerId < p_sys->baseLayerID) 500 | { 501 | p_sys->baseLayerID = nuhLayerId; 502 | } 503 | if (nuhLayerId == p_sys->baseLayerID) 504 | { 505 | date_Increment(&p_sys->dts, 1); 506 | if (dts > VLC_TS_INVALID) 507 | date_Set(&p_sys->dts, dts); 508 | p_sys->pts = pts; 509 | } 510 | *pb_ts_used = true; 511 | p_output = OutputQueues(p_sys, p_sys->b_init_sequence_complete); 512 | } 513 | p_sys->sliceInPicture = currentIsFirstSlice; 514 | } 515 | p_sys->lastTid = i_nal_temporal_ID; 516 | if (maybeNew) 517 | { 518 | block_ChainLastAppend(&p_sys->frame2.pp_chain_last, p_frag); 519 | } 520 | else 521 | { 522 | if (p_sys->frame2.p_chain) 523 | { 524 | block_ChainLastAppend(&p_sys->frame.pp_chain_last, p_sys->frame2.p_chain); 525 | INITQ(frame2); 526 | } 527 | block_ChainLastAppend(&p_sys->frame.pp_chain_last, p_frag); 528 | } 529 | 530 | p_output = GatherAndValidateChain(p_output); 531 | 532 | return p_output; 533 | } 534 | 535 | static block_t *PacketizeParse(void *p_private, bool *pb_ts_used, block_t *p_block) 536 | { 537 | decoder_t *p_dec = (decoder_t *)p_private; 538 | decoder_sys_t *p_sys = p_dec->p_sys; 539 | 540 | /* Remove trailing 0 bytes */ 541 | while (p_block->i_buffer > 5 && p_block->p_buffer[p_block->i_buffer-1] == 0x00 ) 542 | p_block->i_buffer--; 543 | 544 | p_block = ParseNALBlock( p_dec, pb_ts_used, p_block ); 545 | 546 | return p_block; 547 | } 548 | 549 | static int PacketizeValidate( void *p_private, block_t *p_au ) 550 | { 551 | VLC_UNUSED(p_private); 552 | VLC_UNUSED(p_au); 553 | return VLC_SUCCESS; 554 | } 555 | 556 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /DecoderPluginVLC/libVVCDecoder_plugin.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * libVVCDecoder_plugin.cpp: h.266/vvc video decoder wrapper 3 | ***************************************************************************** 4 | * Copyright (C) 2021 interdigital 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU Lesser General Public License as published by 9 | * the Free Software Foundation; either version 2.1 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 20 | *****************************************************************************/ 21 | 22 | /***************************************************************************** 23 | * Preamble 24 | *****************************************************************************/ 25 | #if defined(_MSC_VER) 26 | #define NOMINMAX 27 | #include 28 | typedef SSIZE_T ssize_t; 29 | #define strcasecmp(s1, s2) _stricmp(s1, s2) 30 | #include 31 | #include 32 | #else 33 | #include 34 | #endif 35 | 36 | #include 37 | #include 38 | #include 39 | #include "LibVTMDec.h" 40 | 41 | #ifdef HAVE_CONFIG_H 42 | # include "config.h" 43 | #endif 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #define N_(str) (str) 50 | 51 | /***************************************************************************** 52 | * decoder_sys_t : video decoder descriptor 53 | *****************************************************************************/ 54 | struct decoder_sys_t 55 | { 56 | /* 57 | * Common properties 58 | */ 59 | date_t pts; 60 | 61 | DecVTMInstance* decVtm = nullptr; 62 | 63 | bool b_first_frame; 64 | bool b_format_init; 65 | bool b_frameRateDetect; 66 | bool enable_hurryMode; 67 | mtime_t firstBlock_dts; 68 | bool firstBlock; 69 | mtime_t lastOutput_pts; 70 | mtime_t firstOutput_pts; 71 | mtime_t firstOutput_time; 72 | mtime_t lastOutput_time; 73 | static const int MAX_SPEED_UP_LEVEL = 4; 74 | static const int SPDUP_DELAY_BASE = 10; 75 | int speedUpLevel; 76 | int speedUpLevel_delai_increase; 77 | int speedUpLevel_delai_decrease; 78 | mtime_t speedUpLevel_previous_lateness; 79 | mtime_t speedUpLevel_delai_derivative; 80 | mtime_t dts1; 81 | mtime_t diff_dts; 82 | mtime_t dts2; 83 | size_t dec_frame_count; 84 | size_t out_frame_count; 85 | struct layer_info 86 | { 87 | int id; 88 | int width; 89 | int height; 90 | int posx; 91 | int posy; 92 | }; 93 | std::vector outputLayers; 94 | picture_t* p_pic; 95 | }; 96 | 97 | /**************************************************************************** 98 | * Local prototypes 99 | ****************************************************************************/ 100 | static int OpenDecoder(vlc_object_t*); 101 | static void CloseDec(vlc_object_t*); 102 | static int DecodeFrame(decoder_t* p_dec, block_t* p_block); 103 | static void FillPicture(decoder_t* p_dec, picture_t* p_pic, int posx, int posy, 104 | int picWidth, int picHeight, short* planes[3], int strides[3]); 105 | static void Flush(decoder_t* p_dec); 106 | static bool getOutputFrame(decoder_t* p_dec, bool waitUntilReady, mtime_t i_dts); 107 | static int initVideoFormat(decoder_t* p_dec, decoder_sys_t* p_sys, 108 | vlc_fourcc_t videoFormat = VLC_CODEC_I420_10L, 109 | unsigned int frame_width = 0, unsigned int frame_height = 0); 110 | static int initVideoFrameRate(decoder_t* p_dec, decoder_sys_t* p_sys, 111 | unsigned int i_frame_rate = 0, unsigned int i_frame_rate_base = 0); 112 | 113 | namespace VvcDecoder 114 | { 115 | int OpenPack(vlc_object_t*); 116 | void ClosePack(vlc_object_t*); 117 | 118 | int OpenDemux(vlc_object_t*); 119 | void CloseDemux(vlc_object_t*); 120 | 121 | block_t* PacketizeVVC(decoder_t* p_dec, block_t** pp_block); 122 | } 123 | 124 | /***************************************************************************** 125 | * Module descriptor 126 | *****************************************************************************/ 127 | vlc_module_begin() 128 | set_shortname(N_("vvcdec")) 129 | set_description(N_("VVC decoder")) 130 | set_capability("video decoder", 50) 131 | set_category(CAT_INPUT) 132 | set_subcategory(SUBCAT_INPUT_VCODEC) 133 | set_callbacks(OpenDecoder, CloseDec) 134 | add_integer("nb-threads", 0, N_("Number of threads for decoding"), N_("number of threads for decoding in the range [1-32]; 0: automatic detection of cores "), false) 135 | add_integer("nb-threads-parsing", -1, N_("Maximum number of threads for CABAC parsing"), N_("Maximum number of threads for CABAC parsing (from same pool as decoding threads) [1-32]; -1: auto; 0: sequantial parsing and decoding"), false) 136 | add_integer("target-layer-set", -1, N_("Target output layer set"), N_("Target output layer set (for multi-layer streams)"), false) 137 | add_bool("vvc-enable-hurry-mode", true, N_("Enable hurry-up mode"), N_("hurry-up mode: skip decoding pictures if late"), false) 138 | add_string("vvc-opt", "", N_("other decoder options"), N_("generic decoder option: --option1=value1 --option2=value2 ... --optionN=valueN"), false) 139 | 140 | add_submodule() 141 | set_description(N_("VVC binary demuxer")) 142 | set_capability("demux", 100) 143 | set_category(CAT_INPUT) 144 | set_subcategory(SUBCAT_INPUT_DEMUX) 145 | set_callbacks(VvcDecoder::OpenDemux, VvcDecoder::CloseDemux) 146 | add_float("vvc-fps", 0, N_("Frames per second"), N_("Frames per Second; 0: try automatic, default 50Hz"), false) 147 | 148 | add_submodule() 149 | set_category(CAT_SOUT) 150 | set_subcategory(SUBCAT_SOUT_PACKETIZER) 151 | set_description(N_("VVC/H.266 video packetizer")) 152 | set_capability("packetizer", 50) 153 | set_callbacks(VvcDecoder::OpenPack, VvcDecoder::ClosePack) 154 | 155 | vlc_module_end() 156 | 157 | /** 158 | * initialization for decoder 159 | */ 160 | static int OpenDecoder(vlc_object_t * p_this) 161 | { 162 | decoder_t* p_dec = (decoder_t*)p_this; 163 | 164 | // Allocate the memory needed to store the decoder's structure 165 | decoder_sys_t* p_sys = new decoder_sys_t; 166 | if (unlikely(p_sys == NULL)) 167 | return VLC_ENOMEM; 168 | 169 | //////////// 170 | char psz_threadsvar[30]; 171 | int nbThreads = 0, nbThreadsForParsing = -1; 172 | if (sprintf(psz_threadsvar, "nb-threads")) 173 | { 174 | nbThreads = std::max(nbThreads, (int)var_CreateGetInteger(p_dec, psz_threadsvar)); 175 | } 176 | 177 | if(nbThreads <= 0) 178 | { 179 | #if _WIN32 180 | int processor_count = 1; 181 | { 182 | DWORD length = 0; 183 | const BOOL result_first = GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, &length); 184 | assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER); 185 | 186 | std::unique_ptr< uint8_t[] > buffer(new uint8_t[length]); 187 | const PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info = 188 | reinterpret_cast(buffer.get()); 189 | 190 | const BOOL result_second = GetLogicalProcessorInformationEx(RelationProcessorCore, info, &length); 191 | assert(result_second != FALSE); 192 | 193 | processor_count = 0; 194 | size_t offset = 0; 195 | do { 196 | const PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX current_info = 197 | reinterpret_cast(buffer.get() + offset); 198 | offset += current_info->Size; 199 | ++processor_count; 200 | } while (offset < length); 201 | } 202 | 203 | #else 204 | const auto processor_count = std::thread::hardware_concurrency(); 205 | #endif 206 | msg_Info(p_dec, "found %d cores", processor_count); 207 | nbThreads = std::max(1, (int)processor_count); 208 | } 209 | if (sprintf(psz_threadsvar, "nb-threads-parsing")) 210 | { 211 | nbThreadsForParsing = std::max(nbThreadsForParsing, (int)var_CreateGetInteger(p_dec, psz_threadsvar)); 212 | } 213 | if(nbThreadsForParsing <= -1) 214 | { 215 | nbThreadsForParsing = std::max(1, nbThreads / 2); 216 | msg_Info(p_dec, "use %d threads for parsing", nbThreadsForParsing); 217 | } 218 | 219 | char psz_hurryvar[30]; 220 | p_sys->enable_hurryMode = true; 221 | if (sprintf(psz_hurryvar, "vvc-enable-hurry-mode")) 222 | { 223 | p_sys->enable_hurryMode = var_CreateGetBool(p_dec, psz_hurryvar); 224 | } 225 | 226 | char psz_targetLayer[30]; 227 | int targetLayerSet = -1; 228 | if (sprintf(psz_targetLayer, "target-layer-set")) 229 | { 230 | targetLayerSet = (int)var_CreateGetInteger(p_dec, psz_targetLayer); 231 | } 232 | 233 | char psz_vvcOpt[30]; 234 | char *opt; 235 | if (sprintf(psz_vvcOpt, "vvc-opt")) 236 | { 237 | opt = var_CreateGetString(p_dec, psz_vvcOpt); 238 | } 239 | 240 | #ifdef WIN32 241 | SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); 242 | char* baseName = strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__); 243 | HMODULE loadLibRes = LoadLibrary(VTM_LIB_NAME); 244 | if (!loadLibRes) 245 | { 246 | msg_Info(p_dec, "%s: could not load library %s on default path, try plugin directory", baseName, VTM_LIB_NAME); 247 | char currentDllDir[MAX_PATH]; 248 | HMODULE hm = NULL; 249 | 250 | if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 251 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 252 | (LPCSTR) & __FUNCTION__, &hm) == 0) 253 | { 254 | int ret = GetLastError(); 255 | msg_Info(p_dec, "GetModuleHandle failed, error = %d\n", ret); 256 | return VLC_EGENERIC; 257 | } 258 | if (GetModuleFileName(hm, currentDllDir, sizeof(currentDllDir)) == 0) 259 | { 260 | int ret = GetLastError(); 261 | msg_Info(p_dec, "GetModuleFileName failed, error = %d\n", ret); 262 | return VLC_EGENERIC; 263 | } 264 | char *lastOfDir = std::max(strrchr(currentDllDir, '/'), strrchr(currentDllDir, '\\')); 265 | if (lastOfDir) 266 | { 267 | *lastOfDir = '\0'; 268 | } 269 | msg_Info(p_dec, "found directory %s\n", currentDllDir); 270 | 271 | char dllPath[MAX_PATH]; 272 | dllPath[0] = '\0'; 273 | strcat_s(dllPath, currentDllDir); 274 | dllPath[strlen(dllPath)+1] = '\0'; 275 | dllPath[strlen(dllPath)] = '/'; 276 | strcat_s(dllPath, VTM_LIB_NAME); 277 | 278 | 279 | msg_Info(p_dec, "try to load dll %s", dllPath); 280 | HMODULE loaded = LoadLibrary(dllPath); 281 | 282 | if (!loaded) 283 | { 284 | msg_Info(p_dec, "%s: could not load library %s, in plugin directory %s", baseName, VTM_LIB_NAME, currentDllDir); 285 | vlc_dialog_display_error(p_dec, "could not load dll", 286 | "VLC could not decode library %s for codec plugin %s (tried to load from directory %s)", 287 | VTM_LIB_NAME, baseName, currentDllDir); 288 | return VLC_EGENERIC; 289 | } 290 | } 291 | msg_Info(p_dec, "%s: successfully loaded library %s", baseName, VTM_LIB_NAME); 292 | #endif 293 | 294 | msg_Info(p_dec, "using decoder with cfg --nbThreads=%d --nbThreadsForParsing=%d %s", nbThreads, nbThreadsForParsing, opt); 295 | // create & initialize internal classes 296 | if (!(p_sys->decVtm = decVTM_create(nbThreads, nbThreadsForParsing, targetLayerSet, opt))) 297 | { 298 | return VLC_EGENERIC; 299 | } 300 | 301 | p_sys->b_first_frame = true; 302 | p_sys->lastOutput_pts = VLC_TS_INVALID; 303 | p_sys->firstOutput_pts = VLC_TS_INVALID; 304 | p_sys->firstOutput_time = VLC_TS_INVALID; 305 | p_sys->lastOutput_time = VLC_TS_INVALID; 306 | p_sys->firstBlock_dts = VLC_TS_INVALID; 307 | p_sys->firstBlock = true; 308 | p_sys->b_format_init = true; 309 | p_sys->b_frameRateDetect = false; 310 | p_sys->dec_frame_count = 0; 311 | p_sys->out_frame_count = 0; 312 | p_sys->speedUpLevel = 0; 313 | p_sys->speedUpLevel_delai_increase = 0; 314 | p_sys->speedUpLevel_delai_decrease = 0; 315 | p_sys->speedUpLevel_previous_lateness = 0; 316 | p_sys->speedUpLevel_delai_derivative = 0; 317 | 318 | ///////////////////////////// 319 | 320 | if (initVideoFrameRate(p_dec, p_sys) != VLC_SUCCESS) 321 | { 322 | return VLC_EGENERIC; 323 | } 324 | p_dec->p_sys = p_sys; 325 | p_dec->pf_decode = DecodeFrame; 326 | p_dec->pf_flush = Flush; 327 | p_dec->i_extra_picture_buffers = 32; 328 | 329 | return VLC_SUCCESS; 330 | } 331 | 332 | static int initVideoFormat(decoder_t* p_dec, decoder_sys_t* p_sys, 333 | vlc_fourcc_t videoFormat, 334 | unsigned int frame_width, unsigned int frame_height) 335 | { 336 | const vlc_chroma_description_t* dsc = 337 | vlc_fourcc_GetChromaDescription(videoFormat); 338 | if (dsc == NULL || dsc->plane_count == 0) 339 | return VLC_EGENERIC; 340 | 341 | if (!p_dec->fmt_in.video.i_visible_width) 342 | p_dec->fmt_in.video.i_visible_width = p_dec->fmt_in.video.i_width; 343 | if (!p_dec->fmt_in.video.i_visible_height) 344 | p_dec->fmt_in.video.i_visible_height = p_dec->fmt_in.video.i_height; 345 | 346 | es_format_Init(&p_dec->fmt_out, VIDEO_ES, videoFormat); 347 | 348 | if (frame_width == 0 || frame_height == 0) 349 | { 350 | frame_width = p_dec->fmt_in.video.i_width; 351 | frame_height = p_dec->fmt_in.video.i_height; 352 | } 353 | 354 | p_dec->fmt_out.video.primaries = p_dec->fmt_in.video.primaries; 355 | p_dec->fmt_out.video.transfer = p_dec->fmt_in.video.transfer; 356 | p_dec->fmt_out.video.space = p_dec->fmt_in.video.space; 357 | unsigned int primaries = 0, transfer = 0, matrix = -1, full_range_flag = -1, maxCLL = 0, maxFALL = 0; 358 | if (decVTM_getColourDescriptionInfo(p_sys->decVtm, &primaries, &transfer, &matrix, &full_range_flag, &maxCLL, &maxFALL)) 359 | { 360 | switch (primaries) 361 | { 362 | case 0: 363 | break; 364 | case 1: 365 | p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT709; 366 | break; 367 | case 4: 368 | p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT470_M; 369 | break; 370 | case 5: 371 | p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT601_625; 372 | break; 373 | case 6: 374 | p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT601_525; 375 | break; 376 | case 7: 377 | p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_SMTPE_240; 378 | break; 379 | case 9: 380 | p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT2020; 381 | break; 382 | case 11: 383 | case 12: 384 | p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_DCI_P3; 385 | break; 386 | default: 387 | p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_UNDEF; 388 | } 389 | 390 | switch (transfer) 391 | { 392 | case 0: 393 | break; 394 | case 1: 395 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_BT709; 396 | break; 397 | case 4: 398 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_BT470_M; 399 | break; 400 | case 5: 401 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_BT470_BG; 402 | break; 403 | case 6: 404 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_SMPTE_170; 405 | break; 406 | case 7: 407 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_SMPTE_240; 408 | break; 409 | case 8: 410 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_LINEAR; 411 | break; 412 | case 13: 413 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_SRGB; 414 | break; 415 | case 14: 416 | case 15: 417 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_BT2020; 418 | break; 419 | case 16: 420 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_SMPTE_ST2084; 421 | break; 422 | case 18: 423 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_HLG; 424 | break; 425 | default: 426 | p_dec->fmt_out.video.transfer = TRANSFER_FUNC_UNDEF; 427 | } 428 | 429 | switch (matrix) 430 | { 431 | case -1: 432 | break; 433 | case 0: 434 | p_dec->fmt_out.video.space = COLOR_SPACE_SRGB; 435 | break; 436 | case 1: 437 | p_dec->fmt_out.video.space = COLOR_SPACE_BT709; 438 | break; 439 | case 5: 440 | case 6: 441 | p_dec->fmt_out.video.space = COLOR_SPACE_BT601; 442 | break; 443 | case 9: 444 | // Rec. ITU-R BT.2020-2 (non-constant luminance) 445 | case 10: 446 | //Rec. ITU-R BT.2020-2 (constant luminance) 447 | p_dec->fmt_out.video.space = COLOR_SPACE_BT2020; 448 | break; 449 | default: 450 | p_dec->fmt_out.video.space = COLOR_SPACE_UNDEF; 451 | } 452 | if (full_range_flag >= 0) 453 | { 454 | p_dec->fmt_out.video.b_color_range_full = full_range_flag; 455 | } 456 | if (maxFALL > 0) 457 | { 458 | p_dec->fmt_out.video.lighting.MaxCLL = maxCLL; 459 | p_dec->fmt_out.video.lighting.MaxFALL = maxFALL; 460 | } 461 | } 462 | 463 | video_format_Setup(&p_dec->fmt_out.video, videoFormat, 464 | frame_width, frame_height, frame_width, frame_height, 1, 1); 465 | 466 | return VLC_SUCCESS; 467 | } 468 | 469 | static int initVideoFrameRate(decoder_t* p_dec, decoder_sys_t* p_sys, 470 | unsigned int i_frame_rate, unsigned int i_frame_rate_base) 471 | { 472 | decVTM_getFrameRate(p_sys->decVtm, &i_frame_rate, &i_frame_rate_base); 473 | 474 | if (i_frame_rate == 0 || i_frame_rate_base == 0) 475 | { 476 | if (p_dec->fmt_out.video.i_frame_rate != 0 && 477 | p_dec->fmt_out.video.i_frame_rate_base != 0) 478 | { 479 | return VLC_SUCCESS; 480 | } 481 | i_frame_rate = p_dec->fmt_in.video.i_frame_rate; 482 | i_frame_rate_base = p_dec->fmt_in.video.i_frame_rate_base; 483 | } 484 | p_dec->fmt_out.video.i_frame_rate = i_frame_rate; 485 | p_dec->fmt_out.video.i_frame_rate_base = i_frame_rate_base; 486 | 487 | if (!p_sys->b_frameRateDetect) 488 | { 489 | if (p_dec->fmt_out.video.i_frame_rate == 0 || 490 | p_dec->fmt_out.video.i_frame_rate_base == 0) 491 | { 492 | msg_Warn(p_dec, "invalid frame rate %d/%d, using 50 fps while detecting", 493 | p_dec->fmt_out.video.i_frame_rate, 494 | p_dec->fmt_out.video.i_frame_rate_base); 495 | date_Init(&p_sys->pts, 50, 1); 496 | p_sys->b_frameRateDetect = true; 497 | p_sys->dts1 = VLC_TS_INVALID; 498 | p_sys->dts2 = VLC_TS_INVALID; 499 | } 500 | else 501 | date_Init(&p_sys->pts, p_dec->fmt_out.video.i_frame_rate, 502 | p_dec->fmt_out.video.i_frame_rate_base); 503 | } 504 | return VLC_SUCCESS; 505 | } 506 | 507 | /***************************************************************************** 508 | * Flush: 509 | *****************************************************************************/ 510 | static void Flush(decoder_t* p_dec) 511 | { 512 | decoder_sys_t* p_sys = p_dec->p_sys; 513 | 514 | msg_Warn(p_dec, "decoder flush called at pts: %d ", date_Get(&p_sys->pts)); 515 | //date_Set(&p_sys->pts, VLC_TS_INVALID); 516 | 517 | //getOutputFrame(p_dec, nullptr); 518 | } 519 | 520 | short chromaGreyValue(vlc_fourcc_t i_chroma) 521 | { 522 | short greyChromaVal = 0; 523 | switch (i_chroma) 524 | { 525 | case VLC_CODEC_I420_12L: 526 | case VLC_CODEC_I422_12L: 527 | case VLC_CODEC_I444_12L: 528 | greyChromaVal = (1 << (12 - 1)); 529 | break; 530 | case VLC_CODEC_I420_10L: 531 | case VLC_CODEC_I422_10L: 532 | case VLC_CODEC_I444_10L: 533 | greyChromaVal = (1 << (10 - 1)); 534 | break; 535 | case VLC_CODEC_GREY: 536 | case VLC_CODEC_I420: 537 | case VLC_CODEC_I422: 538 | case VLC_CODEC_I444: 539 | default: 540 | greyChromaVal = (1 << (8 - 1)); 541 | } 542 | return greyChromaVal; 543 | } 544 | /***************************************************************************** 545 | * FillPicture: 546 | *****************************************************************************/ 547 | static void FillPicture(decoder_t* p_dec, picture_t* p_pic, int posx, int posy, 548 | int picWidth, int picHeight, short* planes[3], int strides[3]) 549 | { 550 | decoder_sys_t* p_sys = p_dec->p_sys; 551 | for (int i = 0; i < p_pic->i_planes; i++) 552 | { 553 | if (planes[i]) 554 | { 555 | const int widthRatio = (i == 0) ? 1 : p_pic->p[0].i_visible_pitch / p_pic->p[i].i_visible_pitch; 556 | const int heightRatio = (i == 0) ? 1 : p_pic->p[0].i_visible_lines / p_pic->p[i].i_visible_lines; 557 | const int planeWidth = picWidth / widthRatio; 558 | const int planeHeight = picHeight / heightRatio; 559 | const int xOffset = (posx / widthRatio) * p_pic->p[i].i_pixel_pitch; 560 | const int yOffset = (posy / heightRatio); 561 | const int picPitch = std::min(planeWidth * p_pic->p[i].i_pixel_pitch, p_pic->p[i].i_visible_pitch - xOffset); 562 | uint8_t* p_dstPlane = p_pic->p[i].p_pixels + yOffset * p_pic->p[i].i_pitch + xOffset; 563 | const int lines = std::min(planeHeight, p_pic->p[i].i_visible_lines - yOffset); 564 | short* p_src = planes[i]; 565 | if (p_pic->p[i].i_pixel_pitch == 1) 566 | { 567 | for (int y = 0; y < lines; y++) 568 | { 569 | for (int x = 0; x < picPitch; x++) 570 | { 571 | p_dstPlane[x] = (uint8_t)p_src[x]; 572 | } 573 | p_dstPlane += p_pic->p[i].i_pitch; 574 | p_src += strides[i]; 575 | } 576 | } 577 | else if(picPitch > 0) 578 | 579 | { 580 | for (int y = 0; y < lines; y++) 581 | { 582 | memcpy(p_dstPlane, p_src, picPitch); 583 | p_dstPlane += p_pic->p[i].i_pitch; 584 | p_src += strides[i]; 585 | } 586 | const int visibleWidth = picPitch / p_pic->p[i].i_pixel_pitch; 587 | const short fillVal = (i == 0) ? 0 : chromaGreyValue(p_dec->fmt_out.video.i_chroma); 588 | 589 | for (int y = lines; y < p_pic->p[i].i_visible_lines - yOffset; y++) 590 | { 591 | short* dst = (short*)p_dstPlane; 592 | for (int x = 0; x < visibleWidth; x++) 593 | { 594 | dst[x] = fillVal; 595 | } 596 | p_dstPlane += p_pic->p[i].i_pitch; 597 | } 598 | } 599 | } 600 | else if (i > 0 && p_pic->p[i].p_pixels) 601 | { 602 | const int widthRatio = (i == 0) ? 1 : p_pic->p[0].i_visible_pitch / p_pic->p[i].i_visible_pitch; 603 | const int heightRatio = (i == 0) ? 1 : p_pic->p[0].i_visible_lines / p_pic->p[i].i_visible_lines; 604 | const int planeWidth = picWidth / widthRatio; 605 | const int planeHeight = picHeight / heightRatio; 606 | const int xOffset = (posx / widthRatio) * p_pic->p[i].i_pixel_pitch; 607 | const int yOffset = (posy / heightRatio); 608 | const int picPitch = std::min(planeWidth * p_pic->p[i].i_pixel_pitch, p_pic->p[i].i_visible_pitch - xOffset); 609 | uint8_t* p_dstPlane = p_pic->p[i].p_pixels + yOffset * p_pic->p[i].i_pitch + xOffset; 610 | //const int lines = std::min(planeHeight, p_pic->p[i].i_visible_lines - yOffset); 611 | const int visibleWidth = picPitch / p_pic->p[i].i_pixel_pitch; 612 | for (int y = 0; y < p_pic->p[i].i_visible_lines - yOffset; y++) 613 | { 614 | short* dst = (short*)p_dstPlane; 615 | const short fillVal = (i == 0) ? 0 : chromaGreyValue(p_dec->fmt_out.video.i_chroma); 616 | for (int x = 0; x < visibleWidth; x++) 617 | { 618 | dst[x] = fillVal; 619 | } 620 | p_dstPlane += p_pic->p[i].i_pitch; 621 | } 622 | } 623 | } 624 | } 625 | 626 | 627 | static vlc_fourcc_t getVideoFormat(decoder_t* p_dec, int chromaFormat, const int bitDepths) 628 | { 629 | vlc_fourcc_t videoFormat = p_dec->fmt_out.video.i_chroma; 630 | switch (chromaFormat) 631 | { 632 | case 400: 633 | if (bitDepths == 8) 634 | { 635 | videoFormat = VLC_CODEC_GREY; 636 | } 637 | else if (bitDepths == 10) 638 | { 639 | videoFormat = VLC_CODEC_I420_10L; 640 | } 641 | else if (bitDepths == 12) 642 | { 643 | videoFormat = VLC_CODEC_I420_12L; 644 | } 645 | break; 646 | case 420: 647 | if (bitDepths == 8) 648 | { 649 | videoFormat = VLC_CODEC_I420; 650 | } 651 | else if (bitDepths == 10) 652 | { 653 | videoFormat = VLC_CODEC_I420_10L; 654 | } 655 | else if (bitDepths == 12) 656 | { 657 | videoFormat = VLC_CODEC_I420_12L; 658 | } 659 | break; 660 | case 422: 661 | if (bitDepths == 8) 662 | { 663 | videoFormat = VLC_CODEC_I422; 664 | } 665 | else if (bitDepths == 10) 666 | { 667 | videoFormat = VLC_CODEC_I422_10L; 668 | } 669 | else if (bitDepths == 12) 670 | { 671 | videoFormat = VLC_CODEC_I422_12L; 672 | } 673 | break; 674 | case 444: 675 | if (bitDepths == 8) 676 | { 677 | videoFormat = VLC_CODEC_I444; 678 | } 679 | else if (bitDepths == 10) 680 | { 681 | videoFormat = VLC_CODEC_I444_10L; 682 | } 683 | else if (bitDepths == 12) 684 | { 685 | videoFormat = VLC_CODEC_I444_12L; 686 | } 687 | break; 688 | } 689 | return videoFormat; 690 | } 691 | 692 | static vlc_fourcc_t getVideoFormat(decoder_t* p_dec, decoder_sys_t* p_sys) 693 | { 694 | vlc_fourcc_t videoFormat = p_dec->fmt_out.video.i_chroma; 695 | int chromaFormat; 696 | int bitDepths; 697 | if (decVTM_getvideoFormat(p_sys->decVtm, &chromaFormat, &bitDepths)) 698 | { 699 | videoFormat = getVideoFormat(p_dec, chromaFormat, bitDepths); 700 | } 701 | return videoFormat; 702 | } 703 | /***************************************************************************** 704 | * DecodeFrame: decodes a video frame. 705 | *****************************************************************************/ 706 | static int DecodeFrame(decoder_t* p_dec, block_t* p_block) 707 | { 708 | decoder_sys_t* p_sys = p_dec->p_sys; 709 | if (p_sys->b_frameRateDetect) 710 | { 711 | if (p_sys->dts1 == VLC_TS_INVALID) 712 | { 713 | p_sys->dts1 = p_block->i_dts; 714 | } 715 | else if (p_sys->dts2 == VLC_TS_INVALID && p_block->i_dts > p_sys->dts1) 716 | { 717 | p_sys->diff_dts = p_block->i_dts - p_sys->dts1; 718 | p_sys->dts2 = p_block->i_dts; 719 | } 720 | else if (p_block->i_dts > p_sys->dts2) 721 | { 722 | mtime_t diff_dts = p_block->i_dts - p_sys->dts2; 723 | if (diff_dts == p_sys->diff_dts) 724 | { 725 | p_sys->b_frameRateDetect = false; 726 | initVideoFrameRate(p_dec, p_sys, (unsigned int)(1000000 / (diff_dts / 1000)), 1000); 727 | msg_Info(p_dec, "detected frame rate %d/%d, from timestamps", 728 | p_dec->fmt_out.video.i_frame_rate, 729 | p_dec->fmt_out.video.i_frame_rate_base); 730 | } 731 | else 732 | { 733 | p_sys->dts1 = VLC_TS_INVALID; 734 | p_sys->dts2 = VLC_TS_INVALID; 735 | } 736 | } 737 | } 738 | if (p_block && p_sys->firstBlock) 739 | { 740 | p_sys->firstBlock = false; 741 | if (p_block->i_dts > VLC_TS_INVALID) 742 | { 743 | p_sys->firstBlock_dts = p_block->i_dts; 744 | } 745 | else 746 | { 747 | p_sys->firstBlock_dts = VLC_TS_0; 748 | } 749 | } 750 | 751 | //msg_Warn(p_dec, "decVtm decode frame %d with nalu size %d ", p_sys->dec_frame_count, (p_block != nullptr) ? p_block->i_buffer : 0); 752 | decVTM_decode(p_sys->decVtm, (const char*)((p_block != nullptr) ? p_block->p_buffer : nullptr), (p_block != nullptr) ? p_block->i_buffer : 0, p_sys->speedUpLevel); 753 | if (p_block != nullptr) 754 | { 755 | p_sys->dec_frame_count++; 756 | } 757 | 758 | if (p_sys->b_format_init) 759 | { 760 | int width = 0, height = 0; 761 | decVTM_getFrameSize(p_sys->decVtm, &width, &height); 762 | vlc_fourcc_t videoFormat = getVideoFormat(p_dec, p_sys); 763 | 764 | if (width > 0 && height > 0) 765 | { 766 | p_sys->b_format_init = false; 767 | initVideoFormat(p_dec, p_sys, videoFormat, width, height); 768 | if(decoder_UpdateVideoFormat(p_dec)) 769 | { 770 | return false; 771 | } 772 | } 773 | } 774 | 775 | while (getOutputFrame(p_dec, false, p_block? p_block->i_dts: VLC_TS_INVALID)); 776 | 777 | if (p_block) 778 | { 779 | block_Release(p_block); 780 | } 781 | else 782 | { 783 | msg_Warn(p_dec, "flushDecoder called at pts: %d ", date_Get(&p_sys->pts)); 784 | decVTM_flush(p_dec->p_sys->decVtm); 785 | msg_Warn(p_dec, "decoder flushed ! "); 786 | while (getOutputFrame(p_dec, true, VLC_TS_INVALID)); 787 | 788 | p_sys->b_first_frame = true; 789 | p_sys->lastOutput_pts = VLC_TS_INVALID; 790 | p_sys->firstOutput_pts = VLC_TS_INVALID; 791 | p_sys->firstOutput_time = VLC_TS_INVALID; 792 | p_sys->lastOutput_time = VLC_TS_INVALID; 793 | p_sys->firstBlock_dts = VLC_TS_INVALID; 794 | p_sys->firstBlock = true; 795 | p_sys->b_format_init = true; 796 | p_sys->b_frameRateDetect = false; 797 | p_sys->speedUpLevel = 0; 798 | p_sys->speedUpLevel_delai_increase = 0; 799 | p_sys->speedUpLevel_delai_decrease = 0; 800 | p_sys->speedUpLevel_previous_lateness = 0; 801 | p_sys->speedUpLevel_delai_derivative = 0; 802 | } 803 | return VLCDEC_SUCCESS; 804 | } 805 | 806 | static bool getOutputFrame(decoder_t* p_dec, bool waitUntilReady, mtime_t i_dts) 807 | { 808 | decoder_sys_t* p_sys = p_dec->p_sys; 809 | short* planes[3]; 810 | int strides[3]; 811 | int width = 0, height = 0; 812 | int chromaFormat; 813 | int bitDepths; 814 | int outputLayer = 0; 815 | int nbSkippedPictures = 0; 816 | if (decVTM_getNextOutputFrame(p_sys->decVtm, waitUntilReady, planes, strides, &width, &height, &chromaFormat, &bitDepths, &outputLayer, &nbSkippedPictures)) 817 | { 818 | // Get a new picture 819 | picture_t* p_pic = p_sys->p_pic; 820 | p_sys->out_frame_count++; 821 | vlc_fourcc_t videoFormat = getVideoFormat(p_dec, chromaFormat, bitDepths); 822 | bool outputLayerNew = true; 823 | unsigned int outputLayerIdx = 0; 824 | 825 | for (unsigned int i=0; ioutputLayers.size(); i++) 826 | { 827 | if (p_sys->outputLayers[i].id == outputLayer) 828 | { 829 | outputLayerIdx = i; 830 | outputLayerNew = false; 831 | p_sys->outputLayers[i].width = width; 832 | p_sys->outputLayers[i].height = height; 833 | break; 834 | } 835 | } 836 | if (outputLayerNew) 837 | { 838 | outputLayerIdx = (unsigned int)p_sys->outputLayers.size(); 839 | decoder_sys_t::layer_info layer = { outputLayer, width, height, 0, 0 }; 840 | p_sys->outputLayers.push_back(layer); 841 | msg_Dbg(p_dec, "new layer nb %d: %d (total %d) ", outputLayerIdx, outputLayer, p_sys->outputLayers.size()); 842 | } 843 | if (p_sys->outputLayers.size() > 1) 844 | { 845 | int totalWidth = 0, totalHeight = 0; 846 | for (auto& l : p_sys->outputLayers) 847 | { 848 | l.posx = totalWidth; 849 | l.posy = 0; 850 | totalWidth += l.width; 851 | totalHeight = std::max(totalHeight, l.height); 852 | } 853 | width = totalWidth; 854 | height = totalHeight; 855 | } 856 | if (width != p_dec->fmt_out.video.i_width 857 | || height != p_dec->fmt_out.video.i_height 858 | || videoFormat != p_dec->fmt_out.video.i_chroma) 859 | { 860 | initVideoFormat(p_dec, p_sys, videoFormat, width, height); 861 | if(decoder_UpdateVideoFormat(p_dec)) 862 | { 863 | return false; 864 | } 865 | initVideoFrameRate(p_dec, p_sys); 866 | } 867 | 868 | mtime_t dat = mdate(); 869 | if (planes[0] != nullptr) 870 | { 871 | if (!decoder_UpdateVideoFormat(p_dec) && (outputLayerIdx == 0 || outputLayerNew)) 872 | { 873 | p_pic = decoder_NewPicture(p_dec); 874 | p_sys->p_pic = p_pic; 875 | } 876 | if (p_pic == NULL) 877 | { 878 | return false; 879 | } 880 | FillPicture(p_dec, p_pic, p_sys->outputLayers[outputLayerIdx].posx, p_sys->outputLayers[outputLayerIdx].posy, 881 | p_sys->outputLayers[outputLayerIdx].width, p_sys->outputLayers[outputLayerIdx].height, planes, strides); 882 | } 883 | decVTM_setlastPicDisplayed(p_sys->decVtm); 884 | 885 | // Date management: 1 frame per packet 886 | if (p_sys->b_first_frame) 887 | { 888 | initVideoFrameRate(p_dec, p_sys); 889 | p_sys->b_first_frame = false; 890 | 891 | mtime_t i_pts = date_Get(&p_sys->pts); 892 | if (i_pts == VLC_TS_INVALID) 893 | { 894 | date_t firstBlockDate = p_sys->pts; 895 | int decoderFrameDelay = std::min(p_dec->i_extra_picture_buffers, std::max(0, (int)p_sys->dec_frame_count - (int)p_sys->out_frame_count)); 896 | date_Set(&firstBlockDate, p_sys->firstBlock_dts); 897 | date_Increment(&firstBlockDate, decoderFrameDelay); 898 | i_pts = date_Get(&firstBlockDate); 899 | } 900 | if (i_pts > VLC_TS_INVALID) 901 | date_Set(&p_sys->pts, i_pts); 902 | 903 | p_sys->firstOutput_time = mdate(); 904 | p_sys->firstOutput_pts = i_pts; 905 | } 906 | 907 | if (outputLayerIdx == 0 && nbSkippedPictures) 908 | { 909 | date_Increment(&p_sys->pts, nbSkippedPictures); 910 | } 911 | mtime_t i_pts = date_Get(&p_sys->pts); 912 | if (outputLayerIdx == 0) 913 | { 914 | p_sys->lastOutput_pts = i_pts - p_sys->firstOutput_pts; 915 | p_sys->lastOutput_time = mdate() - p_sys->firstOutput_time; 916 | 917 | if (p_sys->enable_hurryMode) 918 | { 919 | mtime_t period = CLOCK_FREQ * p_sys->pts.i_divider_den / p_sys->pts.i_divider_num; 920 | mtime_t lateness = p_sys->lastOutput_time - p_sys->lastOutput_pts; 921 | mtime_t lateness_derivative = lateness - p_sys->speedUpLevel_previous_lateness; 922 | p_sys->speedUpLevel_previous_lateness = lateness; 923 | p_sys->speedUpLevel_delai_derivative = (7 * p_sys->speedUpLevel_delai_derivative + 1 * lateness_derivative) / 8; 924 | if (p_sys->speedUpLevel < decoder_sys_t::MAX_SPEED_UP_LEVEL 925 | && p_sys->speedUpLevel_delai_derivative >= 0 926 | && lateness > period && p_sys->speedUpLevel_delai_increase-- <= 0) 927 | { 928 | p_sys->speedUpLevel_delai_increase = (1 << p_sys->speedUpLevel) * decoder_sys_t::SPDUP_DELAY_BASE; 929 | p_sys->speedUpLevel = std::min(decoder_sys_t::MAX_SPEED_UP_LEVEL, p_sys->speedUpLevel + 2); 930 | p_sys->speedUpLevel_delai_decrease = decoder_sys_t::SPDUP_DELAY_BASE; 931 | } 932 | else if (p_sys->speedUpLevel > 0 933 | && p_sys->speedUpLevel_delai_derivative < 0 934 | && (lateness < -period && p_sys->speedUpLevel_delai_decrease-- <= 0)) 935 | { 936 | p_sys->speedUpLevel_delai_decrease = (1 << p_sys->speedUpLevel) * decoder_sys_t::SPDUP_DELAY_BASE; 937 | p_sys->speedUpLevel--; 938 | p_sys->speedUpLevel_delai_increase = decoder_sys_t::SPDUP_DELAY_BASE; 939 | } 940 | if (p_sys->speedUpLevel > 0) 941 | msg_Info(p_dec, "decoding frame %d (delay %d, derivative %d) - speed up %d", p_sys->out_frame_count, lateness, p_sys->speedUpLevel_delai_derivative, p_sys->speedUpLevel); 942 | } 943 | 944 | date_Increment(&p_sys->pts, 1); 945 | } 946 | 947 | if (planes[0] != nullptr && (outputLayerIdx == p_sys->outputLayers.size()-1 || outputLayerNew)) 948 | { 949 | 950 | p_pic->date = i_pts; 951 | p_pic->b_force = true; 952 | p_pic->i_nb_fields = 2; 953 | p_pic->b_progressive = true; 954 | 955 | decoder_QueueVideo(p_dec, p_pic); 956 | } 957 | return true; 958 | } 959 | return false; 960 | } 961 | 962 | /** 963 | * Common deinitialization 964 | */ 965 | static void CloseDec(vlc_object_t* p_this) 966 | { 967 | decoder_t* p_dec = (decoder_t*)p_this; 968 | msg_Info(p_dec, "decoded %d frames",p_dec->p_sys->dec_frame_count); 969 | msg_Info(p_dec, "output %d frames",p_dec->p_sys->out_frame_count); 970 | decVTM_flush(p_dec->p_sys->decVtm); 971 | decVTM_destroy(p_dec->p_sys->decVtm); 972 | video_format_Setup(&p_dec->fmt_out.video, VLC_CODEC_UNKNOWN, p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height, p_dec->fmt_out.video.i_visible_width, p_dec->fmt_out.video.i_visible_height-2, 1, 1); 973 | decoder_UpdateVideoFormat(p_dec); 974 | delete p_dec->p_sys; 975 | } 976 | --------------------------------------------------------------------------------