Repository: aliakseis/FFmpegPlayer Branch: master Commit: a547dbec76bd Files: 181 Total size: 1.6 MB Directory structure: gitextract_546ruw75/ ├── .clang-format ├── .gitattributes ├── .github/ │ └── workflows/ │ └── msbuild.yml ├── .gitignore ├── .gitmodules ├── Anime4KCPPCore.vcxproj ├── Anime4KCPPCore.vcxproj.filters ├── Dlls/ │ └── Dlls.csproj ├── HttpDownload/ │ ├── HttpDownload.cpp │ ├── HttpDownload.vcxproj │ ├── HttpDownload.vcxproj.filters │ ├── ReadMe.txt │ ├── stdafx.cpp │ ├── stdafx.h │ └── targetver.h ├── LICENSE ├── Player/ │ ├── AsyncGetUrlUnderMouseCursor.cpp │ ├── AsyncGetUrlUnderMouseCursor.h │ ├── ByteStreamBuffer.h │ ├── D3DFONT.CPP │ ├── D3DFONT.H │ ├── DialogBarPlayerControl.cpp │ ├── DialogBarPlayerControl.h │ ├── DialogBarRange.cpp │ ├── DialogBarRange.h │ ├── DialogOpenURL.cpp │ ├── DialogOpenURL.h │ ├── DialogVideoFilter.cpp │ ├── DialogVideoFilter.h │ ├── EditTime.cpp │ ├── EditTime.h │ ├── FrameToHglobal.cpp │ ├── FrameToHglobal.h │ ├── FrameTransformer.cpp │ ├── FrameTransformer.h │ ├── GetClipboardText.h │ ├── HandleFilesSequence.cpp │ ├── HandleFilesSequence.h │ ├── I420Effect.cpp │ ├── I420Effect.h │ ├── I420Effect_PS.hlsl │ ├── IEraseableArea.h │ ├── ImageUpscale.cpp │ ├── ImageUpscale.h │ ├── MainFrm.cpp │ ├── MainFrm.h │ ├── MakeDelegate.h │ ├── MemoryMappedFile.h │ ├── OpenSubtitlesFile.cpp │ ├── OpenSubtitlesFile.h │ ├── Player.cpp │ ├── Player.h │ ├── Player.rc │ ├── Player.vcxproj │ ├── Player.vcxproj.filters │ ├── PlayerDoc.cpp │ ├── PlayerDoc.h │ ├── PlayerView.cpp │ ├── PlayerView.h │ ├── PlayerViewD2D.cpp │ ├── PlayerViewD2D.h │ ├── ReadMe.txt │ ├── SecondsToString.h │ ├── StringDifference.cpp │ ├── StringDifference.h │ ├── YouTuber.cpp │ ├── YouTuber.h │ ├── res/ │ │ ├── Player.rc2 │ │ └── launch.mkv │ ├── resource.h │ ├── stdafx.cpp │ ├── stdafx.h │ ├── targetver.h │ └── update_version.cmd ├── Player.sln ├── QtPlayer/ │ ├── .gitattributes │ ├── .gitignore │ ├── CMakeLists.txt │ ├── customdockwidget.cpp │ ├── customdockwidget.h │ ├── ffmpegdecoder.cpp │ ├── ffmpegdecoder.h │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── mainwindow.ui │ ├── mousehoverbutton.cpp │ ├── mousehoverbutton.h │ ├── opengldisplay.cpp │ ├── opengldisplay.h │ ├── portaudioplayer.cpp │ ├── portaudioplayer.h │ ├── resources/ │ │ ├── qt.conf │ │ ├── resources.qrc │ │ ├── style.css │ │ ├── win7.manifest │ │ └── winres.rc.in │ ├── videocontrol.cpp │ ├── videocontrol.h │ ├── videocontrol.ui │ ├── videodisplay.cpp │ ├── videodisplay.h │ ├── videoplayer.cpp │ ├── videoplayer.h │ ├── videoplayerwidget.cpp │ ├── videoplayerwidget.h │ ├── videoprogressbar.cpp │ ├── videoprogressbar.h │ ├── videowidget.cpp │ ├── videowidget.h │ ├── volumeprogressbar.cpp │ ├── volumeprogressbar.h │ ├── widgetdisplay.cpp │ └── widgetdisplay.h ├── README.md ├── Setup/ │ └── Setup.vdproj ├── ThirdParty/ │ └── include/ │ ├── cmdline/ │ │ └── cmdline.hpp │ ├── ini17/ │ │ └── ini17.hpp │ └── opencl/ │ └── CL/ │ └── opencl.hpp ├── ToUTF8/ │ ├── ToUTF8.cpp │ ├── ToUTF8.vcxproj │ └── ToUTF8.vcxproj.filters ├── audio/ │ ├── AudioPitchDecorator.cpp │ ├── AudioPitchDecorator.h │ ├── AudioPlayerImpl.cpp │ ├── AudioPlayerImpl.h │ ├── AudioPlayerWasapi.cpp │ ├── AudioPlayerWasapi.h │ ├── ReadMe.txt │ ├── audio.vcxproj │ ├── audio.vcxproj.filters │ ├── smbPitchShift.cpp │ └── smbPitchShift.h ├── core/ │ └── ac_export.h ├── edit_git_subst_cfg.cmd ├── ffmpeg-3.3.3-experimental-patch/ │ ├── common.before.h │ ├── common.h │ ├── hevcdsp_template.before.c │ └── hevcdsp_template.c ├── ffmpeg-4.3.2-experimental-patch/ │ ├── common.before.h │ ├── common.h │ ├── hevcdsp_template.before.c │ └── hevcdsp_template.c ├── getYoutubeCombined.py ├── networking/ │ ├── ReadMe.txt │ ├── crypt.h │ ├── http_download.cpp │ ├── http_download.h │ ├── http_get.cpp │ ├── http_get.h │ ├── httpioapi.cpp │ ├── httprequest_h.h │ ├── ioapi.h │ ├── networking.vcxproj │ ├── networking.vcxproj.filters │ ├── unzip.c │ └── unzip.h ├── remove_pytube.cmd ├── remove_youtube_transcript_api.cmd └── video/ ├── audioparserunnable.cpp ├── audioplayer.h ├── decoderinterface.h ├── decoderiocontext.cpp ├── decoderiocontext.h ├── displayrunnable.cpp ├── ffmpeg_dxva2.cpp ├── ffmpeg_dxva2.h ├── ffmpegdecoder.cpp ├── ffmpegdecoder.h ├── fqueue.h ├── interlockedadd.h ├── makeguard.h ├── ordered_scoped_token.h ├── parserunnable.cpp ├── subtitles.cpp ├── subtitles.h ├── video.vcxproj ├── video.vcxproj.filters ├── videoframe.h ├── videoparserunnable.cpp └── vqueue.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ BasedOnStyle: Google UseTab: Never IndentWidth: 4 BreakBeforeBraces: Allman AllowShortIfStatementsOnASingleLine: false IndentCaseLabels: false ColumnLimit: 99 IndentAccessModifiers: false AccessModifierOffset: -4 ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain ================================================ FILE: .github/workflows/msbuild.yml ================================================ # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. name: MSBuild on: workflow_dispatch: env: # Path to the solution file relative to the root of the project. SOLUTION_FILE_PATH: ./Player.sln # Configuration type to build. # You can convert this to a build matrix if you need coverage of multiple configuration types. # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix BUILD_CONFIGURATION: Release # https://learn.microsoft.com/en-us/vcpkg/consume/binary-caching-github-actions-cache?source=recommendations VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" jobs: build: runs-on: windows-latest strategy: matrix: platform: [x86, x64] steps: - uses: actions/checkout@v4 with: submodules: true # Ensure submodules are checked out - name: Create Directory.Build.props run: | $content = "10.0v4.6.2v143" $filePath = "./Directory.Build.props" Set-Content -Path $filePath -Value $content - name: Add MSBuild to PATH id: setup-msbuild uses: microsoft/setup-msbuild@v2 - name: Export GitHub Actions cache environment variables uses: actions/github-script@v7 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Install vcpkg run: | git clone https://github.com/microsoft/vcpkg.git cd vcpkg # see https://github.com/microsoft/vcpkg/issues/43802 #git checkout 62aa44929954469878eb8fc562af706a8f5615a5 ./bootstrap-vcpkg.bat ./vcpkg integrate install - name: Install dependencies run: ./vcpkg/vcpkg install boost boost-log dtl ffmpeg[ffmpeg,x264,nonfree,gpl,vpx,webp,zlib,xml2] opencv4 python3 boost-python opencl --triplet=${{matrix.platform}}-windows - name: Restore NuGet packages working-directory: ${{env.GITHUB_WORKSPACE}} run: nuget restore ${{env.SOLUTION_FILE_PATH}} - name: Find OpenCV Header run: | $opencvHeader = Get-ChildItem -Path "./vcpkg/" -Recurse -Filter "opencv.hpp" | Where-Object { $_.FullName -match "opencv2\\opencv.hpp" } if ($opencvHeader) { Write-Output "Found OpenCV header at: $($opencvHeader.FullName)" } else { Write-Error "OpenCV header not found" } - name: Build working-directory: ${{env.GITHUB_WORKSPACE}} # Add additional options to the MSBuild command line here (like platform or verbosity level). # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} /p:Platform=${{matrix.platform == 'x86' && 'Win32' || matrix.platform}} "/t:Player;ToUTF8" ${{env.SOLUTION_FILE_PATH}} - name: Complete build artifacts run: | $CRT_path = Get-ChildItem -Path "${{ steps.setup-msbuild.outputs.msbuildPath }}/../../../VC/Redist/MSVC/*/${{matrix.platform}}/Microsoft.VC143.CRT" | Select-Object -First 1 -ExpandProperty FullName $MFC_path = Get-ChildItem -Path "${{ steps.setup-msbuild.outputs.msbuildPath }}/../../../VC/Redist/MSVC/*/${{matrix.platform}}/Microsoft.VC143.MFC" | Select-Object -First 1 -ExpandProperty FullName Write-Output "CRT PATH=$CRT_path" Write-Output "MFC PATH=$MFC_path" ./vcpkg/scripts/buildsystems/msbuild/applocal.ps1 -targetBinary "./${{matrix.platform == 'x64' && matrix.platform || ''}}/Release/Player.exe" -installedDir "$CRT_path" -OutVariable out ./vcpkg/scripts/buildsystems/msbuild/applocal.ps1 -targetBinary "./${{matrix.platform == 'x64' && matrix.platform || ''}}/Release/Player.exe" -installedDir "$MFC_path" -OutVariable out - name: Copy FFmpeg tools run: Copy-Item -Path "./vcpkg/installed/${{matrix.platform}}-windows/tools/ffmpeg/*" -Destination "./${{matrix.platform == 'x64' && matrix.platform || ''}}/Release/" -Recurse -Force -ErrorAction SilentlyContinue - name: Delete .pdb files run: Remove-Item -Path "./${{matrix.platform == 'x64' && matrix.platform || ''}}/Release/*.pdb" -Force - name: Delete .lib files run: Remove-Item -Path "./${{matrix.platform == 'x64' && matrix.platform || ''}}/Release/*.lib" -Force - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: build-artifacts-${{matrix.platform}} path: ./${{matrix.platform == 'x64' && matrix.platform || ''}}/Release ================================================ FILE: .gitignore ================================================ ################# ## Eclipse ################# *.pydevproject .project .metadata bin/ tmp/ *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ################# ## Visual Studio ################# ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ x64/ build/ [Bb]in/ [Oo]bj/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.log *.scc # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* .*crunch*.local.xml # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.Publish.xml *.pubxml *.publishproj # NuGet Packages Directory ## TODO: If you have NuGet Package Restore enabled, uncomment the next line #packages/ # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.[Pp]ublish.xml *.pfx *.publishsettings # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files App_Data/*.mdf App_Data/*.ldf ############# ## Windows detritus ############# # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Mac crap .DS_Store ############# ## Python ############# *.py[cod] # Packages *.egg *.egg-info dist/ build/ eggs/ parts/ var/ sdist/ develop-eggs/ .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg ipch/ ffmpeg/ Player/I420Effect_PS.h Player.VC.db Player.VC.VC.opendb *.diagsession CMakeLists.txt.user* .clang-tidy .vs/ Player/version.h build-*/ Directory.Build.props ================================================ FILE: .gitmodules ================================================ [submodule "Anime4KCPP"] path = Anime4KCPP url = https://github.com/TianZerL/Anime4KCPP.git ================================================ FILE: Anime4KCPPCore.vcxproj ================================================ Debug Win32 Debug x64 Release Win32 Release x64 {632353E4-4856-38F9-9E74-ED41BD99D7E5} Win32Proj Win32 Anime4KCPPCore NoUpgrade true StaticLibrary MultiByte StaticLibrary MultiByte StaticLibrary MultiByte StaticLibrary MultiByte <_ProjectFileVersion>10.0.20506.1 Anime4KCPP\build\bin\Debug\ Anime4KCPPCore.dir\Debug\ Anime4KCPPCore Anime4KCPPCore .lib .lib Anime4KCPP\build\bin\Release\ Anime4KCPPCore.dir\Release\ Anime4KCPPCore Anime4KCPPCore .lib .lib Anime4KCPP\core\include;core;ThirdParty\include\opencl;$(INTELOCLSDKROOT)\include;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;%(AdditionalIncludeDirectories) $(IntDir) EnableFastChecks ProgramDatabase Sync Disabled stdcpp17 Disabled NotUsing MultiThreadedDebugDLL true false Level3 %(PreprocessorDefinitions);WIN32;_WINDOWS;ENABLE_OPENCL;ENABLE_VIDEO;ENABLE_PREVIEW_GUI;ENABLE_IMAGE_IO;ANIME4KCPP_CORE_PARALLEL_LIBRARY="PPL";ANIME4KCPP_CORE_COMPILER="MSVC";ANIME4KCPP_CORE_BUILD_DATE="2021-10-01";ANIME4KCPP_CORE_VERSION="2.6.0";ANIME4KCPP_CORE_VERSION_MAJOR=2;ANIME4KCPP_CORE_VERSION_MINOR=6;ANIME4KCPP_CORE_VERSION_PATCH=0;ANIME4KCPP_CORE_VERSION_STATUS="dev";BUILT_IN_KERNEL;USE_PPL;CMAKE_INTDIR="Debug" $(IntDir) %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;ENABLE_OPENCL;ENABLE_VIDEO;ENABLE_PREVIEW_GUI;ENABLE_IMAGE_IO;ANIME4KCPP_CORE_PARALLEL_LIBRARY=\"PPL\";ANIME4KCPP_CORE_COMPILER=\"MSVC\";ANIME4KCPP_CORE_BUILD_DATE=\"2021-10-01\";ANIME4KCPP_CORE_VERSION=\"2.6.0\";ANIME4KCPP_CORE_VERSION_MAJOR=2;ANIME4KCPP_CORE_VERSION_MINOR=6;ANIME4KCPP_CORE_VERSION_PATCH=0;ANIME4KCPP_CORE_VERSION_STATUS=\"dev\";BUILT_IN_KERNEL;USE_PPL;CMAKE_INTDIR=\"Debug\" Anime4KCPP\core\include;Anime4KCPP\build\core;Anime4KCPP\ThirdParty\include\opencl;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;$(INTELOCLSDKROOT)\include;%(AdditionalIncludeDirectories) Anime4KCPP\core\include;Anime4KCPP\build\core;Anime4KCPP\ThirdParty\include\opencl;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;$(INTELOCLSDKROOT)\include;%(AdditionalIncludeDirectories) $(ProjectDir)/$(IntDir) %(Filename).h %(Filename).tlb %(Filename)_i.c %(Filename)_p.c Anime4KCPP\core\include;core;ThirdParty\include\opencl;$(INTELOCLSDKROOT)\include;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;%(AdditionalIncludeDirectories) $(IntDir) EnableFastChecks ProgramDatabase Sync Disabled stdcpp17 Disabled NotUsing MultiThreadedDebugDLL true false Level3 %(PreprocessorDefinitions);WIN32;_WINDOWS;ENABLE_OPENCL;ENABLE_VIDEO;ENABLE_PREVIEW_GUI;ENABLE_IMAGE_IO;ANIME4KCPP_CORE_PARALLEL_LIBRARY="PPL";ANIME4KCPP_CORE_COMPILER="MSVC";ANIME4KCPP_CORE_BUILD_DATE="2021-10-01";ANIME4KCPP_CORE_VERSION="2.6.0";ANIME4KCPP_CORE_VERSION_MAJOR=2;ANIME4KCPP_CORE_VERSION_MINOR=6;ANIME4KCPP_CORE_VERSION_PATCH=0;ANIME4KCPP_CORE_VERSION_STATUS="dev";BUILT_IN_KERNEL;USE_PPL;CMAKE_INTDIR="Debug" $(IntDir) %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;ENABLE_OPENCL;ENABLE_VIDEO;ENABLE_PREVIEW_GUI;ENABLE_IMAGE_IO;ANIME4KCPP_CORE_PARALLEL_LIBRARY=\"PPL\";ANIME4KCPP_CORE_COMPILER=\"MSVC\";ANIME4KCPP_CORE_BUILD_DATE=\"2021-10-01\";ANIME4KCPP_CORE_VERSION=\"2.6.0\";ANIME4KCPP_CORE_VERSION_MAJOR=2;ANIME4KCPP_CORE_VERSION_MINOR=6;ANIME4KCPP_CORE_VERSION_PATCH=0;ANIME4KCPP_CORE_VERSION_STATUS=\"dev\";BUILT_IN_KERNEL;USE_PPL;CMAKE_INTDIR=\"Debug\" Anime4KCPP\core\include;Anime4KCPP\build\core;Anime4KCPP\ThirdParty\include\opencl;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;$(INTELOCLSDKROOT)\include;%(AdditionalIncludeDirectories) Anime4KCPP\core\include;Anime4KCPP\build\core;Anime4KCPP\ThirdParty\include\opencl;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;$(INTELOCLSDKROOT)\include;%(AdditionalIncludeDirectories) $(ProjectDir)/$(IntDir) %(Filename).h %(Filename).tlb %(Filename)_i.c %(Filename)_p.c Anime4KCPP\core\include;core;ThirdParty\include\opencl;$(INTELOCLSDKROOT)\include;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;%(AdditionalIncludeDirectories) $(IntDir) ProgramDatabase Sync OnlyExplicitInline stdcpp17 MaxSpeed NotUsing MultiThreadedDLL true false Level3 %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;ENABLE_OPENCL;ENABLE_VIDEO;ENABLE_PREVIEW_GUI;ENABLE_IMAGE_IO;ANIME4KCPP_CORE_PARALLEL_LIBRARY="PPL";ANIME4KCPP_CORE_COMPILER="MSVC";ANIME4KCPP_CORE_BUILD_DATE="2021-10-01";ANIME4KCPP_CORE_VERSION="2.6.0";ANIME4KCPP_CORE_VERSION_MAJOR=2;ANIME4KCPP_CORE_VERSION_MINOR=6;ANIME4KCPP_CORE_VERSION_PATCH=0;ANIME4KCPP_CORE_VERSION_STATUS="dev";BUILT_IN_KERNEL;USE_PPL;CMAKE_INTDIR="Release" $(IntDir) %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;ENABLE_OPENCL;ENABLE_VIDEO;ENABLE_PREVIEW_GUI;ENABLE_IMAGE_IO;ANIME4KCPP_CORE_PARALLEL_LIBRARY=\"PPL\";ANIME4KCPP_CORE_COMPILER=\"MSVC\";ANIME4KCPP_CORE_BUILD_DATE=\"2021-10-01\";ANIME4KCPP_CORE_VERSION=\"2.6.0\";ANIME4KCPP_CORE_VERSION_MAJOR=2;ANIME4KCPP_CORE_VERSION_MINOR=6;ANIME4KCPP_CORE_VERSION_PATCH=0;ANIME4KCPP_CORE_VERSION_STATUS=\"dev\";BUILT_IN_KERNEL;USE_PPL;CMAKE_INTDIR=\"Release\" Anime4KCPP\core\include;Anime4KCPP\build\core;Anime4KCPP\ThirdParty\include\opencl;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;$(INTELOCLSDKROOT)\include;%(AdditionalIncludeDirectories) Anime4KCPP\core\include;Anime4KCPP\build\core;Anime4KCPP\ThirdParty\include\opencl;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;$(INTELOCLSDKROOT)\include;%(AdditionalIncludeDirectories) $(ProjectDir)/$(IntDir) %(Filename).h %(Filename).tlb %(Filename)_i.c %(Filename)_p.c Anime4KCPP\core\include;core;ThirdParty\include\opencl;$(INTELOCLSDKROOT)\include;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;%(AdditionalIncludeDirectories) $(IntDir) ProgramDatabase Sync OnlyExplicitInline stdcpp17 MaxSpeed NotUsing MultiThreadedDLL true false Level3 %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;ENABLE_OPENCL;ENABLE_VIDEO;ENABLE_PREVIEW_GUI;ENABLE_IMAGE_IO;ANIME4KCPP_CORE_PARALLEL_LIBRARY="PPL";ANIME4KCPP_CORE_COMPILER="MSVC";ANIME4KCPP_CORE_BUILD_DATE="2021-10-01";ANIME4KCPP_CORE_VERSION="2.6.0";ANIME4KCPP_CORE_VERSION_MAJOR=2;ANIME4KCPP_CORE_VERSION_MINOR=6;ANIME4KCPP_CORE_VERSION_PATCH=0;ANIME4KCPP_CORE_VERSION_STATUS="dev";BUILT_IN_KERNEL;USE_PPL;CMAKE_INTDIR="Release" $(IntDir) %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;ENABLE_OPENCL;ENABLE_VIDEO;ENABLE_PREVIEW_GUI;ENABLE_IMAGE_IO;ANIME4KCPP_CORE_PARALLEL_LIBRARY=\"PPL\";ANIME4KCPP_CORE_COMPILER=\"MSVC\";ANIME4KCPP_CORE_BUILD_DATE=\"2021-10-01\";ANIME4KCPP_CORE_VERSION=\"2.6.0\";ANIME4KCPP_CORE_VERSION_MAJOR=2;ANIME4KCPP_CORE_VERSION_MINOR=6;ANIME4KCPP_CORE_VERSION_PATCH=0;ANIME4KCPP_CORE_VERSION_STATUS=\"dev\";BUILT_IN_KERNEL;USE_PPL;CMAKE_INTDIR=\"Release\" Anime4KCPP\core\include;Anime4KCPP\build\core;Anime4KCPP\ThirdParty\include\opencl;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;$(INTELOCLSDKROOT)\include;%(AdditionalIncludeDirectories) Anime4KCPP\core\include;Anime4KCPP\build\core;Anime4KCPP\ThirdParty\include\opencl;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;$(INTELOCLSDKROOT)\include;%(AdditionalIncludeDirectories) $(ProjectDir)/$(IntDir) %(Filename).h %(Filename).tlb %(Filename)_i.c %(Filename)_p.c ================================================ FILE: Anime4KCPPCore.vcxproj.filters ================================================  Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {047624CC-513D-36E8-B51C-5F37C672EBF6} {788BFBE6-65CD-3D66-A4BD-D9B8297F3AEF} ================================================ FILE: Dlls/Dlls.csproj ================================================  Release {BC1BC9F1-893D-4715-818A-F37F74EB5710} Library Properties Dlls Dlls 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 PreserveNewest ================================================ FILE: HttpDownload/HttpDownload.cpp ================================================ // HttpDownload.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "http_download.h" #include #include #include int _tmain(int argc, TCHAR *argv[]) { if (argc != 3) return EXIT_FAILURE; try { const bool ok = HttpDownload(argv[1], argv[2]); return ok ? EXIT_SUCCESS : EXIT_FAILURE; } catch (const std::exception& ex) { std::cerr << ex.what() << '\n'; return EXIT_FAILURE; } } ================================================ FILE: HttpDownload/HttpDownload.vcxproj ================================================  Debug Win32 Release Win32 Debug x64 Release x64 {A4113679-4736-494B-B8D2-3C35B34E5491} Win32Proj HttpDownload Application true Unicode Application false true Unicode Application true Unicode Application false true Unicode true true false false Use Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true ../networking;%(AdditionalIncludeDirectories) Console true Winhttp.lib;%(AdditionalDependencies) Use Level3 Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true ../networking;%(AdditionalIncludeDirectories) Console true Winhttp.lib;%(AdditionalDependencies) Level3 Use MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true ../networking;%(AdditionalIncludeDirectories) Console true true true Winhttp.lib;%(AdditionalDependencies) Level3 Use MaxSpeed true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true ../networking;%(AdditionalIncludeDirectories) Console true true true Winhttp.lib;%(AdditionalDependencies) Create Create Create Create {3de6c2d2-fdfc-4745-8282-981df7561405} ================================================ FILE: HttpDownload/HttpDownload.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Source Files Source Files ================================================ FILE: HttpDownload/ReadMe.txt ================================================ ======================================================================== CONSOLE APPLICATION : HttpDownload Project Overview ======================================================================== AppWizard has created this HttpDownload application for you. This file contains a summary of what you will find in each of the files that make up your HttpDownload application. HttpDownload.vcxproj This is the main project file for VC++ projects generated using an Application Wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations, and project features selected with the Application Wizard. HttpDownload.vcxproj.filters This is the filters file for VC++ projects generated using an Application Wizard. It contains information about the association between the files in your project and the filters. This association is used in the IDE to show grouping of files with similar extensions under a specific node (for e.g. ".cpp" files are associated with the "Source Files" filter). HttpDownload.cpp This is the main application source file. ///////////////////////////////////////////////////////////////////////////// Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named HttpDownload.pch and a precompiled types file named StdAfx.obj. ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" comments to indicate parts of the source code you should add to or customize. ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: HttpDownload/stdafx.cpp ================================================ // stdafx.cpp : source file that includes just the standard includes // HttpDownload.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file ================================================ FILE: HttpDownload/stdafx.h ================================================ // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #include "targetver.h" #include #include // TODO: reference additional headers your program requires here ================================================ FILE: HttpDownload/targetver.h ================================================ #pragma once // Including SDKDDKVer.h defines the highest available Windows platform. // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #include ================================================ FILE: LICENSE ================================================ The MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Player/AsyncGetUrlUnderMouseCursor.cpp ================================================ #include "stdafx.h" #include "AsyncGetUrlUnderMouseCursor.h" namespace { class CComUsageScope { bool m_bInitialized; public: explicit CComUsageScope(DWORD dwCoInit = COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY) { m_bInitialized = SUCCEEDED(CoInitializeEx(NULL, dwCoInit)); } ~CComUsageScope() { if (m_bInitialized) CoUninitialize(); } }; bool LinkHandled(const CComPtr& pacc) { VARIANT v; v.vt = VT_I4; v.lVal = CHILDID_SELF; CComVariant vRole; if (SUCCEEDED(pacc->get_accRole(v, &vRole)) && vRole.vt == VT_I4 && vRole.lVal == ROLE_SYSTEM_LINK) { CComBSTR url; if (SUCCEEDED(pacc->get_accValue(v, &url)) && url != NULL) { AfxGetApp()->PostThreadMessage(WM_ON_ASYNC_URL, (WPARAM)url.Detach(), NULL); return true; } } return false; } VOID CALLBACK SendAsyncProc( HWND, UINT, ULONG_PTR dwData, LRESULT lResult) { CComUsageScope scope; CComPtr pacc_; if (FAILED(ObjectFromLresult(lResult, __uuidof(IAccessible), 0, (void**)&pacc_))) return; enum { MAX_ITER_NUM = 100 }; POINT ptScreen{ LOWORD(dwData), HIWORD(dwData) }; for (int i = 0; i < 2; ++i) { CComPtr pacc = pacc_; { int iter = 0; CComVariant vtChild; CComQIPtr paccChild; for (; SUCCEEDED(pacc->accHitTest(ptScreen.x, ptScreen.y, &vtChild)) && VT_DISPATCH == vtChild.vt && (paccChild = vtChild.pdispVal) != NULL; vtChild.Clear()) { if (LinkHandled(pacc)) return; if (iter++ >= MAX_ITER_NUM) return; pacc.Attach(paccChild.Detach()); } } int iter = 0; while (pacc) { if (LinkHandled(pacc)) return; if (iter++ >= MAX_ITER_NUM) return; CComPtr spDisp; if (FAILED(pacc->get_accParent(&spDisp))) return; CComQIPtr spParent(spDisp); pacc.Attach(spParent.Detach()); } if (i == 0) { ::Sleep(100); } } } } // namespace void AsyncGetUrlUnderMouseCursor() { POINT pt; if (!GetCursorPos(&pt)) return; HWND hWnd = WindowFromPoint(pt); if (NULL == hWnd) return; TCHAR szBuffer[64]; const int classNameLength = ::GetClassName(hWnd, szBuffer, sizeof(szBuffer) / sizeof(szBuffer[0])); szBuffer[sizeof(szBuffer) / sizeof(szBuffer[0]) - 1] = _T('\0'); if (_tcscmp(szBuffer, _T("MozillaWindowClass")) != 0 && _tcscmp(szBuffer, _T("Chrome_RenderWidgetHostHWND")) != 0) return; VERIFY(SendMessageCallback(hWnd, WM_GETOBJECT, 0L, OBJID_CLIENT, SendAsyncProc, MAKELONG(pt.x, pt.y))); } ================================================ FILE: Player/AsyncGetUrlUnderMouseCursor.h ================================================ #pragma once enum { WM_ON_ASYNC_URL = 1234 }; void AsyncGetUrlUnderMouseCursor(); ================================================ FILE: Player/ByteStreamBuffer.h ================================================ #pragma once #include class ByteStreamBuffer: public std::streambuf { public: ByteStreamBuffer(char* base, size_t length) { setg(base, base, base + length); } protected: pos_type seekoff( off_type offset, std::ios_base::seekdir dir, std::ios_base::openmode ) override { char* whence = eback(); if (dir == std::ios_base::cur) { whence = gptr(); } else if (dir == std::ios_base::end) { whence = egptr(); } char* to = whence + offset; // check limits if (to >= eback() && to <= egptr()) { setg(eback(), to, egptr()); return gptr() - eback(); } return -1; } }; ================================================ FILE: Player/D3DFONT.CPP ================================================ //----------------------------------------------------------------------------- // File: D3DFont.cpp // // Desc: Texture-based font class //----------------------------------------------------------------------------- #include "stdafx.h" #ifndef STRICT #define STRICT #endif #include #include //#include #include "D3DFont.h" //#include "D3DUtil.h" //#include "DXUtil.h" #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } //----------------------------------------------------------------------------- // Custom vertex types for rendering text //----------------------------------------------------------------------------- #define MAX_NUM_VERTICES 50*6 typedef struct D3DXVECTOR3 { FLOAT x; FLOAT y; FLOAT z; } D3DXVECTOR3, *LPD3DXVECTOR3; typedef struct D3DXVECTOR4 { FLOAT x; FLOAT y; FLOAT z; FLOAT w; } D3DXVECTOR4, *LPD3DXVECTOR4; struct FONT2DVERTEX { D3DXVECTOR4 p; DWORD color; FLOAT tu, tv; }; struct FONT3DVERTEX { D3DXVECTOR3 p; D3DXVECTOR3 n; FLOAT tu, tv; }; #define D3DFVF_FONT2DVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1) #define D3DFVF_FONT3DVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1) inline FONT2DVERTEX InitFont2DVertex( const D3DXVECTOR4& p, D3DCOLOR color, FLOAT tu, FLOAT tv ) { FONT2DVERTEX v; v.p = p; v.color = color; v.tu = tu; v.tv = tv; return v; } inline FONT3DVERTEX InitFont3DVertex( const D3DXVECTOR3& p, const D3DXVECTOR3& n, FLOAT tu, FLOAT tv ) { FONT3DVERTEX v; v.p = p; v.n = n; v.tu = tu; v.tv = tv; return v; } //----------------------------------------------------------------------------- // Name: CD3DFont() // Desc: Font class constructor //----------------------------------------------------------------------------- CD3DFont::CD3DFont( const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags ) { _tcsncpy_s( m_strFontName, strFontName, sizeof(m_strFontName) / sizeof(TCHAR) ); m_strFontName[sizeof(m_strFontName) / sizeof(TCHAR) - 1] = _T('\0'); m_dwFontHeight = dwHeight; m_dwFontFlags = dwFlags; m_dwSpacing = 0; m_pd3dDevice = NULL; m_pTexture = NULL; m_pVB = NULL; m_pStateBlockSaved = NULL; m_pStateBlockDrawText = NULL; } //----------------------------------------------------------------------------- // Name: ~CD3DFont() // Desc: Font class destructor //----------------------------------------------------------------------------- CD3DFont::~CD3DFont() { InvalidateDeviceObjects(); DeleteDeviceObjects(); } //----------------------------------------------------------------------------- // Name: InitDeviceObjects() // Desc: Initializes device-dependent objects, including the vertex buffer used // for rendering text and the texture map which stores the font image. //----------------------------------------------------------------------------- HRESULT CD3DFont::InitDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice ) { HRESULT hr; // Keep a local copy of the device m_pd3dDevice = pd3dDevice; // Establish the font and texture size m_fTextScale = 1.0f; // Draw fonts into texture without scaling // Large fonts need larger textures if( m_dwFontHeight > 60 ) m_dwTexWidth = m_dwTexHeight = 2048; else if( m_dwFontHeight > 30 ) m_dwTexWidth = m_dwTexHeight = 1024; else if( m_dwFontHeight > 15 ) m_dwTexWidth = m_dwTexHeight = 512; else m_dwTexWidth = m_dwTexHeight = 256; // If requested texture is too big, use a smaller texture and smaller font, // and scale up when rendering. D3DCAPS9 d3dCaps; m_pd3dDevice->GetDeviceCaps( &d3dCaps ); if( m_dwTexWidth > d3dCaps.MaxTextureWidth ) { m_fTextScale = (FLOAT)d3dCaps.MaxTextureWidth / (FLOAT)m_dwTexWidth; m_dwTexWidth = m_dwTexHeight = d3dCaps.MaxTextureWidth; } // Create a new texture for the font hr = m_pd3dDevice->CreateTexture( m_dwTexWidth, m_dwTexHeight, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &m_pTexture, NULL ); if( FAILED(hr) ) return hr; // Prepare to create a bitmap DWORD* pBitmapBits; BITMAPINFO bmi; ZeroMemory( &bmi.bmiHeader, sizeof(BITMAPINFOHEADER) ); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = (int)m_dwTexWidth; bmi.bmiHeader.biHeight = -(int)m_dwTexHeight; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biBitCount = 32; // Create a DC and a bitmap for the font HDC hDC = CreateCompatibleDC( NULL ); HBITMAP hbmBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS, (void**)&pBitmapBits, NULL, 0 ); SetMapMode( hDC, MM_TEXT ); // Create a font. By specifying ANTIALIASED_QUALITY, we might get an // antialiased font, but this is not guaranteed. INT nHeight = -MulDiv( m_dwFontHeight, (INT)(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72 ); DWORD dwBold = (m_dwFontFlags&D3DFONT_BOLD) ? FW_BOLD : FW_NORMAL; DWORD dwItalic = (m_dwFontFlags&D3DFONT_ITALIC) ? TRUE : FALSE; HFONT hFont = CreateFont( nHeight, 0, 0, 0, dwBold, dwItalic, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, m_strFontName ); if (NULL == hFont) { DeleteObject(hbmBitmap); DeleteDC(hDC); return E_FAIL; } HGDIOBJ hbmOld = SelectObject( hDC, hbmBitmap ); HGDIOBJ hFontOld = SelectObject( hDC, hFont ); // Set text properties SetTextColor( hDC, RGB(255,255,255) ); SetBkColor( hDC, 0x00000000 ); SetTextAlign( hDC, TA_TOP ); // Loop through all printable character and output them to the bitmap.. // Meanwhile, keep track of the corresponding tex coords for each character. DWORD x = 0; DWORD y = 0; TCHAR str[2] = _T("x"); SIZE size; // Calculate the spacing between characters based on line height GetTextExtentPoint32( hDC, TEXT(" "), 1, &size ); x = m_dwSpacing = (DWORD) ceil(size.cy * 0.3f); for( TCHAR c=32; c<127; c++ ) { str[0] = c; GetTextExtentPoint32( hDC, str, 1, &size ); if( (DWORD)(x + size.cx + m_dwSpacing) > m_dwTexWidth ) { x = m_dwSpacing; y += size.cy+1; } ExtTextOut( hDC, x+0, y+0, ETO_OPAQUE, NULL, str, 1, NULL ); m_fTexCoords[c-32][0] = ((FLOAT)(x + 0 - m_dwSpacing))/m_dwTexWidth; m_fTexCoords[c-32][1] = ((FLOAT)(y + 0 + 0 ))/m_dwTexHeight; m_fTexCoords[c-32][2] = ((FLOAT)(x + size.cx + m_dwSpacing))/m_dwTexWidth; m_fTexCoords[c-32][3] = ((FLOAT)(y + size.cy + 0 ))/m_dwTexHeight; x += size.cx + (2 * m_dwSpacing); } // Lock the surface and write the alpha values for the set pixels D3DLOCKED_RECT d3dlr; m_pTexture->LockRect( 0, &d3dlr, 0, 0 ); BYTE* pDstRow = (BYTE*)d3dlr.pBits; for( y=0; y < m_dwTexHeight; y++ ) { WORD* pDst16 = (WORD*)pDstRow; for( x=0; x < m_dwTexWidth; x++ ) { BYTE bAlpha = (BYTE)((pBitmapBits[m_dwTexWidth*y + x] & 0xff) >> 4); // 4-bit measure of pixel intensity if (bAlpha > 0) { *pDst16++ = (WORD) ((bAlpha << 12) | 0x0fff); } else { *pDst16++ = 0x0000; } } pDstRow += d3dlr.Pitch; } // Done updating texture, so clean up used objects m_pTexture->UnlockRect(0); SelectObject( hDC, hbmOld ); SelectObject( hDC, hFontOld ); DeleteObject( hbmBitmap ); DeleteObject( hFont ); DeleteDC( hDC ); return S_OK; } //----------------------------------------------------------------------------- // Name: RestoreDeviceObjects() // Desc: //----------------------------------------------------------------------------- HRESULT CD3DFont::RestoreDeviceObjects() { HRESULT hr; // Create vertex buffer for the letters int vertexSize = max( sizeof(FONT2DVERTEX), sizeof(FONT3DVERTEX ) ); if( FAILED( hr = m_pd3dDevice->CreateVertexBuffer( MAX_NUM_VERTICES * vertexSize, D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &m_pVB, NULL ) ) ) { return hr; } // Create the state blocks for rendering text for( UINT which=0; which<2; which++ ) { m_pd3dDevice->BeginStateBlock(); m_pd3dDevice->SetTexture( 0, m_pTexture ); if ( D3DFONT_ZENABLE & m_dwFontFlags ) m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); else m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE ); m_pd3dDevice->SetRenderState( D3DRS_ALPHAREF, 0x08 ); m_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ); m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW ); m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_CLIPPING, TRUE ); m_pd3dDevice->SetRenderState( D3DRS_CLIPPLANEENABLE, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ); m_pd3dDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 ); m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE ); m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE ); if( which==0 ) m_pd3dDevice->EndStateBlock( &m_pStateBlockSaved ); else m_pd3dDevice->EndStateBlock( &m_pStateBlockDrawText ); } return S_OK; } //----------------------------------------------------------------------------- // Name: InvalidateDeviceObjects() // Desc: Destroys all device-dependent objects //----------------------------------------------------------------------------- HRESULT CD3DFont::InvalidateDeviceObjects() { SAFE_RELEASE( m_pVB ); SAFE_RELEASE( m_pStateBlockSaved ); SAFE_RELEASE( m_pStateBlockDrawText ); return S_OK; } //----------------------------------------------------------------------------- // Name: DeleteDeviceObjects() // Desc: Destroys all device-dependent objects //----------------------------------------------------------------------------- HRESULT CD3DFont::DeleteDeviceObjects() { SAFE_RELEASE( m_pTexture ); m_pd3dDevice = NULL; return S_OK; } //----------------------------------------------------------------------------- // Name: GetTextExtent() // Desc: Get the dimensions of a text string //----------------------------------------------------------------------------- HRESULT CD3DFont::GetTextExtent( const TCHAR* strText, SIZE* pSize ) { if( NULL==strText || NULL==pSize ) return E_FAIL; FLOAT fRowWidth = 0.0f; FLOAT fRowHeight = (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight; FLOAT fWidth = 0.0f; FLOAT fHeight = fRowHeight; while( *strText ) { TCHAR c = *strText++; if( c == _T('\n') ) { if (*strText == _T('\0')) break; fRowWidth = 0.0f; fHeight += fRowHeight; } if( (c-32) < 0 || (c-32) >= 128-32 ) continue; FLOAT tx1 = m_fTexCoords[c-32][0]; FLOAT tx2 = m_fTexCoords[c-32][2]; fRowWidth += (tx2-tx1)*m_dwTexWidth - 2*m_dwSpacing; if( fRowWidth > fWidth ) fWidth = fRowWidth; } pSize->cx = (int)fWidth; pSize->cy = (int)fHeight; return S_OK; } //----------------------------------------------------------------------------- // Name: DrawTextScaled() // Desc: Draws scaled 2D text. Note that x and y are in viewport coordinates // (ranging from -1 to +1). fXScale and fYScale are the size fraction // relative to the entire viewport. For example, a fXScale of 0.25 is // 1/8th of the screen width. This allows you to output text at a fixed // fraction of the viewport, even if the screen or window size changes. //----------------------------------------------------------------------------- HRESULT CD3DFont::DrawTextScaled( FLOAT x, FLOAT y, FLOAT z, FLOAT fXScale, FLOAT fYScale, DWORD dwColor, const TCHAR* strText, DWORD dwFlags ) { if( m_pd3dDevice == NULL ) return E_FAIL; // Set up renderstate m_pStateBlockSaved->Capture(); m_pStateBlockDrawText->Apply(); m_pd3dDevice->SetFVF( D3DFVF_FONT2DVERTEX ); m_pd3dDevice->SetPixelShader( NULL ); m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) ); // Set filter states if( dwFlags & D3DFONT_FILTERED ) { m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); } D3DVIEWPORT9 vp; m_pd3dDevice->GetViewport( &vp ); FLOAT fLineHeight = ( m_fTexCoords[0][3] - m_fTexCoords[0][1] ) * m_dwTexHeight; // Center the text block in the viewport if( dwFlags & D3DFONT_CENTERED_X ) { const TCHAR* strTextTmp = strText; float xFinal = 0.0f; while( *strTextTmp ) { TCHAR c = *strTextTmp++; if( c == _T('\n') ) break; // Isn't supported. if( (c-32) < 0 || (c-32) >= 128-32 ) continue; FLOAT tx1 = m_fTexCoords[c-32][0]; FLOAT tx2 = m_fTexCoords[c-32][2]; FLOAT w = (tx2-tx1)*m_dwTexWidth; w *= (fXScale*vp.Height)/fLineHeight; xFinal += w - (2 * m_dwSpacing) * (fXScale*vp.Height)/fLineHeight; } x = -xFinal/vp.Width; } if( dwFlags & D3DFONT_CENTERED_Y ) { y = -fLineHeight/vp.Height; } FLOAT sx = (x+1.0f)*vp.Width/2; FLOAT sy = (y+1.0f)*vp.Height/2; FLOAT sz = z; FLOAT rhw = 1.0f; // Adjust for character spacing sx -= m_dwSpacing * (fXScale*vp.Height)/fLineHeight; FLOAT fStartX = sx; // Fill vertex buffer FONT2DVERTEX* pVertices; DWORD dwNumTriangles = 0L; m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD ); while( *strText ) { TCHAR c = *strText++; if( c == _T('\n') ) { sx = fStartX; sy += fYScale*vp.Height; } if( (c-32) < 0 || (c-32) >= 128-32 ) continue; FLOAT tx1 = m_fTexCoords[c-32][0]; FLOAT ty1 = m_fTexCoords[c-32][1]; FLOAT tx2 = m_fTexCoords[c-32][2]; FLOAT ty2 = m_fTexCoords[c-32][3]; FLOAT w = (tx2-tx1)*m_dwTexWidth; FLOAT h = (ty2-ty1)*m_dwTexHeight; w *= (fXScale*vp.Height)/fLineHeight; h *= (fYScale*vp.Height)/fLineHeight; if( c != _T(' ') ) { *pVertices++ = InitFont2DVertex({ sx + 0 - 0.5f, sy + h - 0.5f, sz, rhw }, dwColor, tx1, ty2); *pVertices++ = InitFont2DVertex({ sx + 0 - 0.5f, sy + 0 - 0.5f, sz, rhw }, dwColor, tx1, ty1); *pVertices++ = InitFont2DVertex({ sx + w - 0.5f, sy + h - 0.5f, sz, rhw }, dwColor, tx2, ty2); *pVertices++ = InitFont2DVertex({ sx + w - 0.5f, sy + 0 - 0.5f, sz, rhw }, dwColor, tx2, ty1); *pVertices++ = InitFont2DVertex({ sx + w - 0.5f, sy + h - 0.5f, sz, rhw }, dwColor, tx2, ty2); *pVertices++ = InitFont2DVertex({ sx + 0 - 0.5f, sy + 0 - 0.5f, sz, rhw }, dwColor, tx1, ty1); dwNumTriangles += 2; if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) ) { // Unlock, render, and relock the vertex buffer m_pVB->Unlock(); m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles ); m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD ); dwNumTriangles = 0L; } } sx += w - (2 * m_dwSpacing) * (fXScale*vp.Height)/fLineHeight; } // Unlock and render the vertex buffer m_pVB->Unlock(); if( dwNumTriangles > 0 ) m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles ); // Restore the modified renderstates m_pStateBlockSaved->Apply(); return S_OK; } //----------------------------------------------------------------------------- // Name: DrawText() // Desc: Draws 2D text. Note that sx and sy are in pixels //----------------------------------------------------------------------------- HRESULT CD3DFont::DrawText( FLOAT sx, FLOAT sy, DWORD dwColor, const TCHAR* strText, DWORD dwFlags ) { if( m_pd3dDevice == NULL ) return E_FAIL; // Setup renderstate m_pStateBlockSaved->Capture(); m_pStateBlockDrawText->Apply(); m_pd3dDevice->SetFVF( D3DFVF_FONT2DVERTEX ); m_pd3dDevice->SetPixelShader( NULL ); m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) ); // Set filter states if( dwFlags & D3DFONT_FILTERED ) { m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); } // Center the text block in the viewport if( dwFlags & D3DFONT_CENTERED_X ) { D3DVIEWPORT9 vp; m_pd3dDevice->GetViewport( &vp ); const TCHAR* strTextTmp = strText; float xFinal = 0.0f; while( *strTextTmp ) { TCHAR c = *strTextTmp++; if( c == _T('\n') ) break; // Isn't supported. if( (c-32) < 0 || (c-32) >= 128-32 ) continue; FLOAT tx1 = m_fTexCoords[c-32][0]; FLOAT tx2 = m_fTexCoords[c-32][2]; FLOAT w = (tx2-tx1) * m_dwTexWidth / m_fTextScale; xFinal += w - (2 * m_dwSpacing); } sx = (vp.Width-xFinal)/2.0f; } if( dwFlags & D3DFONT_CENTERED_Y ) { D3DVIEWPORT9 vp; m_pd3dDevice->GetViewport( &vp ); float fLineHeight = ((m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight); sy = (vp.Height-fLineHeight)/2; } // Adjust for character spacing sx -= m_dwSpacing; FLOAT fStartX = sx; // Fill vertex buffer FONT2DVERTEX* pVertices = NULL; DWORD dwNumTriangles = 0; m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD ); while( *strText ) { TCHAR c = *strText++; if( c == _T('\n') ) { sx = fStartX; sy += (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight; } if( (c-32) < 0 || (c-32) >= 128-32 ) continue; FLOAT tx1 = m_fTexCoords[c-32][0]; FLOAT ty1 = m_fTexCoords[c-32][1]; FLOAT tx2 = m_fTexCoords[c-32][2]; FLOAT ty2 = m_fTexCoords[c-32][3]; FLOAT w = (tx2-tx1) * m_dwTexWidth / m_fTextScale; FLOAT h = (ty2-ty1) * m_dwTexHeight / m_fTextScale; if( c != _T(' ') ) { *pVertices++ = InitFont2DVertex({ sx + 0 - 0.5f, sy + h - 0.5f, 0.9f, 1.0f }, dwColor, tx1, ty2); *pVertices++ = InitFont2DVertex({ sx + 0 - 0.5f, sy + 0 - 0.5f, 0.9f, 1.0f }, dwColor, tx1, ty1); *pVertices++ = InitFont2DVertex({ sx + w - 0.5f, sy + h - 0.5f, 0.9f, 1.0f }, dwColor, tx2, ty2); *pVertices++ = InitFont2DVertex({ sx + w - 0.5f, sy + 0 - 0.5f, 0.9f, 1.0f }, dwColor, tx2, ty1); *pVertices++ = InitFont2DVertex({ sx + w - 0.5f, sy + h - 0.5f, 0.9f, 1.0f }, dwColor, tx2, ty2); *pVertices++ = InitFont2DVertex({ sx + 0 - 0.5f, sy + 0 - 0.5f, 0.9f, 1.0f }, dwColor, tx1, ty1); dwNumTriangles += 2; if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) ) { // Unlock, render, and relock the vertex buffer m_pVB->Unlock(); m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles ); pVertices = NULL; m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD ); dwNumTriangles = 0L; } } sx += w - (2 * m_dwSpacing); } // Unlock and render the vertex buffer m_pVB->Unlock(); if( dwNumTriangles > 0 ) m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles ); // Restore the modified renderstates m_pStateBlockSaved->Apply(); return S_OK; } //----------------------------------------------------------------------------- // Name: Render3DText() // Desc: Renders 3D text //----------------------------------------------------------------------------- HRESULT CD3DFont::Render3DText( const TCHAR* strText, DWORD dwFlags ) { if( m_pd3dDevice == NULL ) return E_FAIL; // Setup renderstate m_pStateBlockSaved->Capture(); m_pStateBlockDrawText->Apply(); m_pd3dDevice->SetFVF( D3DFVF_FONT3DVERTEX ); m_pd3dDevice->SetPixelShader( NULL ); m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT3DVERTEX) ); // Set filter states if( dwFlags & D3DFONT_FILTERED ) { m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); } // Position for each text element FLOAT x = 0.0f; FLOAT y = 0.0f; // Center the text block at the origin (not the viewport) if( dwFlags & D3DFONT_CENTERED_X ) { SIZE sz; GetTextExtent( strText, &sz ); x = -(((FLOAT)sz.cx)/10.0f)/2.0f; } if( dwFlags & D3DFONT_CENTERED_Y ) { SIZE sz; GetTextExtent( strText, &sz ); y = -(((FLOAT)sz.cy)/10.0f)/2.0f; } // Turn off culling for two-sided text if( dwFlags & D3DFONT_TWOSIDED ) m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); // Adjust for character spacing x -= m_dwSpacing / 10.0f; FLOAT fStartX = x; TCHAR c; // Fill vertex buffer FONT3DVERTEX* pVertices; DWORD dwNumTriangles = 0L; m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD ); while( (c = *strText++) != 0 ) { if( c == '\n' ) { x = fStartX; y -= (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight/10.0f; } if( (c-32) < 0 || (c-32) >= 128-32 ) continue; FLOAT tx1 = m_fTexCoords[c-32][0]; FLOAT ty1 = m_fTexCoords[c-32][1]; FLOAT tx2 = m_fTexCoords[c-32][2]; FLOAT ty2 = m_fTexCoords[c-32][3]; FLOAT w = (tx2-tx1) * m_dwTexWidth / ( 10.0f * m_fTextScale ); FLOAT h = (ty2-ty1) * m_dwTexHeight / ( 10.0f * m_fTextScale ); if( c != _T(' ') ) { *pVertices++ = InitFont3DVertex({ x + 0, y + 0, 0 }, { 0, 0, -1 }, tx1, ty2); *pVertices++ = InitFont3DVertex({ x + 0, y + h, 0 }, { 0, 0, -1 }, tx1, ty1); *pVertices++ = InitFont3DVertex({ x + w, y + 0, 0 }, { 0, 0, -1 }, tx2, ty2); *pVertices++ = InitFont3DVertex({ x + w, y + h, 0 }, { 0, 0, -1 }, tx2, ty1); *pVertices++ = InitFont3DVertex({ x + w, y + 0, 0 }, { 0, 0, -1 }, tx2, ty2); *pVertices++ = InitFont3DVertex({ x + 0, y + h, 0 }, { 0, 0, -1 }, tx1, ty1); dwNumTriangles += 2; if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) ) { // Unlock, render, and relock the vertex buffer m_pVB->Unlock(); m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles ); m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD ); dwNumTriangles = 0L; } } x += w - (2 * m_dwSpacing) / 10.0f; } // Unlock and render the vertex buffer m_pVB->Unlock(); if( dwNumTriangles > 0 ) m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles ); // Restore the modified renderstates m_pStateBlockSaved->Apply(); return S_OK; } ================================================ FILE: Player/D3DFONT.H ================================================ //----------------------------------------------------------------------------- // File: D3DFont.h // // Desc: Texture-based font class //----------------------------------------------------------------------------- #ifndef D3DFONT_H #define D3DFONT_H #include #include // Font creation flags #define D3DFONT_BOLD 0x0001 #define D3DFONT_ITALIC 0x0002 #define D3DFONT_ZENABLE 0x0004 // Font rendering flags #define D3DFONT_CENTERED_X 0x0001 #define D3DFONT_CENTERED_Y 0x0002 #define D3DFONT_TWOSIDED 0x0004 #define D3DFONT_FILTERED 0x0008 //----------------------------------------------------------------------------- // Name: class CD3DFont // Desc: Texture-based font class for doing text in a 3D scene. //----------------------------------------------------------------------------- class CD3DFont { TCHAR m_strFontName[80]; // Font properties DWORD m_dwFontHeight; DWORD m_dwFontFlags; LPDIRECT3DDEVICE9 m_pd3dDevice; // A D3DDevice used for rendering LPDIRECT3DTEXTURE9 m_pTexture; // The d3d texture for this font LPDIRECT3DVERTEXBUFFER9 m_pVB; // VertexBuffer for rendering text DWORD m_dwTexWidth {}; // Texture dimensions DWORD m_dwTexHeight {}; FLOAT m_fTextScale {}; FLOAT m_fTexCoords[128 - 32][4] {}; DWORD m_dwSpacing; // Character pixel spacing per side // Stateblocks for setting and restoring render states LPDIRECT3DSTATEBLOCK9 m_pStateBlockSaved; LPDIRECT3DSTATEBLOCK9 m_pStateBlockDrawText; public: // 2D and 3D text drawing functions HRESULT DrawText( FLOAT x, FLOAT y, DWORD dwColor, const TCHAR* strText, DWORD dwFlags=0L ); HRESULT DrawTextScaled( FLOAT x, FLOAT y, FLOAT z, FLOAT fXScale, FLOAT fYScale, DWORD dwColor, const TCHAR* strText, DWORD dwFlags=0L ); HRESULT Render3DText( const TCHAR* strText, DWORD dwFlags=0L ); // Function to get extent of text HRESULT GetTextExtent( const TCHAR* strText, SIZE* pSize ); // Initializing and destroying device-dependent objects HRESULT InitDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice ); HRESULT RestoreDeviceObjects(); HRESULT InvalidateDeviceObjects(); HRESULT DeleteDeviceObjects(); // Constructor / destructor CD3DFont( const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags=0L ); ~CD3DFont(); }; #endif ================================================ FILE: Player/DialogBarPlayerControl.cpp ================================================ // DialogBarPlayerControl.cpp : implementation file // #include "stdafx.h" #include "Player.h" #include "DialogBarPlayerControl.h" #include "MainFrm.h" #include "PlayerDoc.h" #include "MakeDelegate.h" #include "SecondsToString.h" #include #include #ifdef _DEBUG #define new DEBUG_NEW #endif enum { RANGE_MAX = 0x7FFF }; enum { WM_SET_TIME = WM_USER + 101 }; namespace { HICON LoadIcon(int idr, int iconSize) { return (HICON) LoadImage( AfxGetApp()->m_hInstance, MAKEINTRESOURCE(idr), IMAGE_ICON, iconSize, iconSize, // use actual size LR_DEFAULTCOLOR); } double GetValueByMouseClick(CWnd* pDlg, CSliderCtrl* pSliderCtrl) { CRect rectClient, rectChannel, rectThumb; pDlg->GetClientRect(rectClient); pSliderCtrl->GetChannelRect(rectChannel); pSliderCtrl->GetThumbRect(rectThumb); rectChannel.DeflateRect(rectThumb.Width() / 2, 0); CPoint mousePt(AfxGetCurrentMessage()->pt); pSliderCtrl->ScreenToClient(&mousePt); return std::clamp( double(mousePt.x - rectClient.left - rectChannel.left) / (rectChannel.right - rectChannel.left), 0., 1.); } } // CDialogBarPlayerControl IMPLEMENT_DYNAMIC(CDialogBarPlayerControl, CPaneDialog) CDialogBarPlayerControl::CDialogBarPlayerControl() : m_pDoc(nullptr) , m_hPlay(NULL) , m_hPause(NULL) , m_hAudio(NULL) , m_hAudioOff(NULL) , m_hFullScreen(NULL) , m_savedVolume(0) , m_oldTotalTime(-1) // unset , m_oldCurrentTime(-1) // unset , m_tracking(false) , m_selStart(0) , m_selEnd(RANGE_MAX) { CDialogTemplate dlgtemplate; if (dlgtemplate.Load(MAKEINTRESOURCE(IDD))) { CSize size; dlgtemplate.GetSizeInPixels(&size); SetMinSize(size); } } CDialogBarPlayerControl::~CDialogBarPlayerControl() { onDocDetaching(); } void CDialogBarPlayerControl::onDocDetaching() { if (m_pDoc) { m_pDoc->framePositionChanged.disconnect(MAKE_DELEGATE(&CDialogBarPlayerControl::onFramePositionChanged, this)); m_pDoc->totalTimeUpdated.disconnect(MAKE_DELEGATE(&CDialogBarPlayerControl::onTotalTimeUpdated, this)); m_pDoc->currentTimeUpdated.disconnect(MAKE_DELEGATE(&CDialogBarPlayerControl::onCurrentTimeUpdated, this)); m_pDoc->rangeStartTimeChanged.disconnect(MAKE_DELEGATE(&CDialogBarPlayerControl::onRangeStartTimeChanged, this)); m_pDoc->rangeEndTimeChanged.disconnect(MAKE_DELEGATE(&CDialogBarPlayerControl::onRangeEndTimeChanged, this)); m_pDoc->onDestructing.disconnect(MAKE_DELEGATE(&CDialogBarPlayerControl::onDocDetaching, this)); m_pDoc = nullptr; } } void CDialogBarPlayerControl::DoDataExchange(CDataExchange* pDX) { __super::DoDataExchange(pDX); //{{AFX_DATA_MAP(CDialogBarPlayerControl) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP DDX_Control(pDX, IDC_PROGRESS_SLIDER, m_progressSlider); DDX_Control(pDX, IDC_VOLUME_SLIDER, m_volumeSlider); } BEGIN_MESSAGE_MAP(CDialogBarPlayerControl, CPaneDialog) ON_WM_HSCROLL() ON_UPDATE_COMMAND_UI(IDC_PLAY_PAUSE, &CDialogBarPlayerControl::OnUpdatePlayPause) ON_UPDATE_COMMAND_UI(IDC_AUDIO_ON_OFF, &CDialogBarPlayerControl::OnUpdateAudioOnOff) ON_BN_CLICKED(IDC_PLAY_PAUSE, &CDialogBarPlayerControl::OnClickedPlayPause) ON_BN_CLICKED(IDC_AUDIO_ON_OFF, &CDialogBarPlayerControl::OnClickedAudioOnOff) ON_MESSAGE(WM_SET_TIME, &CDialogBarPlayerControl::OnSetTime) ON_MESSAGE(WM_INITDIALOG, &CDialogBarPlayerControl::HandleInitDialog) ON_UPDATE_COMMAND_UI(IDC_FRAME_STEP, &CDialogBarPlayerControl::OnUpdateFrameStep) ON_UPDATE_COMMAND_UI(IDC_VOLUME_SLIDER, &CDialogBarPlayerControl::OnUpdateVolumeSlider) ON_UPDATE_COMMAND_UI(IDC_CURRENT_TIME, &CDialogBarPlayerControl::OnUpdateCurrentTime) END_MESSAGE_MAP() // CDialogBarPlayerControl message handlers LRESULT CDialogBarPlayerControl::HandleInitDialog(WPARAM wParam, LPARAM lParam) { __super::HandleInitDialog(wParam, lParam); CRect btnRect; GetDlgItem(IDC_PLAY_PAUSE)->GetClientRect(btnRect); const int iconSizeLimit = min(btnRect.Width() - 2 * GetSystemMetrics(SM_CXBORDER), btnRect.Height() - 2 * GetSystemMetrics(SM_CYBORDER)) * 4 / 5; const int iconSize = min(iconSizeLimit & ((iconSizeLimit > 32)? ~15 : ~7), 48); m_hPlay = LoadIcon(IDI_PLAY, iconSize); m_hPause = LoadIcon(IDI_PAUSE, iconSize); m_hAudio = LoadIcon(IDI_AUDIO, iconSize); m_hAudioOff = LoadIcon(IDI_AUDIO_OFF, iconSize); m_hFullScreen = LoadIcon(IDI_FULL_SCREEN, iconSize); static_cast(GetDlgItem(IDC_PLAY_PAUSE))->SetIcon(m_hPlay); static_cast(GetDlgItem(IDC_AUDIO_ON_OFF))->SetIcon(m_hAudio); static_cast(GetDlgItem(IDC_FULL_SCREEN))->SetIcon(m_hFullScreen); m_progressSlider.SetRange(0, RANGE_MAX); m_progressSlider.SetPageSize(0); m_progressSlider.ModifyStyle(0, TBS_ENABLESELRANGE); m_volumeSlider.SetRange(0, RANGE_MAX); m_volumeSlider.SetPos(RANGE_MAX); m_volumeSlider.SetPageSize(0); return TRUE; } void CDialogBarPlayerControl::setDocument(CPlayerDoc* pDoc) { ASSERT(!m_pDoc); m_pDoc = pDoc; m_volumeSlider.SetPos(int(RANGE_MAX * pDoc->soundVolume())); m_pDoc->framePositionChanged.connect(MAKE_DELEGATE(&CDialogBarPlayerControl::onFramePositionChanged, this)); m_pDoc->totalTimeUpdated.connect(MAKE_DELEGATE(&CDialogBarPlayerControl::onTotalTimeUpdated, this)); m_pDoc->currentTimeUpdated.connect(MAKE_DELEGATE(&CDialogBarPlayerControl::onCurrentTimeUpdated, this)); m_pDoc->rangeStartTimeChanged.connect(MAKE_DELEGATE(&CDialogBarPlayerControl::onRangeStartTimeChanged, this)); m_pDoc->rangeEndTimeChanged.connect(MAKE_DELEGATE(&CDialogBarPlayerControl::onRangeEndTimeChanged, this)); m_pDoc->onDestructing.connect(MAKE_DELEGATE(&CDialogBarPlayerControl::onDocDetaching, this)); } void CDialogBarPlayerControl::onFramePositionChanged(long long frame, long long total) { if (!m_tracking) { ASSERT(total >= 0); const int pos = (total > 0)? int((frame * RANGE_MAX) / total) : 0; m_progressSlider.SendNotifyMessage(TBM_SETPOS, TRUE, pos); } } enum { MIN_SEL_WIDTH = 50 }; void CDialogBarPlayerControl::onRangeStartTimeChanged(long long frame, long long total) { ASSERT(total >= 0); const int pos = (total > 0) ? int((frame * (RANGE_MAX - MIN_SEL_WIDTH)) / total) : 0; m_selStart = pos; m_progressSlider.SendNotifyMessage(TBM_SETSEL, TRUE, MAKELPARAM(pos, m_selEnd)); } void CDialogBarPlayerControl::onRangeEndTimeChanged(long long frame, long long total) { ASSERT(total >= 0); const int pos = (total > 0) ? int((frame * (RANGE_MAX - MIN_SEL_WIDTH)) / total) + MIN_SEL_WIDTH : 0; m_selEnd = pos; m_progressSlider.SendNotifyMessage(TBM_SETSEL, TRUE, MAKELPARAM(m_selStart, pos)); } void CDialogBarPlayerControl::onTotalTimeUpdated(double secs) { int totalTime = int(secs * 1000); if (totalTime == m_oldTotalTime) return; m_oldTotalTime = totalTime; SendNotifyMessage(WM_SET_TIME, IDC_TOTAL_TIME, totalTime); } void CDialogBarPlayerControl::onCurrentTimeUpdated(double secs) { int currentTime = int(secs * 1000); const bool notify = (currentTime / 1000 != m_oldCurrentTime / 1000) || m_pDoc && m_pDoc->isPaused() && currentTime != m_oldCurrentTime; m_oldCurrentTime = currentTime; if (notify) SendNotifyMessage(WM_SET_TIME, IDC_CURRENT_TIME, currentTime); } LRESULT CDialogBarPlayerControl::OnSetTime(WPARAM wParam, LPARAM lParam) { const bool milli = wParam == IDC_CURRENT_TIME && m_pDoc && m_pDoc->isPaused(); SetDlgItemText(wParam, secondsToString(lParam, milli).c_str()); return 0; } void CDialogBarPlayerControl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (m_pDoc) { if (static_cast(pScrollBar) == &m_progressSlider) { switch (nSBCode) { case SB_LINELEFT: if (m_pDoc->isPaused()) { m_pDoc->prevFrame(); } break; case SB_LINERIGHT: if (m_pDoc->isPaused()) { m_pDoc->nextFrame(); } break; case SB_PAGELEFT: case SB_PAGERIGHT: m_pDoc->seekByPercent(GetValueByMouseClick(this, &m_progressSlider)); break; case SB_THUMBTRACK: m_pDoc->seekByPercent(m_progressSlider.GetPos() / double(RANGE_MAX)); m_tracking = true; break; case SB_RIGHT: m_pDoc->seekToEnd(); break; case SB_ENDSCROLL: m_tracking = false; break; } } else if (static_cast(pScrollBar) == &m_volumeSlider) { if (nSBCode == SB_PAGELEFT || nSBCode == SB_PAGERIGHT) { const double valueByMouseClick = GetValueByMouseClick(this, &m_volumeSlider); m_volumeSlider.SetPos(int(valueByMouseClick * RANGE_MAX)); m_pDoc->setVolume(valueByMouseClick); } else { m_pDoc->setVolume(m_volumeSlider.GetPos() / double(RANGE_MAX)); } } } __super::OnHScroll(nSBCode, nPos, pScrollBar); } void CDialogBarPlayerControl::OnUpdatePlayPause(CCmdUI *pCmdUI) { if (m_pDoc) { pCmdUI->Enable(m_pDoc->isPlaying()); if (pCmdUI->m_pOther) { static_cast(pCmdUI->m_pOther)->SetIcon( m_pDoc->isPaused() ? m_hPlay : m_hPause); } } } void CDialogBarPlayerControl::OnUpdateAudioOnOff(CCmdUI *pCmdUI) { if (m_pDoc) { pCmdUI->Enable(m_pDoc->isPlaying()); if (pCmdUI->m_pOther) { static_cast(pCmdUI->m_pOther)->SetIcon( (m_volumeSlider.GetPos() > 0) ? m_hAudio : m_hAudioOff); } } } void CDialogBarPlayerControl::OnClickedPlayPause() { if (m_pDoc) { if (m_pDoc->isPaused() && IsDlgButtonChecked(IDC_FRAME_STEP)) { if (GetKeyState(VK_SHIFT) < 0) m_pDoc->prevFrame(); else m_pDoc->nextFrame(); } else { m_pDoc->pauseResume(); } } } void CDialogBarPlayerControl::OnClickedAudioOnOff() { int newVolume; if (m_savedVolume) { newVolume = m_savedVolume; m_savedVolume = 0; } else { m_savedVolume = m_volumeSlider.GetPos(); newVolume = 0; } m_volumeSlider.SetPos(newVolume); m_pDoc->setVolume(newVolume / double(RANGE_MAX)); } void CDialogBarPlayerControl::OnUpdateFrameStep(CCmdUI *pCmdUI) { if (pCmdUI->m_pOther) { pCmdUI->m_pOther->ShowWindow( (m_pDoc && m_pDoc->isPaused()) ? SW_SHOWNA : SW_HIDE); } } void CDialogBarPlayerControl::OnUpdateVolumeSlider(CCmdUI *pCmdUI) { if (pCmdUI->m_pOther) { pCmdUI->m_pOther->ShowWindow( (m_pDoc && m_pDoc->isPaused()) ? SW_HIDE : SW_SHOWNA); } } void CDialogBarPlayerControl::OnUpdateCurrentTime(CCmdUI* pCmdUI) { if (pCmdUI->m_pOther && m_pDoc && m_pDoc->isPaused()) { pCmdUI->m_pOther->SetWindowText(secondsToString(m_oldCurrentTime, true).c_str()); } } ================================================ FILE: Player/DialogBarPlayerControl.h ================================================ #pragma once #include "afxcmn.h" class CPlayerDoc; // CDialogBarPlayerControl class CDialogBarPlayerControl : public CPaneDialog { DECLARE_DYNAMIC(CDialogBarPlayerControl) public: CDialogBarPlayerControl(); virtual ~CDialogBarPlayerControl(); // Dialog Data //{{AFX_DATA(CDialogBarPlayerControl) enum { IDD = IDD_DIALOGBAR_PLAYER_CONTROL }; // NOTE: the ClassWizard will add data members here //}}AFX_DATA void setDocument(CPlayerDoc* pDoc); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CDialogBarPlayerControl) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL int GetCaptionHeight() const override { return 0; } void onFramePositionChanged(long long frame, long long total); void onTotalTimeUpdated(double secs); void onCurrentTimeUpdated(double secs); void onRangeStartTimeChanged(long long frame, long long total); void onRangeEndTimeChanged(long long frame, long long total); void onDocDetaching(); protected: DECLARE_MESSAGE_MAP() afx_msg LRESULT HandleInitDialog(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnSetTime(WPARAM wParam, LPARAM lParam); public: private: CPlayerDoc* m_pDoc; HICON m_hPlay; HICON m_hPause; HICON m_hAudio; HICON m_hAudioOff; HICON m_hFullScreen; int m_savedVolume; volatile int m_oldTotalTime; volatile int m_oldCurrentTime; bool m_tracking; int m_selStart; int m_selEnd; public: CSliderCtrl m_progressSlider; CSliderCtrl m_volumeSlider; afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); afx_msg void OnUpdatePlayPause(CCmdUI *pCmdUI); afx_msg void OnUpdateAudioOnOff(CCmdUI *pCmdUI); afx_msg void OnClickedPlayPause(); afx_msg void OnClickedAudioOnOff(); afx_msg void OnUpdateFrameStep(CCmdUI *pCmdUI); afx_msg void OnUpdateVolumeSlider(CCmdUI *pCmdUI); afx_msg void OnUpdateCurrentTime(CCmdUI *pCmdUI); }; ================================================ FILE: Player/DialogBarRange.cpp ================================================ // DialogBarRange.cpp : implementation file // #include "stdafx.h" #include "Player.h" #include "DialogBarRange.h" #include "PlayerDoc.h" #include "MakeDelegate.h" const WCHAR szwReset[] = { 0x29BB, 0 }; // CDialogBarRange IMPLEMENT_DYNAMIC(CDialogBarRange, CPaneDialog) CDialogBarRange::CDialogBarRange() : m_pDoc(nullptr) { CDialogTemplate dlgtemplate; if (dlgtemplate.Load(MAKEINTRESOURCE(IDD))) { CSize size; dlgtemplate.GetSizeInPixels(&size); SetMinSize(size); } } CDialogBarRange::~CDialogBarRange() { onDocDetaching(); } void CDialogBarRange::onDocDetaching() { if (m_pDoc) { m_pDoc->totalTimeUpdated.disconnect(MAKE_DELEGATE(&CDialogBarRange::onTotalTimeUpdated, this)); m_pDoc->onDestructing.disconnect(MAKE_DELEGATE(&CDialogBarRange::onDocDetaching, this)); m_pDoc = nullptr; } } void CDialogBarRange::setDocument(CPlayerDoc* pDoc) { ASSERT(!m_pDoc); m_pDoc = pDoc; m_pDoc->totalTimeUpdated.connect(MAKE_DELEGATE(&CDialogBarRange::onTotalTimeUpdated, this)); m_pDoc->onDestructing.connect(MAKE_DELEGATE(&CDialogBarRange::onDocDetaching, this)); } void CDialogBarRange::DoDataExchange(CDataExchange* pDX) { __super::DoDataExchange(pDX); //{{AFX_DATA_MAP(CDialogBarRange) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP DDX_Control(pDX, IDC_EDIT_START, m_startTime); DDX_Control(pDX, IDC_EDIT_END, m_endTime); } BEGIN_MESSAGE_MAP(CDialogBarRange, CPaneDialog) ON_COMMAND(IDC_START, &CDialogBarRange::OnStart) ON_COMMAND(IDC_START_RESET, &CDialogBarRange::OnStartReset) ON_COMMAND(IDC_END, &CDialogBarRange::OnEnd) ON_COMMAND(IDC_END_RESET, &CDialogBarRange::OnEndReset) ON_UPDATE_COMMAND_UI(IDC_START, &CDialogBarRange::OnUpdateStart) ON_UPDATE_COMMAND_UI(IDC_START_RESET, &CDialogBarRange::OnUpdateStartReset) ON_UPDATE_COMMAND_UI(IDC_END, &CDialogBarRange::OnUpdateEnd) ON_UPDATE_COMMAND_UI(IDC_END_RESET, &CDialogBarRange::OnUpdateEndReset) ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_COPY_AS, &CDialogBarRange::OnUpdateSave) ON_EN_CHANGE(IDC_EDIT_START, &CDialogBarRange::OnChangeStart) ON_EN_CHANGE(IDC_EDIT_END, &CDialogBarRange::OnChangeEnd) ON_BN_CLICKED(IDC_LOSSLESS_CUT, &CDialogBarRange::OnBnClickedLosslessCut) ON_MESSAGE(WM_INITDIALOG, &CDialogBarRange::HandleInitDialog) END_MESSAGE_MAP() LRESULT CDialogBarRange::HandleInitDialog(WPARAM wParam, LPARAM lParam) { __super::HandleInitDialog(wParam, lParam); if (auto control = static_cast(GetDlgItem(IDC_LOSSLESS_CUT))) { control->SetCheck(BST_CHECKED); } CRect rect(0, 0, 12, 12); MapDialogRect(*this, &rect); // Create the font with the calculated size m_font.CreateFont( rect.Height(), // Height 0, // Width 0, // Escapement 0, // Orientation FW_MEDIUM, // Weight FALSE, // Italic FALSE, // Underline 0, // StrikeOut ANSI_CHARSET, // CharSet OUT_DEFAULT_PRECIS, // OutPrecision CLIP_DEFAULT_PRECIS, // ClipPrecision DEFAULT_QUALITY, // Quality DEFAULT_PITCH | FF_SWISS, // PitchAndFamily _T("Arial")); // Facename for (auto id : {IDC_START_RESET, IDC_END_RESET}) { if (auto control = static_cast(GetDlgItem(id))) { control->SetFont(&m_font); control->SetWindowText(szwReset); } } return TRUE; } void CDialogBarRange::onTotalTimeUpdated(double) { m_startTime.Reset(); m_endTime.Reset(); } // CDialogBarRange message handlers void CDialogBarRange::OnStart() { const double currentTime = m_pDoc->getCurrentTime(); m_pDoc->setRangeStartTime(currentTime); m_startTime.SetValue(currentTime); } void CDialogBarRange::OnStartReset() { m_pDoc->setRangeStartTime(m_pDoc->getStartTime()); m_startTime.Reset(); } void CDialogBarRange::OnEnd() { const double currentTime = m_pDoc->getCurrentTime(); m_pDoc->setRangeEndTime(currentTime); m_endTime.SetValue(currentTime); } void CDialogBarRange::OnEndReset() { m_pDoc->setRangeEndTime(m_pDoc->getEndTime()); m_endTime.Reset(); } void CDialogBarRange::OnUpdateStart(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pDoc->isPlaying()); } void CDialogBarRange::OnUpdateStartReset(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pDoc->isPlaying()); } void CDialogBarRange::OnUpdateEnd(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pDoc->isPlaying()); } void CDialogBarRange::OnUpdateEndReset(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pDoc->isPlaying()); } void CDialogBarRange::OnUpdateSave(CCmdUI *pCmdUI) { pCmdUI->Enable(m_pDoc->isPlaying()); } void CDialogBarRange::OnChangeStart() { m_pDoc->setRangeStartTime( m_startTime.IsEmpty()? m_pDoc->getStartTime() : m_startTime.GetValue()); } void CDialogBarRange::OnChangeEnd() { m_pDoc->setRangeEndTime( m_endTime.IsEmpty()? m_pDoc->getEndTime() : m_endTime.GetValue()); } void CDialogBarRange::OnBnClickedLosslessCut() { if (auto control = static_cast(GetDlgItem(IDC_LOSSLESS_CUT))) { m_pDoc->setLosslessCut(control->GetCheck() != BST_UNCHECKED); } } ================================================ FILE: Player/DialogBarRange.h ================================================ #pragma once #include "EditTime.h" class CPlayerDoc; // CDialogBarRange class CDialogBarRange : public CPaneDialog { DECLARE_DYNAMIC(CDialogBarRange) public: CDialogBarRange(); virtual ~CDialogBarRange(); enum { IDD = IDD_DIALOGBAR_RANGE }; void setDocument(CPlayerDoc* pDoc); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CDialogBarPlayerControl) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL protected: int GetCaptionHeight() const override { return 0; } void onTotalTimeUpdated(double secs); void onDocDetaching(); protected: DECLARE_MESSAGE_MAP() afx_msg LRESULT HandleInitDialog(WPARAM wParam, LPARAM lParam); private: CPlayerDoc* m_pDoc; CFont m_font; public: CEditTime m_startTime; CEditTime m_endTime; afx_msg void OnStart(); afx_msg void OnStartReset(); afx_msg void OnEnd(); afx_msg void OnEndReset(); afx_msg void OnUpdateStart(CCmdUI *pCmdUI); afx_msg void OnUpdateStartReset(CCmdUI *pCmdUI); afx_msg void OnUpdateEnd(CCmdUI *pCmdUI); afx_msg void OnUpdateEndReset(CCmdUI *pCmdUI); afx_msg void OnUpdateSave(CCmdUI *pCmdUI); afx_msg void OnChangeStart(); afx_msg void OnChangeEnd(); afx_msg void OnBnClickedLosslessCut(); }; ================================================ FILE: Player/DialogOpenURL.cpp ================================================ // DialogOpenURL.cpp : implementation file // #include "stdafx.h" #include "Player.h" #include "DialogOpenURL.h" #include "afxdialogex.h" // CDialogOpenURL dialog IMPLEMENT_DYNAMIC(CDialogOpenURL, CDialog) CDialogOpenURL::CDialogOpenURL(CWnd* pParent /*=NULL*/) : CDialog(IDD_DIALOG_OPEN_URL, pParent) , m_URL(_T("")) , m_bParse(false) , m_inputFormt(_T("")) { } CDialogOpenURL::~CDialogOpenURL() { } void CDialogOpenURL::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_URL, m_URL); DDX_Check(pDX, IDC_PARSE, m_bParse); DDX_Text(pDX, IDC_EDIT_INPUT_FORMAT, m_inputFormt); } BEGIN_MESSAGE_MAP(CDialogOpenURL, CDialog) END_MESSAGE_MAP() // CDialogOpenURL message handlers ================================================ FILE: Player/DialogOpenURL.h ================================================ #pragma once // CDialogOpenURL dialog class CDialogOpenURL : public CDialog { DECLARE_DYNAMIC(CDialogOpenURL) public: explicit CDialogOpenURL(CWnd* pParent = NULL); // standard constructor virtual ~CDialogOpenURL(); // Dialog Data #ifdef AFX_DESIGN_TIME enum { IDD = IDD_DIALOG_OPEN_URL }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support DECLARE_MESSAGE_MAP() public: CString m_URL; int m_bParse{}; CString m_inputFormt; }; ================================================ FILE: Player/DialogVideoFilter.cpp ================================================ // DialogVideoFilter.cpp : implementation file // #include "stdafx.h" #include "Player.h" #include "afxdialogex.h" #include "DialogVideoFilter.h" // CDialogVideoFilter dialog IMPLEMENT_DYNAMIC(CDialogVideoFilter, CDialog) CDialogVideoFilter::CDialogVideoFilter(CWnd* pParent /*=nullptr*/) : CDialog(IDD_DIALOG_VIDEO_FILTER, pParent) , m_videoFilter(_T("")) , m_enableVideoFilter(FALSE) { } CDialogVideoFilter::~CDialogVideoFilter() { } void CDialogVideoFilter::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_VIDEO_FILTER, m_videoFilter); DDX_Check(pDX, IDC_ENABLE_VIDEO_FILTER, m_enableVideoFilter); } BEGIN_MESSAGE_MAP(CDialogVideoFilter, CDialog) END_MESSAGE_MAP() // CDialogVideoFilter message handlers ================================================ FILE: Player/DialogVideoFilter.h ================================================ #pragma once #include "afxdialogex.h" // CDialogVideoFilter dialog class CDialogVideoFilter : public CDialog { DECLARE_DYNAMIC(CDialogVideoFilter) public: CDialogVideoFilter(CWnd* pParent = nullptr); // standard constructor virtual ~CDialogVideoFilter(); // Dialog Data #ifdef AFX_DESIGN_TIME enum { IDD = IDD_DIALOG_VIDEO_FILTER }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support DECLARE_MESSAGE_MAP() public: CString m_videoFilter; BOOL m_enableVideoFilter; }; ================================================ FILE: Player/EditTime.cpp ================================================ // EditTime.cpp : implementation file // #include "stdafx.h" #include "EditTime.h" #include "SecondsToString.h" #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif namespace { enum { TIME_INVALID = -1000000000 }; int ReadUint(const TCHAR*& s) { int buf = 0; for (; *s >= '0' && *s <= '9'; ++s) buf = buf * 10 + *s - '0'; return buf; } double ParseTime(const TCHAR* timeStr) { int seconds = 0; int numDelims = 0; const bool minus = *timeStr == '-'; if (minus) ++timeStr; for (;;) { switch (*timeStr) { case '\0': return minus ? -seconds : seconds; case ':': if (++numDelims > 2) return TIME_INVALID; seconds *= 60; ++timeStr; break; case '.': { ++timeStr; const auto prevPtr = timeStr; const auto millis = ReadUint(timeStr); if (*timeStr != '\0') return TIME_INVALID; const int msecStringLen = timeStr - prevPtr; if (msecStringLen > 3) return TIME_INVALID; if (msecStringLen == 0) return minus ? -seconds : seconds; const auto absResult = seconds + millis / std::pow(10, msecStringLen); return minus ? -absResult : absResult; } break; default: const auto prevPtr = timeStr; seconds += ReadUint(timeStr); if (prevPtr == timeStr) return TIME_INVALID; } } } bool Match(const TCHAR* timeStr) { return ParseTime(timeStr) != TIME_INVALID; } } // namespace enum { WM_RESET = WM_USER + 101 }; ///////////////////////////////////////////////////////////////////////////// // CEditTime CEditTime::CEditTime() { } CEditTime::~CEditTime() { } BEGIN_MESSAGE_MAP(CEditTime, CEdit) //{{AFX_MSG_MAP(CEditTime) ON_WM_CHAR() ON_MESSAGE(WM_RESET, &CEditTime::OnReset) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CEditTime message handlers void CEditTime::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { if (nChar >= 32) { int iStartIndex = -1; int iEndIndex = -1; GetSel(iStartIndex, iEndIndex); CString strProposedText; GetWindowText(strProposedText); strProposedText.Delete(iStartIndex, iEndIndex - iStartIndex); strProposedText.Insert(iStartIndex, static_cast(nChar)); if (!Match(strProposedText)) return; } CEdit::OnChar(nChar, nRepCnt, nFlags); } void CEditTime::Reset() { SendNotifyMessage(WM_RESET, 0, 0); } void CEditTime::SetValue(double fTime) { SetWindowText(secondsToString(fTime * 1000, true).c_str()); } double CEditTime::GetValue() const { CString strBuf; GetWindowText(strBuf); return ParseTime(strBuf); } bool CEditTime::IsEmpty() const { CString strBuf; GetWindowText(strBuf); return strBuf.IsEmpty(); } LRESULT CEditTime::OnReset(WPARAM, LPARAM) { SetWindowText(_T("")); return 0; } ================================================ FILE: Player/EditTime.h ================================================ #pragma once ///////////////////////////////////////////////////////////////////////////// // CEditTime window class CEditTime : public CEdit { // Construction public: CEditTime(); // Attributes public: void Reset(); void SetValue(double fTime); double GetValue() const; bool IsEmpty() const; // Operations // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CEditTime) //}}AFX_VIRTUAL // Implementation public: virtual ~CEditTime(); // Generated message map functions protected: //{{AFX_MSG(CEditTime) afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg LRESULT OnReset(WPARAM wParam, LPARAM lParam); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: CFont m_Font; }; ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line. ================================================ FILE: Player/FrameToHglobal.cpp ================================================ #include "stdafx.h" #include "FrameToHglobal.h" #include "ffmpeg_dxva2.h" extern "C" { #include "libavutil/frame.h" #include "libswscale/swscale.h" }; HGLOBAL FrameToHglobal(IDirect3DSurface9* surface, int width, int height, int allocatedHeight) { AVFrame* tmp_frame = av_frame_alloc(); int res = dxva2_convert_data(surface, tmp_frame, width, allocatedHeight); if (res != 0) { av_frame_free(&tmp_frame); return NULL; } const int stride = ((width * 3) + 3) & ~3; auto hMem = GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPINFOHEADER) + stride * height); if (hMem == NULL) { av_frame_free(&tmp_frame); return NULL; } auto bmi = static_cast(GlobalLock(hMem)); if (!bmi) { GlobalFree(hMem); av_frame_free(&tmp_frame); return NULL; } memset(bmi, 0, sizeof(BITMAPINFOHEADER)); bmi->biSize = sizeof(BITMAPINFOHEADER); bmi->biWidth = width; bmi->biHeight = -height; bmi->biPlanes = 1; bmi->biBitCount = 24; bmi->biCompression = BI_RGB; const auto pData = static_cast(static_cast(bmi + 1)); auto img_convert_ctx = sws_getContext(tmp_frame->width, tmp_frame->height, (AVPixelFormat)tmp_frame->format, width, allocatedHeight, AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL); sws_scale(img_convert_ctx, tmp_frame->data, tmp_frame->linesize, 0, height, &pData, &stride); sws_freeContext(img_convert_ctx); GlobalUnlock(hMem); av_frame_free(&tmp_frame); return hMem; } ================================================ FILE: Player/FrameToHglobal.h ================================================ #pragma once struct IDirect3DSurface9; HGLOBAL FrameToHglobal(IDirect3DSurface9* surface, int width, int height, int allocatedHeight); ================================================ FILE: Player/FrameTransformer.cpp ================================================ #include "stdafx.h" #include "FrameTransformer.h" #include #include extern "C" { #include #include #include } const auto PIX_FMT = AV_PIX_FMT_NV12; FrameTransformer::FrameTransformer(std::string filter_desc) : filter_desc_(std::move(filter_desc)) {} int FrameTransformer::init(int in_w, int in_h, AVRational time_base){ time_base_ = time_base; return create_graph(in_w, in_h, PIX_FMT); } int FrameTransformer::create_graph(int in_w, int in_h, AVPixelFormat in_pix_fmt){ if (initialized_) return 0; int ret = 0; char args[512]; { auto graph = avfilter_graph_alloc(); if (!graph) return AVERROR(ENOMEM); graph_.reset(graph, [](AVFilterGraph* p) { avfilter_graph_free(&p); }); } const AVFilter* buffersrc = avfilter_get_by_name("buffer"); const AVFilter* buffersink = avfilter_get_by_name("buffersink"); if (!buffersrc || !buffersink) { ret = AVERROR_FILTER_NOT_FOUND; goto fail; } snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=1/1", in_w, in_h, in_pix_fmt, time_base_.num, time_base_.den); ret = avfilter_graph_create_filter(&buffersrc_ctx_, buffersrc, "in", args, nullptr, graph_.get()); if (ret < 0) goto fail; ret = avfilter_graph_create_filter(&buffersink_ctx_, buffersink, "out", nullptr, nullptr, graph_.get()); if (ret < 0) goto fail; // Set buffersink to accept NV12 output enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_NV12 }; ret = av_opt_set_bin( buffersink_ctx_, "pix_fmts", (const uint8_t*)pix_fmts, sizeof(pix_fmts), AV_OPT_SEARCH_CHILDREN ); if (ret < 0) goto fail; // Parse and link the user filter chain between src and sink AVFilterInOut* outputs = avfilter_inout_alloc(); AVFilterInOut* inputs = avfilter_inout_alloc(); outputs->name = av_strdup("in"); outputs->filter_ctx = buffersrc_ctx_; outputs->pad_idx = 0; outputs->next = nullptr; inputs->name = av_strdup("out"); inputs->filter_ctx = buffersink_ctx_; inputs->pad_idx = 0; inputs->next = nullptr; ret = avfilter_graph_parse_ptr(graph_.get(), filter_desc_.c_str(), &inputs, &outputs, nullptr); avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); if (ret < 0) goto fail; ret = avfilter_graph_config(graph_.get(), nullptr); if (ret < 0) goto fail; w_ = in_w; h_ = in_h; initialized_ = true; return 0; fail: free_graph(); return ret; } bool FrameTransformer::operator()(OrderedScopedTokenGenerator::Token t, uint8_t* input, int in_stride, int in_w, int in_h, int64_t pts, std::vector& output, int& out_w, int& out_h) { if (w_ != in_w || h_ != in_h) free_graph(); if (!initialized_) { int r = init(in_w, in_h, time_base_); // assume input pixfmt; caller can init explicitly if (r < 0) return false;// r; } AVFrame* frame = av_frame_alloc(); frame->format = PIX_FMT; // adjust if needed frame->width = in_w; frame->height = in_h; frame->linesize[0] = in_stride; frame->pts = pts; av_image_fill_arrays(frame->data, frame->linesize, input, (AVPixelFormat)frame->format, in_w, in_h, 1); AVFrame* filt = nullptr; { auto scope = t.lock(); // blocks until it's this token's generation order int ret = av_buffersrc_add_frame_flags(buffersrc_ctx_, frame, AV_BUFFERSRC_FLAG_KEEP_REF); av_frame_free(&frame); if (ret < 0) return false;// ret; // pull filtered frames filt = av_frame_alloc(); ret = av_buffersink_get_frame(buffersink_ctx_, filt); if (ret < 0) { av_frame_free(&filt); return false;// ret; } } // copy NV12 planar data into output vector out_w = filt->width; out_h = filt->height; int y_size = out_w * out_h; int uv_size = out_w * ((out_h+1)/2); output.resize(y_size + uv_size); // Y plane for (int i=0;idata[0] + i*filt->linesize[0], out_w); // UV interleaved plane for (int i=0;i<(out_h+1)/2;i++) memcpy(output.data() + y_size + i*out_w, filt->data[1] + i*filt->linesize[1], out_w); av_frame_free(&filt); return true;// 0; } void FrameTransformer::free_graph() { graph_.reset(); buffersrc_ctx_ = nullptr; buffersink_ctx_ = nullptr; initialized_ = false; w_ = 0; h_ = 0; } ================================================ FILE: Player/FrameTransformer.h ================================================ // FrameTransformer.h #pragma once #include "ordered_scoped_token.h" extern "C" { #include #include #include #include #include #include } #include #include /* Usage: FrameTransformer frameTransformer("crop=iw/2:ih:0:0,split[left][tmp];[tmp]hflip[right];[left][right] hstack"); auto [width, height] = m_frameDecoder->getVideoSize(); frameTransformer.init(width, height); m_frameDecoder->setImageConversionFunc(frameTransformer); */ class FrameTransformer { public: FrameTransformer(std::string filter_desc); // Initialize with input properties (called automatically on first process if not called) int init(int in_w, int in_h, AVRational time_base = {1, 25}); // Process one frame. pts is optional (pass AV_NOPTS_VALUE if unknown). // Returns success. bool operator()(OrderedScopedTokenGenerator::Token t, uint8_t* input, int in_stride, int in_w, int in_h, int64_t pts, std::vector& output, int& out_w, int& out_h); void reset(); private: std::string filter_desc_; std::shared_ptr graph_; AVFilterContext* buffersrc_ctx_ = nullptr; AVFilterContext* buffersink_ctx_ = nullptr; bool initialized_ = false; AVRational time_base_ = {1,25}; int w_ = 0; int h_ = 0; int create_graph(int in_w, int in_h, AVPixelFormat in_pix_fmt); void free_graph(); }; ================================================ FILE: Player/GetClipboardText.h ================================================ #pragma once #include inline std::string GetClipboardText() { std::string text; // Try opening the clipboard if (OpenClipboard(nullptr)) { // Get handle of clipboard object for ANSI text if (HANDLE hData = GetClipboardData(CF_TEXT)) { // Lock the handle to get the actual text pointer if (const char* pszText = static_cast(GlobalLock(hData))) { // Save text in a string class instance text = pszText; } // Release the lock GlobalUnlock(hData); } // Release the clipboard CloseClipboard(); } return text; } ================================================ FILE: Player/HandleFilesSequence.cpp ================================================ #include "stdafx.h" #include "HandleFilesSequence.h" #include #include namespace { auto MakeComparableConsideringNumbers(const CString& s) { std::vector result; unsigned int accum = 0; for (int i = 0; i < s.GetLength(); ++i) { const auto c = s[i]; if ((c >= _T('0') && accum != 0 || c > _T('0')) && c <= _T('9')) { accum = accum * 10 + (c - _T('0')); } else { if (accum != 0) { result.push_back(accum + static_cast(_T('0'))); accum = 0; } result.push_back((static_cast(c) < static_cast(_T('9'))) ? c : (0xFFFF0000 | c)); } } if (accum != 0) { result.push_back(accum + static_cast(_T('0'))); } return result; } bool CompareConsideringNumbers(CString left, CString right) { const auto leftConverted = MakeComparableConsideringNumbers(left.MakeUpper()); const auto rightConverted = MakeComparableConsideringNumbers(right.MakeUpper()); return std::lexicographical_compare( rightConverted.begin(), rightConverted.end(), leftConverted.begin(), leftConverted.end()); } } bool HandleFilesSequence(const CString& pathName, bool looping, std::function tryToOpen, bool invert /*= false*/) { const auto extension = PathFindExtension(pathName); const auto fileName = PathFindFileName(pathName); if (!extension || !fileName) return false; const CString directory(pathName, fileName - pathName); const CString pattern((directory + _T('*')) + extension); WIN32_FIND_DATA ffd{}; const auto hFind = FindFirstFile(pattern, &ffd); if (INVALID_HANDLE_VALUE == hFind) { return false; } std::vector filesArr[2]; const auto extensionLength = pathName.GetLength() - (extension - pathName); const CString justFileName(fileName, extension - fileName); do { if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { const auto length = _tcslen(ffd.cFileName); if (length > extensionLength && ffd.cFileName[length - extensionLength] == _T('.')) { CString cFileName(ffd.cFileName, length - extensionLength); const bool beforeOrEqual = !CompareConsideringNumbers(cFileName, justFileName); filesArr[beforeOrEqual].push_back(cFileName); } } } while (FindNextFile(hFind, &ffd)); FindClose(hFind); for (int i = 0; i <= looping; ++i) { std::vector& files = filesArr[i ^ invert]; std::make_heap(files.begin(), files.end(), CompareConsideringNumbers); while (!files.empty()) { const CString path = directory + files.front() + extension; if (tryToOpen(path)) { return true; } std::pop_heap(files.begin(), files.end(), CompareConsideringNumbers); files.pop_back(); } } return false; } ================================================ FILE: Player/HandleFilesSequence.h ================================================ #pragma once #include bool HandleFilesSequence(const CString& pathName, bool looping, std::function tryToOpen, bool invert = false); ================================================ FILE: Player/I420Effect.cpp ================================================ #include "stdafx.h" #include #include "I420Effect.h" #include "I420Effect_PS.h" I420Effect::I420Effect() : m_refCount(1) { m_originalFrame = D2D1::SizeU(0, 0); } HRESULT I420Effect::Register(_In_ ID2D1Factory1* pFactory) { // Format Effect metadata in XML as expected PCWSTR pszXml = LR"( )"; // Register the effect in the factory return pFactory->RegisterEffectFromString( CLSID_CustomI420Effect, pszXml, nullptr, 0, CreateRippleImpl ); } HRESULT __stdcall I420Effect::CreateRippleImpl(_Outptr_ IUnknown** ppEffectImpl) { // Since the object's refcount is initialized to 1, we don't need to AddRef here. *ppEffectImpl = static_cast(new (std::nothrow) I420Effect()); if (*ppEffectImpl == nullptr) return E_OUTOFMEMORY; return S_OK; } IFACEMETHODIMP I420Effect::Initialize( _In_ ID2D1EffectContext* pEffectContext, _In_ ID2D1TransformGraph* pTransformGraph ) { HRESULT hr = pEffectContext->LoadPixelShader(GUID_I420PixelShader, I420Effect_ByteCode, ARRAYSIZE(I420Effect_ByteCode)); if (SUCCEEDED(hr)) { // This loads the shader into the Direct2D image effects system and associates it with the GUID passed in. // If this method is called more than once (say by other instances of the effect) with the same GUID, // the system will simply do nothing, ensuring that only one instance of a shader is stored regardless of how // many time it is used. // The graph consists of a single transform. In fact, this class is the transform, // reducing the complexity of implementing an effect when all we need to // do is use a single pixel shader. hr = pTransformGraph->SetSingleTransformNode(this); } return S_OK; } IFACEMETHODIMP I420Effect::PrepareForRender(D2D1_CHANGE_TYPE /*changeType*/) { return S_OK; } // SetGraph is only called when the number of inputs changes. This never happens as we publish this effect // as a single input effect. IFACEMETHODIMP I420Effect::SetGraph(_In_ ID2D1TransformGraph* /*pGraph*/) { return E_NOTIMPL; } // Called to assign a new render info class, which is used to inform D2D on // how to set the state of the GPU. IFACEMETHODIMP I420Effect::SetDrawInfo(_In_ ID2D1DrawInfo* pDrawInfo) { HRESULT hr = S_OK; m_drawInfo = pDrawInfo; hr = m_drawInfo->SetPixelShader(GUID_I420PixelShader); if (SUCCEEDED(hr)) { // Providing this hint allows D2D to optimize performance when processing large images. m_drawInfo->SetInstructionCountHint(sizeof(I420Effect_ByteCode)); } return hr; } // Calculates the mapping between the output and input rects. In this case, // we want to request an expanded region to account for pixels that the ripple // may need outside of the bounds of the destination. IFACEMETHODIMP I420Effect::MapOutputRectToInputRects( _In_ const D2D1_RECT_L* /*pOutputRect*/, _Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects, UINT32 inputRectCount ) const { if (inputRectCount != 3) return E_NOTIMPL; pInputRects[0] = m_inputRect; pInputRects[1] = pInputRects[2] = { m_inputRect.left / 2, m_inputRect.top / 2, m_inputRect.right / 2, m_inputRect.bottom / 2 }; return S_OK; } IFACEMETHODIMP I420Effect::MapInputRectsToOutputRect( _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputRects, _In_reads_(inputRectCount) CONST D2D1_RECT_L* /*pInputOpaqueSubRects*/, UINT32 /*inputRectCount*/, _Out_ D2D1_RECT_L* pOutputRect, _Out_ D2D1_RECT_L* pOutputOpaqueSubRect ) { *pOutputRect = pInputRects[0]; if (m_inputRect.bottom != pInputRects[0].bottom || m_inputRect.top != pInputRects[0].top || m_inputRect.right != pInputRects[0].right || m_inputRect.left != pInputRects[0].left) { m_inputRect = pInputRects[0]; m_originalFrame.width = m_inputRect.right; m_originalFrame.height = m_inputRect.bottom; } // Indicate that entire output might contain transparency. ZeroMemory(pOutputOpaqueSubRect, sizeof(*pOutputOpaqueSubRect)); return S_OK; } IFACEMETHODIMP I420Effect::MapInvalidRect( UINT32 /*inputIndex*/, D2D1_RECT_L /*invalidInputRect*/, _Out_ D2D1_RECT_L* pInvalidOutputRect ) const { HRESULT hr = S_OK; // Indicate that the entire output may be invalid. *pInvalidOutputRect = m_inputRect; return hr; } IFACEMETHODIMP_(UINT32) I420Effect::GetInputCount() const { return 3; } // D2D ensures that that effects are only referenced from one thread at a time. // To improve performance, we simply increment/decrement our reference count // rather than use atomic InterlockedIncrement()/InterlockedDecrement() functions. IFACEMETHODIMP_(ULONG) I420Effect::AddRef() { ++m_refCount; return m_refCount; } IFACEMETHODIMP_(ULONG) I420Effect::Release() { --m_refCount; if (m_refCount == 0) { delete this; return 0; } else { return m_refCount; } } // This enables the stack of parent interfaces to be queried. In the instance // of the Ripple interface, this method simply enables the developer // to cast a Ripple instance to an ID2D1EffectImpl or IUnknown instance. IFACEMETHODIMP I420Effect::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppOutput) { static const QITAB rgqit[] = { QITABENT(I420Effect, ID2D1EffectImpl), QITABENT(I420Effect, ID2D1DrawTransform), QITABENT(I420Effect, ID2D1Transform), QITABENT(I420Effect, ID2D1TransformNode), { 0 }, }; return QISearch(this, rgqit, riid, ppOutput); } ================================================ FILE: Player/I420Effect.h ================================================ #pragma once #include // {3AB41678-D4BC-4BE1-8A91-07A63DEEFEA1} DEFINE_GUID(GUID_I420PixelShader, 0x3ab41678, 0xd4bc, 0x4be1, 0x8a, 0x91, 0x7, 0xa6, 0x3d, 0xee, 0xfe, 0xa1); // {DA637E40-44D4-4617-A3D3-A28344549EEA} DEFINE_GUID(CLSID_CustomI420Effect, 0xda637e40, 0x44d4, 0x4617, 0xa3, 0xd3, 0xa2, 0x83, 0x44, 0x54, 0x9e, 0xea); class I420Effect : public ID2D1EffectImpl, public ID2D1DrawTransform { public: // Declare effect registration methods. static HRESULT Register(_In_ ID2D1Factory1* pFactory); static HRESULT __stdcall CreateRippleImpl(_Outptr_ IUnknown** ppEffectImpl); // Declare ID2D1EffectImpl implementation methods. IFACEMETHODIMP Initialize(_In_ ID2D1EffectContext* pContextInternal, _In_ ID2D1TransformGraph* pTransformGraph); IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType); IFACEMETHODIMP SetGraph(_In_ ID2D1TransformGraph* pGraph); // Declare ID2D1DrawTransform implementation methods. IFACEMETHODIMP SetDrawInfo(_In_ ID2D1DrawInfo* pRenderInfo); // Declare ID2D1Transform implementation methods. IFACEMETHODIMP MapOutputRectToInputRects( _In_ const D2D1_RECT_L* pOutputRect, _Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects, UINT32 inputRectCount ) const; IFACEMETHODIMP MapInputRectsToOutputRect( _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputRects, _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputOpaqueSubRects, UINT32 inputRectCount, _Out_ D2D1_RECT_L* pOutputRect, _Out_ D2D1_RECT_L* pOutputOpaqueSubRect ); IFACEMETHODIMP MapInvalidRect( UINT32 inputIndex, D2D1_RECT_L invalidInputRect, _Out_ D2D1_RECT_L* pInvalidOutputRect ) const; // Declare ID2D1TransformNode implementation methods. IFACEMETHODIMP_(UINT32) GetInputCount() const; // Declare IUnknown implementation methods. IFACEMETHODIMP_(ULONG) AddRef(); IFACEMETHODIMP_(ULONG) Release(); IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppOutput); private: I420Effect(); CComPtr m_drawInfo; LONG m_refCount; D2D1_RECT_L m_inputRect; D2D1_SIZE_U m_originalFrame; }; ================================================ FILE: Player/I420Effect_PS.hlsl ================================================ Texture2D yTexture : register(t0); Texture2D uTexture : register(t1); Texture2D vTexture : register(t2); SamplerState ySampler : register(s0); SamplerState uSampler : register(s1); SamplerState vSampler : register(s2); float4 main( float4 pos : SV_POSITION, float4 posScene : SCENE_POSITION, float4 uv0 : TEXCOORD0 ) : SV_Target { float Y = yTexture.Sample(ySampler, uv0).a * 255; float U = uTexture.Sample(uSampler, uv0).a * 255; float V = vTexture.Sample(vSampler, uv0).a * 255; float4 color = float4(0, 0, 0, 1.0f); color.b = clamp(1.164383561643836*(Y - 16) + 2.017232142857142*(U - 128), 0, 255) / 255.0f; color.g = clamp(1.164383561643836*(Y - 16) - 0.812967647237771*(V - 128) - 0.391762290094914*(U - 128), 0, 255) / 255.0f; color.r = clamp(1.164383561643836*(Y - 16) + 1.596026785714286*(V - 128), 0, 255) / 255.0f; return color; } ================================================ FILE: Player/IEraseableArea.h ================================================ #pragma once class CWnd; class CDC; class IEraseableArea { public: virtual void OnErase(CWnd* pInitiator, CDC* pDC, BOOL isFullScreen) = 0; }; ================================================ FILE: Player/ImageUpscale.cpp ================================================ #include "stdafx.h" #include "ImageUpscale.h" #if __has_include("AC.hpp") #include"AC.hpp" #include"ACCreator.hpp" #include"ACProcessor.hpp" #include "Anime4KCPP.hpp" const int currPlatformID = 0; const int currDeviceID = 0; bool CanUpscaleImage() { static const bool ok = LoadLibrary(_T("opencl.dll")) != NULL && Anime4KCPP::OpenCL::checkGPUSupport(currPlatformID, currDeviceID); return ok; } bool EnableImageUpscale() { static Anime4KCPP::ACInitializer initializer; static const auto ok = [] { const int OpenCLQueueNum = 1; const bool OpenCLParallelIO = false; initializer.pushManager>( currPlatformID, currDeviceID, Anime4KCPP::CNNType::Default, OpenCLQueueNum, OpenCLParallelIO); return initializer.init() == initializer.size(); }(); return ok; } bool ImageUpscale(OrderedScopedTokenGenerator::Token, uint8_t* input, int inputStride, int inputWidth, int inputHeight, int64_t, std::vector& output, int& outputWidth, int& outputHeight) { Anime4KCPP::Parameters param{}; auto ac = Anime4KCPP::ACCreator::createUP(param, Anime4KCPP::Processor::Type::OpenCL_ACNet); const cv::Mat y_image(inputHeight, inputWidth, CV_8UC1, input, inputStride); const cv::Mat uv_image(inputHeight / 2, inputWidth / 2, CV_8UC2, input + inputHeight * inputStride, inputStride); cv::Mat cbcr_channels[2]; split(uv_image, cbcr_channels); ac->loadImage(y_image, cbcr_channels[0], cbcr_channels[1]); ac->process(); cv::Mat out_y_image; std::vector out_cbcr_channels(2); ac->saveImage(out_y_image, out_cbcr_channels[0], out_cbcr_channels[1]); outputWidth = out_y_image.cols; outputHeight = out_y_image.rows; cv::Mat CrCb; merge(out_cbcr_channels, CrCb); output.assign(out_y_image.datastart, out_y_image.dataend); output.insert(output.end(), CrCb.datastart, CrCb.dataend); return true; } #else bool CanUpscaleImage() { return false; } bool EnableImageUpscale() { return false; } bool ImageUpscale(OrderedScopedTokenGenerator::Token, uint8_t*, int, int, int, int64_t, std::vector&, int&, int&) { } #endif ================================================ FILE: Player/ImageUpscale.h ================================================ #pragma once #include "ordered_scoped_token.h" #include #include bool CanUpscaleImage(); bool EnableImageUpscale(); bool ImageUpscale(OrderedScopedTokenGenerator::Token, uint8_t* input, int inputStride, int inputWidth, int inputHeight, int64_t, std::vector& output, int& outputWidth, int& outputHeight); ================================================ FILE: Player/MainFrm.cpp ================================================ // MainFrm.cpp : implementation of the CMainFrame class // #include "stdafx.h" #include "Player.h" #include "MainFrm.h" #include "PlayerDoc.h" #include "IEraseableArea.h" #include "MakeDelegate.h" #ifdef _DEBUG #define new DEBUG_NEW #endif namespace { HICON LoadIcon(int idr) { int const cxButton = GetSystemMetrics(SM_CXSMICON); return (HICON) LoadImage( AfxGetApp()->m_hInstance, MAKEINTRESOURCE(idr), IMAGE_ICON, cxButton, cxButton, // use actual size LR_DEFAULTCOLOR); } class FullScreenBarAccessor : public CFullScreenImpl { public: CMFCToolBar* GetFullScreenBar() { return m_pwndFullScreenBar; } }; class FullScreenMgrAccessor : public CFrameImpl { public: CMFCToolBar* GetFullScreenBar() { return static_cast(m_FullScreenMgr).GetFullScreenBar(); } }; class DocumentAccessor : public CView { public: CPlayerDoc* GetDocument() { return dynamic_cast(m_pDocument); } }; UINT s_uTBBC = RegisterWindowMessage(L"TaskbarButtonCreated"); } // namespace // CMainFrame IMPLEMENT_DYNCREATE(CMainFrame, CFrameWndEx) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx) ON_WM_CREATE() ON_WM_CLOSE() ON_COMMAND(IDC_FULL_SCREEN, &CMainFrame::OnFullScreen) ON_WM_ERASEBKGND() ON_WM_WINDOWPOSCHANGED() ON_WM_NCPAINT() ON_WM_POWERBROADCAST() ON_REGISTERED_MESSAGE(s_uTBBC, &CMainFrame::CreateThumbnailToolbar) ON_WM_NCHITTEST() ON_WM_INITMENUPOPUP() END_MESSAGE_MAP() static UINT indicators[] = { ID_SEPARATOR, // status line indicator ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, }; // CMainFrame construction/destruction CMainFrame::CMainFrame() : m_bFullScreen(FALSE) { // TODO: add member initialization code here } CMainFrame::~CMainFrame() { } BOOL CMainFrame::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, // != NULL for popups LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext) { const BOOL result = __super::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, // != NULL for popups lpszMenuName, dwExStyle, pContext); if (result) { ASSERT(pContext->m_pCurrentDoc); ASSERT(pContext->m_pCurrentDoc->IsKindOf(RUNTIME_CLASS(CPlayerDoc))); m_wndPlayerControl.setDocument(static_cast(pContext->m_pCurrentDoc)); m_wndRange.setDocument(static_cast(pContext->m_pCurrentDoc)); static_cast(pContext->m_pCurrentDoc)->onPauseResume.connect( MAKE_DELEGATE(&CMainFrame::onPauseResume, this)); } return result; } void CMainFrame::pauseResume() { if (CView* pView = dynamic_cast(GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE))) { if (CPlayerDoc* pDoc = static_cast(pView)->GetDocument()) { if (pDoc->isPlaying()) pDoc->pauseResume(); } } } BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) { if (LOWORD(wParam) == IDC_PLAY_PAUSE) { pauseResume(); return TRUE; } return __super::OnCommand(wParam, lParam); } BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN && GetKeyState(VK_SHIFT) >= 0 && GetKeyState(VK_CONTROL) >= 0 && GetKeyState(VK_MENU) >= 0) { switch (pMsg->wParam) { case VK_SPACE: pauseResume(); return TRUE; case VK_F11: if (IsFullScreen()) ShowFullScreen(); else OnFullScreen(); return TRUE; } } return __super::PreTranslateMessage(pMsg); } int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (__super::OnCreate(lpCreateStruct) == -1) return -1; CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows7)); //CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerWindows7::Office2007_ObsidianBlack); if (!m_wndMenuBar.Create(this)) { TRACE0("Failed to create menubar\n"); return -1; // fail to create } m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY); //if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || // !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) //{ // TRACE0("Failed to create toolbar\n"); // return -1; // fail to create //} //if (!m_wndStatusBar.Create(this)) //{ // TRACE0("Failed to create status bar\n"); // return -1; // fail to create //} //m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)); if (!m_wndPlayerControl.Create( this, IDD_DIALOGBAR_PLAYER_CONTROL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI, IDD_DIALOGBAR_PLAYER_CONTROL)) { TRACE0("Failed to create player control dialog bar\n"); return -1; // fail to create } if (!m_wndRange.Create( this, IDD_DIALOGBAR_RANGE, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI, IDD_DIALOGBAR_RANGE)) { TRACE0("Failed to create range dialog bar\n"); return -1; // fail to create } // TODO: Delete these three lines if you don't want the toolbar to be dockable m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY); //m_wndToolBar.EnableDocking(CBRS_ALIGN_TOP); m_wndPlayerControl.EnableDocking(CBRS_ALIGN_BOTTOM); m_wndRange.EnableDocking(CBRS_ALIGN_BOTTOM); CString strBuffer; VERIFY(strBuffer.LoadString(IDS_PLAYER_CONTROL)); m_wndPlayerControl.SetWindowText(strBuffer); VERIFY(strBuffer.LoadString(IDS_RANGE)); m_wndRange.SetWindowText(strBuffer); EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM); DockPane(&m_wndMenuBar); //DockPane(&m_wndToolBar, AFX_IDW_DOCKBAR_TOP); DockPane(&m_wndPlayerControl, AFX_IDW_DOCKBAR_BOTTOM); DockPane(&m_wndRange, AFX_IDW_DOCKBAR_BOTTOM); // Enable toolbar and docking window menu replacement VERIFY(strBuffer.LoadString(IDS_TOOLBAR_CUSTOMIZE)); //m_wndToolBar.EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize); EnablePaneMenu(TRUE, ID_VIEW_CUSTOMIZE, strBuffer, ID_VIEW_TOOLBAR); m_hPlay = LoadIcon(IDI_PLAY); m_hPause = LoadIcon(IDI_PAUSE); // In case the application is run elevated, allow the // TaskbarButtonCreated and WM_COMMAND messages through. ChangeWindowMessageFilter(s_uTBBC, MSGFLT_ADD); ChangeWindowMessageFilter(WM_COMMAND, MSGFLT_ADD); EnableFullScreenMode(IDC_FULL_SCREEN); EnableFullScreenMainMenu(FALSE); return 0; } void CMainFrame::OnClose() { m_pTaskbarList.Release(); __super::OnClose(); } LRESULT CMainFrame::CreateThumbnailToolbar(WPARAM, LPARAM) { HRESULT hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pTaskbarList)); if (SUCCEEDED(hr)) { hr = m_pTaskbarList->HrInit(); if (SUCCEEDED(hr)) { enum { NUM_ICONS = 1 }; int const cxButton = GetSystemMetrics(SM_CXSMICON); if (auto himl = ImageList_Create(cxButton, cxButton, ILC_MASK, NUM_ICONS, 0)) { hr = m_pTaskbarList->ThumbBarSetImageList(*this, himl); if (SUCCEEDED(hr)) { THUMBBUTTON buttons[NUM_ICONS] = {}; // First button buttons[0].dwMask = THB_ICON | THB_TOOLTIP | THB_FLAGS; buttons[0].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK; buttons[0].iId = IDC_PLAY_PAUSE; buttons[0].hIcon = m_hPause; CStringW strBuffer; VERIFY(strBuffer.LoadString(IDS_PAUSE)); wcscpy_s(buttons[0].szTip, strBuffer); // Set the buttons to be the thumbnail toolbar hr = m_pTaskbarList->ThumbBarAddButtons(*this, ARRAYSIZE(buttons), buttons); } ImageList_Destroy(himl); } } } return TRUE; } void CMainFrame::onPauseResume(bool paused) { if (m_pTaskbarList) { THUMBBUTTON buttons[1] = {}; // First button buttons[0].dwMask = THB_ICON | THB_TOOLTIP | THB_FLAGS; buttons[0].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK; buttons[0].iId = IDC_PLAY_PAUSE; buttons[0].hIcon = paused? m_hPlay : m_hPause; CStringW strBuffer; VERIFY(strBuffer.LoadString(paused? IDS_PLAY : IDS_PAUSE)); wcscpy_s(buttons[0].szTip, strBuffer); m_pTaskbarList->ThumbBarUpdateButtons(*this, 1, buttons); } } BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !__super::PreCreateWindow(cs) ) return FALSE; // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs return TRUE; } // CMainFrame diagnostics #ifdef _DEBUG void CMainFrame::AssertValid() const { __super::AssertValid(); } void CMainFrame::Dump(CDumpContext& dc) const { __super::Dump(dc); } #endif //_DEBUG // CMainFrame message handlers void CMainFrame::OnFullScreen() { ModifyStyle(WS_OVERLAPPEDWINDOW, 0, SWP_FRAMECHANGED); const bool semiTransparentMode = GetKeyState(VK_SHIFT) < 0 && GetKeyState(VK_CONTROL) < 0; if (semiTransparentMode) { SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); ModifyStyleEx(0, WS_EX_LAYERED | WS_EX_TRANSPARENT); SetLayeredWindowAttributes(0, (255 * 40) / 100, LWA_ALPHA); } ShowFullScreen(); if (CMFCToolBar* toolBar = static_cast(m_Impl).GetFullScreenBar()) { if (auto pFrame = toolBar->GetParentMiniFrame()) { if (semiTransparentMode) { m_dockManager.RemoveMiniFrame(pFrame); } else { pFrame->ShowWindow(SW_HIDE); } } } } BOOL CMainFrame::OnEraseBkgnd(CDC* pDC) { CWnd* pWnd = GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE); if (IEraseableArea* pEraseableArea = dynamic_cast(pWnd)) { pEraseableArea->OnErase(this, pDC, IsFullScreen()); } return TRUE; } void CMainFrame::OnWindowPosChanged(WINDOWPOS* lpwndpos) { CFrameWndEx::OnWindowPosChanged(lpwndpos); // message handler code here if (!IsFullScreen() && m_bFullScreen) { ModifyStyle(0, WS_OVERLAPPEDWINDOW, 0); // clear semi transparent settings SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); ModifyStyleEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, 0); } m_bFullScreen = IsFullScreen(); } void CMainFrame::OnNcPaint() { if (!IsFullScreen()) Default(); } UINT CMainFrame::OnPowerBroadcast(UINT nPowerEvent, LPARAM nEventData) { if (nPowerEvent == PBT_APMSUSPEND) { if (CView* pView = dynamic_cast(GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE))) { if (CPlayerDoc* pDoc = static_cast(pView)->GetDocument()) { if (pDoc->isPlaying() && !pDoc->isPaused()) pDoc->pauseResume(); ShowWindow(SW_MINIMIZE); } } } return CFrameWndEx::OnPowerBroadcast(nPowerEvent, nEventData); } LRESULT CMainFrame::OnNcHitTest(CPoint point) { enum { SPOT_SIZE = 8 }; CRect rect; GetClientRect(rect); rect.left = rect.right - SPOT_SIZE; rect.top = rect.bottom - SPOT_SIZE; rect.right += SPOT_SIZE; rect.bottom += SPOT_SIZE; ClientToScreen(&rect); if (rect.PtInRect(point)) { BOOL bRTL = GetExStyle() & WS_EX_LAYOUTRTL; return bRTL ? HTBOTTOMLEFT : HTBOTTOMRIGHT; } return __super::OnNcHitTest(point); } void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) { CFrameWndEx::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu); if (bSysMenu || pPopupMenu->GetMenuItemCount() != 1) return; if (CView* pView = dynamic_cast(GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE))) { if (CPlayerDoc* pDoc = static_cast(pView)->GetDocument()) { switch (pPopupMenu->GetMenuItemID(0)) { case ID_FIRST_SUBTITLE_DUMMY: { pPopupMenu->DeleteMenu(0, MF_BYPOSITION); int id = ID_FIRST_SUBTITLE; for (auto& item : pDoc->getFrameDecoder()->listSubtitles()) { pPopupMenu->AppendMenu(MF_STRING, id++, CA2T(item.c_str(), CP_UTF8)); } } break; case ID_TRACK1_DUMMY: { pPopupMenu->DeleteMenu(0, MF_BYPOSITION); int id = ID_TRACK1; const int nTracks = pDoc->getFrameDecoder()->getNumAudioTracks(); for (int i = 1; i <= nTracks; ++i) { CString name; name.Format(_T("Track %d"), i); pPopupMenu->AppendMenu(MF_STRING, id++, name); } } break; } } } } ================================================ FILE: Player/MainFrm.h ================================================ // MainFrm.h : interface of the CMainFrame class // #pragma once #include "DialogBarPlayerControl.h" #include "DialogBarRange.h" struct ITaskbarList3; class CMainFrame : public CFrameWndEx { protected: // create from serialization only CMainFrame(); DECLARE_DYNCREATE(CMainFrame) // Attributes public: // Operations private: void pauseResume(); void onPauseResume(bool paused); // Overrides public: BOOL OnCommand(WPARAM wParam, LPARAM lParam) override; BOOL PreCreateWindow(CREATESTRUCT& cs) override; BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle = WS_OVERLAPPEDWINDOW, const RECT& rect = rectDefault, CWnd* pParentWnd = NULL, // != NULL for popups LPCTSTR lpszMenuName = NULL, DWORD dwExStyle = 0, CCreateContext* pContext = NULL) override; BOOL PreTranslateMessage(MSG* pMsg) override; // Implementation public: virtual ~CMainFrame(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // control bar embedded members CMFCMenuBar m_wndMenuBar; //CMFCToolBar m_wndToolBar; //CMFCStatusBar m_wndStatusBar; CDialogBarPlayerControl m_wndPlayerControl; CDialogBarRange m_wndRange; BOOL m_bFullScreen; CComPtr m_pTaskbarList; HICON m_hPlay; HICON m_hPause; // Generated message map functions protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnClose(); afx_msg LRESULT CreateThumbnailToolbar(WPARAM wParam, LPARAM lParam); afx_msg void OnFullScreen(); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnWindowPosChanged(WINDOWPOS* lpwndpos); afx_msg void OnNcPaint(); afx_msg UINT OnPowerBroadcast(UINT nPowerEvent, LPARAM nEventData); afx_msg LRESULT OnNcHitTest(CPoint point); DECLARE_MESSAGE_MAP() public: afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu); }; ================================================ FILE: Player/MakeDelegate.h ================================================ #pragma once #include template class Delegate { public: explicit Delegate(T* callee) : fpCallee(callee) {} template decltype(auto) operator()(Args&&... xs) const { return (fpCallee->*TMethod)(std::forward(xs)...); } bool operator == (const Delegate& other) const { return fpCallee == other.fpCallee; } bool operator != (const Delegate& other) const { return fpCallee != other.fpCallee; } private: T* fpCallee; }; template inline auto MakeDelegate(T* ptr) { return Delegate(ptr); } #define MAKE_DELEGATE(foo, thisPtr) (MakeDelegate(thisPtr)) ================================================ FILE: Player/MemoryMappedFile.h ================================================ #pragma once class MemoryMappedFile { public: MemoryMappedFile() : hFile(INVALID_HANDLE_VALUE) , hFileMapping(NULL) , pData(NULL) {} ~MemoryMappedFile() { if (pData) UnmapViewOfFile(pData); if (hFileMapping) CloseHandle(hFileMapping); if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile); } MemoryMappedFile(const MemoryMappedFile&) = delete; MemoryMappedFile& operator=(const MemoryMappedFile&) = delete; bool MapFlie(LPCTSTR szFile) { hFile = CreateFile( szFile, // pointer to name of the file GENERIC_READ, // access (read-write) mode FILE_SHARE_READ, // share mode NULL, // pointer to security attributes OPEN_EXISTING, // how to create FILE_ATTRIBUTE_NORMAL, // file attributes NULL // handle to file with attributes to copy ); if (INVALID_HANDLE_VALUE != hFile && GetFileSizeEx(hFile, &fileSize) && fileSize.HighPart == 0) { hFileMapping = CreateFileMapping( hFile, // handle to file to map NULL, // optional security attributes PAGE_READONLY, // protection for mapping object fileSize.HighPart, // high-order 32 bits of object size fileSize.LowPart, // low-order 32 bits of object size NULL // name of file-mapping object ); if (NULL != hFileMapping) { pData = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, (SIZE_T)fileSize.QuadPart); return pData != NULL; } } return false; } LPVOID data() const { return pData; } LONGLONG size() const { return fileSize.QuadPart; } private: HANDLE hFile; LARGE_INTEGER fileSize; HANDLE hFileMapping; LPVOID pData; }; ================================================ FILE: Player/OpenSubtitlesFile.cpp ================================================ #include "stdafx.h" #include "OpenSubtitlesFile.h" #include #include #include #include #include namespace { /* * from mpv/sub/sd_ass.c * ass_to_plaintext() was written by wm4 and he says it can be under LGPL */ std::string ass_to_plaintext(const char *in) { std::string result; bool in_tag = false; const char *open_tag_pos = nullptr; bool in_drawing = false; while (*in) { if (in_tag) { if (in[0] == '}') { in += 1; in_tag = false; } else if (in[0] == '\\' && in[1] == 'p') { in += 2; // Skip text between \pN and \p0 tags. A \p without a number // is the same as \p0, and leading 0s are also allowed. in_drawing = false; while (in[0] >= '0' && in[0] <= '9') { if (in[0] != '0') in_drawing = true; in += 1; } } else { in += 1; } } else { if (in[0] == '\\' && (in[1] == 'N' || in[1] == 'n')) { in += 2; result += '\n'; } else if (in[0] == '\\' && in[1] == 'h') { in += 2; result += ' '; } else if (in[0] == '{') { open_tag_pos = in; in += 1; in_tag = true; } else { if (!in_drawing) result += in[0]; in += 1; } } } // A '{' without a closing '}' is always visible. if (in_tag) { result += open_tag_pos; } return result; } CString ChangePathExtension(const TCHAR* videoPathName, const TCHAR* ext) { CString subRipPathName(videoPathName); PathRemoveExtension(subRipPathName.GetBuffer()); subRipPathName.ReleaseBuffer(); subRipPathName += ext; return subRipPathName; } bool IsTextUtf8(const std::string& text) { auto Src = text.begin(); const auto SrcEnd = text.end(); while (Src != SrcEnd) { int C = *(Src++); int HighOne = 0; // Number of leftmost '1' bits. for (int Mask = 0x80; Mask != 0 && (C & Mask) != 0; Mask >>= 1) HighOne++; if (HighOne == 1 || HighOne > 4) return false; while (--HighOne > 0) if (Src == SrcEnd || (*(Src++) & 0xc0) != 0x80) return false; } return true; } bool OpenSubRipFile(std::istream& s, bool& unicodeSubtitles, AddIntervalCallback addIntervalCallback) { bool autoDetectedUnicode = true; bool ok = false; std::string buffer; while (std::getline(s, buffer)) { if (std::find_if(buffer.begin(), buffer.end(), [](unsigned char c) { return !std::isspace(c); }) == buffer.end()) { continue; } if (!std::getline(s, buffer)) break; int startHr, startMin, startSec, startMsec; int endHr, endMin, endSec, endMsec; if (sscanf_s( buffer.c_str(), "%d:%d:%d,%d --> %d:%d:%d,%d", &startHr, &startMin, &startSec, &startMsec, &endHr, &endMin, &endSec, &endMsec) != 8) { if (std::find_if(buffer.begin(), buffer.end(), [](unsigned char c) { return !std::isspace(c); }) == buffer.end()) { continue; } break; } const double start = startHr * 3600 + startMin * 60 + startSec + startMsec / 1000.; const double end = endHr * 3600 + endMin * 60 + endSec + endMsec / 1000.; std::string subtitle; while (std::getline(s, buffer) && std::find_if(buffer.begin(), buffer.end(), [](unsigned char c) { return !std::isspace(c); }) != buffer.end()) { subtitle += buffer; subtitle += '\n'; // The last '\n' is for aggregating overlapped subtitles (if any) } if (!unicodeSubtitles && autoDetectedUnicode) autoDetectedUnicode = IsTextUtf8(subtitle); if (!subtitle.empty()) { addIntervalCallback(start, end, subtitle); ok = true; } } if (!unicodeSubtitles) unicodeSubtitles = autoDetectedUnicode; return ok; } bool OpenSubStationAlphaFile(std::istream& s, bool& unicodeSubtitles, AddIntervalCallback addIntervalCallback) { bool autoDetectedUnicode = true; bool ok = false; std::string buffer; while (std::getline(s, buffer)) { std::istringstream ss(buffer); std::getline(ss, buffer, ':'); if (buffer != "Dialogue") continue; double start, end; bool skip = false; for (int i = 0; i < 9; ++i) // TODO indices from Format? { std::getline(ss, buffer, ','); if (i == 1 || i == 2) { int hr, min, sec; char msecString[10]{}; if (sscanf_s( buffer.c_str(), "%d:%d:%d.%9s", &hr, &min, &sec, msecString, (unsigned)_countof(msecString)) != 4) { return true; } double msec = 0; if (const auto msecStringLen = strlen(msecString)) { msec = atoi(msecString) / pow(10, msecStringLen); } ((i == 1) ? start : end) = hr * 3600 + min * 60 + sec + msec; } else if (i == 3 && buffer == "OP_kar") { skip = true; break; } } if (skip) continue; std::getline(ss, buffer, {}); std::string subtitle = ass_to_plaintext(buffer.c_str()); if (!unicodeSubtitles && autoDetectedUnicode) autoDetectedUnicode = IsTextUtf8(subtitle); if (!subtitle.empty()) { subtitle += '\n'; // The last '\n' is for aggregating overlapped subtitles (if any) addIntervalCallback(start, end, subtitle); ok = true; } } if (!unicodeSubtitles) unicodeSubtitles = autoDetectedUnicode; return ok; } template class FilterBuf : public std::streambuf { char readBuf_; const char* pExternBuf_; int_type underflow() override { if (gptr() == &readBuf_) return traits_type::to_int_type(readBuf_); for (; *pExternBuf_ == C; ++pExternBuf_); if (*pExternBuf_ == '\0') return traits_type::eof(); int_type nextChar = *pExternBuf_++; readBuf_ = traits_type::to_char_type(nextChar); setg(&readBuf_, &readBuf_, &readBuf_ + 1); return traits_type::to_int_type(readBuf_); } public: explicit FilterBuf(const char* pExternBuf) : pExternBuf_(pExternBuf) { setg(nullptr, nullptr, nullptr); } }; bool OpenSubFile( bool(*doOpenSubFile)(std::istream&, bool&, AddIntervalCallback), const TCHAR* videoPathName, bool& unicodeSubtitles, AddIntervalCallback addIntervalCallback) { do { std::ifstream s(videoPathName); if (!s) return false; unicodeSubtitles = false; const char ch = s.peek(); if (ch == char(0xFF)) { s.get(); if (char(s.peek()) == char(0xFE)) { break; // go to unicode part } s.unget(); } else if (ch == char(0xEF)) { s.get(); if (char(s.peek()) == char(0xBB)) { s.get(); if (char(s.peek()) == char(0xBF)) { unicodeSubtitles = true; } else { s.unget(); s.unget(); } } else s.unget(); } return doOpenSubFile(s, unicodeSubtitles, addIntervalCallback); } while (false); std::ifstream s(videoPathName, std::ios::binary); if (!s) return false; s.seekg(0, std::ios::end); const auto size = static_cast(s.tellg()) - 2; s.seekg(2, std::ios::beg); std::wstring str(size / 2 + 1, L'\0'); s.read(static_cast(static_cast(str.data())), size); ATL::CW2A text(str.c_str(), CP_UTF8); FilterBuf<'\r'> buf(text); std::iostream ss(&buf); unicodeSubtitles = true; return doOpenSubFile(ss, unicodeSubtitles, addIntervalCallback); } } // namespace bool OpenSubtitlesFile(const TCHAR* videoPathName, bool& unicodeSubtitles, AddIntervalCallback addIntervalCallback) { const auto extension = PathFindExtension(videoPathName); if (!_tcsicmp(extension, _T(".srt")) && OpenSubFile(OpenSubRipFile, videoPathName, unicodeSubtitles, addIntervalCallback)) { return true; } return OpenSubFile(OpenSubStationAlphaFile, videoPathName, unicodeSubtitles, addIntervalCallback); } bool OpenMatchingSubtitlesFile(const TCHAR* videoPathName, bool& unicodeSubtitles, AddIntervalCallback addIntervalCallback) { for (auto ext : { _T(".ass"), _T(".ssa") }) { if (OpenSubFile(OpenSubStationAlphaFile, ChangePathExtension(videoPathName, ext), unicodeSubtitles, addIntervalCallback)) return true; } const auto ext = ChangePathExtension(videoPathName, _T(".srt")); return OpenSubFile(OpenSubRipFile, ext, unicodeSubtitles, addIntervalCallback) || OpenSubFile(OpenSubStationAlphaFile, ext, unicodeSubtitles, addIntervalCallback); } ================================================ FILE: Player/OpenSubtitlesFile.h ================================================ #pragma once #include #include #include typedef std::function AddIntervalCallback; bool OpenSubtitlesFile(const TCHAR* videoPathName, bool& unicodeSubtitles, AddIntervalCallback addIntervalCallback); bool OpenMatchingSubtitlesFile(const TCHAR* videoPathName, bool& unicodeSubtitles, AddIntervalCallback addIntervalCallback); ================================================ FILE: Player/Player.cpp ================================================ // Player.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "afxwinappex.h" #include "afxdialogex.h" #include "Player.h" #include "MainFrm.h" #include "PlayerDoc.h" #include "PlayerView.h" #include "PlayerViewD2D.h" #include "I420Effect.h" #include "AsyncGetUrlUnderMouseCursor.h" #include "YouTuber.h" #include "version.h" #include #include #include #include #include #include #include #include #ifdef _DEBUG #define new DEBUG_NEW #endif //#define USE_DIRECT2D_VIEW namespace { // https://www.experts-exchange.com/articles/3189/In-Memory-Compression-and-Decompression-Using-ZLIB.html int GetMaxCompressedLen(int nLenSrc) { int n16kBlocks = (nLenSrc + 16383) / 16384; // round up any fraction of a block return (nLenSrc + 6 + (n16kBlocks * 5)); } int CompressData(const BYTE* abSrc, int nLenSrc, BYTE* abDst, int nLenDst) { z_stream zInfo { }; zInfo.total_in = zInfo.avail_in = nLenSrc; zInfo.total_out = zInfo.avail_out = nLenDst; zInfo.next_in = const_cast(abSrc); zInfo.next_out = abDst; int nRet = -1; int nErr = deflateInit(&zInfo, Z_BEST_COMPRESSION); // zlib function if (nErr == Z_OK) { nErr = deflate(&zInfo, Z_FINISH); // zlib function if (nErr == Z_STREAM_END) { nRet = zInfo.total_out; } } deflateEnd(&zInfo); // zlib function return nRet; } int UncompressData(const BYTE* abSrc, int nLenSrc, BYTE* abDst, int nLenDst) { z_stream zInfo { }; zInfo.total_in = zInfo.avail_in = nLenSrc; zInfo.total_out = zInfo.avail_out = nLenDst; zInfo.next_in = const_cast(abSrc); zInfo.next_out = abDst; int nRet = -1; int nErr = inflateInit(&zInfo); // zlib function if (nErr == Z_OK) { nErr = inflate(&zInfo, Z_FINISH); // zlib function if (nErr == Z_STREAM_END) { nRet = zInfo.total_out; } } inflateEnd(&zInfo); // zlib function return nRet; // -1 or len of output } void init_logging() { namespace expr = boost::log::expressions; boost::log::add_common_attributes(); auto core = boost::log::core::get(); // Create the sink. The backend requires synchronization in the frontend. auto sink(boost::make_shared>()); sink->set_formatter(expr::stream //<< '[' << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%H:%M:%S.%f") << ']' << expr::if_(expr::has_attr("Severity")) [ expr::stream << '[' << expr::attr< boost::log::trivial::severity_level >("Severity") << ']' ] << expr::if_(expr::has_attr("Channel")) [ expr::stream << '[' << expr::attr< std::string >("Channel") << ']' ] << expr::smessage << '\n'); // Set the special filter to the frontend // in order to skip the sink when no debugger is available //sink->set_filter(expr::is_debugger_present()); core->add_sink(sink); } class PlayerCommandLineInfo : public CCommandLineInfo { public: bool m_bCheckPython = false; private: void ParseParam(const TCHAR* pszParam, BOOL bFlag, BOOL bLast) override { if (bFlag && _tcsicmp(pszParam, _T("check_python")) == 0) { m_bCheckPython = true; return; } CCommandLineInfo::ParseParam(pszParam, bFlag, bLast); } }; } // namespace // CPlayerApp BEGIN_MESSAGE_MAP(CPlayerApp, CWinAppEx) ON_COMMAND(ID_APP_ABOUT, &CPlayerApp::OnAppAbout) // Standard file based document commands ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew) ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen) // Standard print setup command ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinAppEx::OnFilePrintSetup) ON_THREAD_MESSAGE(WM_ON_ASYNC_URL, &CPlayerApp::OnAsyncUrl) END_MESSAGE_MAP() // CPlayerApp construction CPlayerApp::CPlayerApp() { // support Restart Manager m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_ALL_ASPECTS; #ifdef _MANAGED // If the application is built using Common Language Runtime support (/clr): // 1) This additional setting is needed for Restart Manager support to work properly. // 2) In your project, you must add a reference to System.Windows.Forms in order to build. System::Windows::Forms::Application::SetUnhandledExceptionMode(System::Windows::Forms::UnhandledExceptionMode::ThrowException); #endif // TODO: replace application ID string below with unique ID string; recommended // format for string is CompanyName.ProductName.SubProduct.VersionInformation SetAppID(_T("Player.AppID.NoVersion")); // Place all significant initialization in InitInstance init_logging(); } // The one and only CPlayerApp object CPlayerApp theApp; // CPlayerApp initialization BOOL CPlayerApp::InitInstance() { CPane::m_bHandleMinSize = true; // InitCommonControlsEx() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // Set this to include all the common control classes you want to use // in your application. InitCtrls.dwICC = ICC_STANDARD_CLASSES; InitCommonControlsEx(&InitCtrls); __super::InitInstance(); AfxOleInit(); // Parse command line for standard shell commands, DDE, file open PlayerCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (cmdInfo.m_bCheckPython) { CheckPython(); return FALSE; } if (cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew) { AsyncGetUrlUnderMouseCursor(); } #ifdef USE_DIRECT2D_VIEW if (AfxGetD2DState()->GetDirect2dFactory() == NULL) { return FALSE; } HRESULT hr_create = I420Effect::Register(static_cast(AfxGetD2DState()->GetDirect2dFactory())); if (FAILED(hr_create)) { return FALSE; } #endif // USE_DIRECT2D_VIEW EnableTaskbarInteraction(FALSE); // AfxInitRichEdit2() is required to use RichEdit control // AfxInitRichEdit2(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need // Change the registry key under which our settings are stored // TODO: You should modify this string to be something appropriate // such as the name of your company or organization SetRegistryKey(_T("FFMPEG Player")); LoadStdProfileSettings(_AFX_MRU_MAX_COUNT); // Load standard INI file options (including MRU) // MFC Feature Pack InitContextMenuManager(); InitShellManager(); InitKeyboardManager(); InitTooltipManager(); CMFCToolTipInfo ttParams; ttParams.m_bVislManagerTheme = TRUE; theApp.GetTooltipManager()-> SetTooltipParams(AFX_TOOLTIP_TYPE_ALL, RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams); // Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views auto* pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CPlayerDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window #ifdef USE_DIRECT2D_VIEW RUNTIME_CLASS(CPlayerViewD2D)); #else RUNTIME_CLASS(CPlayerView)); #endif if (!pDocTemplate) { return FALSE; } AddDocTemplate(pDocTemplate); HandleMruList(); // Dispatch commands specified on the command line. Will return FALSE if // app was launched with /RegServer, /Register, /Unregserver or /Unregister. if (cmdInfo.m_nShellCommand == CCommandLineInfo::FileOpen) { if (!pDocTemplate->OpenDocumentFile(cmdInfo.m_strFileName)) { return FALSE; } } else if (!ProcessShellCommand(cmdInfo)) { return FALSE; } // https://stackoverflow.com/a/56079903/10472202 //enum { TIME_PERIOD = 1 }; //if (timeBeginPeriod(TIME_PERIOD) == TIMERR_NOERROR) //{ // atexit([] { timeEndPeriod(TIME_PERIOD); }); //} // The one and only window has been initialized, so show and update it m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; } // CPlayerApp message handlers // CAboutDlg dialog used for App About class CAboutDlg : public CDialogEx { public: CAboutDlg(); BOOL OnInitDialog() override; // Dialog Data enum { IDD = IDD_ABOUTBOX }; CString m_videoProperties; protected: void DoDataExchange(CDataExchange* pDX) override; // DDX/DDV support // Implementation protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) { } BOOL CAboutDlg::OnInitDialog() { ModifyStyleEx(0, WS_EX_LAYERED); SetLayeredWindowAttributes(0, (255 * 75) / 100, LWA_ALPHA); #ifdef GIT_COMMIT CString text; GetDlgItemText(IDC_APP_NAME_VERSION, text); text += " " BOOST_STRINGIZE(GIT_COMMIT); SetDlgItemText(IDC_APP_NAME_VERSION, text); #endif return __super::OnInitDialog(); } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_VIDEO_PROPERTIES, m_videoProperties); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() CPlayerDoc* CPlayerApp::GetPlayerDocument() { POSITION pos1 = GetFirstDocTemplatePosition(); if (CDocTemplate* templ = GetNextDocTemplate(pos1)) { POSITION pos2 = templ->GetFirstDocPosition(); return dynamic_cast(templ->GetNextDoc(pos2)); } return nullptr; } // App command to run the dialog void CPlayerApp::OnAppAbout() { CAboutDlg aboutDlg; if (CPlayerDoc* doc = GetPlayerDocument()) { const auto properties = doc->getFrameDecoder()->getProperties(); for (const auto& prop : properties) { if (!aboutDlg.m_videoProperties.IsEmpty()) { aboutDlg.m_videoProperties += '\n'; } aboutDlg.m_videoProperties += prop.c_str(); } } aboutDlg.DoModal(); } // CPlayerApp message handlers void CPlayerApp::OnAsyncUrl(WPARAM wParam, LPARAM /*unused*/) { CComBSTR url; url.Attach((BSTR)wParam); if (CPlayerDoc* doc = GetPlayerDocument()) { doc->OnAsyncUrl(CString(url)); } } const TCHAR szMappedAudioFilesEntry[] = _T("MappedAudioFiles"); const int MappedAudioFilesEntryVersion = 2; bool CPlayerApp::GetMappedAudioFiles(CMapStringToString& map) { map.RemoveAll(); LPBYTE pData = nullptr; UINT bytes = 0; if (!GetBinary(szMappedAudioFilesEntry, &pData, &bytes) || bytes == 0) { return false; } const auto size = 65536; std::vector unpacked(size); int nLen = UncompressData(pData, bytes, unpacked.data(), size); delete[] pData; CMemFile mf(unpacked.data(), nLen); { CArchive ar(&mf, CArchive::load); int version = 0; ar >> version; if (version != MappedAudioFilesEntryVersion) { return false; } map.Serialize(ar); } mf.Detach(); return true; } void CPlayerApp::SetMappedAudioFiles(CMapStringToString& map) { CMemFile mf; { CArchive ar(&mf, CArchive::store); ar << MappedAudioFilesEntryVersion; map.Serialize(ar); } UINT uiDataSize = static_cast(mf.GetLength()); LPBYTE lpbData = mf.Detach(); if (lpbData == nullptr) { return; } int nLenDst = GetMaxCompressedLen(uiDataSize); std::vector packed(nLenDst); int nLenPacked = CompressData(lpbData, uiDataSize, packed.data(), nLenDst); free(lpbData); if (nLenPacked == -1) { return; // error } WriteBinary(szMappedAudioFilesEntry, packed.data(), nLenPacked); } void CPlayerApp::HandleMruList() { CMapStringToString oldMappedAudioFiles; GetMappedAudioFiles(oldMappedAudioFiles); CMapStringToString newMappedAudioFiles; if (m_pRecentFileList != nullptr) { for (int i = m_pRecentFileList->GetSize(); --i >= 0;) { const auto& key = (*m_pRecentFileList)[i]; if (_taccess(key, 04) != 0) { m_pRecentFileList->Remove(i); } else { CString value; if (oldMappedAudioFiles.Lookup(key, value)) { newMappedAudioFiles[key] = value; } } } } if (oldMappedAudioFiles.GetSize() != newMappedAudioFiles.GetSize()) { SetMappedAudioFiles(newMappedAudioFiles); } } CString CPlayerApp::GetMappedAudioFile(LPCTSTR key) { CMapStringToString mappedAudioFiles; GetMappedAudioFiles(mappedAudioFiles); CString result; mappedAudioFiles.Lookup(key, result); return result; } void CPlayerApp::SetMappedAudioFile(LPCTSTR key, LPCTSTR value) { CMapStringToString mappedAudioFiles; GetMappedAudioFiles(mappedAudioFiles); mappedAudioFiles[key] = value; SetMappedAudioFiles(mappedAudioFiles); } ================================================ FILE: Player/Player.h ================================================ // Player.h : main header file for the Player application // #pragma once #ifndef __AFXWIN_H__ #error "include 'stdafx.h' before including this file for PCH" #endif #include "resource.h" // main symbols class CPlayerDoc; // CPlayerApp: // See Player.cpp for the implementation of this class // class CPlayerApp : public CWinAppEx { public: CPlayerApp(); // Overrides public: BOOL InitInstance() override; CString GetMappedAudioFile(LPCTSTR key); void SetMappedAudioFile(LPCTSTR key, LPCTSTR value); // Implementation afx_msg void OnAppAbout(); afx_msg void OnAsyncUrl(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() CPlayerDoc* GetPlayerDocument(); private: bool GetMappedAudioFiles(CMapStringToString& map); void SetMappedAudioFiles(CMapStringToString& map); void HandleMruList(); }; extern CPlayerApp theApp; enum { ID_FIRST_SUBTITLE = 7000, ID_TRACK1 = 7100 }; ================================================ FILE: Player/Player.rc ================================================ // Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #ifndef APSTUDIO_INVOKED #include "targetver.h" #endif #include "afxres.h" #include "verrsrc.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#ifndef APSTUDIO_INVOKED\r\n" "#include ""targetver.h""\r\n" "#endif\r\n" "#include ""afxres.h""\r\n" "#include ""verrsrc.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "#define _AFX_NO_OLE_RESOURCES\r\n" "#define _AFX_NO_TRACKER_RESOURCES\r\n" "#define _AFX_NO_PROPERTY_RESOURCES\r\n" "\r\n" "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" "LANGUAGE 9, 1\r\n" "#include ""res\\Player.rc2"" // non-Microsoft Visual C++ edited resources\r\n" "#include ""afxres.rc"" // Standard components\r\n" "#include ""afxprint.rc"" // printing/print preview resources\r\n" "#endif\r\n" "\0" END 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDR_MAINFRAME ICON "res\\Player.ico" IDR_PlayerTYPE ICON "res\\PlayerDoc.ico" IDI_PAUSE ICON "res\\pause.ico" IDI_PLAY ICON "res\\play.ico" IDI_AUDIO ICON "res\\audio.ico" IDI_AUDIO_OFF ICON "res\\audio_off.ico" IDI_FULL_SCREEN ICON "res\\full_screen.ico" ///////////////////////////////////////////////////////////////////////////// // // Bitmap // IDR_MAINFRAME BITMAP "res\\Toolbar.bmp" ///////////////////////////////////////////////////////////////////////////// // // Toolbar // IDR_MAINFRAME TOOLBAR 16, 15 BEGIN BUTTON ID_FILE_NEW BUTTON ID_FILE_OPEN BUTTON ID_FILE_SAVE SEPARATOR BUTTON ID_EDIT_CUT BUTTON ID_EDIT_COPY BUTTON ID_EDIT_PASTE SEPARATOR BUTTON ID_FILE_PRINT BUTTON ID_APP_ABOUT END ///////////////////////////////////////////////////////////////////////////// // // Menu // IDR_MAINFRAME MENU BEGIN POPUP "&File" BEGIN MENUITEM "&New\tCtrl+N", ID_FILE_NEW MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE MENUITEM "Save Copy &As...", ID_FILE_SAVE_COPY_AS MENUITEM "Using Host Header Override Mode", ID_USING_HHO MENUITEM SEPARATOR MENUITEM "Copy URL To Clipboard", ID_COPY_URL_TO_CLIPBOARD MENUITEM "Convert Videos Into Compatible Format", ID_CONVERT_VIDEOS_INTO_COMPATIBLE_FORMAT MENUITEM SEPARATOR MENUITEM "Autoplay", ID_AUTOPLAY MENUITEM "Looping", ID_LOOPING MENUITEM SEPARATOR MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED MENUITEM SEPARATOR MENUITEM "E&xit", ID_APP_EXIT END POPUP "Audio/Video" BEGIN POPUP "Audio Track" BEGIN MENUITEM "Track 1", ID_TRACK1_DUMMY END POPUP "Video Speed" BEGIN MENUITEM "0.5x", ID_VIDEO_SPEED1 MENUITEM "0.63x", ID_VIDEO_SPEED2 MENUITEM "0.8x", ID_VIDEO_SPEED3 MENUITEM "1x", ID_VIDEO_SPEED4 MENUITEM "1.25x", ID_VIDEO_SPEED5 MENUITEM "1.6x", ID_VIDEO_SPEED6 MENUITEM "2x", ID_VIDEO_SPEED7 MENUITEM "Nightcore", ID_NIGHTCORE END POPUP "Orientation" BEGIN MENUITEM "Do Nothing", ID_ORIENTATION_DO_NOTHING MENUITEM "Rotate 90", ID_ORIENTATION_RORATE_90 MENUITEM "Rotate 180", ID_ORIENTATION_RORATE_180 MENUITEM "Rotate 270", ID_ORIENTATION_RORATE_270 MENUITEM SEPARATOR MENUITEM "Horizontal Mirror", ID_ORIENTATION_MIRRORX MENUITEM "Vertical Mirror", ID_ORIENTATION_MIRRORY MENUITEM "Upend", ID_ORIENTATION_UPEND END MENUITEM "Video Filter...", ID_VIDEO_FILTER MENUITEM SEPARATOR MENUITEM "Maximal Resolution", ID_MAXIMALRESOLUTION MENUITEM "Hardware Acceleration", ID_HW_ACCELERATION MENUITEM "Super Resolution", ID_SUPER_RESOLUTION MENUITEM SEPARATOR MENUITEM "Open Audio File...", ID_OPEN_AUDIO_FILE MENUITEM "Open Subtitles File...", ID_OPENSUBTITLESFILE POPUP "Open Subtitles" BEGIN MENUITEM "Subtitle 1", ID_FIRST_SUBTITLE_DUMMY END MENUITEM "Fix Encoding", ID_FIX_ENCODING END POPUP "&View" BEGIN MENUITEM "&Toolbar", ID_VIEW_TOOLBAR MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR END POPUP "&Help" BEGIN MENUITEM "&About Player...", ID_APP_ABOUT END END ///////////////////////////////////////////////////////////////////////////// // // Accelerator // IDR_MAINFRAME ACCELERATORS BEGIN "N", ID_FILE_NEW, VIRTKEY, CONTROL "O", ID_FILE_OPEN, VIRTKEY, CONTROL "S", ID_FILE_SAVE, VIRTKEY, CONTROL "P", ID_FILE_PRINT, VIRTKEY, CONTROL "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL "X", ID_EDIT_CUT, VIRTKEY, CONTROL "C", ID_EDIT_COPY, VIRTKEY, CONTROL "V", ID_EDIT_PASTE, VIRTKEY, CONTROL VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT VK_F6, ID_NEXT_PANE, VIRTKEY VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT END ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_ABOUTBOX DIALOGEX 0, 0, 341, 178 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About FFmpeg Player" FONT 8, "MS Shell Dlg", 0, 0, 0x1 BEGIN ICON IDR_MAINFRAME,IDC_STATIC,9,14,20,20,SS_REALSIZEIMAGE DEFPUSHBUTTON "OK",IDOK,284,158,50,14,WS_GROUP LTEXT "FFmpeg Player, Version 1.0",IDC_APP_NAME_VERSION,178,14,149,8,SS_NOPREFIX LTEXT "github.com/aliakseis/FFmpegPlayer",IDC_STATIC,178,22,114,8 LTEXT "Icons: www.danieledesantis.net",IDC_STATIC,178,36,114,8 LTEXT "www.iconfinder.com/Rudityas",IDC_STATIC,178,44,97,8 LTEXT "YouTube: github.com/yt-dlp",IDC_STATIC,178,58,156,8 LTEXT "github.com/jdepoix/youtube-transcript-api",IDC_STATIC,178,66,156,8 LTEXT "Pitch shifting by Stephan M. Bernsee",IDC_STATIC,178,80,141,8 LTEXT "",IDC_VIDEO_PROPERTIES,178,109,156,48 LTEXT "Upscaling: github.com/TianZerL/Anime4KCPP",IDC_STATIC,178,94,156,8 END IDD_DIALOGBAR_PLAYER_CONTROL DIALOGEX 0, 0, 374, 22 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE FONT 8, "MS Shell Dlg", 400, 0, 0x0 BEGIN CONTROL "",IDC_PROGRESS_SLIDER,"msctls_trackbar32",TBS_TOP | TBS_NOTICKS | WS_TABSTOP,88,4,103,15 PUSHBUTTON "",IDC_PLAY_PAUSE,4,0,26,22,BS_ICON PUSHBUTTON "",IDC_AUDIO_ON_OFF,315,0,26,22,BS_ICON CONTROL "",IDC_VOLUME_SLIDER,"msctls_trackbar32",TBS_TOP | TBS_NOTICKS | WS_TABSTOP,237,4,74,15 LTEXT "",IDC_CURRENT_TIME,39,6,48,8 LTEXT "",IDC_TOTAL_TIME,196,6,34,8 PUSHBUTTON "",IDC_FULL_SCREEN,345,0,26,22,BS_ICON CONTROL "Frame step",IDC_FRAME_STEP,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,252,6,52,10 END IDD_DIALOG_OPEN_URL DIALOGEX 0, 0, 309, 70 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Open URL" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN EDITTEXT IDC_EDIT_URL,7,19,295,14,ES_AUTOHSCROLL LTEXT "Enter a URL to play:",IDC_STATIC,7,7,66,8 EDITTEXT IDC_EDIT_INPUT_FORMAT,7,49,121,14,ES_AUTOHSCROLL LTEXT "Input format (advanced):",IDC_STATIC,7,37,174,8 CONTROL "Parse",IDC_PARSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,146,51,34,10 DEFPUSHBUTTON "OK",IDOK,198,49,50,14 PUSHBUTTON "Cancel",IDCANCEL,252,49,50,14 END IDD_DIALOGBAR_RANGE DIALOGEX 0, 0, 340, 16 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN PUSHBUTTON "Start",IDC_START,7,1,36,14 EDITTEXT IDC_EDIT_START,46,1,52,14,ES_AUTOHSCROLL PUSHBUTTON "X",IDC_START_RESET,101,1,16,14 PUSHBUTTON "End",IDC_END,122,1,36,14 EDITTEXT IDC_EDIT_END,161,1,52,14,ES_AUTOHSCROLL PUSHBUTTON "X",IDC_END_RESET,216,1,16,14 CONTROL "Lossless Cut",IDC_LOSSLESS_CUT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,238,3,55,10 PUSHBUTTON "Save as",ID_FILE_SAVE_COPY_AS,293,1,40,14 END IDD_DIALOG_VIDEO_FILTER DIALOGEX 0, 0, 310, 122 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Enter Video Filter:" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,199,101,50,14 PUSHBUTTON "Cancel",IDCANCEL,253,101,50,14 EDITTEXT IDC_VIDEO_FILTER,7,7,296,86,ES_MULTILINE CONTROL "Enable video filter",IDC_ENABLE_VIDEO_FILTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,74,10 END ///////////////////////////////////////////////////////////////////////////// // // RCDATA // IDR_LAUNCH RCDATA "res\\launch.mkv" ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,1 PRODUCTVERSION 1,0,0,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x40004L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" BEGIN VALUE "CompanyName", "Sanko" VALUE "FileDescription", "FFmpeg Player" VALUE "FileVersion", "1.0.0.1" VALUE "InternalName", "Player.exe" VALUE "LegalCopyright", " 2015-2021 Sanko. All rights reserved." VALUE "OriginalFilename", "Player.exe" VALUE "ProductName", "FFmpeg Player" VALUE "ProductVersion", "1.0.0.1" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_ABOUTBOX, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 334 TOPMARGIN, 7 BOTTOMMARGIN, 171 END IDD_DIALOGBAR_PLAYER_CONTROL, DIALOG BEGIN RIGHTMARGIN, 365 TOPMARGIN, 7 END IDD_DIALOG_OPEN_URL, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 302 TOPMARGIN, 7 BOTTOMMARGIN, 63 END IDD_DIALOGBAR_RANGE, DIALOG BEGIN RIGHTMARGIN, 333 END IDD_DIALOG_VIDEO_FILTER, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 303 TOPMARGIN, 7 BOTTOMMARGIN, 115 END END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // AFX_DIALOG_LAYOUT // IDD_DIALOGBAR_PLAYER_CONTROL AFX_DIALOG_LAYOUT BEGIN 0, 0, 50, 100, 0, 0, 50, 0, 0, 100, 50, 0, 0, 100, 50, 0, 0, 0, 50, 0, 0, 100, 50, 0, 0, 100, 50, 0, 0, 100, 50, 0, 0 END IDD_DIALOGBAR_RANGE AFX_DIALOG_LAYOUT BEGIN 0, 25, 50, 0, 0, 25, 50, 0, 0, 25, 50, 0, 0, 50, 50, 0, 0, 50, 50, 0, 0, 50, 50, 0, 0, 75, 50, 0, 0, 75, 50, 0, 0 END IDD_DIALOG_OPEN_URL AFX_DIALOG_LAYOUT BEGIN 0 END IDD_ABOUTBOX AFX_DIALOG_LAYOUT BEGIN 0 END IDD_DIALOG_VIDEO_FILTER AFX_DIALOG_LAYOUT BEGIN 0 END ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN IDR_MAINFRAME "Player\n\nPlayer\n\n\nPlayer.Document\nPlayer.Document" IDS_TOOLBAR_CUSTOMIZE "Customize..." IDS_PLAYER_CONTROL "Player Control" IDS_RANGE "Range" IDS_PAUSE "Pause" IDS_PLAY "Play" END STRINGTABLE BEGIN AFX_IDS_APP_TITLE "Player" AFX_IDS_IDLEMESSAGE "Ready" END STRINGTABLE BEGIN ID_INDICATOR_EXT "EXT" ID_INDICATOR_CAPS "CAP" ID_INDICATOR_NUM "NUM" ID_INDICATOR_SCRL "SCRL" ID_INDICATOR_OVR "OVR" ID_INDICATOR_REC "REC" END STRINGTABLE BEGIN ID_FILE_NEW "Create a new document\nNew" ID_FILE_OPEN "Open an existing document\nOpen" ID_FILE_CLOSE "Close the active document\nClose" ID_FILE_SAVE "Save the active document\nSave" ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" ID_FILE_PRINT "Print the active document\nPrint" ID_FILE_PRINT_DIRECT "Print the active document using current options\nQuick Print" ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" END STRINGTABLE BEGIN ID_APP_ABOUT "Display program information, version number and copyright\nAbout" ID_APP_EXIT "Quit the application; prompts to save documents\nExit" END STRINGTABLE BEGIN ID_FILE_MRU_FILE1 "Open this document" ID_FILE_MRU_FILE2 "Open this document" ID_FILE_MRU_FILE3 "Open this document" ID_FILE_MRU_FILE4 "Open this document" ID_FILE_MRU_FILE5 "Open this document" ID_FILE_MRU_FILE6 "Open this document" ID_FILE_MRU_FILE7 "Open this document" ID_FILE_MRU_FILE8 "Open this document" ID_FILE_MRU_FILE9 "Open this document" ID_FILE_MRU_FILE10 "Open this document" ID_FILE_MRU_FILE11 "Open this document" ID_FILE_MRU_FILE12 "Open this document" ID_FILE_MRU_FILE13 "Open this document" ID_FILE_MRU_FILE14 "Open this document" ID_FILE_MRU_FILE15 "Open this document" ID_FILE_MRU_FILE16 "Open this document" END STRINGTABLE BEGIN ID_NEXT_PANE "Switch to the next window pane\nNext Pane" ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" END STRINGTABLE BEGIN ID_WINDOW_SPLIT "Split the active window into panes\nSplit" END STRINGTABLE BEGIN ID_EDIT_CLEAR "Erase the selection\nErase" ID_EDIT_CLEAR_ALL "Erase everything\nErase All" ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" ID_EDIT_FIND "Find the specified text\nFind" ID_EDIT_PASTE "Insert Clipboard contents\nPaste" ID_EDIT_REPEAT "Repeat the last action\nRepeat" ID_EDIT_REPLACE "Replace specific text with different text\nReplace" ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" ID_EDIT_UNDO "Undo the last action\nUndo" ID_EDIT_REDO "Redo the previously undone action\nRedo" END STRINGTABLE BEGIN ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle Status Bar" END STRINGTABLE BEGIN AFX_IDS_SCSIZE "Change the window size" AFX_IDS_SCMOVE "Change the window position" AFX_IDS_SCMINIMIZE "Reduce the window to an icon" AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" AFX_IDS_SCNEXTWINDOW "Switch to the next document window" AFX_IDS_SCPREVWINDOW "Switch to the previous document window" AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" END STRINGTABLE BEGIN AFX_IDS_SCRESTORE "Restore the window to normal size" AFX_IDS_SCTASKLIST "Activate Task List" END STRINGTABLE BEGIN AFX_IDS_PICTYPE_ICON "Close print preview mode\nCancel Preview" END #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // #define _AFX_NO_OLE_RESOURCES #define _AFX_NO_TRACKER_RESOURCES #define _AFX_NO_PROPERTY_RESOURCES #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE 9, 1 #include "res\Player.rc2" // non-Microsoft Visual C++ edited resources #include "afxres.rc" // Standard components #include "afxprint.rc" // printing/print preview resources #endif ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ================================================ FILE: Player/Player.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C} Player MFCProj Application true Unicode Dynamic Application true Unicode Dynamic Application false true Unicode Dynamic Application false true Unicode Dynamic $(ExecutablePath) true $(ExecutablePath) true false $(ExecutablePath) true false $(ExecutablePath) true Use Level4 WIN32;_WINDOWS;_HAS_AUTO_PTR_ETC=1;_HAS_FUNCTION_ASSIGN=1;_HAS_OLD_IOSTREAMS_MEMBERS=1;ENABLE_OPENCL;_DEBUG;%(PreprocessorDefinitions) true ..\audio;..\video;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.9;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.10;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.11;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.12;..\networking;..\Anime4KCPP\core\include;..\core;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;%(AdditionalIncludeDirectories) 4996 stdcpp17 Windows true Winmm.lib;dxva2.lib;d3d9.lib;Oleacc.lib;$(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\Python*.lib;%(AdditionalDependencies);OpenCL.lib opencl.dll $(INTELOCLSDKROOT)\lib\x86\;%(AdditionalLibraryDirectories) false true _DEBUG;%(PreprocessorDefinitions) 0x0409 _DEBUG;%(PreprocessorDefinitions) $(IntDir);%(AdditionalIncludeDirectories) $(ProjectDir)update_version.cmd Use Level4 WIN32;_WINDOWS;_HAS_AUTO_PTR_ETC=1;_HAS_FUNCTION_ASSIGN=1;_HAS_OLD_IOSTREAMS_MEMBERS=1;ENABLE_OPENCL;_DEBUG;%(PreprocessorDefinitions) true ..\audio;..\video;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.9;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.10;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.11;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.12;..\networking;..\Anime4KCPP\core\include;..\core;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;%(AdditionalIncludeDirectories) 4996 stdcpp17 Windows Winmm.lib;dxva2.lib;d3d9.lib;Oleacc.lib;OpenCL.lib;$(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\Python*.lib;%(AdditionalDependencies) opencl.dll $(INTELOCLSDKROOT)\lib\x64\;%(AdditionalLibraryDirectories) false _DEBUG;%(PreprocessorDefinitions) 0x0409 _DEBUG;%(PreprocessorDefinitions) $(IntDir);%(AdditionalIncludeDirectories) $(ProjectDir)update_version.cmd Level4 Use MaxSpeed true true WIN32;_WINDOWS;_HAS_AUTO_PTR_ETC=1;_HAS_FUNCTION_ASSIGN=1;_HAS_OLD_IOSTREAMS_MEMBERS=1;ENABLE_OPENCL;NDEBUG;%(PreprocessorDefinitions) true ..\audio;..\video;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.9;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.10;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.11;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.12;..\networking;..\Anime4KCPP\core\include;..\core;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;%(AdditionalIncludeDirectories) stdcpp17 Windows true true true Winmm.lib;dxva2.lib;d3d9.lib;Oleacc.lib;%(AdditionalDependencies);OpenCL.lib false opencl.dll $(INTELOCLSDKROOT)\lib\x86\;%(AdditionalLibraryDirectories) false true NDEBUG;%(PreprocessorDefinitions) 0x0409 NDEBUG;%(PreprocessorDefinitions) $(IntDir);%(AdditionalIncludeDirectories) $(ProjectDir)update_version.cmd Level4 Use MaxSpeed true true WIN32;_WINDOWS;_HAS_AUTO_PTR_ETC=1;_HAS_FUNCTION_ASSIGN=1;_HAS_OLD_IOSTREAMS_MEMBERS=1;ENABLE_OPENCL;NDEBUG;%(PreprocessorDefinitions) true ..\audio;..\video;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.9;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.10;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.11;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\python3.12;..\networking;..\Anime4KCPP\core\include;..\core;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\opencv4;%(AdditionalIncludeDirectories) stdcpp17 Windows true true true Winmm.lib;dxva2.lib;d3d9.lib;Oleacc.lib;OpenCL.lib;%(AdditionalDependencies) false opencl.dll $(INTELOCLSDKROOT)\lib\x64\;%(AdditionalLibraryDirectories) false NDEBUG;%(PreprocessorDefinitions) 0x0409 NDEBUG;%(PreprocessorDefinitions) $(IntDir);%(AdditionalIncludeDirectories) $(ProjectDir)update_version.cmd Create Create Create Create Pixel Pixel Pixel Pixel I420Effect_PS.h I420Effect_PS.h I420Effect_PS.h I420Effect_PS.h I420Effect_ByteCode I420Effect_ByteCode I420Effect_ByteCode I420Effect_ByteCode 4.0_level_9_1 4.0_level_9_1 4.0_level_9_1 4.0_level_9_1 {632353e4-4856-38f9-9e74-ed41bd99d7e5} {8b955995-b5ec-41f0-940a-48a6f17bcbb8} {3de6c2d2-fdfc-4745-8282-981df7561405} {3013c140-ddfc-4bf4-9091-0c4131a0d2a6} ================================================ FILE: Player/Player.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Resource Files Resource Files Resource Files Resource Files Resource Files Resource Files Resource Files Resource Files Resource Files Resource Files ================================================ FILE: Player/PlayerDoc.cpp ================================================ // PlayerDoc.cpp : implementation of the CPlayerDoc class // #include "stdafx.h" // SHARED_HANDLERS can be defined in an ATL project implementing preview, thumbnail // and search filter handlers and allows sharing of document code with that project. #ifndef SHARED_HANDLERS #include "Player.h" #endif #include "PlayerDoc.h" #include "AudioPlayerImpl.h" #include "AudioPlayerWasapi.h" #include "HandleFilesSequence.h" #include "AudioPitchDecorator.h" #include "OpenSubtitlesFile.h" #include "StringDifference.h" #include "DialogOpenURL.h" #include "YouTuber.h" #include "ImageUpscale.h" #include "ByteStreamBuffer.h" #include "DialogVideoFilter.h" #include "FrameTransformer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma comment(lib, "Sensapi") #ifdef _DEBUG #define new DEBUG_NEW #endif namespace { void SetForegroundWindowInternal(HWND hWnd) { if (!hWnd || !::IsWindow(hWnd) || ::SetForegroundWindow(hWnd)) return; //to unlock SetForegroundWindow we need to imitate pressing [Alt] key bool bPressed = false; if ((::GetAsyncKeyState(VK_MENU) & 0x8000) == 0) { bPressed = true; ::keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | 0, 0); } ::SetForegroundWindow(hWnd); if (bPressed) { ::keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); } } const RationalNumber videoSpeeds[] { { 1, 2 }, { 5, 8 }, { 4, 5 }, { 1, 1 }, { 5, 4 }, { 8, 5 }, { 2, 1 }, { 5, 4 }, //nightcore }; bool IsCalledFromMruList() { auto msg = AfxGetCurrentMessage(); return msg && msg->message == WM_COMMAND && msg->wParam >= ID_FILE_MRU_FILE1 && msg->wParam < ID_FILE_MRU_FILE1 + _AFX_MRU_MAX_COUNT; } CString GetUrlFromUrlFile(LPCTSTR lpszPathName) { CString url; auto result = GetPrivateProfileString( _T("InternetShortcut"), _T("URL"), nullptr, url.GetBuffer(4095), 4096, lpszPathName); if (!result) return {}; url.ReleaseBuffer(); return url; } bool PumpMessages() { MSG msg; while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!AfxGetApp()->PumpMessage()) { ::PostQuitMessage(0); return false; } } // let MFC do its idle processing LONG lIdle = 0; while (AfxGetApp()->OnIdle(lIdle++)) ; return true; } void CopyTextToClipboard(const CString& strText) { #ifdef _UNICODE enum { AFX_TCF_TEXT = CF_UNICODETEXT }; #else enum { AFX_TCF_TEXT = CF_TEXT }; #endif if (!strText.IsEmpty() && AfxGetMainWnd()->OpenClipboard()) { EmptyClipboard(); const auto textSize = (strText.GetLength() + 1) * sizeof(TCHAR); if (HGLOBAL hClipbuffer = ::GlobalAlloc(GMEM_MOVEABLE, textSize)) { if (LPTSTR lpszBuffer = (LPTSTR)GlobalLock(hClipbuffer)) { memcpy(lpszBuffer, (LPCTSTR)strText, textSize); ::GlobalUnlock(hClipbuffer); } ::SetClipboardData(AFX_TCF_TEXT, hClipbuffer); } CloseClipboard(); } } CString getScriptTempPath() { TCHAR szTempPath[MAX_PATH]; GetTempPath(MAX_PATH, szTempPath); PathAppend(szTempPath, _T("ffmpeg-video-conversion-script.cmd")); return szTempPath; } // A function that locks a file for writing but leaves it unmodified HANDLE lockFile(LPCTSTR fileName) { // Try to open the file with exclusive access HANDLE hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); DWORD dwError = GetLastError(); // If the file does not exist, create a new one if (dwError == ERROR_FILE_NOT_FOUND) { hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); dwError = GetLastError(); } // If the file is locked by another process, report an error if (dwError == ERROR_SHARING_VIOLATION) { TRACE("The file is locked by another process.\n"); return NULL; } // If any other error occurs, report an error if (hFile == INVALID_HANDLE_VALUE) { TRACE("Unable to lock the file. Error code: %d.\n", dwError); return NULL; } // Return the handle to the file return hFile; } // A function that overwrites the file with some stuff void writeFile(HANDLE hFile, const char* stuff, DWORD size) { // Move the file pointer to the beginning of the file SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // Write the stuff to the file DWORD dwWritten = 0; WriteFile(hFile, stuff, size, &dwWritten, NULL); // Check if the write operation was successful if (dwWritten != size) { TRACE("Unable to write to the file.\n"); } // Truncate the file to the current position of the file pointer SetEndOfFile(hFile); } // A function that ensures that a CString based file path ends with a file path separator void ensureSeparator(CString& filePath) { // Remove any trailing whitespace from the file path filePath.TrimRight(); // Get the length of the file path int length = filePath.GetLength(); // If the file path is not empty if (length > 0) { // Get the last character of the file path const auto lastChar = filePath.GetAt(length - 1); // If the last character is not the separator character if (lastChar != _T('\\') && lastChar != _T('/')) { // Append the separator character to the file path filePath.Append(_T("\\")); } } } bool FileExists(LPCTSTR pszPath, LPCTSTR pszFile) { TCHAR szFullPath[MAX_PATH]; _tcscpy_s(szFullPath, pszPath); PathAppend(szFullPath, pszFile); return !!PathFileExists(szFullPath); } CString StrikeThrough(const CString& str, bool doIt) { if (!doIt) return L"\u00A0\u2714" + str; CString result(L"\u00A0\u2718"); // Iterate over each character in the original string for (int i = 0; i < str.GetLength(); ++i) { auto ch = str[i]; if (ch == L' ') ch = L'\u00A0'; // Append the current character result += ch; // Append the Combining Overlay character if (i != str.GetLength() - 1) result += L'\u0338'; } return result; } CString NoBreak(CString s) { s.Replace(L' ', L'\u00A0'); return s; } std::unique_ptr GetAudioPlayer() { if (IsWindowsVistaOrGreater()) return std::make_unique(); return std::make_unique(); } template auto GetAddToSubtitlesMapLambda(T& map) { return [&map](double start, double end, const std::string& subtitle) { map->add({ boost::icl::interval::closed(start, end), subtitle }); }; } CCriticalSection s_csSubtitles; } // namespace class CPlayerDoc::SubtitlesMap : public boost::icl::interval_map { public: bool m_unicodeSubtitles = false; }; // CPlayerDoc IMPLEMENT_DYNCREATE(CPlayerDoc, CDocument) BEGIN_MESSAGE_MAP(CPlayerDoc, CDocument) ON_COMMAND_RANGE(ID_TRACK1, ID_TRACK1 + 99, OnAudioTrack) ON_UPDATE_COMMAND_UI_RANGE(ID_TRACK1, ID_TRACK1 + 99, OnUpdateAudioTrack) ON_COMMAND_RANGE(ID_VIDEO_SPEED1, ID_NIGHTCORE, OnVideoSpeed) ON_UPDATE_COMMAND_UI_RANGE(ID_VIDEO_SPEED1, ID_NIGHTCORE, OnUpdateVideoSpeed) ON_COMMAND(ID_AUTOPLAY, &CPlayerDoc::OnAutoplay) ON_UPDATE_COMMAND_UI(ID_AUTOPLAY, &CPlayerDoc::OnUpdateAutoplay) ON_COMMAND(ID_LOOPING, &CPlayerDoc::OnLooping) ON_UPDATE_COMMAND_UI(ID_LOOPING, &CPlayerDoc::OnUpdateLooping) ON_COMMAND(ID_FILE_SAVE_COPY_AS, &CPlayerDoc::OnFileSaveCopyAs) ON_COMMAND(ID_OPENSUBTITLESFILE, &CPlayerDoc::OnOpensubtitlesfile) ON_UPDATE_COMMAND_UI(ID_OPENSUBTITLESFILE, &CPlayerDoc::OnUpdateOpensubtitlesfile) ON_COMMAND(ID_COPY_URL_TO_CLIPBOARD, &CPlayerDoc::OnCopyUrlToClipboard) ON_COMMAND(ID_MAXIMALRESOLUTION, &CPlayerDoc::OnMaximalresolution) ON_UPDATE_COMMAND_UI(ID_MAXIMALRESOLUTION, &CPlayerDoc::OnUpdateMaximalresolution) ON_COMMAND(ID_HW_ACCELERATION, &CPlayerDoc::OnHwAcceleration) ON_UPDATE_COMMAND_UI(ID_HW_ACCELERATION, &CPlayerDoc::OnUpdateHwAcceleration) ON_COMMAND_RANGE(ID_FIRST_SUBTITLE, ID_FIRST_SUBTITLE+99, OnGetSubtitles) ON_UPDATE_COMMAND_UI_RANGE(ID_FIRST_SUBTITLE, ID_FIRST_SUBTITLE + 99, OnUpdateOpensubtitlesfile) ON_COMMAND(ID_SUPER_RESOLUTION, &CPlayerDoc::OnSuperResolution) ON_UPDATE_COMMAND_UI(ID_SUPER_RESOLUTION, &CPlayerDoc::OnUpdateSuperResolution) ON_COMMAND(ID_ORIENTATION_MIRRORX, &CPlayerDoc::OnOrientationMirrorx) ON_UPDATE_COMMAND_UI(ID_ORIENTATION_MIRRORX, &CPlayerDoc::OnUpdateOrientationMirrorx) ON_COMMAND(ID_ORIENTATION_MIRRORY, &CPlayerDoc::OnOrientationMirrory) ON_UPDATE_COMMAND_UI(ID_ORIENTATION_MIRRORY, &CPlayerDoc::OnUpdateOrientationMirrory) ON_COMMAND(ID_ORIENTATION_UPEND, &CPlayerDoc::OnOrientationUpend) ON_UPDATE_COMMAND_UI(ID_ORIENTATION_UPEND, &CPlayerDoc::OnUpdateOrientationUpend) ON_COMMAND(ID_ORIENTATION_DO_NOTHING, &CPlayerDoc::OnOrientationDoNothing) ON_COMMAND(ID_ORIENTATION_RORATE_90, &CPlayerDoc::OnOrientationRotate90) ON_COMMAND(ID_ORIENTATION_RORATE_180, &CPlayerDoc::OnOrientationRotate180) ON_COMMAND(ID_ORIENTATION_RORATE_270, &CPlayerDoc::OnOrientationRotate270) ON_COMMAND(ID_FIX_ENCODING, &CPlayerDoc::OnFixEncoding) ON_UPDATE_COMMAND_UI(ID_FIX_ENCODING, &CPlayerDoc::OnUpdateFixEncoding) ON_COMMAND(ID_CONVERT_VIDEOS_INTO_COMPATIBLE_FORMAT, &CPlayerDoc::OnConvertVideosIntoCompatibleFormat) ON_UPDATE_COMMAND_UI(ID_CONVERT_VIDEOS_INTO_COMPATIBLE_FORMAT, &CPlayerDoc::OnUpdateConvertVideosIntoCompatibleFormat) ON_COMMAND(ID_OPEN_AUDIO_FILE, &CPlayerDoc::OnOpenAudioFile) ON_UPDATE_COMMAND_UI(ID_OPEN_AUDIO_FILE, &CPlayerDoc::OnUpdateOpenAudioFile) ON_COMMAND(ID_USING_HHO, &CPlayerDoc::OnUsingHostHeaderOverride) ON_UPDATE_COMMAND_UI(ID_USING_HHO, &CPlayerDoc::OnUpdateUsingHostHeaderOverride) ON_COMMAND(ID_VIDEO_FILTER, &CPlayerDoc::OnVideoFilter) END_MESSAGE_MAP() const TCHAR szPlayerInitFlags[] = _T("PlayerInitFlags"); const TCHAR szMaximalResolution[] = _T("MaximalResolution"); const TCHAR szUsingHHO[] = _T("UsingHHO"); const TCHAR szPlayerState[] = _T("PlayerState"); const TCHAR szVideoFilter[] = _T("VideoFilter"); // CPlayerDoc construction/destruction CPlayerDoc::CPlayerDoc() : m_frameDecoder( GetFrameDecoder( std::make_unique(GetAudioPlayer(), std::bind(&CPlayerDoc::getVideoSpeed, this)))) { m_frameDecoder->setDecoderListener(this); if (auto pApp = AfxGetApp()) { m_maximalResolution = !!pApp->GetProfileInt(szPlayerInitFlags, szMaximalResolution, false); m_bUsingHHO = !!pApp->GetProfileInt(szPlayerInitFlags, szUsingHHO, false); m_videoFilter = pApp->GetProfileString(szPlayerState, szVideoFilter, _T("")); } } CPlayerDoc::~CPlayerDoc() { if (auto pApp = AfxGetApp()) { pApp->WriteProfileInt(szPlayerInitFlags, szMaximalResolution, m_maximalResolution); pApp->WriteProfileInt(szPlayerInitFlags, szUsingHHO, m_bUsingHHO); pApp->WriteProfileString(szPlayerState, szVideoFilter, m_videoFilter); } onDestructing(); ASSERT(framePositionChanged.empty()); ASSERT(startTimeUpdated.empty()); ASSERT(totalTimeUpdated.empty()); ASSERT(currentTimeUpdated.empty()); ASSERT(rangeStartTimeChanged.empty()); ASSERT(rangeEndTimeChanged.empty()); if (m_hConversionProcess != NULL) { CloseHandle(m_hConversionProcess); } } BOOL CPlayerDoc::OnNewDocument() { // (SDI documents will reuse this document) if (AfxGetApp()->m_pMainWnd) // false if the document is being initialized for the first time { CDialogOpenURL dlg; if (dlg.DoModal() == IDOK && !dlg.m_URL.IsEmpty() && CDocument::OnNewDocument()) { reset(); if (!dlg.m_inputFormt.IsEmpty()) { std::string url(CT2A(dlg.m_URL, CP_UTF8)); std::string inputFormat(CT2A(dlg.m_inputFormt, CP_UTF8)); return openUrl(url, inputFormat); } return openTopLevelUrl(dlg.m_URL, dlg.m_bParse); } return false; } else if (GetKeyState(VK_SHIFT) < 0 && GetKeyState(VK_CONTROL) < 0) { HRSRC hFound = ::FindResource(NULL, MAKEINTRESOURCE(IDR_LAUNCH), RT_RCDATA); HGLOBAL hRes = ::LoadResource(NULL, hFound); if (!m_frameDecoder->openStream(std::make_unique( (char*)::LockResource(hRes), ::SizeofResource(NULL, hFound)))) { return false; } m_frameDecoder->play(true); UpdateAllViews(nullptr, UPDATE_HINT_CLOSING); m_frameDecoder->pauseResume(); onPauseResume(false); return true; } return CDocument::OnNewDocument(); } bool CPlayerDoc::openTopLevelUrl(const CString& topLevelUrl, bool force, const CString& pathName) { std::string url(CT2A(topLevelUrl, CP_UTF8)); auto playList = ParsePlaylist(url, force); if (!playList.empty()) { if (openUrlFromList(playList, pathName)) return true; } else if (openUrl(url)) { m_playList.clear(); m_reopenFunc = [this, url, pathName] { UpdateAllViews(nullptr, UPDATE_HINT_CLOSING); if (openUrl(url) && !pathName.IsEmpty()) SetPathName(pathName, FALSE); }; return true; } return false; } bool CPlayerDoc::openUrl(const std::string& originalUrl, const std::string& inputFormat) { std::pair urls; if (!inputFormat.empty()) { if (!m_frameDecoder->openUrls({ originalUrl }, inputFormat)) return false; urls.first = originalUrl; } else { urls = getYoutubeUrl(originalUrl, m_maximalResolution, m_bUsingHHO); if (urls.first.empty() || !((m_maximalResolution && !urls.second.empty()) ? m_frameDecoder->openUrls({ urls.first, urls.second }, {}, m_bUsingHHO) : m_frameDecoder->openUrls({ urls.first }, {}, m_bUsingHHO))) { return false; } } m_frameDecoder->play(true); m_originalUrl = originalUrl; m_url = urls.first; if (m_maximalResolution && !urls.second.empty()) { m_separateFileDiff = std::make_unique( std::basic_string(urls.first.begin(), urls.first.end()), std::basic_string(urls.second.begin(), urls.second.end())); } else { m_separateFileDiff.reset(); } m_subtitles.reset(); m_nightcore = false; ++m_documentGeneration; UpdateAllViews(nullptr, UPDATE_HINT_CLOSING); if (inputFormat.empty()) { auto map(std::make_unique()); if (getYoutubeTranscripts(originalUrl, [&map](double start, double duration, const std::string& text) { map->add({ boost::icl::interval::closed(start, start + duration), boost::algorithm::trim_copy(text) + '\n' }); })) { map->m_unicodeSubtitles = true; m_subtitles = std::move(map); } } m_frameDecoder->pauseResume(); onPauseResume(false); return true; } bool CPlayerDoc::openUrlFromList() { bool networkCkecked = false; while (!m_playList.empty()) { auto buffer = m_playList.front(); m_playList.pop_front(); auto playList = ParsePlaylist(buffer, false); if (!playList.empty()) m_playList.insert(m_playList.begin(), playList.begin(), playList.end()); else if (openUrl(buffer)) return true; else if (!networkCkecked && PathIsURLA(buffer.c_str())) { networkCkecked = true; DWORD flags = NETWORK_ALIVE_INTERNET; if (!IsNetworkAlive(&flags)) { m_playList.push_front(buffer); return false; } } if (!m_playList.empty()) { const auto documentGeneration = m_documentGeneration; if (!PumpMessages()) return false; if (m_playList.empty() || documentGeneration != m_documentGeneration) AfxThrowUserException(); } } return false; } bool CPlayerDoc::openUrlFromList(const std::vector& playList, const CString& pathName) { m_playList = { playList.begin(), playList.end() }; if (openUrlFromList()) { m_reopenFunc = [this, playList, pathName] { UpdateAllViews(nullptr, UPDATE_HINT_CLOSING); m_playList = { playList.begin(), playList.end() }; if (openUrlFromList() && !pathName.IsEmpty()) SetPathName(pathName, FALSE); }; return true; } return false; } void CPlayerDoc::reset() { m_frameDecoder->close(); m_subtitles.reset(); m_reopenFunc = nullptr; m_originalUrl.clear(); m_url.clear(); m_nightcore = false; ++m_documentGeneration; UpdateAllViews(nullptr, UPDATE_HINT_CLOSING); } // CPlayerDoc serialization void CPlayerDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } } #ifdef SHARED_HANDLERS // Support for thumbnails void CPlayerDoc::OnDrawThumbnail(CDC& dc, LPRECT lprcBounds) { // Modify this code to draw the document's data dc.FillSolidRect(lprcBounds, RGB(255, 255, 255)); CString strText = _T("TODO: implement thumbnail drawing here"); LOGFONT lf; CFont* pDefaultGUIFont = CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT)); pDefaultGUIFont->GetLogFont(&lf); lf.lfHeight = 36; CFont fontDraw; fontDraw.CreateFontIndirect(&lf); CFont* pOldFont = dc.SelectObject(&fontDraw); dc.DrawText(strText, lprcBounds, DT_CENTER | DT_WORDBREAK); dc.SelectObject(pOldFont); } // Support for Search Handlers void CPlayerDoc::InitializeSearchContent() { CString strSearchContent; // Set search contents from document's data. // The content parts should be separated by ";" // For example: strSearchContent = _T("point;rectangle;circle;ole object;"); SetSearchContent(strSearchContent); } void CPlayerDoc::SetSearchContent(const CString& value) { if (value.IsEmpty()) { RemoveChunk(PKEY_Search_Contents.fmtid, PKEY_Search_Contents.pid); } else { CMFCFilterChunkValueImpl *pChunk = NULL; ATLTRY(pChunk = new CMFCFilterChunkValueImpl); if (pChunk != NULL) { pChunk->SetTextValue(PKEY_Search_Contents, value, CHUNK_TEXT); SetChunkValue(pChunk); } } } #endif // SHARED_HANDLERS // CPlayerDoc diagnostics #ifdef _DEBUG void CPlayerDoc::AssertValid() const { CDocument::AssertValid(); } void CPlayerDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif //_DEBUG // CPlayerDoc commands BOOL CPlayerDoc::OnOpenDocument(LPCTSTR lpszPathName) { const bool shiftAndControlPressed = GetKeyState(VK_SHIFT) < 0 && GetKeyState(VK_CONTROL) < 0; if (shiftAndControlPressed && IsCalledFromMruList()) return false; return openDocument(lpszPathName, shiftAndControlPressed); } BOOL CPlayerDoc::OnSaveDocument(LPCTSTR lpszPathName) { const bool isLocalFile = m_url.empty(); if (isLocalFile && !_tcsicmp(m_strPathName, lpszPathName)) { return false; } CString source(isLocalFile? m_strPathName : CString(m_url.data(), m_url.length())); if (source.IsEmpty()) return false; const bool transform = m_bOrientationMirrorx || m_bOrientationMirrory || m_bOrientationUpend; TCHAR pszPath[MAX_PATH] = { 0 }; GetModuleFileName(NULL, pszPath, ARRAYSIZE(pszPath)); PathRemoveFileSpec(pszPath); CString strFile; CString strParams; if (isFullFrameRange() && !m_separateFileDiff && !transform && m_videoFilter.IsEmpty()) { if (isLocalFile) { return CopyFile(source, lpszPathName, FALSE); // overwrites the existing file } strFile = _T("HttpDownload.exe"); if (FileExists(pszPath, strFile)) { strParams = source + _T(" \"") + lpszPathName + _T('"'); } else { strFile = _T("curl"); strParams = CString(_T("-o \"")) + lpszPathName + _T("\" ") + source; } } else { CString timeClause; if (!isFullFrameRange()) { timeClause.Format( _T("-ss %.3f -t %.3f -accurate_seek "), m_rangeStartTime, m_rangeEndTime - m_rangeStartTime); } strFile = _T("ffmpeg.exe"); strParams = timeClause + _T("-i \"") + source + _T('"'); CString mapClause; // will hold -map ... parts const int audioIndex = m_frameDecoder->getAudioTrack(); if (audioIndex == 0) { mapClause = _T(" -map 0:v:0 -map 0:a"); } else { mapClause.Format(_T(" -map 0:v:0 -map 0:a:%d"), audioIndex); } if (m_separateFileDiff) { const auto s = m_separateFileDiff->patch({ source.GetString(), source.GetString() + source.GetLength() }); if (!s.empty()) { // second input (patched audio/video) added strParams += _T(" ") + timeClause + _T("-i \"") + s.c_str() + _T("\""); // Determine mapping based on preferred audio track index // We assume GetPreferredAudioTrack() returns 0 to mean "copy all audio tracks", // non-zero (1-based) to mean "copy that audio track index" from the second input if (audioIndex == 0) { // copy all audio tracks from second input and the first video from first input mapClause = _T(" -map 0:v:0 -map 1:a"); } else { // copy the specified audio track (convert 1-based to 0-based index used by ffmpeg) mapClause.Format(_T(" -map 0:v:0 -map 1:a:%d"), audioIndex); } } } const bool streamcopy = (m_losslessCut || isFullFrameRange()) && !transform; if (streamcopy) strParams += _T(" -c copy"); // rotation https://webcache.googleusercontent.com/search?q=cache:IiCyGV1Tp7oJ:https://annimon.com/article/3997+ CString filterChain; if (!m_videoFilter.IsEmpty()) { filterChain = m_videoFilter; // start with user filter } // Orientation / rotation filters CString orientationFilter; if (m_bOrientationUpend) { if (m_bOrientationMirrory) orientationFilter = m_bOrientationMirrorx ? _T("transpose=3") : _T("transpose=1"); else orientationFilter = m_bOrientationMirrorx ? _T("transpose=2") : _T("transpose=0"); } else if (m_bOrientationMirrory) { orientationFilter = m_bOrientationMirrorx ? _T("hflip,vflip") : _T("vflip"); } else if (m_bOrientationMirrorx) { orientationFilter = _T("hflip"); } // Combine filters if needed if (!orientationFilter.IsEmpty()) { if (!filterChain.IsEmpty()) filterChain += _T(",") + orientationFilter; else filterChain = orientationFilter; } // Add final -vf parameter if any filter exists if (!filterChain.IsEmpty()) { strParams += _T(" -vf ") + filterChain; } if (!isFullFrameRange()) { strParams += _T(" -avoid_negative_ts 1 -map_chapters -1"); } // Append the mapClause after -avoid_negative_ts / -map_chapters but before re-encode options. if (!mapClause.IsEmpty()) strParams += mapClause; if (!streamcopy) strParams += _T(" -q:v 4"); strParams += _T(" -y \""); strParams += lpszPathName; strParams += _T('"'); TRACE(_T("FFmpeg parameters generated: %s\n"), static_cast(strParams)); } const auto result = ShellExecute(NULL, NULL, strFile, strParams, pszPath, SW_MINIMIZE); return int(result) > 32; } bool CPlayerDoc::openDocument(LPCTSTR lpszPathName, bool openSeparateFile /*= false*/) { if (!openSeparateFile) reset(); CString currentDirectory; if (auto fileName = PathFindFileName(lpszPathName)) { currentDirectory = CString(lpszPathName, fileName - lpszPathName); SetCurrentDirectory(currentDirectory); } const auto extension = PathFindExtension(lpszPathName); if (!_tcsicmp(extension, _T(".lst"))) { std::ifstream s(lpszPathName); if (!s) return false; m_playList.clear(); std::string buffer; while (std::getline(s, buffer)) { m_playList.push_back(buffer); } if (!openUrlFromList()) return false; } else if (!_tcsicmp(extension, _T(".url"))) { CString url = GetUrlFromUrlFile(lpszPathName); return !url.IsEmpty() && openTopLevelUrl(url, false, lpszPathName); // sets m_reopenFunc } else { // https://community.spiceworks.com/topic/1968971-opening-web-links-downloading-1-item-to-zcrksihu if (extension[0] == _T('\0') && (_tcsstr(lpszPathName, _T("playlist")) || _tcsstr(lpszPathName, _T("watch"))) || !_tcsicmp(extension, _T(".html")) || !_tcsicmp(extension, _T(".txt"))) { auto playList = ParsePlaylistFile(lpszPathName); if (!playList.empty()) { return openUrlFromList(playList); } } CString mappedAudioFile; if (openSeparateFile) { if (!AfxGetApp()->m_pMainWnd && AfxGetMainWnd()) { SetForegroundWindowInternal(*AfxGetMainWnd()); AfxGetMainWnd()->ShowWindow(SW_SHOW); } CFileDialog dlg(TRUE); if (!currentDirectory.IsEmpty()) dlg.GetOFN().lpstrInitialDir = currentDirectory; if (dlg.DoModal() != IDOK) { return false; } reset(); mappedAudioFile = dlg.GetPathName(); static_cast(AfxGetApp())->SetMappedAudioFile(lpszPathName, dlg.GetPathName()); } else { mappedAudioFile = static_cast(AfxGetApp())->GetMappedAudioFile(lpszPathName); } if (!mappedAudioFile.IsEmpty()) { m_separateFileDiff = std::make_unique( lpszPathName, static_cast(mappedAudioFile)); } else if (m_autoPlay && m_separateFileDiff) { const auto s = m_separateFileDiff->patch(lpszPathName); if (!s.empty() && 0 != _tcscmp(s.c_str(), lpszPathName) && 0 == _taccess(s.c_str(), 04)) { mappedAudioFile = s.c_str(); } else { m_separateFileDiff.reset(); } } else { m_separateFileDiff.reset(); } std::string pathNameA(CT2A(lpszPathName, CP_UTF8)); if (!mappedAudioFile.IsEmpty()) { if (!m_frameDecoder->openUrls( {pathNameA, std::string(CT2A(mappedAudioFile, CP_UTF8))})) { return false; } } else if (!m_frameDecoder->openUrls({pathNameA})) return false; m_playList.clear(); m_subtitles.reset(); if (m_autoPlay && m_subtitlesFileDiff) { const auto s = m_subtitlesFileDiff->patch(lpszPathName); auto map(std::make_unique()); if (!s.empty() && 0 != _tcscmp(s.c_str(), lpszPathName) && OpenSubtitlesFile(s.c_str(), map->m_unicodeSubtitles, GetAddToSubtitlesMapLambda(map))) { m_subtitles = std::move(map); } else { m_subtitlesFileDiff.reset(); } } else { m_subtitlesFileDiff.reset(); } if (!m_subtitles) { auto map(std::make_unique()); if (OpenMatchingSubtitlesFile(lpszPathName, map->m_unicodeSubtitles, GetAddToSubtitlesMapLambda(map))) { m_subtitles = std::move(map); } } m_frameDecoder->play(); onPauseResume(false); } m_reopenFunc = [this, path = CString(lpszPathName)] { if (openDocument(path)) SetPathName(path, FALSE); }; return true; } void CPlayerDoc::OnIdle() { __super::OnIdle(); if (m_onEndOfStream) { m_onEndOfStream = false; MoveToNextFile(); } if (m_hConversionProcess != NULL && WaitForSingleObject(m_hConversionProcess, 0) == WAIT_OBJECT_0) { CloseHandle(m_hConversionProcess); m_hConversionProcess = NULL; } } void CPlayerDoc::OnFileSaveCopyAs() { if (!DoSave(NULL, FALSE)) TRACE("Warning: File save-as failed.\n"); } void CPlayerDoc::MoveToNextFile() { auto saveReopenFunc = m_reopenFunc; if (openUrlFromList() || m_autoPlay && HandleFilesSequence( GetPathName(), m_looping, [this](const CString& path) { if (openDocument(path)) { SetPathName(path, FALSE); return true; } return false; })) { m_reopenFunc = saveReopenFunc; return; } if (m_playList.empty() && m_looping && m_reopenFunc) { // m_reopenFunc can be reset during invocation auto tempReopenFunc = m_reopenFunc; tempReopenFunc(); } } void CPlayerDoc::OnCloseDocument() { m_frameDecoder->close(); m_subtitles.reset(); CDocument::OnCloseDocument(); } void CPlayerDoc::SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU) { if (_tcslen(lpszPathName) < _MAX_PATH) { __super::SetPathName(lpszPathName, bAddToMRU); return; } m_strPathName = lpszPathName; ASSERT(!m_strPathName.IsEmpty()); // must be set to something m_bEmbedded = FALSE; // set the document title based on path name TCHAR szTitle[_MAX_FNAME]; if (::GetFileTitle(lpszPathName, szTitle, _countof(szTitle)) == 0) { SetTitle(szTitle); } else { SetTitle(::PathFindFileName(lpszPathName)); } // add it to the file MRU list //if (bAddToMRU) // AfxGetApp()->AddToRecentFileList(m_strPathName); } void CPlayerDoc::changedFramePosition(long long start, long long frame, long long total) { framePositionChanged(frame - start, total - start); const double currentTime = m_frameDecoder->getDurationSecs(frame); m_currentTime = currentTime; currentTimeUpdated(currentTime); if (m_looping && !m_autoPlay && !isFullFrameRange() && m_currentTime >= m_rangeEndTime && m_endTime > m_startTime) { const double percent = (m_rangeStartTime - m_startTime) / (m_endTime - m_startTime); m_frameDecoder->seekByPercent(percent); } } void CPlayerDoc::decoderClosed(bool /*fileReleased*/) { m_onEndOfStream = false; } void CPlayerDoc::fileLoaded(long long start, long long total) { const double startTime = m_frameDecoder->getDurationSecs(start); m_startTime = startTime; startTimeUpdated(startTime); const double endTime = m_frameDecoder->getDurationSecs(total); m_endTime = endTime; totalTimeUpdated(endTime); setRangeStartTime(startTime); setRangeEndTime(endTime); if (CWnd* pMainWnd = AfxGetApp()->GetMainWnd()) pMainWnd->PostMessage(WM_KICKIDLE); // trigger idle update } void CPlayerDoc::onEndOfStream(int idx, bool error) { if (idx != 0) return; if (!error && m_looping && !m_autoPlay && !isFullFrameRange() && m_endTime > m_startTime) { const double percent = (m_rangeStartTime - m_startTime) / (m_endTime - m_startTime); if (m_frameDecoder->seekByPercent(percent)) return; } if (!m_onEndOfStream) { m_onEndOfStream = true; if (CWnd* pMainWnd = AfxGetApp()->GetMainWnd()) pMainWnd->PostMessage(WM_KICKIDLE); // trigger idle update } } bool CPlayerDoc::pauseResume() { if (m_frameDecoder->pauseResume()) { onPauseResume(m_frameDecoder->isPaused()); return true; } return false; } bool CPlayerDoc::nextFrame() { return m_frameDecoder->nextFrame(); } bool CPlayerDoc::prevFrame() { return m_frameDecoder->prevFrame(); } bool CPlayerDoc::seekByPercent(double percent) { return m_frameDecoder->seekByPercent(percent); } void CPlayerDoc::seekToEnd() { if (m_autoPlay && m_currentTime - m_startTime > 5) // enable after 5 seconds { m_onEndOfStream = false; MoveToNextFile(); } } void CPlayerDoc::setVolume(double volume) { m_frameDecoder->setVolume(volume); } bool CPlayerDoc::isPlaying() const { return m_frameDecoder->isPlaying(); } bool CPlayerDoc::isPaused() const { return m_frameDecoder->isPaused(); } double CPlayerDoc::soundVolume() const { return m_frameDecoder->volume(); } std::wstring CPlayerDoc::getSubtitle() const { std::wstring result; { CSingleLock lock(&s_csSubtitles, TRUE); if (m_subtitles) { auto it = m_subtitles->find(m_currentTime); if (it != m_subtitles->end()) { if (m_subtitles->m_unicodeSubtitles && m_bFixEncoding) { CA2W bufW(it->second.c_str(), CP_UTF8); CW2A bufA(bufW, CP_ACP); result = CA2W(bufA, CP_UTF8); } else { result = CA2W(it->second.c_str(), m_subtitles->m_unicodeSubtitles ? CP_UTF8 : CP_ACP); } } } } if (!result.empty()) { using std::wregex; // Replace any whitespace followed by a newline with an empty string result = regex_replace(result, wregex(L"\\s+(?=\\n)"), L""); // Replace punctuation followed by whitespace with the punctuation followed by a Unicode // thin space character result = regex_replace(result, wregex(L"([,.?!;:])\\s"), L"$1\u2009"); // Replace an ellipsis occurring after a non-period character or at the start of the string // with a Unicode ellipsis character result = regex_replace(result, wregex(L"(^|[^.])\\.{3}([^.]|$)"), L"$1\u2026$2"); } return result; } void CPlayerDoc::setRangeStartTime(double time) { if (time < min(0., m_startTime)) time = m_endTime + time; m_rangeStartTime = time; rangeStartTimeChanged(time - m_startTime, m_endTime - m_startTime); } void CPlayerDoc::setRangeEndTime(double time) { if (time <= 0) time = m_endTime + time; m_rangeEndTime = time; rangeEndTimeChanged(time - m_startTime, m_endTime - m_startTime); } void CPlayerDoc::setLosslessCut(bool flag) { m_losslessCut = flag; } bool CPlayerDoc::isFullFrameRange() const { return m_startTime == m_rangeStartTime && m_endTime == m_rangeEndTime; } void CPlayerDoc::OnAsyncUrl(const CString& url) { if (!url.IsEmpty() && m_strPathName.IsEmpty() && m_url.empty()) { openTopLevelUrl(url, false); } } void CPlayerDoc::OnDropFiles(HDROP hDropInfo) { const UINT cFiles = DragQueryFile(hDropInfo, (UINT)-1, NULL, 0); if (cFiles == 0) return; if (cFiles == 1) { TCHAR lpszFileName[MAX_PATH]; if (DragQueryFile(hDropInfo, 0, lpszFileName, MAX_PATH) && openDocument(lpszFileName)) { SetPathName(lpszFileName, TRUE); } } else { std::vector playList; for (UINT i = 0; i < cFiles; ++i) { TCHAR lpszFileName[MAX_PATH]{}; if (DragQueryFile(hDropInfo, i, lpszFileName, MAX_PATH)) playList.emplace_back(CT2A(lpszFileName, CP_UTF8)); } if (!playList.empty()) { GetDocTemplate()->SetDefaultTitle(this); ClearPathName(); openUrlFromList(playList); } } } void CPlayerDoc::OnEditPaste(const std::string& text) { auto playList = ParsePlaylistText(text); if (!playList.empty()) { GetDocTemplate()->SetDefaultTitle(this); ClearPathName(); openUrlFromList(playList); } } void CPlayerDoc::OnAudioTrack(UINT id) { m_frameDecoder->setAudioTrack(id - ID_TRACK1); } void CPlayerDoc::OnUpdateAudioTrack(CCmdUI* pCmdUI) { const int idx = pCmdUI->m_nID - ID_TRACK1; pCmdUI->Enable(idx < m_frameDecoder->getNumAudioTracks()); pCmdUI->SetCheck(idx == m_frameDecoder->getAudioTrack()); } void CPlayerDoc::OnAutoplay() { m_autoPlay = !m_autoPlay; } void CPlayerDoc::OnUpdateAutoplay(CCmdUI *pCmdUI) { pCmdUI->SetCheck(m_autoPlay); } void CPlayerDoc::OnLooping() { m_looping = !m_looping; } void CPlayerDoc::OnUpdateLooping(CCmdUI *pCmdUI) { pCmdUI->SetCheck(m_looping); } void CPlayerDoc::OnVideoSpeed(UINT id) { const int idx = id - ID_VIDEO_SPEED1; if (idx >= 0 && idx < sizeof(videoSpeeds) / sizeof(videoSpeeds[0])) { m_frameDecoder->setSpeedRational(videoSpeeds[idx]); m_nightcore = (id == ID_NIGHTCORE); } } void CPlayerDoc::OnUpdateVideoSpeed(CCmdUI* pCmdUI) { const int idx = pCmdUI->m_nID - ID_VIDEO_SPEED1; if (idx >= 0 && idx < sizeof(videoSpeeds) / sizeof(videoSpeeds[0])) { pCmdUI->Enable(m_frameDecoder->isPlaying()); pCmdUI->SetCheck((pCmdUI->m_nID == ID_NIGHTCORE) ? m_nightcore : !m_nightcore && m_frameDecoder->getSpeedRational() == videoSpeeds[idx]); } } float CPlayerDoc::getVideoSpeed() const { if (m_nightcore) return 1.f; const auto speedRational = m_frameDecoder->getSpeedRational(); return static_cast(speedRational.denominator) / speedRational.numerator; } CString CPlayerDoc::generateConversionScript() const { const auto scriptTempPath = getScriptTempPath(); CHandle scriptFileHandle{ lockFile(scriptTempPath) }; if (!scriptFileHandle) { return {}; } CFolderPickerDialog dlg; if (IDOK != dlg.DoModal()) { return {}; } auto [isVideoCompatible, isAudioCompatible] = m_frameDecoder->isVideoAudioCompatible(); auto [width, height] = m_frameDecoder->getVideoSize(); // make script decrease resolution if too high (larger than 1080p) // Downscale only if resolution is above 1080p const bool needsDownscale = (height > 1080 || width > 1920); if (needsDownscale) { isVideoCompatible = false; // force conversion if downscaling is needed } // Build base message CString msg = _T("Destination: ") + NoBreak(dlg.GetPathName()) + _T("\nOptions:\n") + StrikeThrough(_T("Following,"), !m_autoPlay) + StrikeThrough(_T("Preceding,"), !m_looping) + StrikeThrough(_T("Separate Audio,"), !m_separateFileDiff) + StrikeThrough(_T("Separate Subtitles"), !m_subtitlesFileDiff); if (!isVideoCompatible) { // Mandatory conversion msg += _T("\n\nThe video format is NOT compatible."); msg += _T("\nConversion is required to proceed."); msg += _T("\n\nOK = Convert\nCANCEL = Abort"); UINT buttons = MB_OKCANCEL | MB_ICONWARNING; auto r = AfxMessageBox(msg, buttons); if (r != IDOK) { return {}; } isVideoCompatible = false; // enforce conversion } else { // Optional conversion msg += _T("\n\nThe video is compatible."); msg += _T("\nYou may still convert it (e.g., to reduce size or ensure consistent encoding)."); msg += _T("\n\nConvert the video?"); msg += _T("\nYES = Convert\nNO = Keep original\nCANCEL = Abort"); UINT buttons = MB_YESNOCANCEL | MB_ICONQUESTION; auto r = AfxMessageBox(msg, buttons); if (r == IDCANCEL) { return {}; } if (r == IDYES) isVideoCompatible = false; // force conversion else isVideoCompatible = true; // keep original } auto outputFolder = dlg.GetPathName(); ensureSeparator(outputFolder); std::vector videoFiles; if (m_autoPlay || m_looping) { if (!m_looping) { videoFiles.push_back(GetPathName()); } HandleFilesSequence( GetPathName(), m_autoPlay && m_looping, [&videoFiles](const CString& path) { videoFiles.push_back(path); return false; }, m_looping); } else { videoFiles.push_back(GetPathName()); } TCHAR pszAppFolderPath[MAX_PATH] = { 0 }; GetModuleFileName(NULL, pszAppFolderPath, ARRAYSIZE(pszAppFolderPath)); const auto pszAppFolderPathEnd = PathFindFileName(pszAppFolderPath); *pszAppFolderPathEnd = 0; CString ffmpegPath; { const TCHAR ffmpegExeName[] = _T("ffmpeg.exe"); PathAppend(pszAppFolderPath, ffmpegExeName); ffmpegPath = (_taccess(pszAppFolderPath, 04) == 0) ? (_T('"') + CString(pszAppFolderPath) + _T('"')) : ffmpegExeName; *pszAppFolderPathEnd = 0; } CString strText(_T("chcp 65001\r\n")); for (const auto& source : videoFiles) { CString command = ffmpegPath + _T(" -i \"") + source + _T('"'); std::basic_string separateFilePart; if (m_separateFileDiff) { auto s = m_separateFileDiff->patch( {source.GetString(), source.GetString() + source.GetLength()}); if (!s.empty()) { separateFilePart = _T(" -i \"") + std::move(s) + _T('"'); } } command += separateFilePart.c_str(); if (!isVideoCompatible) { CString vf; if (needsDownscale) { // Proportional downscale to fit within 1080p, preserve aspect ratio vf = _T("scale='min(1920,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease,") _T("pad=ceil(iw/2)*2:ceil(ih/2)*2"); } else { // Only pad to even dimensions vf = _T("pad=ceil(iw/2)*2:ceil(ih/2)*2"); } command += _T(" -vf ") + vf; } command += separateFilePart.empty() ? _T(" -map 0:v? -map 0:a?") : _T(" -map 0:v:0 -map 1:a:0"); command += _T(" -map 0:s?"); command += isVideoCompatible ? _T(" -c:v copy") : _T(" -c:v libx264 -crf 25 -pix_fmt yuv420p"); command += (isAudioCompatible && !m_autoPlay && !m_looping) ? _T(" -c:a copy") : _T(" -c:a aac -ac 2"); command += _T(" -c:s copy -preset superfast"); if (m_separateFileDiff) { command += _T(" -max_interleave_delta 0"); } command += _T(" \""); command += outputFolder; command += ::PathFindFileName(source); command += _T("\"\r\n"); strText += command; } if (m_subtitlesFileDiff) { PathAppend(pszAppFolderPath, _T("ToUTF8.exe")); CString subtitlesUtilPath = (_taccess(pszAppFolderPath, 04) == 0) ? (_T('"') + CString(pszAppFolderPath) + _T('"')) : _T("copy"); *pszAppFolderPathEnd = 0; for (const auto& source : videoFiles) { const auto s = m_subtitlesFileDiff->patch( {source.GetString(), source.GetString() + source.GetLength()}); if (s.empty()) continue; const auto audioExt = ::PathFindExtension(s.c_str()); const auto fileNameWithExt = ::PathFindFileName(source); std::basic_string fileName{fileNameWithExt, ::PathFindExtension(fileNameWithExt)}; auto command = static_cast(subtitlesUtilPath) + (_T(" \"") + s) + _T("\" \"") + static_cast(outputFolder) + fileName + audioExt + _T("\"\r\n"); strText += command.c_str(); } } CT2A bufA(strText, CP_UTF8); writeFile(scriptFileHandle, bufA, strlen(bufA)); return scriptTempPath; } void CPlayerDoc::OnOpensubtitlesfile() { CFileDialog dlg(TRUE); // TODO extensions CString currentDirectory; if (auto fileName = PathFindFileName(static_cast(m_strPathName))) { currentDirectory = CString(static_cast(m_strPathName), fileName - static_cast(m_strPathName)); dlg.GetOFN().lpstrInitialDir = currentDirectory; } if (dlg.DoModal() != IDOK) { return; } auto map(std::make_unique()); if (OpenSubtitlesFile(dlg.GetPathName(), map->m_unicodeSubtitles, GetAddToSubtitlesMapLambda(map))) { m_subtitlesFileDiff = std::make_unique( static_cast(m_strPathName), static_cast(dlg.GetPathName())); CSingleLock lock(&s_csSubtitles, TRUE); m_subtitles = std::move(map); } } void CPlayerDoc::OnUpdateOpensubtitlesfile(CCmdUI *pCmdUI) { pCmdUI->Enable(isPlaying() && m_url.empty()); } void CPlayerDoc::OnCopyUrlToClipboard() { const bool shiftAndControlPressed = GetKeyState(VK_SHIFT) < 0 && GetKeyState(VK_CONTROL) < 0; const auto& url = shiftAndControlPressed ? m_url : m_originalUrl; CString strText(url.empty() ? m_strPathName : CString(url.data(), url.length())); CopyTextToClipboard(strText); } void CPlayerDoc::OnMaximalresolution() { m_maximalResolution = !m_maximalResolution; } void CPlayerDoc::OnUpdateMaximalresolution(CCmdUI *pCmdUI) { pCmdUI->SetCheck(m_maximalResolution); } void CPlayerDoc::OnHwAcceleration() { m_frameDecoder->setHwAccelerated(!m_frameDecoder->getHwAccelerated()); } void CPlayerDoc::OnUpdateHwAcceleration(CCmdUI *pCmdUI) { pCmdUI->SetCheck(m_frameDecoder->getHwAccelerated()); } void CPlayerDoc::OnGetSubtitles(UINT id) { typedef std::pair GetSubtitlesParams; auto threadLam = [](LPVOID pParam) { std::unique_ptr params(static_cast(pParam)); auto map(std::make_shared()); map->m_unicodeSubtitles = true; auto addToSubtitlesLam = [weakPtr = std::weak_ptr(map)]( double start, double end, const std::string& subtitle) { if (auto map = weakPtr.lock()) { CSingleLock lock(&s_csSubtitles, TRUE); map->set({ boost::icl::interval::closed(start, end), subtitle }); return true; } return false; }; { CSingleLock lock(&s_csSubtitles, TRUE); params->first->m_subtitles = std::move(map); } params->first->m_frameDecoder->getSubtitles(params->second, addToSubtitlesLam); return UINT(); }; AfxBeginThread(threadLam, new GetSubtitlesParams(this, id - ID_FIRST_SUBTITLE)); } void CPlayerDoc::OnSuperResolution() { m_superResolution = !m_superResolution; if (m_superResolution) { CWaitCursor wait; EnableImageUpscale(); m_frameDecoder->setImageConversionFunc(ImageUpscale); } else m_frameDecoder->setImageConversionFunc({}); } void CPlayerDoc::OnUpdateSuperResolution(CCmdUI *pCmdUI) { pCmdUI->Enable(CanUpscaleImage()); pCmdUI->SetCheck(m_superResolution); } void CPlayerDoc::OnOrientationMirrorx() { m_bOrientationMirrorx = !m_bOrientationMirrorx; AfxGetApp()->GetMainWnd()->Invalidate(); } void CPlayerDoc::OnUpdateOrientationMirrorx(CCmdUI *pCmdUI) { pCmdUI->SetCheck(m_bOrientationMirrorx); } void CPlayerDoc::OnOrientationMirrory() { m_bOrientationMirrory = !m_bOrientationMirrory; AfxGetApp()->GetMainWnd()->Invalidate(); } void CPlayerDoc::OnUpdateOrientationMirrory(CCmdUI *pCmdUI) { pCmdUI->SetCheck(m_bOrientationMirrory); } void CPlayerDoc::OnOrientationUpend() { m_bOrientationUpend = !m_bOrientationUpend; AfxGetApp()->GetMainWnd()->Invalidate(); } void CPlayerDoc::OnUpdateOrientationUpend(CCmdUI *pCmdUI) { pCmdUI->SetCheck(m_bOrientationUpend); } void CPlayerDoc::OnOrientationDoNothing() { m_bOrientationMirrorx = false; m_bOrientationMirrory = false; m_bOrientationUpend = false; AfxGetApp()->GetMainWnd()->Invalidate(); } void CPlayerDoc::OnOrientationRotate90() { m_bOrientationMirrorx = false; m_bOrientationMirrory = true; m_bOrientationUpend = true; AfxGetApp()->GetMainWnd()->Invalidate(); } void CPlayerDoc::OnOrientationRotate180() { m_bOrientationMirrorx = true; m_bOrientationMirrory = true; m_bOrientationUpend = false; AfxGetApp()->GetMainWnd()->Invalidate(); } void CPlayerDoc::OnOrientationRotate270() { m_bOrientationMirrorx = true; m_bOrientationMirrory = false; m_bOrientationUpend = true; AfxGetApp()->GetMainWnd()->Invalidate(); } void CPlayerDoc::OnFixEncoding() { m_bFixEncoding = !m_bFixEncoding; } void CPlayerDoc::OnUpdateFixEncoding(CCmdUI* pCmdUI) { pCmdUI->SetCheck(m_bFixEncoding); } void CPlayerDoc::OnConvertVideosIntoCompatibleFormat() { if (m_hConversionProcess != NULL) { if (WaitForSingleObject(m_hConversionProcess, 0) == WAIT_OBJECT_0) { CloseHandle(m_hConversionProcess); m_hConversionProcess = NULL; } else { return; } } auto scriptTempPath = generateConversionScript(); if (scriptTempPath.IsEmpty()) return; // Try to execute the file with the "open" verb SHELLEXECUTEINFO ShExecInfo {}; ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; ShExecInfo.hwnd = NULL; ShExecInfo.lpVerb = _T("open"); ShExecInfo.lpFile = scriptTempPath; ShExecInfo.lpParameters = NULL; ShExecInfo.lpDirectory = NULL; ShExecInfo.nShow = SW_MINIMIZE; ShExecInfo.hInstApp = NULL; if (!ShellExecuteEx(&ShExecInfo)) { TRACE("ShellExecuteEx failed with error code %d.\n", GetLastError()); } else { m_hConversionProcess = ShExecInfo.hProcess; } } void CPlayerDoc::OnUpdateConvertVideosIntoCompatibleFormat(CCmdUI* pCmdUI) { if (GetPathName().IsEmpty() || !m_url.empty()) { pCmdUI->Enable(false); return; } const auto extension = PathFindExtension(GetPathName()); if (!_tcsicmp(extension, _T(".lst")) || !_tcsicmp(extension, _T(".url"))) { pCmdUI->Enable(false); return; } if (m_hConversionProcess != NULL) { if (WaitForSingleObject(m_hConversionProcess, 0) == WAIT_OBJECT_0) { CloseHandle(m_hConversionProcess); m_hConversionProcess = NULL; } else { pCmdUI->Enable(false); return; } } const auto scriptTempPath = getScriptTempPath(); if (const auto scriptFileHandle = lockFile(scriptTempPath)) { CloseHandle(scriptFileHandle); pCmdUI->Enable(true); } else { pCmdUI->Enable(false); } } void CPlayerDoc::OnOpenAudioFile() { openDocument(GetPathName(), true); } void CPlayerDoc::OnUpdateOpenAudioFile(CCmdUI* pCmdUI) { if (GetPathName().IsEmpty() || !m_url.empty()) { pCmdUI->Enable(false); return; } const auto extension = PathFindExtension(GetPathName()); if (!_tcsicmp(extension, _T(".lst")) || !_tcsicmp(extension, _T(".url"))) { pCmdUI->Enable(false); return; } pCmdUI->Enable(true); } void CPlayerDoc::OnUsingHostHeaderOverride() { m_bUsingHHO = !m_bUsingHHO; } void CPlayerDoc::OnUpdateUsingHostHeaderOverride(CCmdUI* pCmdUI) { pCmdUI->SetCheck(m_bUsingHHO); } void CPlayerDoc::OnVideoFilter() { BOOL enableVideoFilter = FALSE; { CDialogVideoFilter dlg; dlg.m_videoFilter = m_videoFilter; dlg.m_enableVideoFilter = m_enableVideoFilter; if (dlg.DoModal() != IDOK) return; dlg.m_videoFilter.Trim(); // in-place trim if (m_videoFilter == dlg.m_videoFilter && m_enableVideoFilter == dlg.m_enableVideoFilter) return; m_videoFilter = dlg.m_videoFilter; enableVideoFilter = dlg.m_enableVideoFilter; } if (enableVideoFilter && !m_videoFilter.IsEmpty()) { CW2A videoFilter(m_videoFilter, CP_UTF8); FrameTransformer frameTransformer(static_cast(videoFilter)); auto [width, height] = m_frameDecoder->getVideoSize(); auto ret = frameTransformer.init(width, height); if (ret >= 0) { m_frameDecoder->setImageConversionFunc(frameTransformer); m_enableVideoFilter = TRUE; return; // success } else { CString msg; msg.Format(_T("Video filter setting failed. Error code: %d."), ret); AfxMessageBox(msg); } } // fallback to no filter if (m_enableVideoFilter) { if (m_superResolution) m_frameDecoder->setImageConversionFunc(ImageUpscale); else m_frameDecoder->setImageConversionFunc({}); m_enableVideoFilter = FALSE; } } ================================================ FILE: Player/PlayerDoc.h ================================================ // PlayerDoc.h : interface of the CPlayerDoc class // #pragma once #pragma warning(disable:4996) #include #include #include #include #include #include #include "decoderinterface.h" class StringDifference; enum { UPDATE_HINT_CLOSING = 1 }; class CPlayerDoc : public CDocument, public FrameDecoderListener { protected: // create from serialization only CPlayerDoc(); DECLARE_DYNCREATE(CPlayerDoc) // Attributes public: IFrameDecoder* getFrameDecoder() const { return m_frameDecoder.get(); } // Operations public: // Overrides public: BOOL OnNewDocument() override; void Serialize(CArchive& ar) override; void OnCloseDocument() override; void SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU = TRUE) override; #ifdef SHARED_HANDLERS virtual void InitializeSearchContent(); virtual void OnDrawThumbnail(CDC& dc, LPRECT lprcBounds); #endif // SHARED_HANDLERS // Implementation public: ~CPlayerDoc() override; #ifdef _DEBUG void AssertValid() const override; void Dump(CDumpContext& dc) const override; #endif protected: void changedFramePosition(long long start, long long frame, long long total) override; void decoderClosed(bool fileReleased) override; void fileLoaded(long long start, long long total) override; void onEndOfStream(int idx, bool error) override; // Generated message map functions protected: afx_msg void OnAudioTrack(UINT id); afx_msg void OnUpdateAudioTrack(CCmdUI* pCmdUI); afx_msg void OnVideoSpeed(UINT id); afx_msg void OnUpdateVideoSpeed(CCmdUI* pCmdUI); afx_msg void OnAutoplay(); afx_msg void OnUpdateAutoplay(CCmdUI *pCmdUI); afx_msg void OnLooping(); afx_msg void OnUpdateLooping(CCmdUI *pCmdUI); afx_msg void OnOpensubtitlesfile(); afx_msg void OnUpdateOpensubtitlesfile(CCmdUI *pCmdUI); afx_msg void OnCopyUrlToClipboard(); afx_msg void OnGetSubtitles(UINT id); DECLARE_MESSAGE_MAP() #ifdef SHARED_HANDLERS // Helper function that sets search content for a Search Handler void SetSearchContent(const CString& value); #endif // SHARED_HANDLERS public: BOOL OnOpenDocument(LPCTSTR lpszPathName) override; BOOL DoFileSave() override { return FALSE; } BOOL OnSaveDocument(LPCTSTR) override; void OnIdle() override; void OnFileSaveCopyAs(); bool pauseResume(); bool nextFrame(); bool prevFrame(); bool seekByPercent(double percent); void seekToEnd(); void setVolume(double volume); bool isPlaying() const; bool isPaused() const; double soundVolume() const; boost::signals2::signal framePositionChanged; boost::signals2::signal startTimeUpdated; boost::signals2::signal totalTimeUpdated; boost::signals2::signal currentTimeUpdated; boost::signals2::signal rangeStartTimeChanged; boost::signals2::signal rangeEndTimeChanged; boost::signals2::signal onPauseResume; boost::signals2::signal onDestructing; std::wstring getSubtitle() const; void OnDropFiles(HDROP hDropInfo); void OnEditPaste(const std::string& text); double getCurrentTime() const { return m_currentTime; } double getStartTime() const { return m_startTime; } double getEndTime() const { return m_endTime; } void setRangeStartTime(double time); void setRangeEndTime(double time); void setLosslessCut(bool flag); bool isFullFrameRange() const; void OnAsyncUrl(const CString& url); bool isOrientationMirrorx() const { return m_bOrientationMirrorx; } bool isOrientationMirrory() const { return m_bOrientationMirrory; } bool isOrientationUpend() const { return m_bOrientationUpend; } private: void MoveToNextFile(); bool openDocument(LPCTSTR lpszPathName, bool openSeparateFile = false); bool openTopLevelUrl(const CString& url, bool force, const CString& pathName = {}); bool openUrl(const std::string& url, const std::string& inputFormat = {}); bool openUrlFromList(); bool openUrlFromList(const std::vector& playList, const CString& pathName = {}); void reset(); float getVideoSpeed() const; CString generateConversionScript() const; private: std::unique_ptr m_frameDecoder; std::atomic m_currentTime; double m_startTime; double m_endTime; double m_rangeStartTime{}; double m_rangeEndTime{}; bool m_losslessCut = true; class SubtitlesMap; std::shared_ptr m_subtitles; bool m_onEndOfStream = false; bool m_autoPlay = false; bool m_looping = false; std::string m_originalUrl; std::string m_url; std::unique_ptr m_separateFileDiff; std::unique_ptr m_subtitlesFileDiff; std::deque m_playList; std::function m_reopenFunc; bool m_nightcore = false; unsigned int m_documentGeneration = 0; bool m_maximalResolution = false; bool m_superResolution = false; bool m_bOrientationMirrorx = false; bool m_bOrientationMirrory = false; bool m_bOrientationUpend = false; bool m_bFixEncoding = false; bool m_bUsingHHO = true; HANDLE m_hConversionProcess = NULL; CString m_videoFilter; BOOL m_enableVideoFilter = FALSE; public: afx_msg void OnMaximalresolution(); afx_msg void OnUpdateMaximalresolution(CCmdUI *pCmdUI); afx_msg void OnHwAcceleration(); afx_msg void OnUpdateHwAcceleration(CCmdUI *pCmdUI); afx_msg void OnSuperResolution(); afx_msg void OnUpdateSuperResolution(CCmdUI *pCmdUI); afx_msg void OnOrientationMirrorx(); afx_msg void OnUpdateOrientationMirrorx(CCmdUI *pCmdUI); afx_msg void OnOrientationMirrory(); afx_msg void OnUpdateOrientationMirrory(CCmdUI *pCmdUI); afx_msg void OnOrientationUpend(); afx_msg void OnUpdateOrientationUpend(CCmdUI *pCmdUI); afx_msg void OnOrientationDoNothing(); afx_msg void OnOrientationRotate90(); afx_msg void OnOrientationRotate180(); afx_msg void OnOrientationRotate270(); afx_msg void OnFixEncoding(); afx_msg void OnUpdateFixEncoding(CCmdUI* pCmdUI); afx_msg void OnConvertVideosIntoCompatibleFormat(); afx_msg void OnUpdateConvertVideosIntoCompatibleFormat(CCmdUI* pCmdUI); afx_msg void OnOpenAudioFile(); afx_msg void OnUpdateOpenAudioFile(CCmdUI* pCmdUI); afx_msg void OnUsingHostHeaderOverride(); afx_msg void OnUpdateUsingHostHeaderOverride(CCmdUI* pCmdUI); afx_msg void OnVideoFilter(); }; ================================================ FILE: Player/PlayerView.cpp ================================================ // PlayerViewDxva2.cpp : implementation file // #include "stdafx.h" #include "Player.h" #include "PlayerView.h" #include "PlayerDoc.h" #include "GetClipboardText.h" #include "FrameToHglobal.h" #include "decoderinterface.h" //#include "D3DFont.h" #include #include #ifdef USE_DXVA2 #include #endif #include #include #ifdef _DEBUG #define new DEBUG_NEW #endif #define CONVERT_FROM_YUV420P namespace { #ifdef USE_DXVA2 const UINT VIDEO_REQUIED_OP = DXVA2_VideoProcess_YUV2RGB | DXVA2_VideoProcess_StretchX | DXVA2_VideoProcess_StretchY/* | DXVA2_VideoProcess_SubRects | DXVA2_VideoProcess_SubStreams*/; #endif const D3DFORMAT VIDEO_RENDER_TARGET_FORMAT = D3DFMT_X8R8G8B8; const D3DFORMAT VIDEO_MAIN_FORMAT = D3DFMT_YUY2; const D3DFORMAT VIDEO_IMC3_FORMAT = (D3DFORMAT)MAKEFOURCC('I', 'M', 'C', '3'); const UINT BACK_BUFFER_COUNT = 1; const UINT SUB_STREAM_COUNT = 0; const UINT DWM_BUFFER_COUNT = 4; const UINT VIDEO_FPS = 60; HMODULE g_hRgb9rastDLL = NULL; PVOID g_pfnD3D9GetSWInfo = nullptr; ////////////////////////////////////////////////////////////////////////////// DWORD RGBtoYUV(const D3DCOLOR rgb) { const INT A = HIBYTE(HIWORD(rgb)); const INT R = LOBYTE(HIWORD(rgb)) - 16; const INT G = HIBYTE(LOWORD(rgb)) - 16; const INT B = LOBYTE(LOWORD(rgb)) - 16; // // studio RGB [16...235] to SDTV ITU-R BT.601 YCbCr // INT Y = (77 * R + 150 * G + 29 * B + 128) / 256 + 16; INT U = (-44 * R - 87 * G + 131 * B + 128) / 256 + 128; INT V = (131 * R - 110 * G - 21 * B + 128) / 256 + 128; return D3DCOLOR_AYUV(A, Y, U, V); } BOOL RegisterSoftwareRasterizer(IDirect3D9* g_pD3D9) { if (!g_hRgb9rastDLL) { return FALSE; } HRESULT hr = g_pD3D9->RegisterSoftwareDevice(g_pfnD3D9GetSWInfo); if (FAILED(hr)) { TRACE("RegisterSoftwareDevice failed with error 0x%x.\n", hr); return FALSE; } return TRUE; } BOOL InitializeModule() { // // Load these DLLs dynamically because these may not be available prior to Vista. // g_hRgb9rastDLL = LoadLibrary(TEXT("rgb9rast.dll")); if (!g_hRgb9rastDLL) { TRACE("LoadLibrary(rgb9rast.dll) failed with error %d.\n", GetLastError()); } else { g_pfnD3D9GetSWInfo = GetProcAddress(g_hRgb9rastDLL, "D3D9GetSWInfo"); if (!g_pfnD3D9GetSWInfo) { TRACE("GetProcAddress(D3D9GetSWInfo) failed with error %d.\n", GetLastError()); return FALSE; } } return TRUE; } D3DPRESENT_PARAMETERS GetD3dPresentParams(HWND hWnd) { D3DPRESENT_PARAMETERS D3DPP = { 0 }; D3DPP.BackBufferWidth = GetSystemMetrics(SM_CXSCREEN); D3DPP.BackBufferHeight = GetSystemMetrics(SM_CYSCREEN); D3DPP.BackBufferFormat = VIDEO_RENDER_TARGET_FORMAT; D3DPP.BackBufferCount = BACK_BUFFER_COUNT; D3DPP.SwapEffect = D3DSWAPEFFECT_DISCARD; D3DPP.hDeviceWindow = hWnd; D3DPP.Windowed = TRUE;//g_bWindowed; D3DPP.Flags = D3DPRESENTFLAG_VIDEO; D3DPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; D3DPP.PresentationInterval = D3DPRESENT_INTERVAL_ONE; return D3DPP; } #ifdef USE_DXVA2 DXVA2_VideoDesc GetVideoDesc(const CSize& sourceSize) { DXVA2_VideoDesc videoDesc; videoDesc.SampleWidth = sourceSize.cx; videoDesc.SampleHeight = sourceSize.cy; videoDesc.SampleFormat.VideoChromaSubsampling = DXVA2_VideoChromaSubsampling_MPEG2; videoDesc.SampleFormat.NominalRange = DXVA2_NominalRange_16_235; videoDesc.SampleFormat.VideoTransferMatrix = DXVA2_VideoTransferMatrix_BT601; videoDesc.SampleFormat.VideoLighting = DXVA2_VideoLighting_dim; videoDesc.SampleFormat.VideoPrimaries = DXVA2_VideoPrimaries_BT709; videoDesc.SampleFormat.VideoTransferFunction = DXVA2_VideoTransFunc_709; videoDesc.SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame; videoDesc.Format = VIDEO_MAIN_FORMAT; videoDesc.InputSampleFreq.Numerator = VIDEO_FPS; videoDesc.InputSampleFreq.Denominator = 1; videoDesc.OutputFrameFreq.Numerator = VIDEO_FPS; videoDesc.OutputFrameFreq.Denominator = 1; return videoDesc; } DXVA2_AYUVSample16 GetBackgroundColor() { const D3DCOLOR yuv = RGBtoYUV(0); const BYTE Y = LOBYTE(HIWORD(yuv)); const BYTE U = HIBYTE(LOWORD(yuv)); const BYTE V = LOBYTE(LOWORD(yuv)); DXVA2_AYUVSample16 color; color.Cr = V * 0x100; color.Cb = U * 0x100; color.Y = Y * 0x100; color.Alpha = 0xFFFF; return color; } const LONGLONG start_100ns = 0;// frame * LONGLONG(VIDEO_100NSPF); const LONGLONG end_100ns = 0;// start_100ns + LONGLONG(VIDEO_100NSPF); DXVA2_VideoProcessBltParams GetVideoProcessBltParams( const CRect& target, const LONG (&procAmpValues)[4], const LONG (&nFilterValues)[6], const LONG (&dFilterValues)[6]) { DXVA2_VideoProcessBltParams blt {}; // Initialize VPBlt parameters. blt.TargetFrame = start_100ns; blt.TargetRect = target; // DXVA2_VideoProcess_Constriction blt.ConstrictionSize.cx = target.Width(); blt.ConstrictionSize.cy = target.Height(); blt.BackgroundColor = GetBackgroundColor(); // DXVA2_VideoProcess_YUV2RGBExtended blt.DestFormat.VideoChromaSubsampling = DXVA2_VideoChromaSubsampling_Unknown; blt.DestFormat.NominalRange = DXVA2_NominalRange_Unknown; blt.DestFormat.VideoTransferMatrix = DXVA2_VideoTransferMatrix_Unknown; blt.DestFormat.VideoLighting = DXVA2_VideoLighting_dim; blt.DestFormat.VideoPrimaries = DXVA2_VideoPrimaries_BT709; blt.DestFormat.VideoTransferFunction = DXVA2_VideoTransFunc_709; blt.DestFormat.SampleFormat = DXVA2_SampleProgressiveFrame; // DXVA2_ProcAmp_Brightness blt.ProcAmpValues.Brightness.ll = procAmpValues[0]; // DXVA2_ProcAmp_Contrast blt.ProcAmpValues.Contrast.ll = procAmpValues[1]; // DXVA2_ProcAmp_Hue blt.ProcAmpValues.Hue.ll = procAmpValues[2]; // DXVA2_ProcAmp_Saturation blt.ProcAmpValues.Saturation.ll = procAmpValues[3]; // DXVA2_VideoProcess_AlphaBlend blt.Alpha = DXVA2_Fixed32OpaqueAlpha(); // DXVA2_VideoProcess_NoiseFilter blt.NoiseFilterLuma.Level.ll = nFilterValues[0]; blt.NoiseFilterLuma.Threshold.ll = nFilterValues[1]; blt.NoiseFilterLuma.Radius.ll = nFilterValues[2]; blt.NoiseFilterChroma.Level.ll = nFilterValues[3]; blt.NoiseFilterChroma.Threshold.ll = nFilterValues[4]; blt.NoiseFilterChroma.Radius.ll = nFilterValues[5]; // DXVA2_VideoProcess_DetailFilter blt.DetailFilterLuma.Level.ll = dFilterValues[0]; blt.DetailFilterLuma.Threshold.ll = dFilterValues[1]; blt.DetailFilterLuma.Radius.ll = dFilterValues[2]; blt.DetailFilterChroma.Level.ll = dFilterValues[3]; blt.DetailFilterChroma.Threshold.ll = dFilterValues[4]; blt.DetailFilterChroma.Radius.ll = dFilterValues[5]; return blt; } DXVA2_VideoSample GetVideoSample( const CSize& m_sourceSize, const CRect& target, IDirect3DSurface9* srcSurface) { DXVA2_VideoSample sample {}; // Initialize main stream video sample. sample.Start = start_100ns; sample.End = end_100ns; // DXVA2_VideoProcess_YUV2RGBExtended sample.SampleFormat.VideoChromaSubsampling = DXVA2_VideoChromaSubsampling_MPEG2; sample.SampleFormat.NominalRange = DXVA2_NominalRange_16_235; sample.SampleFormat.VideoTransferMatrix = DXVA2_VideoTransferMatrix_BT601; sample.SampleFormat.VideoLighting = DXVA2_VideoLighting_dim; sample.SampleFormat.VideoPrimaries = DXVA2_VideoPrimaries_BT709; sample.SampleFormat.VideoTransferFunction = DXVA2_VideoTransFunc_709; sample.SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame; sample.SrcSurface = srcSurface; //m_pMainStream; // DXVA2_VideoProcess_SubRects sample.SrcRect = { 0, 0, m_sourceSize.cx, m_sourceSize.cy }; // DXVA2_VideoProcess_StretchX, Y sample.DstRect = target; // DXVA2_VideoProcess_PlanarAlpha sample.PlanarAlpha = DXVA2FloatToFixed(1.f); return sample; } #endif void SimdCopyAndConvert( __m128i* const __restrict origin0, __m128i* const __restrict origin1, const __m128i* const __restrict src00, const __m128i* const __restrict src01, const double* const __restrict src0, const double* const __restrict src1, size_t count) { for (size_t i = 0; i < count; ++i) { __m128i uv = _mm_unpacklo_epi8( _mm_castpd_si128(_mm_load_sd(src0 + i)), _mm_castpd_si128(_mm_load_sd(src1 + i))); _mm_stream_si128(origin0 + i * 2, _mm_unpacklo_epi8(src00[i], uv)); _mm_stream_si128(origin0 + i * 2 + 1, _mm_unpackhi_epi8(src00[i], uv)); _mm_stream_si128(origin1 + i * 2, _mm_unpacklo_epi8(src01[i], uv)); _mm_stream_si128(origin1 + i * 2 + 1, _mm_unpackhi_epi8(src01[i], uv)); } } void CopyAndConvert( uint32_t* __restrict origin0, uint32_t* __restrict origin1, const uint8_t* __restrict src00, const uint8_t* __restrict src01, const uint8_t* __restrict src0, const uint8_t* __restrict src1, size_t count) { if (!((intptr_t(origin0) & 15) || (intptr_t(origin1) & 15) || (intptr_t(src00) & 15) || (intptr_t(src01) & 15) || (intptr_t(src0) & 7) || (intptr_t(src1) & 7))) { const auto simdCount = count / 8; SimdCopyAndConvert( (__m128i*) origin0, (__m128i*) origin1, (const __m128i*) src00, (const __m128i*) src01, (const double*) src0, (const double*) src1, simdCount); origin0 += simdCount * 8; origin1 += simdCount * 8; src00 += simdCount * 16; src01 += simdCount * 16; src0 += simdCount * 8; src1 += simdCount * 8; count -= simdCount * 8; } for (unsigned int j = 0; j < count; ++j) { const uint32_t uv = (src0[j] << 8) | (src1[j] << 24); origin0[j] = uv | src00[j * 2] | (src00[j * 2 + 1] << 16); origin1[j] = uv | src01[j * 2] | (src01[j * 2 + 1] << 16); } } void CopyBuffer(uint8_t* dst, const uint8_t* src, size_t numBytes) { if ((intptr_t(dst) & 15) || (intptr_t(src) & 15)) { memcpy(dst, src, numBytes); return; } size_t i = 0; for (; i + 128 <= numBytes; i += 128) { __m128i d0 = _mm_load_si128((__m128i*)&src[i + 0 * 16]); __m128i d1 = _mm_load_si128((__m128i*)&src[i + 1 * 16]); __m128i d2 = _mm_load_si128((__m128i*)&src[i + 2 * 16]); __m128i d3 = _mm_load_si128((__m128i*)&src[i + 3 * 16]); __m128i d4 = _mm_load_si128((__m128i*)&src[i + 4 * 16]); __m128i d5 = _mm_load_si128((__m128i*)&src[i + 5 * 16]); __m128i d6 = _mm_load_si128((__m128i*)&src[i + 6 * 16]); __m128i d7 = _mm_load_si128((__m128i*)&src[i + 7 * 16]); _mm_stream_si128((__m128i*)&dst[i + 0 * 16], d0); _mm_stream_si128((__m128i*)&dst[i + 1 * 16], d1); _mm_stream_si128((__m128i*)&dst[i + 2 * 16], d2); _mm_stream_si128((__m128i*)&dst[i + 3 * 16], d3); _mm_stream_si128((__m128i*)&dst[i + 4 * 16], d4); _mm_stream_si128((__m128i*)&dst[i + 5 * 16], d5); _mm_stream_si128((__m128i*)&dst[i + 6 * 16], d6); _mm_stream_si128((__m128i*)&dst[i + 7 * 16], d7); } for (; i + 16 <= numBytes; i += 16) { __m128i d = _mm_load_si128((__m128i*)&src[i]); _mm_stream_si128((__m128i*)&dst[i], d); } for (; i + 4 <= numBytes; i += 4) { *(uint32_t*)&dst[i] = *(const uint32_t*)&src[i]; } for (; i < numBytes; i++) { dst[i] = src[i]; } //_mm_sfence(); } // subtitles enum { MAX_NUM_VERTICES = 50 * 6 }; struct D3DXVECTOR4 { FLOAT x; FLOAT y; FLOAT z; FLOAT w; }; struct FONT2DVERTEX { D3DXVECTOR4 p; DWORD color; FLOAT tu, tv; }; CComPtr InitStateBlock(LPDIRECT3DDEVICE9 pd3dDevice, IDirect3DTexture9* pTexture) { pd3dDevice->BeginStateBlock(); pd3dDevice->SetTexture(0, pTexture); //if (D3DFONT_ZENABLE & m_dwFontFlags) // pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE); //else pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE); pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); pd3dDevice->SetRenderState(D3DRS_ALPHAREF, 0x08); pd3dDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE); pd3dDevice->SetRenderState(D3DRS_CLIPPLANEENABLE, FALSE); pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE); pd3dDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE); pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); pd3dDevice->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA); pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); pd3dDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE); pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); CComPtr result; pd3dDevice->EndStateBlock(&result); return result; } void DrawSubtitleText(LPDIRECT3DDEVICE9 pd3dDevice, int width, int height, const std::wstring& text) { using namespace Gdiplus; const int fontSize = max(width / 60, 9); auto font = std::make_unique(L"MS Sans Serif", (REAL)fontSize); RectF boundingBox; { Bitmap bitmap(1, 1); Graphics graphics(&bitmap); graphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixelGridFit); graphics.MeasureString(text.c_str(), text.length(), font.get(), PointF(0, 0), &boundingBox); } if (boundingBox.Width > width) { font = std::make_unique(L"Arial Narrow", (REAL)fontSize); Bitmap bitmap(1, 1); Graphics graphics(&bitmap); graphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixelGridFit); graphics.MeasureString(text.c_str(), text.length(), font.get(), PointF(0, 0), &boundingBox); } CComPtr pTexture; // Create a new texture for the font if (FAILED(pd3dDevice->CreateTexture(boundingBox.Width, boundingBox.Height, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &pTexture, nullptr))) { return; } D3DLOCKED_RECT d3dlr; if (FAILED(pTexture->LockRect(0, &d3dlr, nullptr, 0))) { return; } { Bitmap bitmap(boundingBox.Width, boundingBox.Height, d3dlr.Pitch, PixelFormat16bppRGB565, static_cast(d3dlr.pBits)); Graphics graphics(&bitmap); graphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixelGridFit); SolidBrush whiteBrush(Color(0xFF, 0xFF, 0xFF)); graphics.DrawString(text.c_str(), text.length(), font.get(), PointF(0, 0), &whiteBrush); } pTexture->UnlockRect(0); CComPtr pVB; // Create vertex buffer for the letters if (FAILED(pd3dDevice->CreateVertexBuffer(MAX_NUM_VERTICES * sizeof(FONT2DVERTEX), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &pVB, nullptr))) { return; } CComPtr pStateBlockSaved(InitStateBlock(pd3dDevice, pTexture)); if (!pStateBlockSaved) { return; } CComPtr pStateBlockDrawText(InitStateBlock(pd3dDevice, pTexture)); if (!pStateBlockDrawText) { return; } // Setup renderstate pStateBlockSaved->Capture(); pStateBlockDrawText->Apply(); pd3dDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1); pd3dDevice->SetPixelShader(nullptr); pd3dDevice->SetStreamSource(0, pVB, 0, sizeof(FONT2DVERTEX)); const FLOAT tx1 = 0; const FLOAT ty1 = 0; const FLOAT tx2 = 1; const FLOAT ty2 = 1; const FLOAT w = boundingBox.Width; const FLOAT h = boundingBox.Height; // Fill vertex buffer FONT2DVERTEX* pVertices = nullptr; DWORD dwNumTriangles = 0; if (FAILED(pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD))) { return; } for (int pass = 0; pass < 2; ++pass) { const FLOAT sx = (width - boundingBox.Width + 1) / 2 + !pass; const FLOAT sy = height - boundingBox.Height - fontSize / 3 + !pass; const DWORD dwColor = pass? D3DCOLOR_XRGB(255, 255, 255) : D3DCOLOR_XRGB(0, 0, 0); *pVertices++ = { { sx + 0, sy + h, 0.9F, 1.0F }, dwColor, tx1, ty2 }; *pVertices++ = { { sx + 0, sy + 0, 0.9F, 1.0F }, dwColor, tx1, ty1 }; *pVertices++ = { { sx + w, sy + h, 0.9F, 1.0F }, dwColor, tx2, ty2 }; *pVertices++ = { { sx + w, sy + 0, 0.9F, 1.0F }, dwColor, tx2, ty1 }; *pVertices++ = { { sx + w, sy + h, 0.9F, 1.0F }, dwColor, tx2, ty2 }; *pVertices++ = { { sx + 0, sy + 0, 0.9F, 1.0F }, dwColor, tx1, ty1 }; dwNumTriangles += 2; } // Unlock and render the vertex buffer pVB->Unlock(); if (dwNumTriangles > 0) { pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles); } // Restore the modified renderstates pStateBlockSaved->Apply(); } bool Transform(LPDIRECT3DDEVICE9 m_pD3DD9, IDirect3DSurface9* m_pMainStream, const CSize& m_sourceSize, int w, int h, bool mirrorX, bool mirrorY, bool upend) { CComPtr pTexture; // Create a new texture for the stuff if (FAILED(m_pD3DD9->CreateTexture(m_sourceSize.cx & ~1, m_sourceSize.cy & ~1, 1, D3DUSAGE_RENDERTARGET, VIDEO_RENDER_TARGET_FORMAT, D3DPOOL_DEFAULT, &pTexture, nullptr))) { return false; } CComPtr dest; if (FAILED(pTexture->GetSurfaceLevel(0, &dest))) { return false; } RECT srcRect = {0, 0, m_sourceSize.cx & ~1, m_sourceSize.cy & ~1}; if (FAILED(m_pD3DD9->StretchRect(m_pMainStream, &srcRect, dest, NULL, D3DTEXF_LINEAR))) { return false; } CComPtr pVB; // Create vertex buffer for the stuff if (FAILED(m_pD3DD9->CreateVertexBuffer(MAX_NUM_VERTICES * sizeof(FONT2DVERTEX), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &pVB, nullptr))) { return false; } // capture/apply? CComPtr pStateBlockSaved(InitStateBlock(m_pD3DD9, pTexture)); if (!pStateBlockSaved) { return false; } CComPtr pStateBlockDrawText(InitStateBlock(m_pD3DD9, pTexture)); if (!pStateBlockDrawText) { return false; } // Setup renderstate pStateBlockSaved->Capture(); pStateBlockDrawText->Apply(); m_pD3DD9->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1); m_pD3DD9->SetPixelShader(nullptr); m_pD3DD9->SetStreamSource(0, pVB, 0, sizeof(FONT2DVERTEX)); const FLOAT tx1 = mirrorX; const FLOAT tx2 = !mirrorX; const FLOAT ty1 = mirrorY; const FLOAT ty2 = !mirrorY; // Fill vertex buffer FONT2DVERTEX* pVertices = nullptr; if (FAILED(pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD))) { return false; } const FLOAT sx = 0; const FLOAT sy = 0; const DWORD dwColor = D3DCOLOR_XRGB(255, 255, 255); *pVertices++ = {{sx + 0, sy + h, 0.9F, 1.0F}, dwColor, upend ? tx2 : tx1, upend ? ty1 : ty2}; *pVertices++ = {{sx + 0, sy + 0, 0.9F, 1.0F}, dwColor, tx1, ty1}; *pVertices++ = {{sx + w, sy + h, 0.9F, 1.0F}, dwColor, tx2, ty2}; *pVertices++ = {{sx + w, sy + 0, 0.9F, 1.0F}, dwColor, upend ? tx1 : tx2, upend ? ty2 : ty1}; *pVertices++ = {{sx + w, sy + h, 0.9F, 1.0F}, dwColor, tx2, ty2}; *pVertices++ = {{sx + 0, sy + 0, 0.9F, 1.0F}, dwColor, tx1, ty1}; const UINT dwNumTriangles = 2; // Unlock and render the vertex buffer pVB->Unlock(); if (FAILED(m_pD3DD9->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles))) { return false; } // Restore the modified renderstates pStateBlockSaved->Apply(); return true; } } // namespace class FrameListener : public IFrameListener { public: explicit FrameListener(CPlayerView* playerView) : m_playerView(playerView) {} private: void updateFrame(IFrameDecoder* decoder, unsigned int generation) override { m_playerView->updateFrame(); decoder->finishedDisplayingFrame(generation); } void drawFrame(IFrameDecoder* /*decoder*/, unsigned int /*generation*/) override { m_playerView->ProcessVideo(); //m_playerView->Invalidate(); //decoder->finishedDisplayingFrame(generation); } void decoderClosing() override { if (!m_playerView->m_pD3D9) { m_playerView->DestroyExtra(); m_playerView->DestroyD3D9(); } } private: CPlayerView* m_playerView; }; // CPlayerView IMPLEMENT_DYNCREATE(CPlayerView, CView) CPlayerView::CPlayerView() : m_frameListener(new FrameListener(this)) , m_aspectRatio(1, 1) { } CPlayerView::~CPlayerView() { GetDocument()->getFrameDecoder()->setFrameListener(nullptr); } BEGIN_MESSAGE_MAP(CPlayerView, CView) ON_COMMAND(ID_EDIT_PASTE, &CPlayerView::OnEditPaste) ON_WM_PAINT() ON_WM_CREATE() ON_WM_ERASEBKGND() ON_WM_DROPFILES() ON_COMMAND(ID_EDIT_COPY, &CPlayerView::OnEditCopy) END_MESSAGE_MAP() bool CPlayerView::InitializeD3D9() { m_pD3D9.Attach(Direct3DCreate9(D3D_SDK_VERSION)); if (!m_pD3D9) { TRACE("Direct3DCreate9 failed.\n"); return false; } auto D3DPP = GetD3dPresentParams(*this); // // First try to create a hardware D3D9 device. // HRESULT hr = m_pD3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd, D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING, &D3DPP, &m_pD3DD9); if (FAILED(hr)) { TRACE("CreateDevice(HAL) failed with error 0x%x.\n", hr); } if (!m_pD3DD9) { hr = m_pD3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd, D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &D3DPP, &m_pD3DD9); if (FAILED(hr)) { TRACE("CreateDevice(HAL+SOFTWARE_VERTEXPROCESSING) failed with error 0x%x.\n", hr); } } // // Next try to create a software D3D9 device. // if (!m_pD3DD9) { RegisterSoftwareRasterizer(m_pD3D9); hr = m_pD3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_SW, m_hWnd, D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &D3DPP, &m_pD3DD9); if (FAILED(hr)) { TRACE("CreateDevice(SW) failed with error 0x%x.\n", hr); } } return !!m_pD3DD9; } #ifdef USE_DXVA2 bool CPlayerView::CreateDXVA2VPDevice(REFGUID guid, bool bDXVA2SW, bool createSurface) { // // Query the supported render target format. // UINT i, count; D3DFORMAT* formats = NULL; auto videoDesc = GetVideoDesc(m_sourceSize); HRESULT hr = m_pDXVAVPS->GetVideoProcessorRenderTargets(guid, &videoDesc, &count, &formats); if (FAILED(hr)) { TRACE("GetVideoProcessorRenderTargets failed with error 0x%x.\n", hr); return false; } for (i = 0; i < count; ++i) { if (formats[i] == VIDEO_RENDER_TARGET_FORMAT) { break; } } CoTaskMemFree(formats); if (i >= count) { TRACE("GetVideoProcessorRenderTargets doesn't support that format.\n"); return false; } DXVA2_VideoProcessorCaps g_VPCaps = { 0 }; // // Query video processor capabilities. // hr = m_pDXVAVPS->GetVideoProcessorCaps(guid, &videoDesc, VIDEO_RENDER_TARGET_FORMAT, &g_VPCaps); if (FAILED(hr)) { TRACE("GetVideoProcessorCaps failed with error 0x%x.\n", hr); return false; } // // Check to see if the device is software device. // if (g_VPCaps.DeviceCaps & DXVA2_VPDev_SoftwareDevice) { if (!bDXVA2SW) { TRACE("The DXVA2 device isn't a hardware device.\n"); return false; } } else { if (bDXVA2SW) { TRACE("The DXVA2 device isn't a software device.\n"); return false; } } // // This is a progressive device and we cannot provide any reference sample. // if (g_VPCaps.NumForwardRefSamples > 0 || g_VPCaps.NumBackwardRefSamples > 0) { TRACE("NumForwardRefSamples or NumBackwardRefSamples is greater than 0.\n"); return false; } // // Check to see if the device supports all the VP operations we want. // if ((g_VPCaps.VideoProcessorOperations & VIDEO_REQUIED_OP) != VIDEO_REQUIED_OP) { TRACE("The DXVA2 device doesn't support the VP operations.\n"); return false; } // // Create a main stream surface. // if (createSurface) { hr = m_pDXVAVPS->CreateSurface( (m_sourceSize.cx + 7) & ~7, m_sourceSize.cy, 0, VIDEO_MAIN_FORMAT, g_VPCaps.InputPool, 0, DXVA2_VideoSoftwareRenderTarget, &m_pMainStream, NULL); if (FAILED(hr)) { TRACE("CreateSurface(MainStream) failed with error 0x%x.\n", hr); return false; } } // // Query ProcAmp ranges. // DXVA2_ValueRange range; for (i = 0; i < ARRAYSIZE(m_ProcAmpValues); ++i) { if (g_VPCaps.ProcAmpControlCaps & (1 << i)) { hr = m_pDXVAVPS->GetProcAmpRange(guid, &videoDesc, VIDEO_RENDER_TARGET_FORMAT, 1 << i, &range); if (FAILED(hr)) { TRACE("GetProcAmpRange failed with error 0x%x.\n", hr); return false; } m_ProcAmpValues[i] = range.DefaultValue.ll; } } // // Query Noise Filter ranges. // if (g_VPCaps.VideoProcessorOperations & DXVA2_VideoProcess_NoiseFilter) { for (i = 0; i < ARRAYSIZE(m_NFilterValues); ++i) { hr = m_pDXVAVPS->GetFilterPropertyRange(guid, &videoDesc, VIDEO_RENDER_TARGET_FORMAT, DXVA2_NoiseFilterLumaLevel + i, &range); if (FAILED(hr)) { TRACE("GetFilterPropertyRange(Noise) failed with error 0x%x.\n", hr); return false; } m_NFilterValues[i] = range.DefaultValue.ll; } } // // Query Detail Filter ranges. // if (g_VPCaps.VideoProcessorOperations & DXVA2_VideoProcess_DetailFilter) { for (i = 0; i < ARRAYSIZE(m_DFilterValues); ++i) { hr = m_pDXVAVPS->GetFilterPropertyRange(guid, &videoDesc, VIDEO_RENDER_TARGET_FORMAT, DXVA2_DetailFilterLumaLevel + i, &range); if (FAILED(hr)) { TRACE("GetFilterPropertyRange(Detail) failed with error 0x%x.\n", hr); return false; } m_DFilterValues[i] = range.DefaultValue.ll; } } // // Finally create a video processor device. // hr = m_pDXVAVPS->CreateVideoProcessor(guid, &videoDesc, VIDEO_RENDER_TARGET_FORMAT, SUB_STREAM_COUNT, &m_pDXVAVPD); if (FAILED(hr)) { TRACE("CreateVideoProcessor failed with error 0x%x.\n", hr); return false; } return true; } #endif bool CPlayerView::InitializeExtra(bool createSurface) { m_bUseIMC3 = false; HRESULT hr = S_OK; #ifdef USE_DXVA2 // Create DXVA2 Video Processor Service. hr = DXVA2CreateVideoService(m_pD3DD9, IID_IDirectXVideoProcessorService, (VOID**)&m_pDXVAVPS); if (FAILED(hr)) { TRACE("DXVA2CreateVideoService failed with error 0x%x.\n", hr); return false; } // Initialize the video descriptor. // Query the video processor GUID. UINT count; GUID* guids = NULL; auto videoDesc = GetVideoDesc(m_sourceSize); hr = m_pDXVAVPS->GetVideoProcessorDeviceGuids(&videoDesc, &count, &guids); if (FAILED(hr)) { TRACE("GetVideoProcessorDeviceGuids failed with error 0x%x.\n", hr); return false; } // Create a DXVA2 device. bool created = false; for (UINT i = 0; i < count; ++i) { if (CreateDXVA2VPDevice(guids[i], false, createSurface)) { created = true; break; } } if (!created) { for (UINT i = 0; i < count; ++i) { if (CreateDXVA2VPDevice(guids[i], true, createSurface)) { created = true; break; } } } CoTaskMemFree(guids); if (!m_pDXVAVPD) { TRACE("Failed to create a DXVA2 device.\n"); return false; } #else if (createSurface) { #ifdef CONVERT_FROM_YUV420P hr = m_pD3DD9->CreateOffscreenPlainSurface( (m_sourceSize.cx + 7) & ~7, (m_sourceSize.cy + 31) & ~31, VIDEO_IMC3_FORMAT, D3DPOOL_DEFAULT, &m_pMainStream, nullptr); if (SUCCEEDED(hr)) { m_bUseIMC3 = true; } else #endif { hr = m_pD3DD9->CreateOffscreenPlainSurface( (m_sourceSize.cx + 7) & ~7, m_sourceSize.cy, VIDEO_MAIN_FORMAT, D3DPOOL_DEFAULT, &m_pMainStream, nullptr); if (FAILED(hr)) { TRACE("CreateOffscreenPlainSurface failed with error 0x%x.\n", hr); return false; } } } #endif // Retrieve a back buffer as the video render target. hr = m_pD3DD9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &m_pD3DRT); if (FAILED(hr)) { TRACE("GetBackBuffer failed with error 0x%x.\n", hr); return false; } //m_subtitleFont = std::make_unique( // _T("MS Sans Serif"), // max(m_sourceSize.cx / 50, 9)); //m_subtitleFont->InitDeviceObjects(m_pD3DD9); //m_subtitleFont->RestoreDeviceObjects(); return true; } void CPlayerView::DestroyExtra() { //if (m_subtitleFont) //{ // m_subtitleFont->InvalidateDeviceObjects(); // m_subtitleFont->DeleteDeviceObjects(); // m_subtitleFont.reset(); //} m_pMainStream.Release(); #ifdef USE_DXVA2 m_pDXVAVPD.Release(); m_pDXVAVPS.Release(); #endif m_pD3DRT.Release(); } void CPlayerView::DestroyD3D9() { m_pD3DD9.Release(); m_pD3D9.Release(); } bool CPlayerView::ResetDevice() { bool fullInitialization = true; if (m_pD3DD9) { // // Destroy DXVA2 device because it may be holding any D3D9 resources. // DestroyExtra(); // // Reset will change the parameters, so use a copy instead. // auto d3dpp = GetD3dPresentParams(*this); HRESULT hr = m_pD3DD9->Reset(&d3dpp); if (FAILED(hr)) { TRACE("Reset failed with error 0x%x.\n", hr); } if (SUCCEEDED(hr) && InitializeExtra(true)) { fullInitialization = false; } else { // // If either Reset didn't work or failed to initialize DXVA2 device, // try to recover by recreating the devices from the scratch. // DestroyExtra(); DestroyD3D9(); } } return !(fullInitialization && (!InitializeD3D9() || !InitializeExtra(true))); } CRect CPlayerView::GetScreenPosition(bool swapXY) { CRect desc; GetClientRect(&desc); long long aspectFrameX(m_sourceSize.cx * m_aspectRatio.cx); long long aspectFrameY(m_sourceSize.cy * m_aspectRatio.cy); if (swapXY) { std::swap(aspectFrameX, aspectFrameY); } CRect target; if (aspectFrameY * desc.Width() > aspectFrameX * desc.Height()) { target.top = 0; target.bottom = desc.Height(); LONG width = LONG(aspectFrameX * desc.Height() / aspectFrameY); LONG offset = (desc.Width() - width) / 2; target.left = offset; target.right = width + offset; } else { target.left = 0; target.right = desc.Width(); LONG height = LONG(aspectFrameY * desc.Width() / aspectFrameX); LONG offset = (desc.Height() - height) / 2; target.top = offset; target.bottom = height + offset; } return target; } // CPlayerView drawing bool CPlayerView::ProcessVideo() { if (!m_pD3DD9) { return false; } CSingleLock lock(&m_csSurface, TRUE); // Check the current status of D3D9 device. HRESULT hr = m_pD3DD9->TestCooperativeLevel(); switch (hr) { case D3D_OK: break; case D3DERR_DEVICELOST: TRACE("TestCooperativeLevel returned D3DERR_DEVICELOST.\n"); return true; case D3DERR_DEVICENOTRESET: TRACE("TestCooperativeLevel returned D3DERR_DEVICENOTRESET.\n"); if (!m_pD3D9) { DestroyExtra(); DestroyD3D9(); GetDocument()->getFrameDecoder()->videoReset(); return false; } if (!ResetDevice()) { return false; } break; default: TRACE("TestCooperativeLevel failed with error 0x%x.\n", hr); return false; } RECT srcRect = {0, 0, m_sourceSize.cx & ~1, m_sourceSize.cy & ~1}; CRect screenPosition = GetScreenPosition(GetDocument()->isOrientationUpend()); CRect target(POINT{}, screenPosition.Size()); #ifdef USE_DXVA2 const auto blt = GetVideoProcessBltParams( target, m_ProcAmpValues, m_NFilterValues, m_DFilterValues); const auto sample = GetVideoSample(m_sourceSize, target, m_pMainStream); hr = m_pDXVAVPD->VideoProcessBlt(m_pD3DRT, &blt, &sample, SUB_STREAM_COUNT + 1, NULL); if (FAILED(hr)) { TRACE("VideoProcessBlt failed with error 0x%x.\n", hr); } #else m_pD3DD9->Clear( 0, nullptr, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0F, 0); if (!GetDocument()->isOrientationMirrorx() && !GetDocument()->isOrientationMirrory() && !GetDocument()->isOrientationUpend()) { hr = m_pD3DD9->StretchRect( m_pMainStream, &srcRect, m_pD3DRT, &target, D3DTEXF_NONE); if (FAILED(hr)) { TRACE("StretchRect failed with error 0x%x.\n", hr); } } else { hr = m_pD3DD9->BeginScene(); if (SUCCEEDED(hr)) { Transform(m_pD3DD9, m_pMainStream, m_sourceSize, screenPosition.Width(), screenPosition.Height(), GetDocument()->isOrientationMirrorx(), GetDocument()->isOrientationMirrory(), GetDocument()->isOrientationUpend()); m_pD3DD9->EndScene(); } } #endif if (auto subtitle = GetDocument()->getSubtitle(); !subtitle.empty()) { //const auto& convertedSubtitle = CA2T(subtitle.c_str(), CP_UTF8); hr = m_pD3DD9->BeginScene(); if (SUCCEEDED(hr)) { //CSize boundingBox; //m_subtitleFont->GetTextExtent(convertedSubtitle, &boundingBox); //const CSize frameSize = target.Size(); //const auto left = (frameSize.cx - boundingBox.cx) / 2; //const auto top = frameSize.cy - boundingBox.cy - 2; //m_subtitleFont->DrawText(left + 1, top + 1, D3DCOLOR_XRGB(0, 0, 0), convertedSubtitle); //m_subtitleFont->DrawText(left, top, D3DCOLOR_XRGB(255, 255, 255), convertedSubtitle); DrawSubtitleText(m_pD3DD9, target.Width(), target.Height(), subtitle); m_pD3DD9->EndScene(); } } hr = m_pD3DD9->Present(&target, &screenPosition, GetSafeHwnd(), nullptr); if (FAILED(hr)) { TRACE("Present failed with error 0x%x.\n", hr); } return true; } void CPlayerView::OnDraw(CDC* /*pDC*/) { //CDocument* pDoc = GetDocument(); // TODO: add draw code here } void CPlayerView::OnEditPaste() { const auto text = GetClipboardText(); if (!text.empty()) { GetDocument()->OnEditPaste(text); } } // CPlayerView diagnostics #ifdef _DEBUG void CPlayerView::AssertValid() const { CView::AssertValid(); } #ifndef _WIN32_WCE void CPlayerView::Dump(CDumpContext& dc) const { CView::Dump(dc); } #endif #endif //_DEBUG CPlayerDoc* CPlayerView::GetDocument() const { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CPlayerDoc))); return static_cast(m_pDocument); } // CPlayerView message handlers void CPlayerView::OnPaint() { if (!m_pD3DD9) { __super::OnPaint(); } else { ProcessVideo(); ValidateRect(nullptr); } } BOOL CPlayerView::PreCreateWindow(CREATESTRUCT& cs) { // For the full screen mode cs.style &= ~WS_BORDER; cs.dwExStyle &= ~WS_EX_CLIENTEDGE; return CView::PreCreateWindow(cs); } int CPlayerView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (!InitializeModule()) { return -1; } if (CView::OnCreate(lpCreateStruct) == -1) { return -1; } GetDocument()->getFrameDecoder()->setFrameListener(m_frameListener.get()); GetDocument()->getFrameDecoder()->SetFrameFormat(IFrameDecoder:: #ifdef CONVERT_FROM_YUV420P PIX_FMT_YUV420P #else PIX_FMT_YUYV422 #endif , true); DragAcceptFiles(); return 0; } void CPlayerView::updateFrame() { CSingleLock lock(&m_csSurface, TRUE); FrameRenderingData data; if (!GetDocument()->getFrameDecoder()->getFrameRenderingData(&data)) { return; } data.width &= -2; // must be even m_aspectRatio.cx = data.aspectNum; m_aspectRatio.cy = data.aspectDen; if (data.d3d9device) { if (data.d3d9device != m_pD3DD9) { m_sourceSize.cx = data.width; m_sourceSize.cy = data.height; DestroyExtra(); DestroyD3D9(); m_pD3DD9 = data.d3d9device; m_pD3D9.Release(); InitializeExtra(false); } } else if (!m_pD3D9 || data.width != m_sourceSize.cx || data.height != m_sourceSize.cy) { m_sourceSize.cx = data.width; m_sourceSize.cy = data.height; ResetDevice(); } if (data.surface) { m_pMainStream = data.surface; } else { if (!m_pMainStream) { TRACE("m_pMainStream is NULL!\n"); return; } D3DLOCKED_RECT lr; HRESULT hr = m_pMainStream->LockRect(&lr, nullptr, D3DLOCK_NOSYSLOCK); if (FAILED(hr)) { TRACE("LockRect failed with error 0x%x.\n", hr); return; } #ifdef CONVERT_FROM_YUV420P if (m_bUseIMC3) { const int normalizedHeight = (data.height + 31) & ~31; uint8_t* const pU = (uint8_t*)lr.pBits + (normalizedHeight * lr.Pitch); uint8_t* const pV = (uint8_t*)lr.pBits + (((normalizedHeight * 3) / 2) * lr.Pitch); uint8_t* const planes[] { (uint8_t*)lr.pBits, pU, pV }; int width = data.width; int height = data.height; for (int plane = 0; plane < 3; ++plane) { auto bits = planes[plane]; for (int i = 0; i < height; ++i) { CopyBuffer(bits + lr.Pitch * i, data.image[plane] + data.pitch[plane] * i, width); } width = data.width / 2; height = data.height / 2; } } else { for (int i = 0; i < data.height / 2; ++i) { CopyAndConvert( (uint32_t*)((char*)lr.pBits + lr.Pitch * 2 * i), (uint32_t*)((char*)lr.pBits + lr.Pitch * (2 * i + 1)), data.image[0] + data.pitch[0] * 2 * i, data.image[0] + data.pitch[0] * (2 * i + 1), data.image[1] + data.pitch[1] * i, data.image[2] + data.pitch[2] * i, data.width / 2); } } #else const size_t lineSize = (size_t)min(lr.Pitch, data.width * 2); for (int i = 0; i < data.height; ++i) { memcpy((BYTE*)lr.pBits + lr.Pitch * i, data.image[0] + data.width * 2 * i, lineSize); } #endif hr = m_pMainStream->UnlockRect(); if (FAILED(hr)) { TRACE("UnlockRect failed with error 0x%x.\n", hr); } } lock.Unlock(); if (auto* pMainWnd = dynamic_cast(AfxGetApp()->GetMainWnd())) { if (pMainWnd->IsFullScreen()) { ::SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED | ES_CONTINUOUS); } } } BOOL CPlayerView::OnEraseBkgnd(CDC* pDC) { if (!m_pD3DD9) { // Save old brush CGdiObject* pOldBrush = pDC->SelectStockObject(BLACK_BRUSH); CRect rect; pDC->GetClipBox(&rect); // Erase the area needed pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY); pDC->SelectObject(pOldBrush); } return TRUE; } void CPlayerView::OnErase(CWnd* pInitiator, CDC* pDC, BOOL isFullScreen) { if (!!m_pD3DD9) { CSingleLock lock(&m_csSurface, TRUE); CRect rect; if (isFullScreen) { pDC->GetClipBox(&rect); // Erase the area needed } else { GetClientRect(&rect); MapWindowPoints(pInitiator, &rect); } CRect targetRect = GetScreenPosition(GetDocument()->isOrientationUpend()); targetRect.DeflateRect(1, 1); MapWindowPoints(pInitiator, &targetRect); CRgn clientRgn; VERIFY(clientRgn.CreateRectRgnIndirect(&rect)); CRgn targetRgn; VERIFY(targetRgn.CreateRectRgnIndirect(&targetRect)); CRgn combined; VERIFY(combined.CreateRectRgnIndirect(&rect)); VERIFY(combined.CombineRgn(&clientRgn, &targetRgn, RGN_DIFF) != ERROR); // Save old brush CGdiObject* pOldBrush = pDC->SelectStockObject(BLACK_BRUSH); pDC->PaintRgn(&combined); pDC->SelectObject(pOldBrush); } } void CPlayerView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { if (lHint == UPDATE_HINT_CLOSING) { { CSingleLock lock(&m_csSurface, TRUE); DestroyExtra(); DestroyD3D9(); m_sourceSize = {}; } RedrawWindow(); } __super::OnUpdate(pSender, lHint, pHint); } void CPlayerView::OnDropFiles(HDROP hDropInfo) { GetDocument()->OnDropFiles(hDropInfo); __super::OnDropFiles(hDropInfo); } void CPlayerView::OnEditCopy() { if (!m_pMainStream) { return; } if (!OpenClipboard()) { return; } const int allocatedHeight = m_bUseIMC3 ? ((m_sourceSize.cy + 31) & ~31) : m_sourceSize.cy; if (HGLOBAL hglbl = FrameToHglobal(m_pMainStream, m_sourceSize.cx, m_sourceSize.cy, allocatedHeight)) { EmptyClipboard(); SetClipboardData(CF_DIB, hglbl); } CloseClipboard(); } ================================================ FILE: Player/PlayerView.h ================================================ #pragma once #include "IEraseableArea.h" #include struct IFrameListener; struct IDirect3D9; struct IDirect3DDevice9; struct IDirect3DSurface9; struct IDirectXVideoProcessorService; struct IDirectXVideoProcessor; //class CD3DFont; class CPlayerDoc; //#define USE_DXVA2 // CPlayerView view class CPlayerView : public CView, public IEraseableArea { friend class FrameListener; DECLARE_DYNCREATE(CPlayerView) protected: CPlayerView(); // protected constructor used by dynamic creation virtual ~CPlayerView(); public: virtual void OnDraw(CDC* pDC); // overridden to draw this view #ifdef _DEBUG virtual void AssertValid() const; #ifndef _WIN32_WCE virtual void Dump(CDumpContext& dc) const; #endif #endif CPlayerDoc* GetDocument() const; void updateFrame(); void OnErase(CWnd* pInitiator, CDC* pDC, BOOL isFullScreen) override; protected: DECLARE_MESSAGE_MAP() private: bool InitializeD3D9(); bool InitializeExtra(bool createSurface); void DestroyExtra(); void DestroyD3D9(); #ifdef USE_DXVA2 bool CreateDXVA2VPDevice(REFGUID guid, bool bDXVA2SW, bool createSurface); #endif bool ResetDevice(); bool ProcessVideo(); CRect GetScreenPosition(bool swapXY); private: std::unique_ptr m_frameListener; CSize m_sourceSize; CSize m_aspectRatio; CCriticalSection m_csSurface; CComPtr m_pD3D9; CComPtr m_pD3DD9; CComPtr m_pD3DRT; CComPtr m_pMainStream; bool m_bUseIMC3 = false; #ifdef USE_DXVA2 CComPtr m_pDXVAVPS; CComPtr m_pDXVAVPD; LONG m_ProcAmpValues[4] {}; LONG m_NFilterValues[6] {}; LONG m_DFilterValues[6] {}; #endif //std::unique_ptr m_subtitleFont; public: afx_msg void OnPaint(); virtual BOOL PreCreateWindow(CREATESTRUCT& cs); afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg BOOL OnEraseBkgnd(CDC* pDC); virtual void OnUpdate(CView* /*pSender*/, LPARAM /*lHint*/, CObject* /*pHint*/); afx_msg void OnDropFiles(HDROP hDropInfo); afx_msg void OnEditPaste(); afx_msg void OnEditCopy(); }; ================================================ FILE: Player/PlayerViewD2D.cpp ================================================ #include "stdafx.h" // SHARED_HANDLERS can be defined in an ATL project implementing preview, thumbnail // and search filter handlers and allows sharing of document code with that project. #ifndef SHARED_HANDLERS #include "Player.h" #endif #include "PlayerDoc.h" #include "PlayerViewD2D.h" #include "I420Effect.h" #include "GetClipboardText.h" #include "decoderinterface.h" #include #ifdef _DEBUG #define new DEBUG_NEW #endif enum { WM_DRAW_FRAME = WM_USER + 101 }; namespace { //Use IDWriteTextLayout to get the text size HRESULT GetTextSize(const std::wstring& text, IDWriteTextFormat* pTextFormat, const SIZE& sourceSize, D2D1_SIZE_F& size) { CComPtr pTextLayout; // Create a text layout auto len = text.length(); if (len > 0 && text[len - 1] == L'\n') --len; auto hr = AfxGetD2DState()->GetWriteFactory()->CreateTextLayout( text.c_str(), len, pTextFormat, sourceSize.cx, sourceSize.cy, &pTextLayout); if (SUCCEEDED(hr)) { //Gets the text size DWRITE_TEXT_METRICS textMetrics; hr = pTextLayout->GetMetrics(&textMetrics); size = D2D1::SizeF(textMetrics.width, textMetrics.height); } return hr; } } // namespace class FrameListenerD2D : public IFrameListener { public: explicit FrameListenerD2D(CPlayerViewD2D* playerView) : m_playerView(playerView) {} private: void updateFrame(IFrameDecoder* decoder, unsigned int generation) override { m_playerView->updateFrame(); decoder->finishedDisplayingFrame(generation, IFrameDecoder::RELEASE_FRAME); } void drawFrame(IFrameDecoder*, unsigned int generation) override { m_playerView->SendNotifyMessage(WM_DRAW_FRAME, 0, generation); } void decoderClosing() override { } private: CPlayerViewD2D* m_playerView; }; // CPlayerViewD2D IMPLEMENT_DYNCREATE(CPlayerViewD2D, CView) BEGIN_MESSAGE_MAP(CPlayerViewD2D, CView) // Standard printing commands ON_COMMAND(ID_EDIT_PASTE, &CPlayerViewD2D::OnEditPaste) ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview) ON_REGISTERED_MESSAGE(AFX_WM_DRAW2D, &CPlayerViewD2D::OnDraw2D) ON_MESSAGE(WM_DRAW_FRAME, &CPlayerViewD2D::DrawFrame) ON_WM_CREATE() ON_WM_DROPFILES() END_MESSAGE_MAP() // CPlayerViewD2D construction/destruction CPlayerViewD2D::CPlayerViewD2D() : m_frameListener(new FrameListenerD2D(this)) , m_aspectRatio(1.f) { // Enable D2D support for this window: EnableD2DSupport(); } CPlayerViewD2D::~CPlayerViewD2D() { GetDocument()->getFrameDecoder()->setFrameListener(nullptr); } BOOL CPlayerViewD2D::PreCreateWindow(CREATESTRUCT& cs) { // For the full screen mode cs.style &= ~WS_BORDER; cs.dwExStyle &= ~WS_EX_CLIENTEDGE; return CView::PreCreateWindow(cs); } void CPlayerViewD2D::OnEditPaste() { const auto text = GetClipboardText(); if (!text.empty()) { GetDocument()->OnEditPaste(text); } } // CPlayerViewD2D drawing void CPlayerViewD2D::OnDraw(CDC* /*pDC*/) { CPlayerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here } // CPlayerViewD2D printing BOOL CPlayerViewD2D::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } void CPlayerViewD2D::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing } void CPlayerViewD2D::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing } // CPlayerViewD2D diagnostics #ifdef _DEBUG void CPlayerViewD2D::AssertValid() const { CView::AssertValid(); } void CPlayerViewD2D::Dump(CDumpContext& dc) const { CView::Dump(dc); } #endif //_DEBUG CPlayerDoc* CPlayerViewD2D::GetDocument() const { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CPlayerDoc))); return static_cast(m_pDocument); } // CPlayerViewD2D message handlers afx_msg LRESULT CPlayerViewD2D::OnDraw2D(WPARAM, LPARAM lParam) { CHwndRenderTarget* pRenderTarget = (CHwndRenderTarget*)lParam; ASSERT_VALID(pRenderTarget); CRect rect; GetClientRect(rect); if (rect.Width() <= 1 || rect.Height() <= 1) return TRUE; CComQIPtr spContext(*pRenderTarget); float dpiX; float dpiY; spContext->GetDpi(&dpiX, &dpiY); auto sourceSize = m_sourceSize; auto aspectRatio = m_aspectRatio; if (GetDocument()->isOrientationUpend()) { std::swap(sourceSize.cx, sourceSize.cy); aspectRatio = 1. / aspectRatio; } float scaleW = rect.Width() / (float) sourceSize.cx; float scaleH = rect.Height() / (float) sourceSize.cy; D2D1_POINT_2F offset; if (scaleH * aspectRatio <= scaleW) { scaleW = scaleH * aspectRatio; offset.x = (rect.Width() - (sourceSize.cx * scaleW)) / 2.0f; offset.y = 0.0f; } else { scaleH = scaleW / aspectRatio; offset.x = 0.0f; offset.y = (rect.Height() - (sourceSize.cy * scaleH)) / 2.0f; } auto transform = D2D1::Matrix3x2F::Identity(); if (GetDocument()->isOrientationUpend()) { std::swap(transform._11, transform._21); std::swap(transform._12, transform._22); } if (GetDocument()->isOrientationMirrorx()) { transform._11 = -transform._11; transform._21 = -transform._21; transform._31 += sourceSize.cx; } if (GetDocument()->isOrientationMirrory()) { transform._12 = -transform._12; transform._22 = -transform._22; transform._32 += sourceSize.cy; } transform = transform * D2D1::Matrix3x2F::Scale(scaleW, scaleH) * D2D1::Matrix3x2F::Translation(offset.x, offset.y); spContext->SetTransform(transform); spContext->Clear(D2D1::ColorF(D2D1::ColorF::Black)); if (m_spEffect) { spContext->DrawImage(m_spEffect, //D2D1_INTERPOLATION_MODE_CUBIC D2D1_INTERPOLATION_MODE_LINEAR ); } spContext->SetTransform(D2D1::Matrix3x2F::Scale(scaleW, scaleH) * D2D1::Matrix3x2F::Translation(offset.x, offset.y)); if (auto subtitle = GetDocument()->getSubtitle(); !subtitle.empty()) { CComPtr pTextFormat; if (SUCCEEDED(AfxGetD2DState()->GetWriteFactory()->CreateTextFormat( L"MS Sans Serif", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, std::max({ sourceSize.cx / 60, sourceSize.cy / 60, 9 }), L"", //locale &pTextFormat))) { D2D1_SIZE_F boundingBox; if (SUCCEEDED(GetTextSize(subtitle, pTextFormat, sourceSize, boundingBox))) { const auto left = (sourceSize.cx - boundingBox.width) / 2; const auto top = sourceSize.cy - boundingBox.height - 2; CComPtr pBlackBrush; CComPtr pWhiteBrush; if (SUCCEEDED(spContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black, 1.0f), &pBlackBrush)) && SUCCEEDED(spContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White, 1.0f), &pWhiteBrush))) { spContext->DrawText( subtitle.c_str(), subtitle.length(), pTextFormat, D2D1::RectF(left + 1, top + 1, left + 1 + boundingBox.width, top + 1 + boundingBox.height), pBlackBrush); spContext->DrawText( subtitle.c_str(), subtitle.length(), pTextFormat, D2D1::RectF(left, top, left + boundingBox.width, top + boundingBox.height), pWhiteBrush); } } } } return TRUE; } int CPlayerViewD2D::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; GetDocument()->getFrameDecoder()->setFrameListener(m_frameListener.get()); DragAcceptFiles(); return 0; } void CPlayerViewD2D::updateFrame() { FrameRenderingData data; if (!GetDocument()->getFrameDecoder()->getFrameRenderingData(&data)) { return; } CHwndRenderTarget* renderTarget = LockRenderTarget(); if (!renderTarget) { return; } m_aspectRatio = float(data.aspectNum) / data.aspectDen; m_sourceSize.cx = data.width; m_sourceSize.cy = data.height; CComQIPtr spContext(*renderTarget); CSingleLock lock(&m_csSurface, TRUE); if (!m_spEffect) { HRESULT hr = spContext->CreateEffect(CLSID_CustomI420Effect, &m_spEffect); if (FAILED(hr)) { UnlockRenderTarget(); return; } } // Init bitmap properties in which will store the y (lumi) plane D2D1_BITMAP_PROPERTIES1 props; D2D1_PIXEL_FORMAT pixFormat; pixFormat.alphaMode = D2D1_ALPHA_MODE_STRAIGHT; pixFormat.format = DXGI_FORMAT_A8_UNORM; props.pixelFormat = pixFormat; spContext->GetDpi(&props.dpiX, &props.dpiY); props.bitmapOptions = D2D1_BITMAP_OPTIONS_NONE; props.colorContext = nullptr; CComPtr yBitmap; HRESULT hr = spContext->CreateBitmap( { static_cast(m_sourceSize.cx), static_cast(m_sourceSize.cy) }, data.image[0], data.pitch[0], props, &yBitmap); CComPtr uBitmap; hr = spContext->CreateBitmap( { static_cast(m_sourceSize.cx / 2), static_cast(m_sourceSize.cy / 2) }, data.image[1], data.pitch[1], props, &uBitmap); CComPtr vBitmap; hr = spContext->CreateBitmap( { static_cast(m_sourceSize.cx / 2), static_cast(m_sourceSize.cy / 2) }, data.image[2], data.pitch[2], props, &vBitmap); m_spEffect->SetInput(0, yBitmap); m_spEffect->SetInput(1, uBitmap); m_spEffect->SetInput(2, vBitmap); UnlockRenderTarget(); } LRESULT CPlayerViewD2D::DrawFrame(WPARAM, LPARAM generation) { { CSingleLock lock(&m_csSurface, TRUE); DoD2DPaint(); } GetDocument()->getFrameDecoder()->finishedDisplayingFrame(generation, IFrameDecoder::FINALIZE_DISPLAY); return 0; } BOOL CPlayerViewD2D::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { if (message == WM_PAINT) { CSingleLock lock(&m_csSurface, TRUE); const BOOL lResult = DoD2DPaint(); if (pResult != NULL) *pResult = lResult; return lResult; } return __super::OnWndMsg(message, wParam, lParam, pResult); } void CPlayerViewD2D::OnDropFiles(HDROP hDropInfo) { GetDocument()->OnDropFiles(hDropInfo); CView::OnDropFiles(hDropInfo); } ================================================ FILE: Player/PlayerViewD2D.h ================================================ #pragma once #include struct IFrameListener; struct ID2D1Effect; class CPlayerViewD2D : public CView { friend class FrameListenerD2D; protected: // create from serialization only CPlayerViewD2D(); DECLARE_DYNCREATE(CPlayerViewD2D) // Attributes public: CPlayerDoc* GetDocument() const; // Operations public: // Overrides public: virtual void OnDraw(CDC* pDC); // overridden to draw this view virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) override; // Implementation public: virtual ~CPlayerViewD2D(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: private: // Generated message map functions protected: DECLARE_MESSAGE_MAP() afx_msg LRESULT OnDraw2D(WPARAM wParam, LPARAM lParam); afx_msg LRESULT DrawFrame(WPARAM wParam, LPARAM lParam); public: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); protected: void updateFrame(); private: std::unique_ptr m_frameListener; CSize m_sourceSize; float m_aspectRatio; CComPtr m_spEffect; CCriticalSection m_csSurface; public: afx_msg void OnDropFiles(HDROP hDropInfo); afx_msg void OnEditPaste(); }; ================================================ FILE: Player/ReadMe.txt ================================================ ================================================================================ MICROSOFT FOUNDATION CLASS LIBRARY : Player Project Overview =============================================================================== The application wizard has created this Player application for you. This application not only demonstrates the basics of using the Microsoft Foundation Classes but is also a starting point for writing your application. This file contains a summary of what you will find in each of the files that make up your Player application. Player.vcxproj This is the main project file for VC++ projects generated using an application wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations, and project features selected with the application wizard. Player.vcxproj.filters This is the filters file for VC++ projects generated using an Application Wizard. It contains information about the association between the files in your project and the filters. This association is used in the IDE to show grouping of files with similar extensions under a specific node (for e.g. ".cpp" files are associated with the "Source Files" filter). Player.h This is the main header file for the application. It includes other project specific headers (including Resource.h) and declares the CPlayerApp application class. Player.cpp This is the main application source file that contains the application class CPlayerApp. Player.rc This is a listing of all of the Microsoft Windows resources that the program uses. It includes the icons, bitmaps, and cursors that are stored in the RES subdirectory. This file can be directly edited in Microsoft Visual C++. Your project resources are in 1033. res\Player.ico This is an icon file, which is used as the application's icon. This icon is included by the main resource file Player.rc. res\Player.rc2 This file contains resources that are not edited by Microsoft Visual C++. You should place all resources not editable by the resource editor in this file. ///////////////////////////////////////////////////////////////////////////// For the main frame window: The project includes a standard MFC interface. MainFrm.h, MainFrm.cpp These files contain the frame class CMainFrame, which is derived from CFrameWnd and controls all SDI frame features. res\Toolbar.bmp This bitmap file is used to create tiled images for the toolbar. The initial toolbar and status bar are constructed in the CMainFrame class. Edit this toolbar bitmap using the resource editor, and update the IDR_MAINFRAME TOOLBAR array in Player.rc to add toolbar buttons. ///////////////////////////////////////////////////////////////////////////// The application wizard creates one document type and one view: PlayerDoc.h, PlayerDoc.cpp - the document These files contain your CPlayerDoc class. Edit these files to add your special document data and to implement file saving and loading (via CPlayerDoc::Serialize). PlayerView.h, PlayerView.cpp - the view of the document These files contain your CPlayerView class. CPlayerView objects are used to view CPlayerDoc objects. ///////////////////////////////////////////////////////////////////////////// Other Features: Printing and Print Preview support The application wizard has generated code to handle the print, print setup, and print preview commands by calling member functions in the CView class from the MFC library. ///////////////////////////////////////////////////////////////////////////// Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named Player.pch and a precompiled types file named StdAfx.obj. Resource.h This is the standard header file, which defines new resource IDs. Microsoft Visual C++ reads and updates this file. Player.manifest Application manifest files are used by Windows XP to describe an applications dependency on specific versions of Side-by-Side assemblies. The loader uses this information to load the appropriate assembly from the assembly cache or private from the application. The Application manifest maybe included for redistribution as an external .manifest file that is installed in the same folder as the application executable or it may be included in the executable in the form of a resource. ///////////////////////////////////////////////////////////////////////////// Other notes: The application wizard uses "TODO:" to indicate parts of the source code you should add to or customize. If your application uses MFC in a shared DLL, you will need to redistribute the MFC DLLs. If your application is in a language other than the operating system's locale, you will also have to redistribute the corresponding localized resources mfc110XXX.DLL. For more information on both of these topics, please see the section on redistributing Visual C++ applications in MSDN documentation. ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: Player/SecondsToString.h ================================================ #pragma once #include #include #include inline std::basic_string secondsToString(int seconds, bool milli) { using std::setfill; using std::setw; std::basic_ostringstream buffer; if (seconds < (milli? 0 : -999)) { buffer << '-'; seconds = -seconds; } int ms = seconds % 1000; seconds /= 1000; // towards zero int s = seconds % 60; int m = (seconds / 60) % 60; int h = seconds / 3600; if (h > 0) { buffer << h << ':'; } buffer << setfill(_T('0')) << setw(2) << m << ':' << setfill(_T('0')) << setw(2) << s; if (milli) { buffer << '.' << setfill(_T('0')) << setw(3) << ms; } return buffer.str(); } ================================================ FILE: Player/StringDifference.cpp ================================================ #include "stdafx.h" #include "StringDifference.h" #include static auto SafePathString(std::basic_string path) { enum { MAX_DIFF_SIZE = 2048 }; path.append(MAX_DIFF_SIZE, _T('\0')); return path; } template T reversed(const T& s) { return {s.rbegin(), s.rend()}; } template int levenshteinDistance(const T& s1, const T& s2) { const int len1 = s1.size() + 1; const int len2 = s2.size() + 1; std::vector dp(len1 * len2); for (int i = 0; i < len1; ++i) { dp[i * len2] = i; } for (int j = 0; j < len2; ++j) { dp[j] = j; } for (int i = 1; i < len1; ++i) { for (int j = 1; j < len2; ++j) { const int cost = (s1[i - 1] != s2[j - 1]); dp[i * len2 + j] = (std::min)({dp[(i - 1) * len2 + j] + 1, dp[i * len2 + j - 1] + 1, dp[(i - 1) * len2 + j - 1] + cost}); } } return dp.back(); } StringDifference::StringDifference(const Sequence& a, const Sequence& b) : m_diff(a, b), m_reversedDiff(reversed(a), reversed(b)), m_path_b(b), m_sameNames(m_path_b.has_parent_path() && m_path_b.has_stem() && m_path_b.stem() == std::filesystem::path(a).stem()) { if (!m_sameNames) { m_diff.compose(); m_reversedDiff.compose(); } } StringDifference::Sequence StringDifference::patch(const Sequence& seq) const { if (m_sameNames) { return (m_path_b.parent_path() / std::filesystem::path(seq).stem()) += m_path_b.extension(); } Sequence s = m_reversedDiff.patch(SafePathString(reversed(seq))); std::reverse(s.begin(), s.begin() + _tcslen(s.c_str())); if (s.empty() || s[0] == 0 || 0 != _taccess(s.c_str(), 04)) { s = m_diff.patch(SafePathString(seq)); if (s.empty() || s[0] == 0 || 0 != _taccess(s.c_str(), 04)) { auto closest = LastResort(seq); if (!closest.empty()) return closest; } } s.resize(_tcslen(s.c_str())); return s; } StringDifference::Sequence StringDifference::LastResort( const StringDifference::Sequence& seq) const { try { const auto extension = m_path_b.extension(); const Sequence name = std::filesystem::path(seq).stem(); int minDistance = INT_MAX; std::filesystem::path result_name; for (auto const& dir_entry : std::filesystem::directory_iterator{m_path_b.parent_path()}) { if (dir_entry.is_directory()) continue; auto path = dir_entry.path(); if (_tcsicmp(path.extension().c_str(), extension.c_str()) != 0) continue; const auto stem = path.stem(); const int dist = levenshteinDistance(name, static_cast(stem)); if (dist < minDistance) { minDistance = dist; result_name = stem; } else if (dist == minDistance) { result_name.clear(); } } if (!result_name.empty()) { auto result = (m_path_b.parent_path() / result_name) += extension; if (equivalent(m_path_b, result)) return {}; return result; } return {}; } catch (const std::exception& ex) { TRACE("Exception in StringDifference: %s: %s\n", typeid(ex).name(), ex.what()); return {}; } } ================================================ FILE: Player/StringDifference.h ================================================ #pragma once // vcpkg install dtl #include #include class StringDifference { typedef std::basic_string Sequence; dtl::Diff> m_diff, m_reversedDiff; std::filesystem::path m_path_b; bool m_sameNames; Sequence LastResort(const StringDifference::Sequence& seq) const; public: StringDifference(const Sequence& a, const Sequence& b); Sequence patch(const Sequence& seq) const; }; ================================================ FILE: Player/YouTuber.cpp ================================================ #include "stdafx.h" #include "YouTuber.h" #define YOUTUBE_EXPERIMENT #ifdef YOUTUBE_EXPERIMENT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "http_get.h" #include "MemoryMappedFile.h" namespace { // Parses the value of the active python exception std::string parse_python_exception() { namespace py = boost::python; PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL; PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr); std::string ret("Unfetchable Python error"); if(type_ptr != NULL){ py::handle<> h_type(type_ptr); py::str type_pstr(h_type); py::extract e_type_pstr(type_pstr); if(e_type_pstr.check()) ret = e_type_pstr(); else ret = "Unknown exception type"; } if(value_ptr != NULL){ py::handle<> h_val(value_ptr); py::str a(h_val); py::extract returned(a); try { if (returned.check()) ret += ": " + returned(); else ret += ": Unparseable Python error: "; } catch (const py::error_already_set&) { ret += ": Unparseable Python error: "; } } if(traceback_ptr != NULL){ py::handle<> h_tb(traceback_ptr); py::object tb(py::import("traceback")); py::object fmt_tb(tb.attr("format_tb")); py::object tb_list(fmt_tb(h_tb)); py::object tb_str(py::str("\n").join(tb_list)); py::extract returned(tb_str); if(returned.check()) ret += ": " + returned(); else ret += ": Unparseable Python traceback"; } return ret; } class LoggerStream { public: void write(const std::string& what) { using namespace boost::log; sources::channel_logger_mt<> logger(keywords::channel = "python"); BOOST_LOG(logger) << what; } void flush() {} }; const char COMBINED_TEMPLATE[] = R"(import sys, socket, re sys.stderr = LoggerStream() def install_and_import(package, url=None): import importlib if url is None: url = package try: importlib.import_module(package) except ImportError: import subprocess import os library_dir = os.path.dirname(os.path.abspath(socket.__file__)) subprocess.run([library_dir + "/../scripts/pip3", "install", url]) finally: globals()[package] = importlib.import_module(package) # ---- yt-dlp based getYoutubeUrl ---- install_and_import("yt_dlp", "https://github.com/yt-dlp/yt-dlp/archive/refs/heads/master.zip") from urllib.parse import urlencode, urlparse, parse_qs def parsePlaylist(url: str, force: bool): # 1. Detect search query (no dots or slashes) if all(ch not in url for ch in "./"): parts = url.split() query = "+".join(urlencode({"q": p})[2:] for p in parts) search_url = f"ytsearch50:{query}" return _extract_flat_urls(search_url) # 2. URL case parsed = urlparse(url) qs = parse_qs(parsed.query) is_list = "list" in qs is_channel = _is_channel_url(url) # Playlist or channel -> treat as list if is_list or is_channel: return _extract_flat_urls(url) # 3. Single video page if force: return _extract_from_video_page(url) return [] def _is_channel_url(url: str): """Detects YouTube channel URLs but does NOT treat them as results.""" return any(part in url for part in [ "/channel/", "/user/", "/c/", "/@" ]) def _extract_flat_urls(yt_url: str): ydl_opts = { "quiet": True, "skip_download": True, "extract_flat": True, } with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(yt_url, download=False) entries = info.get("entries", []) urls = [] for e in entries: if "url" in e: # Filter out channel URLs if not _is_channel_url(e["url"]): urls.append(e["url"]) return urls def _extract_from_video_page(video_url: str): ydl_opts = { "quiet": True, "skip_download": True, "extract_flat": False, } with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(video_url, download=False) urls = [] # The video itself if "webpage_url" in info: urls.append(info["webpage_url"]) # Playlist URLs (if video is inside a playlist) for pl in info.get("playlists", []): if "id" in pl: urls.append(f"https://www.youtube.com/playlist?list={pl['id']}") # Try to approximate "related videos" using search title = info.get("title") if title: search_query = f"ytsearch10:{title}" with yt_dlp.YoutubeDL({"quiet": True, "extract_flat": True}) as ydl: s = ydl.extract_info(search_query, download=False) for e in s.get("entries", []): if "url" in e and not _is_channel_url(e["url"]): urls.append(e["url"]) # Deduplicate urls = list(dict.fromkeys(urls)) return urls def _iter_formats(info): if not info: return [] if isinstance(info, dict) and 'entries' in info and info['entries']: for e in info['entries']: if e: info = e break if isinstance(info, dict) and 'requested_formats' in info and info['requested_formats']: return info['requested_formats'] if isinstance(info, dict) and 'formats' in info and info['formats']: return info['formats'] if isinstance(info, dict) and 'url' in info: return [info] return [] def _is_usable_video_format(f): vcodec = f.get('vcodec') proto = f.get('protocol', '') if not vcodec or vcodec == 'none': return False if vcodec.startswith('av01'): return False if proto == 'm3u8_native' or proto == 'm3u8': return False return True def _is_usable_audio_format(f): vcodec = f.get('vcodec', 'none') proto = f.get('protocol', '') if vcodec != 'none': return False if proto == 'm3u8_native' or proto == 'm3u8': return False return 'audio_channels' in f and f.get('audio_channels') is not None or 'abr' in f def getYoutubeUrl(url, adaptive): socket.setdefaulttimeout(10) base_opts = { 'noplaylist': True, 'quiet': True, 'skip_download': True, } ydl_opts = base_opts.copy() ydl_opts['format'] = 'bestvideo+bestaudio' if adaptive else 'best' with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=False) formats = _iter_formats(info) if adaptive: video_candidates = [f for f in formats if _is_usable_video_format(f)] if video_candidates: best_video = max(video_candidates, key=lambda f: (f.get('height') or 0, f.get('tbr') or 0)) video_url = best_video.get('url') else: video_url = info.get('url') audio_candidates = [f for f in formats if _is_usable_audio_format(f)] if audio_candidates: best_audio = max(audio_candidates, key=lambda f: (f.get('audio_channels') or 0, f.get('abr') or 0)) audio_url = best_audio.get('url') else: any_audio = next((f for f in formats if f.get('vcodec') == 'none' and f.get('url')), None) audio_url = any_audio.get('url') if any_audio else None return (video_url, audio_url) combined_candidates = [f for f in formats if f.get('vcodec') != 'none' and f.get('audio_channels') is not None and not f.get('vcodec','').startswith('av01') and f.get('protocol') not in ('m3u8_native', 'm3u8')] if not combined_candidates: combined_candidates = [f for f in formats if 'url' in f] best_combined = max(combined_candidates, key=lambda f: (f.get('height') or 0, f.get('tbr') or 0, f.get('abr') or 0)) return best_combined.get('url') # ---- youtube_transcript_api based getYoutubeTranscript ---- install_and_import("youtube_transcript_api", "https://github.com/jdepoix/youtube-transcript-api/archive/master.zip") _YT_ID_RE = re.compile( r'(?:v=|\/v\/|youtu\.be\/|\/embed\/|\/shorts\/|watch\?.*v=)([A-Za-z0-9_-]{11})' ) def extract_youtube_id(url_or_id: str) -> str: if not isinstance(url_or_id, str): raise ValueError("video id or url must be a string") url_or_id = url_or_id.strip() if len(url_or_id) == 11 and re.fullmatch(r'[A-Za-z0-9_-]{11}', url_or_id): return url_or_id m = _YT_ID_RE.search(url_or_id) if m: return m.group(1) q = re.search(r'[?&]v=([A-Za-z0-9_-]{11})', url_or_id) if q: return q.group(1) raise ValueError("Could not extract YouTube video id from input") def getYoutubeTranscript(url_or_id: str): video_id = extract_youtube_id(url_or_id) return youtube_transcript_api.YouTubeTranscriptApi().fetch(video_id).to_raw_data() )"; int from_hex(char ch) { return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; } std::string UrlUnescapeString(const std::string& s) { std::istringstream ss(s); std::string result; std::getline(ss, result, '%'); std::string buffer; while (std::getline(ss, buffer, '%')) { if (buffer.size() >= 2) { result += char((from_hex(buffer[0]) << 4) | from_hex(buffer[1])) + buffer.substr(2); } } return result; } void hexchar(unsigned char c, unsigned char &hex1, unsigned char &hex2) { hex1 = c / 16; hex2 = c % 16; hex1 += hex1 <= 9 ? '0' : 'a' - 10; hex2 += hex2 <= 9 ? '0' : 'a' - 10; } std::string urlencode(const std::string& s) { std::string v; for (char c : s) { if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || c == ')') { v.push_back(c); } else if (c == ' ') { v.push_back('+'); } else { v.push_back('%'); unsigned char d1, d2; hexchar(c, d1, d2); v.push_back(d1); v.push_back(d2); } } return v; } // Extracts a single URL from s. Accepts URLs with http or https schemes, // or scheme-less URLs that start with a valid host (e.g., "example.com" or "www.example.com"). // Rejects inputs that include a non-http(s) scheme (e.g., "ftp://", "htp://"). // On success sets s to the matched URL (including path, query and fragment) and returns true. // The match is conservative: it stops at whitespace and common delimiters < > " ' ( ). // If the input may be percent-escaped, keep UrlUnescapeString available; we try both raw and unescaped. bool extractHttpOrHostUrl(std::string& s) { // scheme: optional, but if present must be http or https // host: domain (labels + tld), localhost or IPv4 // port: optional // path+query+fragment: optional, but must preserve ? and # if present static const std::regex url_regex( R"((?:https?:\/\/)?(?:(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,}|localhost|\d{1,3}(?:\.\d{1,3}){3})(?::\d{1,5})?(?:\/[^\s\"\'<>()]*)?)", std::regex::icase ); std::string copy = s; for (int unescaped = 0; unescaped < 2; ++unescaped) { std::smatch m; if (std::regex_search(copy, m, url_regex)) { // keep the full matched length so query params and fragments remain s = copy.substr(m.position(), m.length()); // If caller expects the URL to start at the match position and include the rest // of the original string, change to: s = copy.substr(m.position()); return true; } if (!unescaped) { // If you have a UrlUnescapeString implementation, use it here to try unescaped input. // If not needed, remove this block or leave it as a no-op. copy = UrlUnescapeString(copy); } } return false; } std::string loadScriptText(const TCHAR* name) { TCHAR strPath[MAX_PATH]{}; SHGetSpecialFolderPath( nullptr, strPath, CSIDL_LOCAL_APPDATA, FALSE); PathAppend(strPath, name); std::ifstream file(strPath, std::ios::in | std::ios::binary); if (!file) return {}; std::ostringstream contents; contents << file.rdbuf(); BOOST_LOG_TRIVIAL(trace) << "loadScriptText() script loaded: \"" << name << "\""; return contents.str(); } auto getLoggerStream() { return boost::python::class_("LoggerStream") .def("write", &LoggerStream::write) .def("flush", &LoggerStream::flush); } // Execute second application instance with -check_python flag // and return true if exit code is 0 bool isPythonInstalled() { STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; TCHAR szCmdline[MAX_PATH + 20] = _T("\""); auto len = GetModuleFileName(NULL, szCmdline + 1, ARRAYSIZE(szCmdline) - 1); _tcscpy_s(szCmdline + len + 1, ARRAYSIZE(szCmdline) - len - 1, _T("\" -check_python")); if (!CreateProcess(NULL, szCmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { return false; } DWORD exitCode; bool ok = WaitForSingleObject(pi.hProcess, 5000) == WAIT_OBJECT_0 && GetExitCodeProcess(pi.hProcess, &exitCode) && exitCode == 0; CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return ok; } // Shared Python namespace loader/holder bool EnsureSharedPythonNamespaceLoaded(boost::python::object& outNamespace) { using namespace boost::python; static bool s_loaded = false; static bool s_failed = false; static object s_namespace; if (s_failed) return false; if (s_loaded) { outNamespace = s_namespace; return true; } s_failed = true; if (!isPythonInstalled()) { AfxMessageBox(_T("Matching Python is not installed: ") _T(PY_VERSION) #ifdef _WIN64 _T(" (64 bits)") #else _T(" (32 bits)") #endif ); return false; } Py_Initialize(); if (!Py_IsInitialized()) { BOOST_LOG_TRIVIAL(error) << "Py_Initialize failed"; return false; } // Add atexit finalize guard (optional) atexit(Py_Finalize); try { PyObject* sysPath = PySys_GetObject("path"); object path(borrowed(sysPath)); const auto length = len(path); std::vector sysPathList; for (int i = 0; i < length; ++i) { std::string v{extract(path[i])}; v += "/site-packages"; if (_taccess(CA2T(v.c_str(), CP_UTF8), 0) == 0) { sysPathList.push_back(std::move(v)); } } for (const auto& v : sysPathList) { PyList_Insert(sysPath, 0, PyUnicode_FromString(v.c_str())); } // Load script text from local override if present; otherwise use combined template const auto localScript = loadScriptText(_T("getYoutubeCombined.py")); const char* scriptToExec = localScript.empty() ? COMBINED_TEMPLATE : localScript.c_str(); object main = import("__main__"); object global(main.attr("__dict__")); // inject LoggerStream class to python global["LoggerStream"] = getLoggerStream(); exec(scriptToExec, global, global); s_namespace = global; s_loaded = true; s_failed = false; outNamespace = s_namespace; return true; } catch (const error_already_set&) { BOOST_LOG_TRIVIAL(error) << "Shared python bootstrap error \"" << parse_python_exception() << "\""; PyErr_Clear(); return false; } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "Shared python bootstrap exception \"" << ex.what() << "\""; return false; } } class YouTubePlaylistDealer { public: YouTubePlaylistDealer(); ~YouTubePlaylistDealer() = default; bool isValid() const { return !!m_func; } bool getPlaylist(const std::string& url, bool force, std::vector& out); private: boost::python::object m_func; }; YouTubePlaylistDealer::YouTubePlaylistDealer() { using namespace boost::python; try { object ns; if (!EnsureSharedPythonNamespaceLoaded(ns)) return; try { m_func = ns["parsePlaylist"]; } catch (const error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubePlaylistDealer: parsePlaylist not found in shared python namespace: " << parse_python_exception(); PyErr_Clear(); } } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "YouTubePlaylistDealer bootstrap exception \"" << ex.what() << "\""; } catch (const boost::python::error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubePlaylistDealer bootstrap python error \"" << parse_python_exception() << "\""; PyErr_Clear(); } } bool YouTubePlaylistDealer::getPlaylist(const std::string& url, bool force, std::vector& out) { BOOST_LOG_TRIVIAL(trace) << "YouTubePlaylistDealer::getPlaylist() url = \"" << url << "\" force = " << force; using namespace boost::python; if (!isValid()) return false; try { object v = m_func(url, force); if (v.is_none()) return false; const auto length = len(v); out.reserve(length); for (int i = 0; i < length; ++i) { object el = v[i]; std::string s = extract(el); out.push_back(std::move(s)); } return true; } catch (const error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubePlaylistDealer python error \"" << parse_python_exception() << "\""; PyErr_Clear(); } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "YouTubePlaylistDealer exception \"" << ex.what() << "\""; } return false; } class YouTubeDealer { public: YouTubeDealer(); ~YouTubeDealer() = default; bool isValid() const { return !!m_func; } std::vector getYoutubeUrl(const std::string& url, bool adaptive); private: boost::python::object m_func; }; YouTubeDealer::YouTubeDealer() { using namespace boost::python; try { object ns; if (!EnsureSharedPythonNamespaceLoaded(ns)) return; // Attempt to get callable 'getYoutubeUrl' from shared namespace try { m_func = ns["getYoutubeUrl"]; } catch (const error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubeDealer: getYoutubeUrl not found in shared python namespace: " << parse_python_exception(); PyErr_Clear(); } } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "YouTubeDealer bootstrap exception \"" << ex.what() << "\""; } catch (const boost::python::error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubeDealer bootstrap python error \"" << parse_python_exception() << "\""; PyErr_Clear(); } } std::vector YouTubeDealer::getYoutubeUrl(const std::string& url, bool adaptive) { BOOST_LOG_TRIVIAL(trace) << "YouTubeDealer::getYoutubeUrl() url = \"" << url << "\""; using namespace boost::python; if (!isValid()) return {}; try { object py_result = m_func(url, adaptive); if (py_result.is_none()) return {}; extract as_str(py_result); if (as_str.check()) return { as_str() }; std::vector result; for (stl_input_iterator it(py_result), end; it != end; ++it) { extract elem_str(*it); if (elem_str.check()) { result.push_back(elem_str()); } else { result.push_back(extract(str(*it))()); } } return result; } catch (const error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubeDealer python error \"" << parse_python_exception() << "\""; PyErr_Clear(); } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "YouTubeDealer exception \"" << ex.what() << "\""; } return {}; } class YouTubeTranscriptDealer { public: YouTubeTranscriptDealer(); ~YouTubeTranscriptDealer() = default; bool isValid() const { return !!m_func; } bool getYoutubeTranscripts(const std::string& id, AddYoutubeTranscriptCallback cb); private: boost::python::object m_func; }; YouTubeTranscriptDealer::YouTubeTranscriptDealer() { using namespace boost::python; try { object ns; if (!EnsureSharedPythonNamespaceLoaded(ns)) return; try { m_func = ns["getYoutubeTranscript"]; } catch (const error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubeTranscriptDealer: getYoutubeTranscript not found in shared python namespace: " << parse_python_exception(); PyErr_Clear(); } } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "YouTubeTranscriptDealer bootstrap exception \"" << ex.what() << "\""; } catch (const boost::python::error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubeTranscriptDealer bootstrap python error \"" << parse_python_exception() << "\""; PyErr_Clear(); } } bool YouTubeTranscriptDealer::getYoutubeTranscripts(const std::string& id, AddYoutubeTranscriptCallback cb) { BOOST_LOG_TRIVIAL(trace) << "YouTubeTranscriptDealer::getYoutubeTranscripts() id = \"" << id << "\""; using namespace boost::python; if (!isValid()) return false; try { object v = m_func(id); if (v.is_none()) return false; const auto length = len(v); for (int i = 0; i < length; ++i) { object el = v[i]; std::string text = extract(el["text"]); boost::algorithm::trim_right(text); if (!text.empty()) { double start = extract(el["start"]); double duration = extract(el["duration"]); cb(start, duration, text); } } return true; } catch (const error_already_set&) { BOOST_LOG_TRIVIAL(error) << "YouTubeTranscriptDealer python error \"" << parse_python_exception() << "\""; PyErr_Clear(); } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "YouTubeTranscriptDealer exception \"" << ex.what() << "\""; } return false; } std::vector DoParsePlaylist( const char* const pDataBegin, const char* const pDataEnd, bool includeLists = true) { std::vector result; auto doSearch = [pDataBegin, pDataEnd, &result](const auto& watch, const char* prefix) { enum { WATCH_SIZE = sizeof(watch) / sizeof(watch[0]) - 1 }; auto pData = pDataBegin; while ((pData = std::search(pData, pDataEnd, std::begin(watch), std::prev(std::end(watch)))) != pDataEnd) { const auto localEnd = std::find_if(pData + WATCH_SIZE, pDataEnd, [](char ch) { return ch == '&' || ch == '"' || ch == '\'' || ch == '\\' || std::isspace(static_cast(ch)); }); auto el = prefix + std::string(pData, localEnd); if (std::find(result.begin(), result.end(), el) == result.end()) result.push_back(std::move(el)); pData += WATCH_SIZE; } }; doSearch("/watch?v=", "https://www.youtube.com"); doSearch("youtu.be/", "https://"); if (includeLists) { doSearch("/playlist?list=", "https://www.youtube.com"); } return result; } } // namespace void CheckPython() { Py_Initialize(); if (!Py_IsInitialized()) { exit(1); } atexit(Py_Finalize); if (!_PyThreadState_UncheckedGet()) { exit(1); } PyObject* sysPath = PySys_GetObject("path"); if (!sysPath) { exit(1); } try { boost::python::object path(boost::python::borrowed(sysPath)); const auto length = len(path); if (length < 1) { exit(1); } std::string v{ boost::python::extract(path[0]) }; if (v.empty()) { exit(1); } } catch (...) { exit(1); } } std::vector ParsePlaylist(std::string url, bool force) { std::vector result; if (!url.empty()) { CWaitCursor wait; static YouTubePlaylistDealer dealer; if (dealer.isValid()) dealer.getPlaylist(url, force, result); } return result; } std::vector ParsePlaylistFile(const TCHAR* fileName) { MemoryMappedFile memoryMappedFile; if (!memoryMappedFile.MapFlie(fileName)) return{}; auto* const pData = static_cast(memoryMappedFile.data()); return DoParsePlaylist(pData, pData + memoryMappedFile.size()); } std::vector ParsePlaylistText(const std::string& text) { if (text.empty()) { return{}; } auto* const pData = text.data(); return DoParsePlaylist(pData, pData + text.size()); } inline bool isHttpStatusOk(int status) { const bool result = status == 200 || status == 302; if (!result) BOOST_LOG_TRIVIAL(trace) << "HTTP error status: " << status; return result; } std::pair getYoutubeUrl(std::string url, bool adaptive, bool useHHO) { enum { ATTEMPTS_NUMBER = 2 }; if (extractHttpOrHostUrl(url)) { static std::map> mapToDownloadLinks[2]; auto it = mapToDownloadLinks[adaptive].find(url); if (it != mapToDownloadLinks[adaptive].end()) { if (isHttpStatusOk(HttpGetStatus(it->second.first, useHHO)) && (it->second.second.empty() || isHttpStatusOk(HttpGetStatus(it->second.second, useHHO)))) return it->second; else mapToDownloadLinks[adaptive].erase(it); } CWaitCursor wait; static YouTubeDealer buddy; if (buddy.isValid()) { for (int j = 0; j < ATTEMPTS_NUMBER; ++j) { auto urls = buddy.getYoutubeUrl(url, adaptive); if (!urls.empty()) { const auto status = HttpGetStatus(urls[0], useHHO); BOOST_LOG_TRIVIAL(trace) << "Resource status: " << status; if (isHttpStatusOk(status)) { if (urls.size() > 1) { for (int i = 1; i < urls.size(); ++i) { const auto status = HttpGetStatus(urls[i], useHHO); BOOST_LOG_TRIVIAL(trace) << "Resource status: " << status; if (isHttpStatusOk(status)) { return mapToDownloadLinks[adaptive][url] = { urls[i], urls[0] }; } } } else { return mapToDownloadLinks[adaptive][url] = { urls[0],{} }; } } Sleep(50); } } } return{}; } return{ url, {} }; } bool getYoutubeTranscripts(std::string url, AddYoutubeTranscriptCallback cb) { if (extractHttpOrHostUrl(url)) { CWaitCursor wait; static YouTubeTranscriptDealer buddy; if (buddy.isValid()) return buddy.getYoutubeTranscripts(url, cb); } return false; } #else // YOUTUBE_EXPERIMENT void CheckPython() {} std::vector ParsePlaylist(std::string, bool) { return{}; } std::vector ParsePlaylistFile(const TCHAR*) { return{}; } std::vector ParsePlaylistText(const std::string&) { return{}; } std::pair getYoutubeUrl(std::string url, bool /*adaptive*/, bool /*useHHO*/) { return{ url, {} }; } bool getYoutubeTranscripts(std::string, AddYoutubeTranscriptCallback) { return false; } #endif // YOUTUBE_EXPERIMENT ================================================ FILE: Player/YouTuber.h ================================================ #pragma once #include #include #include void CheckPython(); std::vector ParsePlaylist(std::string url, bool force); std::vector ParsePlaylistFile(const TCHAR* fileName); std::vector ParsePlaylistText(const std::string& text); std::pair getYoutubeUrl(std::string url, bool adaptive, bool useHHO); // start, duration, text typedef std::function AddYoutubeTranscriptCallback; bool getYoutubeTranscripts(std::string url, AddYoutubeTranscriptCallback cb); ================================================ FILE: Player/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by Player.rc // #define IDD_ABOUTBOX 100 #define IDD_DIALOGBAR_PLAYER_CONTROL 103 #define ID_VIEW_CUSTOMIZE 126 #define IDR_MAINFRAME 128 #define IDS_TOOLBAR_CUSTOMIZE 129 #define IDR_PlayerTYPE 130 #define IDS_PlayerControl 130 #define IDS_PLAYER_CONTROL 130 #define IDS_RANGE 131 #define IDS_PAUSE 132 #define IDS_PLAY 133 #define IDI_PAUSE 311 #define IDI_PLAY 312 #define IDI_AUDIO 313 #define IDI_AUDIO_OFF 314 #define IDI_FULL_SCREEN 315 #define IDD_DIALOG_OPEN_URL 317 #define IDD_DIALOGBAR_RANGE 319 #define IDR_LAUNCH 320 #define IDD_DIALOG_VIDEO_FILTER 321 #define IDC_PROGRESS_SLIDER 1000 #define IDC_PLAY_PAUSE 1001 #define IDC_AUDIO_ON_OFF 1002 #define IDC_VOLUME_SLIDER 1003 #define IDC_CURRENT_TIME 1004 #define IDC_TOTAL_TIME 1005 #define IDC_EDIT_URL 1005 #define IDC_FULL_SCREEN 1006 #define IDC_FRAME_STEP 1007 #define IDC_START 1008 #define IDC_EDIT_START 1010 #define IDC_START_RESET 1011 #define IDC_END 1012 #define IDC_EDIT_END 1013 #define IDC_END_RESET 1014 #define IDC_PARSE 1016 #define IDC_VIDEO_PROPERTIES 1017 #define IDC_APP_NAME_VERSION 1018 #define IDC_EDIT_INPUT_FORMAT 1019 #define IDC_LOSSLESS_CUT 1020 #define IDC_VIDEO_FILTER 1021 #define IDC_ENABLE_VIDEO_FILTER 1022 #define ID_TRACK1_DUMMY 32771 #define ID_AUTOPLAY 32775 #define ID_LOOPING 32776 #define ID_VIDEO_SPEED1 32777 #define ID_VIDEO_SPEED2 32778 #define ID_VIDEO_SPEED3 32779 #define ID_VIDEO_SPEED4 32780 #define ID_VIDEO_SPEED5 32781 #define ID_VIDEO_SPEED6 32782 #define ID_VIDEO_SPEED7 32783 #define ID_NIGHTCORE 32784 #define ID_OPENSUBTITLESFILE 32785 #define ID_COPY_URL_TO_CLIPBOARD 32786 #define ID_MAXIMALRESOLUTION 32787 #define ID_HW_ACCELERATION 32788 #define ID_AUDIO_OPENSUBTITLES 32789 #define ID_FIRST_SUBTITLE_DUMMY 32790 #define ID_SUPER_RESOLUTION 32791 #define ID_ORIENTATION_MIRRORX 32792 #define ID_ORIENTATION_MIRRORY 32793 #define ID_ORIENTATION_UPEND 32794 #define ID_ORIENTATION_DO_NOTHING 32795 #define ID_ORIENTATION_RORATE_90 32796 #define ID_ORIENTATION_RORATE_180 32797 #define ID_ORIENTATION_RORATE_270 32798 #define ID_FIX_ENCODING 32799 #define ID_CONVERT_VIDEOS_INTO_COMPATIBLE_FORMAT 32800 #define ID_OPEN_AUDIO_FILE 32801 #define ID_USING_HHO 32802 #define ID_VIDEO_FILTER 32803 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 323 #define _APS_NEXT_COMMAND_VALUE 32804 #define _APS_NEXT_CONTROL_VALUE 1023 #define _APS_NEXT_SYMED_VALUE 317 #endif #endif ================================================ FILE: Player/stdafx.cpp ================================================ // stdafx.cpp : source file that includes just the standard includes // Player.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" ================================================ FILE: Player/stdafx.h ================================================ // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, // but are changed infrequently #pragma once #ifndef VC_EXTRALEAN #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers #endif #include "targetver.h" #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit // turns off MFC's hiding of some common and often safely ignored warning messages #define _AFX_ALL_WARNINGS #include // MFC core and standard components #include // MFC extensions #ifndef _AFX_NO_OLE_SUPPORT #include // MFC support for Internet Explorer 4 Common Controls #endif #ifndef _AFX_NO_AFXCMN_SUPPORT #include // MFC support for Windows Common Controls #endif // _AFX_NO_AFXCMN_SUPPORT #include // MFC support for ribbons and control bars #ifdef _UNICODE #if defined _M_IX86 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") #elif defined _M_X64 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") #else #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #endif #endif ================================================ FILE: Player/targetver.h ================================================ #pragma once // Including SDKDDKVer.h defines the highest available Windows platform. // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #include #define _WIN32_WINNT _WIN32_WINNT_WIN8 #define NTDDI_VERSION NTDDI_WIN8 #define WINVER _WIN32_WINNT_WIN8 #define _WIN32_IE _WIN32_IE_WIN8 #include ================================================ FILE: Player/update_version.cmd ================================================ %~dp0\version.h.tmp git --work-tree=%~dp0 rev-parse --short HEAD >> %~dp0\version.h.tmp if %ERRORLEVEL% neq 0 echo #define NO_GIT_COMMIT > %~dp0\version.h.tmp fc %~dp0\version.h.tmp %~dp0\version.h if %ERRORLEVEL% neq 0 copy /Y %~dp0\version.h.tmp %~dp0\version.h del %~dp0\version.h.tmp ================================================ FILE: Player.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.13.35913.81 d17.13 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Player", "Player\Player.vcxproj", "{C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}" ProjectSection(ProjectDependencies) = postProject {3013C140-DDFC-4BF4-9091-0C4131A0D2A6} = {3013C140-DDFC-4BF4-9091-0C4131A0D2A6} {3DE6C2D2-FDFC-4745-8282-981DF7561405} = {3DE6C2D2-FDFC-4745-8282-981DF7561405} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video", "video\video.vcxproj", "{3013C140-DDFC-4BF4-9091-0C4131A0D2A6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C9B5174D-7410-40E3-8962-76AD82A10E70}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "networking", "networking\networking.vcxproj", "{3DE6C2D2-FDFC-4745-8282-981DF7561405}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HttpDownload", "HttpDownload\HttpDownload.vcxproj", "{A4113679-4736-494B-B8D2-3C35B34E5491}" EndProject Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Setup", "Setup\Setup.vdproj", "{72C57644-86FB-4587-9EE5-092F360D146C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dlls", "Dlls\Dlls.csproj", "{BC1BC9F1-893D-4715-818A-F37F74EB5710}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio", "audio\audio.vcxproj", "{8B955995-B5EC-41F0-940A-48A6F17BCBB8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Anime4KCPPCore", "Anime4KCPPCore.vcxproj", "{632353E4-4856-38F9-9E74-ED41BD99D7E5}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToUTF8", "ToUTF8\ToUTF8.vcxproj", "{F082EF32-A1D0-48B9-9AF1-DC56178EDE92}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}.Debug|Win32.ActiveCfg = Debug|Win32 {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}.Debug|Win32.Build.0 = Debug|Win32 {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}.Debug|x64.ActiveCfg = Debug|x64 {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}.Debug|x64.Build.0 = Debug|x64 {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}.Release|Win32.ActiveCfg = Release|Win32 {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}.Release|Win32.Build.0 = Release|Win32 {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}.Release|x64.ActiveCfg = Release|x64 {C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}.Release|x64.Build.0 = Release|x64 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6}.Debug|Win32.ActiveCfg = Debug|Win32 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6}.Debug|Win32.Build.0 = Debug|Win32 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6}.Debug|x64.ActiveCfg = Debug|x64 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6}.Debug|x64.Build.0 = Debug|x64 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6}.Release|Win32.ActiveCfg = Release|Win32 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6}.Release|Win32.Build.0 = Release|Win32 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6}.Release|x64.ActiveCfg = Release|x64 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6}.Release|x64.Build.0 = Release|x64 {3DE6C2D2-FDFC-4745-8282-981DF7561405}.Debug|Win32.ActiveCfg = Debug|Win32 {3DE6C2D2-FDFC-4745-8282-981DF7561405}.Debug|Win32.Build.0 = Debug|Win32 {3DE6C2D2-FDFC-4745-8282-981DF7561405}.Debug|x64.ActiveCfg = Debug|x64 {3DE6C2D2-FDFC-4745-8282-981DF7561405}.Debug|x64.Build.0 = Debug|x64 {3DE6C2D2-FDFC-4745-8282-981DF7561405}.Release|Win32.ActiveCfg = Release|Win32 {3DE6C2D2-FDFC-4745-8282-981DF7561405}.Release|Win32.Build.0 = Release|Win32 {3DE6C2D2-FDFC-4745-8282-981DF7561405}.Release|x64.ActiveCfg = Release|x64 {3DE6C2D2-FDFC-4745-8282-981DF7561405}.Release|x64.Build.0 = Release|x64 {A4113679-4736-494B-B8D2-3C35B34E5491}.Debug|Win32.ActiveCfg = Debug|Win32 {A4113679-4736-494B-B8D2-3C35B34E5491}.Debug|Win32.Build.0 = Debug|Win32 {A4113679-4736-494B-B8D2-3C35B34E5491}.Debug|x64.ActiveCfg = Debug|x64 {A4113679-4736-494B-B8D2-3C35B34E5491}.Debug|x64.Build.0 = Debug|x64 {A4113679-4736-494B-B8D2-3C35B34E5491}.Release|Win32.ActiveCfg = Release|Win32 {A4113679-4736-494B-B8D2-3C35B34E5491}.Release|Win32.Build.0 = Release|Win32 {A4113679-4736-494B-B8D2-3C35B34E5491}.Release|x64.ActiveCfg = Release|x64 {A4113679-4736-494B-B8D2-3C35B34E5491}.Release|x64.Build.0 = Release|x64 {72C57644-86FB-4587-9EE5-092F360D146C}.Debug|Win32.ActiveCfg = Debug {72C57644-86FB-4587-9EE5-092F360D146C}.Debug|x64.ActiveCfg = Debug {72C57644-86FB-4587-9EE5-092F360D146C}.Release|Win32.ActiveCfg = Release {72C57644-86FB-4587-9EE5-092F360D146C}.Release|x64.ActiveCfg = Release {BC1BC9F1-893D-4715-818A-F37F74EB5710}.Debug|Win32.ActiveCfg = Debug|Any CPU {BC1BC9F1-893D-4715-818A-F37F74EB5710}.Debug|Win32.Build.0 = Debug|Any CPU {BC1BC9F1-893D-4715-818A-F37F74EB5710}.Debug|x64.ActiveCfg = Debug|Any CPU {BC1BC9F1-893D-4715-818A-F37F74EB5710}.Debug|x64.Build.0 = Debug|Any CPU {BC1BC9F1-893D-4715-818A-F37F74EB5710}.Release|Win32.ActiveCfg = Release|Any CPU {BC1BC9F1-893D-4715-818A-F37F74EB5710}.Release|Win32.Build.0 = Release|Any CPU {BC1BC9F1-893D-4715-818A-F37F74EB5710}.Release|x64.ActiveCfg = Release|Any CPU {BC1BC9F1-893D-4715-818A-F37F74EB5710}.Release|x64.Build.0 = Release|Any CPU {8B955995-B5EC-41F0-940A-48A6F17BCBB8}.Debug|Win32.ActiveCfg = Debug|Win32 {8B955995-B5EC-41F0-940A-48A6F17BCBB8}.Debug|Win32.Build.0 = Debug|Win32 {8B955995-B5EC-41F0-940A-48A6F17BCBB8}.Debug|x64.ActiveCfg = Debug|x64 {8B955995-B5EC-41F0-940A-48A6F17BCBB8}.Debug|x64.Build.0 = Debug|x64 {8B955995-B5EC-41F0-940A-48A6F17BCBB8}.Release|Win32.ActiveCfg = Release|Win32 {8B955995-B5EC-41F0-940A-48A6F17BCBB8}.Release|Win32.Build.0 = Release|Win32 {8B955995-B5EC-41F0-940A-48A6F17BCBB8}.Release|x64.ActiveCfg = Release|x64 {8B955995-B5EC-41F0-940A-48A6F17BCBB8}.Release|x64.Build.0 = Release|x64 {632353E4-4856-38F9-9E74-ED41BD99D7E5}.Debug|Win32.ActiveCfg = Debug|Win32 {632353E4-4856-38F9-9E74-ED41BD99D7E5}.Debug|Win32.Build.0 = Debug|Win32 {632353E4-4856-38F9-9E74-ED41BD99D7E5}.Debug|x64.ActiveCfg = Debug|x64 {632353E4-4856-38F9-9E74-ED41BD99D7E5}.Debug|x64.Build.0 = Debug|x64 {632353E4-4856-38F9-9E74-ED41BD99D7E5}.Release|Win32.ActiveCfg = Release|Win32 {632353E4-4856-38F9-9E74-ED41BD99D7E5}.Release|Win32.Build.0 = Release|Win32 {632353E4-4856-38F9-9E74-ED41BD99D7E5}.Release|x64.ActiveCfg = Release|x64 {632353E4-4856-38F9-9E74-ED41BD99D7E5}.Release|x64.Build.0 = Release|x64 {F082EF32-A1D0-48B9-9AF1-DC56178EDE92}.Debug|Win32.ActiveCfg = Debug|Win32 {F082EF32-A1D0-48B9-9AF1-DC56178EDE92}.Debug|Win32.Build.0 = Debug|Win32 {F082EF32-A1D0-48B9-9AF1-DC56178EDE92}.Debug|x64.ActiveCfg = Debug|x64 {F082EF32-A1D0-48B9-9AF1-DC56178EDE92}.Debug|x64.Build.0 = Debug|x64 {F082EF32-A1D0-48B9-9AF1-DC56178EDE92}.Release|Win32.ActiveCfg = Release|Win32 {F082EF32-A1D0-48B9-9AF1-DC56178EDE92}.Release|Win32.Build.0 = Release|Win32 {F082EF32-A1D0-48B9-9AF1-DC56178EDE92}.Release|x64.ActiveCfg = Release|x64 {F082EF32-A1D0-48B9-9AF1-DC56178EDE92}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {75699A84-B7AB-4C3D-86E3-5E2370B5D57B} EndGlobalSection EndGlobal ================================================ FILE: QtPlayer/.gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto ================================================ FILE: QtPlayer/.gitignore ================================================ # C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.dylib # Qt-es object_script.*.Release object_script.*.Debug *_plugin_import.cpp /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.qbs.user *.qbs.user.* *.moc moc_*.cpp moc_*.h qrc_*.cpp ui_*.h *.qmlc *.jsc Makefile* *build-* # Qt unit tests target_wrapper.* # QtCreator *.autosave # QtCreator Qml *.qmlproject.user *.qmlproject.user.* # QtCreator CMake CMakeLists.txt.user* CMake*Presets.json CMakeSettings.json ================================================ FILE: QtPlayer/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(QtPlayer LANGUAGES CXX) option(DEVELOPER_OPENGL "Enables opengl display" ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(MSVC) add_definitions(-D_WIN32_WINNT=0x0602) endif() if(DEVELOPER_OPENGL) add_definitions(-DDEVELOPER_OPENGL) endif(DEVELOPER_OPENGL) add_definitions(-DBOOST_LOG_DYN_LINK) # QtCreator supports the following variables for Android, which are identical to qmake Android variables. # Check http://doc.qt.io/qt-5/deployment-android.html for more information. # They need to be set before the find_package(Qt5 ...) call. #if(ANDROID) # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") # if (ANDROID_ABI STREQUAL "armeabi-v7a") # set(ANDROID_EXTRA_LIBS # ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so # ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so) # endif() #endif() find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED) find_package(Boost REQUIRED thread log) find_package(portaudio CONFIG) if(NOT PORTAUDIO_FOUND) find_path(PORTAUDIO_INCLUDE_DIR NAMES portaudio.h DOC "The PortAudio include directory" ) find_library(PORTAUDIO_LIBRARY NAMES portaudio DOC "The PortAudio library" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PortAudio REQUIRED_VARS PORTAUDIO_LIBRARY PORTAUDIO_INCLUDE_DIR ) if(PORTAUDIO_FOUND) set(PORTAUDIO_LIBRARIES ${PORTAUDIO_LIBRARY}) set(PORTAUDIO_INCLUDE_DIRS ${PORTAUDIO_INCLUDE_DIR}) endif() endif() find_path(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h) if (AVCODEC_INCLUDE_DIR) find_library(AVCODEC_LIBRARY avcodec) find_path(AVFORMAT_INCLUDE_DIR libavformat/avformat.h) find_library(AVFORMAT_LIBRARY avformat) find_path(AVUTIL_INCLUDE_DIR libavutil/avutil.h) find_library(AVUTIL_LIBRARY avutil) find_path(AVDEVICE_INCLUDE_DIR libavdevice/avdevice.h) find_library(AVDEVICE_LIBRARY avdevice) find_path(SWSCALE_INCLUDE_DIR libswscale/swscale.h) find_library(SWSCALE_LIBRARY swscale) find_path(SWRESAMPLE_INCLUDE_DIR libswresample/swresample.h) find_library(SWRESAMPLE_LIBRARY swresample) else() find_package(PkgConfig REQUIRED) pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET libavdevice libavfilter libavformat libavcodec libswresample libswscale libavutil ) endif() set(SOURCES customdockwidget.cpp customdockwidget.h ffmpegdecoder.cpp ffmpegdecoder.h main.cpp mainwindow.cpp mainwindow.h mainwindow.ui mousehoverbutton.cpp mousehoverbutton.h opengldisplay.cpp opengldisplay.h portaudioplayer.cpp portaudioplayer.h videocontrol.cpp videocontrol.h videocontrol.ui videodisplay.cpp videodisplay.h videoplayer.cpp videoplayer.h videoplayerwidget.cpp videoplayerwidget.h videoprogressbar.cpp videoprogressbar.h videowidget.cpp videowidget.h volumeprogressbar.cpp volumeprogressbar.h widgetdisplay.cpp widgetdisplay.h) if(WIN32) add_executable(QtPlayer WIN32 ${SOURCES} ${CMAKE_SOURCE_DIR}/resources/resources.qrc) elseif(APPLE) add_executable(QtPlayer MACOSX_BUNDLE ${SOURCES} ${CMAKE_SOURCE_DIR}/resources/resources.qrc) else() add_executable(QtPlayer ${SOURCES} ${CMAKE_SOURCE_DIR}/resources/resources.qrc) endif() file(GLOB VIDEO_SRCS ../video/*.cpp) add_library(video STATIC ${VIDEO_SRCS}) target_include_directories(video PRIVATE ../video) if (AVCODEC_INCLUDE_DIR) target_include_directories(video PRIVATE ${AVCODEC_INCLUDE_DIR} ${AVFORMAT_INCLUDE_DIR} ${AVUTIL_INCLUDE_DIR} ${AVDEVICE_INCLUDE_DIR} ${SWSCALE_INCLUDE_DIR} ${SWRESAMPLE_INCLUDE_DIR} ) endif() target_link_libraries(video ${Boost_LIBRARIES}) if (AVCODEC_INCLUDE_DIR) target_link_libraries(video ${AVCODEC_LIBRARY} ${AVFORMAT_LIBRARY} ${AVUTIL_LIBRARY} ${AVDEVICE_LIBRARY} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARY} ) else() target_link_libraries(video PkgConfig::LIBAV ) endif() #file(GLOB AUDIO_SRCS ../audio/*.cpp) #add_library(audio STATIC ${AUDIO_SRCS}) #target_include_directories(audio PRIVATE ../audio) #target_link_libraries(audio) target_include_directories(QtPlayer PRIVATE ${Boost_INCLUDE_DIRS} ${PORTAUDIO_INCLUDE_DIRS}) target_link_libraries(QtPlayer PRIVATE Qt${QT_VERSION_MAJOR}::Widgets video ${PORTAUDIO_LIBRARIES}) if(WIN32) target_link_libraries(QtPlayer PRIVATE ws2_32) endif() ================================================ FILE: QtPlayer/customdockwidget.cpp ================================================ #include "customdockwidget.h" #include "mainwindow.h" #include "videoplayerwidget.h" #include "videowidget.h" #include #include #include CustomDockWidget::CustomDockWidget(QWidget* widget) : QWidget(widget) { } void CustomDockWidget::setDisplayForFullscreen(VideoDisplay* display) { Q_ASSERT(display != nullptr); m_display = dynamic_cast(display); } void CustomDockWidget::closeEvent(QCloseEvent* event) { event->ignore(); } void CustomDockWidget::keyPressEvent(QKeyEvent* event) { // Player fullscreen in if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && (event->modifiers() & Qt::AltModifier)) { setVisibilityState(FullScreen); event->ignore(); } QWidget::keyPressEvent(event); } void CustomDockWidget::setVisibilityState(VisibilityState state) { if (state == m_state) { return; } switch (state) { case ShownDocked: { this->setVisible(true); } break; case FullScreen: { bool prevIsFullScreen = m_display->isFullScreen(); m_display->fullScreen(!prevIsFullScreen); if (m_display->isFullScreen() == prevIsFullScreen) { return; } // emit enterFullscreen(true); } break; } m_prevState = m_state; m_state = state; } void CustomDockWidget::onLeaveFullScreen() { if (m_display->isFullScreen()) { m_display->fullScreen(false); } setVisibilityState(m_prevState); } ================================================ FILE: QtPlayer/customdockwidget.h ================================================ #pragma once #include class VideoDisplay; class VideoWidget; class CustomDockWidget : public QWidget { Q_OBJECT public: CustomDockWidget(QWidget* widget = nullptr); void setDisplayForFullscreen(VideoDisplay* display); enum VisibilityState { ShownDocked = 0, FullScreen }; void setVisibilityState(VisibilityState state); VisibilityState currentState() const { return m_state; } VisibilityState previousState() const { return m_prevState; } protected: void closeEvent(QCloseEvent* event) override; void keyPressEvent(QKeyEvent* event) override; signals: bool enterFullscreen(bool f); public slots: void onLeaveFullScreen(); private: VisibilityState m_state{ShownDocked}; VisibilityState m_prevState{ShownDocked}; VideoWidget* m_display{}; }; ================================================ FILE: QtPlayer/ffmpegdecoder.cpp ================================================ #include "ffmpegdecoder.h" //#include "../audio/AudioPlayerWasapi.h" #include "portaudioplayer.h" #include "videodisplay.h" FFmpegDecoderWrapper::FFmpegDecoderWrapper() : m_frameDecoder( GetFrameDecoder(std::make_unique())) { m_frameDecoder->setDecoderListener(this); } FFmpegDecoderWrapper::~FFmpegDecoderWrapper() = default; void FFmpegDecoderWrapper::setFrameListener(VideoDisplay* listener) { m_frameDecoder->setFrameListener(listener); if (listener != nullptr) { listener->setDecoderObject(this); } } bool FFmpegDecoderWrapper::openFile(const QString& file) { return m_frameDecoder->openUrls({file.toStdString()}); } ================================================ FILE: QtPlayer/ffmpegdecoder.h ================================================ #pragma once #include "../video/decoderinterface.h" #include #include #include class VideoDisplay; class FFmpegDecoderWrapper : public QObject, public FrameDecoderListener { Q_OBJECT public: FFmpegDecoderWrapper(); ~FFmpegDecoderWrapper() override; FFmpegDecoderWrapper(const FFmpegDecoderWrapper&) = delete; FFmpegDecoderWrapper& operator=(const FFmpegDecoderWrapper&) = delete; void setFrameListener(VideoDisplay* listener); bool openFile(const QString& file); void play(bool isPaused = false) { m_frameDecoder->play(); } bool pauseResume() { return m_frameDecoder->pauseResume(); } void close(bool isBlocking = true) { m_frameDecoder->close(); } void setVolume(double volume) { m_frameDecoder->setVolume(volume); emit volumeChanged(volume); } bool seekByPercent(float percent) { return m_frameDecoder->seekByPercent(percent); } double volume() const { return m_frameDecoder->volume(); } bool isPlaying() const { return m_frameDecoder->isPlaying(); } void finishedDisplayingFrame(unsigned int generation) { m_frameDecoder->finishedDisplayingFrame(generation); } IFrameDecoder* getFrameDecoder() const { return m_frameDecoder.get(); } void playingFinished() override { emit onPlayingFinished(); } void changedFramePosition( long long start, long long frame, long long total) override { emit onChangedFramePosition(frame - start, total - start); } signals: void onPlayingFinished(); void onChangedFramePosition(qint64, qint64); void volumeChanged(double /*unused*/) override; private: std::unique_ptr m_frameDecoder; }; ================================================ FILE: QtPlayer/main.cpp ================================================ #include "mainwindow.h" #include #include #include #include #include #include #include #include #include static void init_logging() { namespace expr = boost::log::expressions; boost::log::add_common_attributes(); auto core = boost::log::core::get(); #ifdef Q_OS_WIN // Create the sink. The backend requires synchronization in the frontend. auto sink(boost::make_shared>()); #else auto sink = boost::make_shared>(); #endif sink->set_formatter(expr::stream //<< '[' << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%H:%M:%S.%f") << ']' << expr::if_(expr::has_attr("Severity")) [ expr::stream << '[' << expr::attr< boost::log::trivial::severity_level >("Severity") << ']' ] << expr::if_(expr::has_attr("Channel")) [ expr::stream << '[' << expr::attr< std::string >("Channel") << ']' ] << expr::smessage << '\n'); // Set the special filter to the frontend // in order to skip the sink when no debugger is available //sink->set_filter(expr::is_debugger_present()); core->add_sink(sink); } int main(int argc, char *argv[]) { init_logging(); QApplication a(argc, argv); MainWindow w; w.show(); return QApplication::exec(); } ================================================ FILE: QtPlayer/mainwindow.cpp ================================================ #include "mainwindow.h" #include "./ui_mainwindow.h" #include "videowidget.h" #include #include MainWindow* getMainWindow() { for (QWidget* widget : QApplication::topLevelWidgets()) { if (auto *mainWindow = qobject_cast(widget)) { return mainWindow; } } return nullptr; } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); auto m_player = ui->videoPlayerWidget; m_player->setProgressbar(ui->videoProgress); ui->dockWidget->installEventFilter(m_player); ui->dockWidget->setDisplayForFullscreen(m_player->getCurrentDisplay()); setCentralWidget(ui->dockWidget); connect(m_player->videoWidget(), &VideoWidget::leaveFullScreen, ui->dockWidget, &CustomDockWidget::onLeaveFullScreen); connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onFileOpen); connect(ui->actionOpenUrl, &QAction::triggered, this, &MainWindow::onUrlOpen); } MainWindow::~MainWindow() { delete ui; } CustomDockWidget* MainWindow::dockWidget() { return ui->dockWidget; } QWidget* MainWindow::videoControlWidget() { return ui->videoControl; } VideoPlayerWidget* MainWindow::getPlayer() { return ui->videoPlayerWidget; } void MainWindow::onFileOpen() { auto fileName = QFileDialog::getOpenFileName( this, tr("Open Video File")); if (!fileName.isEmpty()) { getPlayer()->playFile(fileName); } } void MainWindow::onUrlOpen() { QInputDialog dialog(this); dialog.setWindowTitle(tr("Open URL")); dialog.setLabelText(tr("Url to open:")); if (dialog.exec() == QDialog::Accepted) { QString fileName = dialog.textValue(); if (!fileName.isEmpty()) { getPlayer()->playFile(fileName); } } } ================================================ FILE: QtPlayer/mainwindow.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class CustomDockWidget; class VideoPlayerWidget; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow() override; CustomDockWidget* dockWidget(); QWidget* videoControlWidget(); VideoPlayerWidget* getPlayer(); private: void onFileOpen(); void onUrlOpen(); private: Ui::MainWindow *ui; }; MainWindow* getMainWindow(); ================================================ FILE: QtPlayer/mainwindow.ui ================================================ MainWindow 0 0 803 604 MainWindow 0 0 16777215 16777215 0 0 Qt::StrongFocus QFrame::NoFrame QFrame::Plain 0 0 238 230 16 0 220 229 18 16777215 18 10000 0 false 0 0 803 21 File Open Open URL VideoControl QWidget
videocontrol.h
1
CustomDockWidget QWidget
customdockwidget.h
1
VideoPlayerWidget QFrame
videoplayerwidget.h
1
VideoProgressBar QProgressBar
videoprogressbar.h
================================================ FILE: QtPlayer/mousehoverbutton.cpp ================================================ #include "mousehoverbutton.h" #include MouseHoverButton::MouseHoverButton(QWidget* parent) : QToolButton(parent) { } MouseHoverButton::~MouseHoverButton() = default; void MouseHoverButton::mousePressEvent(QMouseEvent* event) { if (m_defIcon.isNull()) { m_defIcon = icon(); QList normalOnSizes = m_defIcon.availableSizes(QIcon::Normal, QIcon::On); if (!normalOnSizes.empty()) { // use normal/on image as pushed one m_pushedIcon = m_defIcon.pixmap(*normalOnSizes.begin(), QIcon::Normal, QIcon::On); } } QToolButton::mousePressEvent(event); if (event->buttons() & Qt::LeftButton) { setIcon(m_pushedIcon); } } void MouseHoverButton::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { setIcon(m_defIcon); } QToolButton::mouseReleaseEvent(event); } void MouseHoverButton::keyReleaseEvent(QKeyEvent* e) { Q_UNUSED(e) // do nothing so button won't process key events } void MouseHoverButton::paintEvent(QPaintEvent* event) { bool isButtonDown = isDown(); setDown(false); QToolButton::paintEvent(event); setDown(isButtonDown); } ================================================ FILE: QtPlayer/mousehoverbutton.h ================================================ #pragma once #include #include /* Since Qt tool button pushes icon's image for pressed state, we have own implementation. To get it properly worked next images should be assigned to next button states programmatically or in the Qt designer: Normal Off - default image Normal On - clicked image Disabled Off - disabled image Disabled On - disabled image Active Off - hover image Active On - hover image Selected Off - default image Selected On - default image */ class MouseHoverButton : public QToolButton { Q_OBJECT public: MouseHoverButton(QWidget* parent); ~MouseHoverButton() override; protected: void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void keyReleaseEvent(QKeyEvent* e) override; void paintEvent(QPaintEvent* event) override; private: QIcon m_defIcon; QIcon m_pushedIcon; }; ================================================ FILE: QtPlayer/opengldisplay.cpp ================================================ #include "opengldisplay.h" // https://github.com/MasterAler/SampleYUVRenderer #include #include #include #include #include #include #include enum { PROGRAM_VERTEX_ATTRIBUTE = 0, PROGRAM_TEXCOORD_ATTRIBUTE = 1, ATTRIB_VERTEX = 0, ATTRIB_TEXTURE = 1, }; struct OpenGLDisplay::OpenGLDisplayImpl { GLvoid* mBufYuv{nullptr}; unsigned int mFrameSize{0}; QOpenGLShader* mVShader; QOpenGLShader* mFShader; QOpenGLShaderProgram* mShaderProgram; QOpenGLTexture* mTextureY; QOpenGLTexture* mTextureU; QOpenGLTexture* mTextureV; GLuint id_y, id_u, id_v; int textureUniformY, textureUniformU, textureUniformV; GLsizei mVideoW, mVideoH; std::mutex m_mutex; float m_aspectRatio{ 0.75F }; std::atomic_bool m_pendingUpdate = false; }; /*************************************************************************/ OpenGLDisplay::OpenGLDisplay(QWidget* parent) : QOpenGLWidget(parent) , impl(new OpenGLDisplayImpl()) { setAttribute(Qt::WA_OpaquePaintEvent, true); } OpenGLDisplay::~OpenGLDisplay() { delete[] reinterpret_cast(impl->mBufYuv); } void OpenGLDisplay::InitDrawBuffer(unsigned bsize) { if (impl->mFrameSize < bsize) { delete[] reinterpret_cast(impl->mBufYuv); impl->mFrameSize = bsize; impl->mBufYuv = new unsigned char[bsize]; } } //void OpenGLDisplay::DisplayVideoFrame(unsigned char *data, int frameWidth, int frameHeight) //{ // impl->mVideoW = frameWidth; // impl->mVideoH = frameHeight; // memcpy(impl->mBufYuv, data, impl->mFrameSize); // update(); //} void OpenGLDisplay::initializeGL() { initializeOpenGLFunctions(); glEnable(GL_DEPTH_TEST); /* Modern opengl rendering pipeline relies on shaders to handle incoming data. * Shader: is a small function written in OpenGL Shading Language (GLSL). * GLSL is the language that makes up all OpenGL shaders. * The syntax of the specific GLSL language requires the reader to find relevant information. */ // Initialize the vertex shader object impl->mVShader = new QOpenGLShader(QOpenGLShader::Vertex, this); //Vertex shader source const char *vsrc = "attribute vec4 vertexIn; \ attribute vec2 textureIn; \ varying vec2 textureOut; \ void main(void) \ { \ gl_Position = vertexIn; \ textureOut = textureIn; \ }"; //Compile the vertex shader program bool bCompile = impl->mVShader->compileSourceCode(vsrc); if(!bCompile) { throw OpenGlException(); } // Initialize the fragment shader function yuv converted to rgb impl->mFShader = new QOpenGLShader(QOpenGLShader::Fragment, this); // Fragment shader source code const char *fsrc = (context()->isOpenGLES()) ? "precision mediump float; \ varying vec2 textureOut; \ uniform sampler2D tex_y; \ uniform sampler2D tex_u; \ uniform sampler2D tex_v; \ void main(void) \ { \ vec3 yuv; \ vec3 rgb; \ yuv.x = texture2D(tex_y, textureOut).r; \ yuv.y = texture2D(tex_u, textureOut).r - 0.5; \ yuv.z = texture2D(tex_v, textureOut).r - 0.5; \ rgb = mat3( 1, 1, 1, \ 0, -0.39465, 2.03211, \ 1.13983, -0.58060, 0) * yuv; \ gl_FragColor = vec4(rgb, 1); \ }" : "varying vec2 textureOut; \ uniform sampler2D tex_y; \ uniform sampler2D tex_u; \ uniform sampler2D tex_v; \ void main(void) \ { \ vec3 yuv; \ vec3 rgb; \ yuv.x = texture2D(tex_y, textureOut).r; \ yuv.y = texture2D(tex_u, textureOut).r - 0.5; \ yuv.z = texture2D(tex_v, textureOut).r - 0.5; \ rgb = mat3( 1, 1, 1, \ 0, -0.39465, 2.03211, \ 1.13983, -0.58060, 0) * yuv; \ gl_FragColor = vec4(rgb, 1); \ }"; bCompile = impl->mFShader->compileSourceCode(fsrc); if(!bCompile) { throw OpenGlException(); } // Create a shader program container impl->mShaderProgram = new QOpenGLShaderProgram(this); // Add the fragment shader to the program container impl->mShaderProgram->addShader(impl->mFShader); // Add a vertex shader to the program container impl->mShaderProgram->addShader(impl->mVShader); // Bind the property vertexIn to the specified location ATTRIB_VERTEX, this property // has a declaration in the vertex shader source impl->mShaderProgram->bindAttributeLocation("vertexIn", ATTRIB_VERTEX); // Bind the attribute textureIn to the specified location ATTRIB_TEXTURE, the attribute // has a declaration in the vertex shader source impl->mShaderProgram->bindAttributeLocation("textureIn", ATTRIB_TEXTURE); //Link all the shader programs added to impl->mShaderProgram->link(); //activate all links impl->mShaderProgram->bind(); // Read the position of the data variables tex_y, tex_u, tex_v in the shader, the declaration // of these variables can be seen in // fragment shader source impl->textureUniformY = impl->mShaderProgram->uniformLocation("tex_y"); impl->textureUniformU = impl->mShaderProgram->uniformLocation("tex_u"); impl->textureUniformV = impl->mShaderProgram->uniformLocation("tex_v"); //Vertex matrix static const GLfloat vertexVertices[] = { -1.0F, -1.0F, 1.0F, -1.0F, -1.0F, 1.0F, 1.0F, 1.0F, }; //Texture matrix static const GLfloat textureVertices[] = { 0.0F, 1.0F, 1.0F, 1.0F, 0.0F, 0.0F, 1.0F, 0.0F, }; // Set the value of the vertex matrix of the attribute ATTRIB_VERTEX and format glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices); // Set the texture matrix value and format of the attribute ATTRIB_TEXTURE glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices); // Enable the ATTRIB_VERTEX attribute data, the default is off glEnableVertexAttribArray(ATTRIB_VERTEX); // Enable the ATTRIB_TEXTURE attribute data, the default is off glEnableVertexAttribArray(ATTRIB_TEXTURE); // Create y, u, v texture objects respectively impl->mTextureY = new QOpenGLTexture(QOpenGLTexture::Target2D); impl->mTextureU = new QOpenGLTexture(QOpenGLTexture::Target2D); impl->mTextureV = new QOpenGLTexture(QOpenGLTexture::Target2D); impl->mTextureY->create(); impl->mTextureU->create(); impl->mTextureV->create(); // Get the texture index value of the return y component impl->id_y = impl->mTextureY->textureId(); // Get the texture index value of the returned u component impl->id_u = impl->mTextureU->textureId(); // Get the texture index value of the returned v component impl->id_v = impl->mTextureV->textureId(); glClearColor (0.3, 0.3, 0.3, 0.0); // set the background color // qDebug("addr=%x id_y = %d id_u=%d id_v=%d\n", this, impl->id_y, impl->id_u, impl->id_v); } void OpenGLDisplay::paintGL() { std::unique_lock lock(impl->m_mutex); if (!impl->mBufYuv) { return; } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Load y data texture // Activate the texture unit GL_TEXTURE0 glActiveTexture(GL_TEXTURE0); // Use the texture generated from y to generate texture glBindTexture(GL_TEXTURE_2D, impl->id_y); // Fixes abnormality with 174x100 yuv data glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); // Use the memory mBufYuv data to create a real y data texture glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, impl->mVideoW, impl->mVideoH, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, impl->mBufYuv); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Load u data texture glActiveTexture(GL_TEXTURE1);//Activate texture unit GL_TEXTURE1 glBindTexture(GL_TEXTURE_2D, impl->id_u); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, impl->mVideoW/2, impl->mVideoH/2 , 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, static_cast(impl->mBufYuv) + impl->mVideoW * impl->mVideoH); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Load v data texture glActiveTexture(GL_TEXTURE2);//Activate texture unit GL_TEXTURE2 glBindTexture(GL_TEXTURE_2D, impl->id_v); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, impl->mVideoW / 2, impl->mVideoH / 2 , 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, static_cast(impl->mBufYuv) + impl->mVideoW * impl->mVideoH * 5/4); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Specify y texture to use the new value can only use 0, 1, 2, etc. to represent // the index of the texture unit, this is the place where opengl is not humanized //0 corresponds to the texture unit GL_TEXTURE0 1 corresponds to the // texture unit GL_TEXTURE1 2 corresponds to the texture unit GL_TEXTURE2 glUniform1i(impl->textureUniformY, 0); // Specify the u texture to use the new value glUniform1i(impl->textureUniformU, 1); // Specify v texture to use the new value glUniform1i(impl->textureUniformV, 2); // Use the vertex array way to draw graphics glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glFlush(); } ////////////////////////////////////////////////////////////////////////////// void OpenGLDisplay::updateFrame(IFrameDecoder* decoder, unsigned int generation) { FrameRenderingData data; if (decoder->getFrameRenderingData(&data)) { std::unique_lock lock(impl->m_mutex); impl->m_aspectRatio = float(data.height) / data.width; impl->mVideoW = data.width; impl->mVideoH = data.height; InitDrawBuffer(data.height * data.width * 3 / 2); auto dst = reinterpret_cast(impl->mBufYuv); int width = data.width; int height = data.height; for (int i = 0; i < 3; ++i) { auto src = data.image[i]; for (int j = 0; j < height; ++j) { memcpy(dst, src, width); dst += width; src += data.pitch[i]; } width = data.width / 2; height = data.height / 2; } } finishedDisplayingFrame(generation); } void OpenGLDisplay::showPicture(const QImage& img) { if (img.isNull()) { return; } if(img.format()!=QImage::Format_RGB32 && img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB888) { Q_ASSERT(false && "Wrong image format\n"); return; } const auto step = (img.format() == QImage::Format_RGB888)? 3 : 4; const int width = img.width() & ~1; const int height = img.height() & ~1; std::unique_lock lock(impl->m_mutex); impl->m_aspectRatio = float(height) / width; // RGB to YUV420 impl->mVideoW = width; impl->mVideoH = height; InitDrawBuffer(height * width * 3 / 2); int size = width*height; // Y for(unsigned y=0;y(impl->mBufYuv) + y*width; for(unsigned x=0;x> 13, 235U); *d = Y; d++; s+=step; } } // U,V const unsigned int ss = img.bytesPerLine(); for(unsigned y=0;y(impl->mBufYuv) + size+y/2*width/2; for(unsigned x=0;x> 2; int g=(s[1] + s[step+1] + s[ss+1] + s[ss+step+1] + 2) >> 2; int b=(s[0] + s[step] + s[ss+0] + s[ss+step] + 2) >> 2; int Cb = std::clamp((-1214*r - 2384*g + 3598*b + 4096 + 1048576)>>13, 16, 240); int Cr = std::clamp((3598*r - 3013*g - 585*b + 4096 + 1048576)>>13, 16, 240); *d = Cb; *(d+size/4) = Cr; d++; s+=step * 2; } } lock.unlock(); emit update(); } void OpenGLDisplay::showPicture(const QPixmap& picture) { showPicture(picture.toImage()); } void OpenGLDisplay::drawFrame(IFrameDecoder* decoder, unsigned int generation) { if (!(impl->m_pendingUpdate.exchange(true))) { QMetaObject::invokeMethod(this, [this] { impl->m_pendingUpdate = false; setUpdatesEnabled(true); update(); }); } //finishedDisplayingFrame(generation); } void OpenGLDisplay::decoderClosing() { } float OpenGLDisplay::aspectRatio() const { return impl->m_aspectRatio; } ================================================ FILE: QtPlayer/opengldisplay.h ================================================ #ifndef OPENGLDISPLAY_H #define OPENGLDISPLAY_H // https://github.com/MasterAler/SampleYUVRenderer #include #include #include #include #include "videodisplay.h" class OpenGLDisplay : public QOpenGLWidget, public QOpenGLFunctions, public VideoDisplay { Q_OBJECT public: explicit OpenGLDisplay(QWidget* parent = nullptr); ~OpenGLDisplay() override; void InitDrawBuffer(unsigned bsize); void showPicture(const QImage& img) override; void showPicture(const QPixmap& picture) override; // decoder->finishedDisplayingFrame() must be called void updateFrame(IFrameDecoder* decoder, unsigned int generation) override; void drawFrame(IFrameDecoder* decoder, unsigned int generation) override; void decoderClosing() override; float aspectRatio() const; protected: void initializeGL() override; void paintGL() override; private: struct OpenGLDisplayImpl; QScopedPointer impl; }; /***********************************************************************/ class OpenGlException: public QException { public: void raise() const override { throw *this; } OpenGlException *clone() const override { return new OpenGlException(*this); } }; #endif // OPENGLDISPLAY_H ================================================ FILE: QtPlayer/portaudioplayer.cpp ================================================ #include "portaudioplayer.h" #include #include PortAudioPlayer::PortAudioPlayer() { auto err = Pa_Initialize(); } PortAudioPlayer::~PortAudioPlayer() { auto err = Pa_Terminate(); } void PortAudioPlayer::InitializeThread() { QThread::currentThread()->setPriority(QThread::TimeCriticalPriority); } bool PortAudioPlayer::Open(int bytesPerSample, int channels, int* samplesPerSec) { PaStreamParameters params{}; params.device = Pa_GetDefaultOutputDevice(); params.suggestedLatency = Pa_GetDeviceInfo(params.device)->defaultLowOutputLatency; params.channelCount = channels; switch (bytesPerSample) { case 1: params.sampleFormat = paInt8; break; case 2: params.sampleFormat = paInt16; break; case 4: params.sampleFormat = paInt32; break; } auto err{ Pa_OpenStream(&m_stream, nullptr, ¶ms, *samplesPerSec, paFramesPerBufferUnspecified, paNoFlag, nullptr, nullptr) }; if (err != paNoError) { return false; } err = Pa_StartStream(m_stream); m_FrameSize = bytesPerSample * channels; *samplesPerSec = m_samplesPerSec = Pa_GetStreamInfo(m_stream)->sampleRate; return true; } void PortAudioPlayer::Close() { auto err = Pa_CloseStream(m_stream); m_stream = nullptr; } bool PortAudioPlayer::WriteAudio(uint8_t* write_data, int64_t write_size) { if (!m_stream) { return false; } auto* realData = (int16_t*)write_data; for (unsigned int i = 0; i < write_size / 2; ++i) { realData[i] *= m_volume; } const auto framesToWrite = write_size / m_FrameSize; auto err = Pa_WriteStream(m_stream, write_data, framesToWrite); if (err == paStreamIsStopped && Pa_StartStream(m_stream) == paNoError) { err = Pa_WriteStream(m_stream, write_data, framesToWrite); } // count audio pts const double frame_clock = (double)framesToWrite / m_samplesPerSec; m_callback->AppendFrameClock(frame_clock); return err == paNoError; } void PortAudioPlayer::WaveOutReset() { Pa_AbortStream(m_stream); } void PortAudioPlayer::WaveOutPause() { Pa_StopStream(m_stream); } void PortAudioPlayer::WaveOutRestart() { Pa_StartStream(m_stream); } ================================================ FILE: QtPlayer/portaudioplayer.h ================================================ #pragma once #include "../video/audioplayer.h" class PortAudioPlayer : public IAudioPlayer { public: PortAudioPlayer(); ~PortAudioPlayer() override; PortAudioPlayer(const PortAudioPlayer&) = delete; PortAudioPlayer& operator=(const PortAudioPlayer&) = delete; void SetCallback(IAudioPlayerCallback* callback) override { m_callback = callback; } void InitializeThread() override; void DeinitializeThread() override {} void WaveOutReset() override; void Close() override; bool Open(int bytesPerSample, int channels, int* samplesPerSec) override; void SetVolume(double volume) override { m_volume = volume; } double GetVolume() const override { return m_volume; } void WaveOutPause() override; void WaveOutRestart() override; bool WriteAudio(uint8_t* write_data, int64_t write_size) override; private: IAudioPlayerCallback* m_callback{}; void* m_stream{}; int m_FrameSize{}; double m_samplesPerSec{}; double m_volume = 1.; }; ================================================ FILE: QtPlayer/resources/qt.conf ================================================ [Paths] Plugins = . ================================================ FILE: QtPlayer/resources/resources.qrc ================================================ style.css images/help41.png images/style/button_disable.png images/style/button_down.png images/style/button_hover.png images/style/button_normal.png images/style/header_arrow_down.png images/style/down_arrow.png images/style/lineedit.png images/style/down_arrow_grey.png images/play_transparent.png images/video___btn_play___default___(94x94).png images/video___btn_play___hover___(94x94).png images/video___btn_play___clicked___(94x94).png images/style/box-shadow.png images/style/busy.png images/style/button-blank.png images/style/floatpanel.png images/spinner.gif images/triangle_down.png images/triangle_right.png images/style/progress_dropdown.png images/style/circle-shadow.png images/style/tooltip-background.png images/video_seek_cursor.png images/style/header_line.png images/style/scroll-button-down.png images/style/scroll-button-down-hover.png images/style/scroll-button-up.png images/style/scroll-button-up-hover.png images/style/scroll-vhandle.png images/style/scroll-vhandle-hover.png images/style/scroll-button-left.png images/style/scroll-button-left-hover.png images/style/scroll-button-right.png images/style/scroll-button-right-hover.png images/style/scroll-hhandle.png images/style/scroll-hhandle-hover.png images/control/player_bg___(230x41).png images/control/player_bg_fs___(230x41).png images/control/player___btn_play___default___(40x31).png images/control/player___btn_play___clicked___(40x31).png images/control/player___btn_play___hover___(40x31).png images/control/player___btn_stop___default___(31x31).png images/control/player___btn_stop___clicked___(31x31).png images/control/player___btn_stop___hover___(31x31).png images/control/player___btn_globe___default___(31x31).png images/control/player___btn_globe___click___(31x31).png images/control/player___btn_globe___hover___(31x31).png images/control/player___btn_sound_on___default___(31x31).png images/control/player___btn_sound_on___clicked___(31x31).png images/control/player___btn_sound_on___hover___(31x31).png images/control/player___btn_sound_off___default___(31x31).png images/control/player___btn_sound_off___clicked___(31x31).png images/control/player___btn_sound_off___hover___(31x31).png images/control/player___btn_pause___default___(40x31).png images/control/player___btn_pause___clicked___(40x31).png images/control/player___btn_pause___hover___(40x31).png images/control/icodelete.png images/control/icoplay.png ================================================ FILE: QtPlayer/resources/style.css ================================================ /* ********** Global settings ********** */ QWidget { font-family: "Segoe UI"; font-size: 12px; } QDialog { border: 0px; } QMainWindow::separator { background: rgb(128, 133, 137); width: 3; height: 3; } QPushButton, QDialogButtonBox QPushButton, ButtonLabel { /*See in PS for cut lines*/ border-image: url(:/button_normal) 4 5 6 5; border-top: 4px transparent; border-right: 5px transparent; border-bottom: 4px transparent; border-left: 5px transparent; padding-left:-1px; min-width: 6em; height: 16 px; } SearchResultForm QPushButton { min-width: 62px; } QPushButton:hover, QDialogButtonBox QPushButton:hover, ButtonLabel:hove { border-image: url(:/button_hover) 4 5 6 5; } QPushButton:pressed, QDialogButtonBox QPushButton:pressed, ButtonLabel:pressed { border-image: url(:/button_down) 4 5 6 5; padding-top:2px; } QPushButton:disabled, QDialogButtonBox QPushButton:disabled, ButtonLabel:disabled { border-image: url(:/button_disable) 4 5 6 5; color: #8f8f8f; } #mainToolBar { border-bottom: 1px solid #5d6c7a; background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 #d4d4d5, stop:1 #ffffff); } MainToolBar QToolButton { border: 0px; } #SocialNetworksFrame QToolButton { border: 0px; } MainToolBar QLabel { color: rgb(95, 100, 104); } MainToolBar #btnDownloads[active="false"] { image: url(:/images/downloads41.png); } MainToolBar #btnDownloads[active="true"]{ image: url(:/images/downloads41h.png); } MainToolBar #btnSearch[active="false"] { image: url(:/images/search41.png); } MainToolBar #btnSearch[active="true"]{ image: url(:/images/search41h.png); } MainToolBar #btnLibrary[active="false"] { image: url(:/images/library41.png); } MainToolBar #btnLibrary[active="true"]{ image: url(:/images/library41h.png); } /* SearchResultForm #searchFrame { background-color: rgb(148, 153, 157); } */ #topFrame{ background-color: rgb(148, 153, 157); } QTreeView, #declarativeView, #dockFrame, QTableView { border-top: 1px solid #5d6c7a; } #dockFrame{ background-color: lightgrey; } SearchResultForm #leSearch, DownloadsForm #leSearch, LibraryForm #cbSearch { /*background-color: rgb(125, 130, 134); selection-background-color: #b3b7b9;*/ background: #e4e7e9; color: black; border: 0px; height: 22px; font-size: 14px; } DownloadsControl #ctrlFrame { border-width: 1px; border-style: inset; border-color: #535353; border-radius: 5px; background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #dedede, stop:1 #b1b2b3); } /* PlayerHeader QFrame{ background-color: rgb(148, 153, 157); color: white; font-size: 14px; } */ QComboBox#cbSites QAbstractItemView::item { margin-top: 5px; color: rgb(148, 153, 157); } SearchResultForm QHeaderView::section,DownloadsForm QHeaderView::section { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(237, 243, 248), stop:1 rgb(211, 225, 238)); padding-left: 4px; border: 0px; height: 30px; border-top: 1px solid rgb(165, 184, 195); border-bottom: 1px solid rgb(165, 184, 195); font-size: 14px; } /* QComboBox { border-image: url(:/images/style/lineedit.png) 3; border-width: 3; } QComboBox::drop-down { border-style: none; } QComboBox::down-arrow { image: url(:/images/style/down_arrow_grey.png); } */ QComboBox#cbSites { border: 1px solid gray; padding: 1px 22px 1px 3px; min-width: 9em; background: #e4e7e9; height: 20px; } QComboBox#cbSites::drop-down { border-style: none; } QComboBox#cbSites::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 22px; border-left-width: 1px; border-left-color: #c9ccd2; border-left-style: solid; /* just a single line */ } QComboBox#cbSites::down-arrow { image: url(:/images/style/down_arrow_grey.png); } AboutDialog { background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(220, 220, 220, 255), stop:0.460227 rgba(229, 229, 229, 255), stop:1 rgba(239, 239, 239, 255)); } AboutDialog #aboutHeader { background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(184, 188, 192, 255), stop:0.460227 rgba(190, 195, 198, 255), stop:1 rgba(213, 219, 224, 255)); } AboutDialog #lbAppName { font-size: 24px; color: white; } AboutDialog #wLine { background:url(:/style/header_line.png); } VideoControl #playerControlFrame { background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 #e5e8ea, stop:1 #ffffff); border-color: #75787a; border-width: 1px; border-style: outset; border-radius: 5px; } VideoControl QProgressBar { border-color: #777777; border-width: 1px; border-style: outset; border-radius: 2px; } VideoControl QProgressBar::chunk { background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 #278ece, stop:1 #5dc6f8); border-width: 1px; border-style: outset; border-radius: 5px; margin: 1px; } VideoControl QToolButton { border: 0px; } DownloadsControl #frame { background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 #e5e8ea, stop:1 #ffffff); border-color: #75787a; border-width: 1px; border-style: outset; border-radius: 5px; } DownloadsControl QToolButton { border: 0px; } DownloadsControl QFrame #line{ color:green; margin-top: 2px; margin-bottom: 2px; } QScrollBar::add-line:vertical { background-image: url(:/style/scroll-button-down.png); } QScrollBar::sub-line:vertical { background-image: url(:/style/scroll-button-up.png); } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; } QScrollBar:vertical { background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #e9e8e9, stop: 0.5 #d3d3d3, stop: 1.0 #e9e8e9); width: 14px; padding: 14px 0px 14px 0px; } QScrollBar::handle:vertical { background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #fafafa, stop: 0.5 #e9e9e9, stop: 1.0 #c7c7c7); border-radius: 2px; border: 1px solid #787878; min-height: 20px; } QScrollBar:horizontal { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e9e8e9, stop: 0.5 #d3d3d3, stop: 1.0 #e9e8e9); height: 14px; padding: 0px 14px 0px 14px; } QScrollBar::handle:horizontal { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #fafafa, stop: 0.5 #e9e9e9, stop: 1.0 #c7c7c7); border-radius: 2px; border: 1px solid #787878; min-width: 20px; } #DescriptionFrame #textEdit { background-color: rgb(211, 211, 211); border: 0px; padding: 3px 2px 3px 4px; border-top: 1px solid rgb(180, 180, 180); } #descriptionSiteLabel { color: rgb(155, 155, 155); font: 75 11pt "MS Shell Dlg 2"; margin-left: 7px; margin-top: 3px; margin-bottom: 3px; } SocialNetworksWidget #SocialNetworksFrame { background-image: url(:/sites/social_bg); } ================================================ FILE: QtPlayer/resources/win7.manifest ================================================ ================================================ FILE: QtPlayer/resources/winres.rc.in ================================================ #include "verrsrc.h" IDI_ICON1 ICON "@ICON_FILE@" #define VER_FILEVERSION @MAJOR_VER@,@MINOR_VER1@,@MINOR_VER2@,@MINOR_VER3@ #define VER_FILEVERSION_STR "@MAJOR_VER@.@MINOR_VER1@.@MINOR_VER2@.@MINOR_VER3@\0" #define VER_PRODUCTVERSION @MAJOR_VER@,@MINOR_VER1@,@MINOR_VER2@,@MINOR_VER3@ #define VER_PRODUCTVERSION_STR "@MAJOR_VER@.@MINOR_VER1@.@MINOR_VER2@.@MINOR_VER3@\0" 1 VERSIONINFO FILEVERSION VER_FILEVERSION PRODUCTVERSION VER_PRODUCTVERSION FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "@COMPANYNAME@\0" VALUE "FileDescription", "@PRODUCTNAME@\0" VALUE "FileVersion", VER_FILEVERSION_STR VALUE "LegalCopyright", "Copyright (C) 2013 @COMPANYNAME@ All Rights Reserved.\0" VALUE "ProductName", "@PRODUCTNAME@\0" VALUE "ProductVersion", VER_PRODUCTVERSION_STR VALUE "OriginalFilename", "@SHORTPRODUCTNAME@.exe\0" VALUE "InternalName", "@SHORTPRODUCTNAME@\0" END END BLOCK "VarFileInfo" BEGIN /* The following line should only be modified for localized versions. */ /* It consists of any number of WORD,WORD pairs, with each pair */ /* describing a language,codepage combination supported by the file. */ /* */ /* For example, a file might have values "0x409,1252" indicating that it */ /* supports English language (0x409) in the Windows ANSI codepage (1252). */ VALUE "Translation", 0x409, 1252 END END ================================================ FILE: QtPlayer/videocontrol.cpp ================================================ #include "videocontrol.h" #include "ui_videocontrol.h" #include "videoplayerwidget.h" #include "videowidget.h" #include #include #include #include VideoControl::VideoControl(VideoPlayerWidget* parent) : QWidget(parent), ui(new Ui::VideoControl) { ui->setupUi(this); ui->progressBar->installEventFilter(this); // Link it with VideoPlayerWidget parent->setControl(this); videoPlayer = parent; // Default value from the decoder int volume = videoPlayer->getDecoder()->volume() * ui->progressBar->maximum(); m_isVolumeOn = (volume == 0); setVolume(volume, true); connect(videoPlayer->getDecoder(), &FFmpegDecoderWrapper::volumeChanged, this, &VideoControl::onProgramVolumeChange); ui->btnPause->hide(); m_height = geometry().height(); background.load(":/control/background"); backgroundfs.load(":/control/backgroundfs"); } VideoControl::~VideoControl() { delete ui; } bool VideoControl::eventFilter(QObject* obj, QEvent* event) { if (obj == ui->progressBar) { QEvent::Type eventType = event->type(); switch (eventType) { case QEvent::MouseMove: { auto* mEvent = static_cast(event); if (mEvent->buttons() & Qt::LeftButton) { float percent = (mEvent->x() * 1.0) / ui->progressBar->width(); percent *= 100; setVolume(qBound(0, static_cast(percent), 100)); } } break; case QEvent::MouseButtonRelease: { auto* mEvent = static_cast(event); if (mEvent->button() == Qt::LeftButton) { float percent = (mEvent->x() * 1.0) / ui->progressBar->width(); percent *= 100; setVolume(qBound(0, static_cast(percent), 100)); } } break; default:; } } return QWidget::eventFilter(obj, event); } void VideoControl::paintEvent(QPaintEvent* event) { Q_UNUSED(event); QPainter painter(this); qDrawBorderPixmap(&painter, rect(), QMargins(0, 0, 0, 0), parent() == nullptr ? backgroundfs : background); } void VideoControl::resizeEvent(QResizeEvent* event) { Q_UNUSED(event); // separators on background image (got from the image) const int leftSeparator = 94; const int rightSeparator = 137; // margin by X between the controls const int margin = (leftSeparator - ui->btnPlay->width() - ui->btnStop->width()) / 3; // position by Y for the buttons const int posY = (background.height() - ui->btnPlay->height()) / 2; ui->btnPlay->move(2 * margin, posY); ui->btnPause->move(2 * margin, posY); ui->btnStop->move(3 * margin + ui->btnPlay->width(), posY); ui->btnBrowser->move(((rightSeparator - leftSeparator) - ui->btnBrowser->width()) / 2 + leftSeparator, posY); ui->btnVolume->move(rightSeparator + margin, posY); ui->progressBar->move(ui->btnVolume->pos().x() + ui->btnVolume->width(), (height() - ui->progressBar->height()) / 2 + 1); // +1 for just more nice looking ui->progressBar->setFixedSize(width() - ui->progressBar->pos().x() - 3 * margin, ui->progressBar->height()); } void VideoControl::wheelEvent(QWheelEvent* event) { if (event->orientation() != Qt::Horizontal) { if (auto decoder = videoPlayer->getDecoder()) { const int numDegrees = event->delta() / 8; const int numSteps = numDegrees / 15; const double newVolume = std::clamp(decoder->volume() + ((double)numSteps / 20), 0., 1.); decoder->setVolume(newVolume); } } event->accept(); } int VideoControl::getWidth() const { return background.width(); } void VideoControl::on_btnVolume_clicked() { if (ui->progressBar->value() > 0) { setVolume(0); } else { int volume = m_prevVolumeValue; if (volume <= 0) { volume = 50; } setVolume(volume); } } void VideoControl::setVolume(int volume, bool onlyWidget) { ui->progressBar->setValue(volume); switchVolumeButton(volume != 0); int prevVolumeValue = videoPlayer->getDecoder()->volume() * ui->progressBar->maximum(); if ((videoPlayer != nullptr) && !onlyWidget) { videoPlayer->getDecoder()->setVolume( ui->progressBar->maximum() != 0? (double(volume) / ui->progressBar->maximum()) : 0.); } m_prevVolumeValue = prevVolumeValue; } void VideoControl::switchVolumeButton(bool volumeOn) { QIcon progressBarIcon; if (m_isVolumeOn && !volumeOn) { progressBarIcon.addFile(QString::fromUtf8(":/control/sound_off_default"), QSize(), QIcon::Normal, QIcon::Off); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_off_clicked"), QSize(), QIcon::Normal, QIcon::On); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_off_default"), QSize(), QIcon::Disabled, QIcon::Off); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_off_default"), QSize(), QIcon::Disabled, QIcon::On); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_off_hover"), QSize(), QIcon::Active, QIcon::Off); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_off_hover"), QSize(), QIcon::Active, QIcon::On); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_off_default"), QSize(), QIcon::Selected, QIcon::Off); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_off_default"), QSize(), QIcon::Selected, QIcon::On); ui->btnVolume->setIcon(progressBarIcon); m_isVolumeOn = volumeOn; } else if (!m_isVolumeOn && volumeOn) { progressBarIcon.addFile(QString::fromUtf8(":/control/sound_on_default"), QSize(), QIcon::Normal, QIcon::Off); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_on_clicked"), QSize(), QIcon::Normal, QIcon::On); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_on_default"), QSize(), QIcon::Disabled, QIcon::Off); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_on_default"), QSize(), QIcon::Disabled, QIcon::On); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_on_hover"), QSize(), QIcon::Active, QIcon::Off); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_on_hover"), QSize(), QIcon::Active, QIcon::On); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_on_default"), QSize(), QIcon::Selected, QIcon::Off); progressBarIcon.addFile(QString::fromUtf8(":/control/sound_on_default"), QSize(), QIcon::Selected, QIcon::On); ui->btnVolume->setIcon(progressBarIcon); m_isVolumeOn = volumeOn; } } void VideoControl::on_btnPlay_clicked() { videoPlayer->playPauseButtonAction(); } void VideoControl::on_btnPause_clicked() { videoPlayer->playPauseButtonAction(); } void VideoControl::on_btnStop_clicked() { showPlaybutton(true); Q_ASSERT(videoPlayer); if (videoPlayer->state() != VideoPlayerWidget::InitialState) { videoPlayer->stopVideo(true); } } void VideoControl::on_btnBrowser_clicked() { emit browse(); } void VideoControl::onProgramVolumeChange(double volume) { setVolume(volume * ui->progressBar->maximum(), true); } void VideoControl::showPlaybutton(bool show /* = true */) { ui->btnPlay->setVisible(show); ui->btnPause->setVisible(!show); } ================================================ FILE: QtPlayer/videocontrol.h ================================================ #pragma once #include class VideoPlayerWidget; namespace Ui { class VideoControl; } class VideoControl : public QWidget { Q_OBJECT public: explicit VideoControl(VideoPlayerWidget* parent = nullptr); ~VideoControl() override; void setVolume(int volume, bool onlyWidget = false); void showPlaybutton(bool show = true); int getHeight() const { return m_height; }; int getWidth() const; signals: void download(); void browse(); protected: bool eventFilter(QObject* obj, QEvent* event) override; void paintEvent(QPaintEvent* event) override; void resizeEvent(QResizeEvent* event)override; void wheelEvent(QWheelEvent* event) override; public slots: void on_btnPlay_clicked(); void on_btnPause_clicked(); private slots: void onProgramVolumeChange(double volume); void on_btnVolume_clicked(); void on_btnStop_clicked(); void on_btnBrowser_clicked(); private: Ui::VideoControl* ui; VideoPlayerWidget* videoPlayer; int m_height; QPixmap background; QPixmap backgroundfs; bool m_isVolumeOn{false}; int m_prevVolumeValue{0}; void switchVolumeButton(bool volumeOn); }; ================================================ FILE: QtPlayer/videocontrol.ui ================================================ VideoControl 0 0 230 41 0 0 Form 31 8 40 31 Pause :/control/pause_default :/control/pause_clicked :/control/pause_default :/control/pause_default :/control/pause_hover :/control/pause_hover :/control/pause_default :/control/pause_default:/control/pause_default 40 31 true 62 8 31 31 Stop :/control/stop_default :/control/stop_clicked :/control/stop_default :/control/stop_default :/control/stop_hover :/control/stop_hover :/control/stop_default :/control/stop_default:/control/stop_default 31 31 true 150 10 31 31 Mute :/control/sound_on_default :/control/sound_on_clicked :/control/sound_on_default :/control/sound_on_default :/control/sound_on_hover :/control/sound_on_hover :/control/sound_on_default :/control/sound_on_default:/control/sound_on_default 31 31 true 108 8 31 31 Browse :/control/globe_default :/control/globe_clicked :/control/globe_default :/control/globe_default :/control/globe_hover :/control/globe_hover :/control/globe_default :/control/globe_default:/control/globe_default 31 31 true 190 15 30 8 1 0 30 0 30 10 30 0 true Volume control 70 false %p% 0 8 40 31 Play :/control/play_default :/control/play_clicked :/control/play_default :/control/play_default :/control/play_hover :/control/play_hover :/control/play_default :/control/play_default:/control/play_default 40 31 true MouseHoverButton QToolButton
mousehoverbutton.h
VolumeProgressBar QProgressBar
volumeprogressbar.h
================================================ FILE: QtPlayer/videodisplay.cpp ================================================ #include "videodisplay.h" VideoDisplay::VideoDisplay() = default; VideoDisplay::~VideoDisplay() { if (m_decoder != nullptr) { m_decoder->setFrameListener(nullptr); } } void VideoDisplay::setDecoderObject(FFmpegDecoderWrapper* decoder) { Q_ASSERT(decoder != nullptr); if (m_decoder != decoder) { m_decoder = decoder; m_decoder->setFrameListener(this); m_decoder->getFrameDecoder()->SetFrameFormat( #ifdef DEVELOPER_OPENGL IFrameDecoder::PIX_FMT_YUV420P #else IFrameDecoder::PIX_FMT_RGB24 #endif , false); } } void VideoDisplay::finishedDisplayingFrame(unsigned int generation) { m_decoder->finishedDisplayingFrame(generation); } ================================================ FILE: QtPlayer/videodisplay.h ================================================ #pragma once #include #include #include "ffmpegdecoder.h" class VideoDisplay : public IFrameListener { public: VideoDisplay(); ~VideoDisplay() override; void finishedDisplayingFrame(unsigned int generation); void setDecoderObject(FFmpegDecoderWrapper* decoder); virtual void showPicture(const QImage& picture) = 0; virtual void showPicture(const QPixmap& picture) = 0; protected: FFmpegDecoderWrapper* m_decoder{nullptr}; }; ================================================ FILE: QtPlayer/videoplayer.cpp ================================================ #include "videoplayer.h" #include "widgetdisplay.h" #include VideoPlayer::VideoPlayer() = default; VideoPlayer::~VideoPlayer() { delete m_display; } FFmpegDecoderWrapper* VideoPlayer::getDecoder() { return &m_decoder; } VideoDisplay* VideoPlayer::getCurrentDisplay() { return m_display; } void VideoPlayer::setDisplay(VideoDisplay* display) { Q_ASSERT(display); delete m_display; m_display = display; m_decoder.setFrameListener(m_display); } void VideoPlayer::setState(VideoState newState) { qDebug() << __FUNCTION__ << "newState:" << newState; m_state = newState; } ================================================ FILE: QtPlayer/videoplayer.h ================================================ #pragma once #include "ffmpegdecoder.h" class VideoPlayer { public: enum VideoState { InitialState, Playing, Paused, }; VideoPlayer(); virtual ~VideoPlayer(); FFmpegDecoderWrapper* getDecoder(); VideoDisplay* getCurrentDisplay(); virtual void setDisplay(VideoDisplay* display); VideoState state() const { return m_state; } protected: void setState(VideoState newState); private: FFmpegDecoderWrapper m_decoder; VideoDisplay* m_display{nullptr}; VideoState m_state{InitialState}; }; ================================================ FILE: QtPlayer/videoplayerwidget.cpp ================================================ #include "videoplayerwidget.h" #include "videowidget.h" #include "videocontrol.h" #include "videoprogressbar.h" #include #include #include #include #include enum { PROGRESSBAR_VISIBLE_HEIGHT = 5 }; VideoPlayerWidget::VideoPlayerWidget(QWidget* parent) : QFrame(parent) , m_videoWidget(new VideoWidget(this)) { connect(getDecoder(), &FFmpegDecoderWrapper::onPlayingFinished, this, &VideoPlayerWidget::onPlayingFinished); setDisplay(m_videoWidget); m_videoWidget->installEventFilter(this); } void VideoPlayerWidget::setVideoFilename(const QString& fileName) { m_currentFile = fileName; } VideoDisplay* VideoPlayerWidget::getCurrentDisplay() { return VideoPlayer::getCurrentDisplay(); } void VideoPlayerWidget::setDefaultPreviewPicture() { m_videoWidget->setDefaultPreviewPicture(); } QString VideoPlayerWidget::currentFilename() const { return m_currentFile; } void VideoPlayerWidget::setProgressbar(VideoProgressBar* progressbar) { Q_ASSERT(progressbar); m_progressBar = progressbar; connect(getDecoder(), &FFmpegDecoderWrapper::onChangedFramePosition, m_progressBar, &VideoProgressBar::displayPlayedProgress); progressbar->installEventFilter(this); } void VideoPlayerWidget::setControl(VideoControl* controlWidget) { Q_ASSERT(controlWidget); m_controls = controlWidget; controlWidget->installEventFilter(this); } void VideoPlayerWidget::stopVideo(bool showDefaultImage) { getDecoder()->close(true); } void VideoPlayerWidget::playFile(const QString& fileName) { Q_ASSERT(!fileName.isEmpty()); m_currentFile = fileName; FFmpegDecoderWrapper* decoder = getDecoder(); if (decoder->openFile(m_currentFile)) { decoder->play(); setState(Playing); m_controls->showPlaybutton(false); if (!m_videoWidget->isFullScreen()) { updateLayout(); } } else { m_progressBar->setDownloadedCounter(0); setState(InitialState); m_controls->showPlaybutton(true); QMessageBox::information(this, "Player", tr("File %1 cannot be played.").arg(fileName)); } } void VideoPlayerWidget::pauseVideo() { if (state() == Playing && getDecoder()->pauseResume()) { setState(Paused); } } bool VideoPlayerWidget::isPaused() { return (state() == Paused); } void VideoPlayerWidget::seekByPercent(float percent) { getDecoder()->seekByPercent(percent); } void VideoPlayerWidget::playPauseButtonAction() { if (state() == Paused) { m_controls->showPlaybutton(false); resumeVideo(); } else if (state() == Playing) { m_controls->showPlaybutton(true); pauseVideo(); } } void VideoPlayerWidget::resumeVideo() { if (state() == Paused && getDecoder()->pauseResume()) // It actually resumes { setState(Playing); } } void VideoPlayerWidget::updateViewOnVideoStop(bool showDefaultImage /* = false*/) { if (m_videoWidget->isFullScreen()) { m_videoWidget->fullScreen(false); } m_controls->showPlaybutton(); m_currentFile = QString(); m_progressBar->resetProgress(); setState(InitialState); emit fileReleased(); } VideoPlayerWidget::~VideoPlayerWidget() { stopVideo(); m_videoWidget = nullptr; } void VideoPlayerWidget::resizeEvent(QResizeEvent* event) { Q_UNUSED(event); updateLayout(); } bool VideoPlayerWidget::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::KeyRelease) { auto* ke = static_cast(event); if (ke->key() == Qt::Key_Space) { playPauseButtonAction(); } } return QFrame::eventFilter(object, event); } void VideoPlayerWidget::updateLayout() { int currWidth = width(); int currHeight = height(); const int controlsHeight = m_controls->getHeight(); int minPlayerHeight = currHeight - controlsHeight; int playerWidth = currWidth; int yPos = 1; FFmpegDecoderWrapper* dec = getDecoder(); Q_ASSERT(dec != nullptr); if (state() == InitialState) { double aspectRatio = (m_videoWidget->getPictureSize().height() > 0 && m_videoWidget->getPictureSize().width() >0) ? static_cast(m_videoWidget->getPictureSize().height()) / m_videoWidget->getPictureSize().width() : 0.75; int height = aspectRatio * currWidth; // Display too big: do recalculation if (height > minPlayerHeight) // TODO(Usrer): code refactoring { height = minPlayerHeight; playerWidth = static_cast(static_cast(minPlayerHeight) / aspectRatio); } m_videoWidget->setGeometry(0, yPos, playerWidth, height - PROGRESSBAR_VISIBLE_HEIGHT); yPos += height; QImage previewPic = m_videoWidget->startImageButton().scaled(playerWidth, yPos - PROGRESSBAR_VISIBLE_HEIGHT, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); if (m_videoWidget->startImageButton() == m_videoWidget->noPreviewImage()) { m_videoWidget->showPicture(previewPic); } else { m_videoWidget->updatePlayButton(); m_videoWidget->showPicture(m_videoWidget->drawPreview(previewPic)); } } else if (dec->isPlaying()) { //#ifdef DEVELOPER_OPENGL QSize pictureSize = QSize(playerWidth, playerWidth); double aspectRatio = m_videoWidget->aspectRatio(); //#else // QSize pictureSize = dec->getPreferredSize(playerWidth, playerWidth).size(); // double aspectRatio = (double)pictureSize.height() / pictureSize.width(); //#endif int playerHeight = pictureSize.width() * aspectRatio; // Display too big: do recalculation if (playerHeight > minPlayerHeight) { playerHeight = minPlayerHeight; playerWidth = static_cast(static_cast(minPlayerHeight) / aspectRatio); } if (m_videoWidget->isFullScreen()) { m_videoWidget->setGeometry(0, 0, currWidth, currHeight); } else { m_videoWidget->setGeometry(0, yPos, playerWidth, playerHeight - PROGRESSBAR_VISIBLE_HEIGHT); } yPos += playerHeight; } if (m_progressBar != nullptr) { int progressHeight = m_progressBar->height(); m_progressBar->move(0, yPos - (progressHeight + PROGRESSBAR_VISIBLE_HEIGHT) / 2); m_progressBar->resize(playerWidth, progressHeight); } int controlsPos = (playerWidth - m_controls->getWidth()) / 2; if (controlsPos < 0) { controlsPos = 0; } m_controls->move(controlsPos, yPos); m_controls->resize(m_controls->getWidth(), controlsHeight); } void VideoPlayerWidget::exitFullScreen() { onPlayingFinished(); } void VideoPlayerWidget::onPlayingFinished() { if ((m_videoWidget != nullptr) && m_videoWidget->isFullScreen()) { m_videoWidget->fullScreen(false); } } ================================================ FILE: QtPlayer/videoplayerwidget.h ================================================ #pragma once #include "mainwindow.h" #include "videoplayer.h" #include #include class VideoControl; class VideoProgressBar; class VideoWidget; class VideoPlayerWidget; inline VideoPlayerWidget* VideoPlayerWidgetInstance() { if (auto mainWindow = getMainWindow()) { return mainWindow->getPlayer(); } return nullptr; } class VideoPlayerWidget : public QFrame, public VideoPlayer { Q_OBJECT public: explicit VideoPlayerWidget(QWidget* parent = nullptr); ~VideoPlayerWidget() override; void pauseVideo(); void resumeVideo(); void stopVideo(bool showDefaultImage = false); bool isPaused(); void seekByPercent(float percent); VideoDisplay* getCurrentDisplay(); VideoWidget* videoWidget() {return m_videoWidget;} void setProgressbar(VideoProgressBar* progressbar); void setControl(VideoControl* controlWidget); void setDefaultPreviewPicture(); QString currentFilename() const; void updateLayout(); void exitFullScreen(); void playFile(const QString& fileName); protected: void resizeEvent(QResizeEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override; public slots: void playPauseButtonAction(); private slots: void setVideoFilename(const QString& fileName); void updateViewOnVideoStop(bool showDefaultImage = true); void onPlayingFinished(); signals: void fileReleased(); private: friend class VideoWidget; VideoControl* m_controls{nullptr}; VideoProgressBar* m_progressBar{nullptr}; QString m_currentFile; VideoWidget* m_videoWidget; }; ================================================ FILE: QtPlayer/videoprogressbar.cpp ================================================ #include "videoprogressbar.h" #include "ffmpegdecoder.h" #include "videoplayerwidget.h" #include #include #include #include #include #include VideoProgressBar::VideoProgressBar(QWidget* parent) : QProgressBar(parent) { m_downloaded = 0; m_played = 0; setWindowTitle("Video Progress Bar"); resize(500, 200); installEventFilter(this); } VideoProgressBar::~VideoProgressBar() = default; void VideoProgressBar::paintEvent(QPaintEvent* event) { Q_UNUSED(event) QPainter painter(this); QLinearGradient gradient(0, 0, 0, height()); int lineheight = height() / 3; int margintop = (height() - lineheight) / 2; // Draw background line gradient.setColorAt(0, QColor(67, 67, 67)); gradient.setColorAt(1, QColor(49, 49, 50)); painter.setBrush(gradient); painter.setPen(Qt::NoPen); painter.drawRect(0, margintop, width(), lineheight); // Downloaded line gradient.setColorAt(0, QColor(235, 235, 235)); gradient.setColorAt(1, QColor(207, 213, 217)); painter.setBrush(gradient); painter.drawRect(0, margintop, (static_cast(m_downloaded) / m_scale) * width(), lineheight); // 3 step : playing line gradient.setColorAt(0, QColor(209, 63, 70)); gradient.setColorAt(1, QColor(177, 10, 11)); painter.setBrush(gradient); painter.drawRect(0, margintop, (static_cast(m_played) / m_scale) * width(), lineheight); QPixmap clicker = QPixmap(":/images/video_seek_cursor.png"); painter.drawPixmap( QRect((m_played / (m_scale - static_cast(clicker.width())*m_scale / (width()))) * width() - 1 - static_cast(clicker.width())*m_played / m_scale * 1.8 , 1, clicker.width(), clicker.height()), clicker, clicker.rect() ); } void VideoProgressBar::setDownloadedCounter(int downloaded) { downloaded = std::clamp(downloaded, 0, m_scale); if (m_downloaded == downloaded) { return; } m_downloaded = downloaded; repaint(); } void VideoProgressBar::setPlayedCounter(int played) { if (played < 0) { played = 0; } if (m_played == played) { return; } m_played = played; repaint(); } int VideoProgressBar::getScale() const { return m_scale; } void VideoProgressBar::resetProgress() { m_downloaded = 0; m_played = 0; repaint(); setToolTip(QString()); if (underMouse()) { QToolTip::hideText(); } } bool VideoProgressBar::eventFilter(QObject* obj, QEvent* event) { //const FFmpegDecoder* decoder = VideoPlayerWidgetInstance()->getDecoder(); QEvent::Type eventType = event->type(); switch (eventType) { case QEvent::MouseMove: { auto* mevent = static_cast(event); float percent = (mevent->x() * 1.0) / width(); if (m_btn_down) { percent = std::clamp(percent, 0.F, 1.F); if (!m_seekDisabled) { VideoPlayerWidgetInstance()->seekByPercent(percent); } } } break; case QEvent::MouseButtonPress: { m_btn_down = true; } break; case QEvent::MouseButtonRelease: { auto* mevent = static_cast(event); float percent = (mevent->x() * 1.0) / width(); percent = std::clamp(percent, 0.F, 1.F); if (!m_seekDisabled) { VideoPlayerWidgetInstance()->seekByPercent(percent); } m_btn_down = false; } break; default:; } return QWidget::eventFilter(obj, event); } void VideoProgressBar::displayPlayedProgress(qint64 frame, qint64 total) { int progress = (static_cast(frame) / total) * getScale(); if (!m_seekDisabled) { setPlayedCounter(progress); } else { setPlayedCounter(0); } } void VideoProgressBar::seekingEnable(bool enable /* = true*/) { m_seekDisabled = !enable; } ================================================ FILE: QtPlayer/videoprogressbar.h ================================================ #pragma once #include class VideoProgressBar : public QProgressBar { Q_OBJECT public: explicit VideoProgressBar(QWidget* parent = nullptr); ~VideoProgressBar() override; int getScale() const; void resetProgress(); protected: void paintEvent(QPaintEvent* event) override; bool eventFilter(QObject* obj, QEvent* event) override; private: int m_downloaded; int m_played; int m_scale{1000}; bool m_btn_down{false}; bool m_seekDisabled{false}; qint64 m_downloadedTotalOriginal{0}; public slots: void setDownloadedCounter(int downloaded); void setPlayedCounter(int played); void seekingEnable(bool enable = true); public slots: void displayPlayedProgress(qint64 frame, qint64 total); }; ================================================ FILE: QtPlayer/videowidget.cpp ================================================ #include "videowidget.h" #include "customdockwidget.h" #include "videoplayerwidget.h" #include #include #include #include namespace { QWidget* videoControlWidget() { return getMainWindow()->videoControlWidget(); } CustomDockWidget* dockWidget() { return getMainWindow()->dockWidget(); } QWidget* getPlayer() { return getMainWindow()->getPlayer(); } } // namespace static const int HEIGHT_FIX = 1; #ifdef DEVELOPER_OPENGL VideoWidget::VideoWidget(VideoPlayerWidget* parent) : OpenGLDisplay(parent), #else VideoWidget::VideoWidget(VideoPlayerWidget* parent) : WidgetDisplay(parent), #endif m_defPlayButton(":/images/video___btn_play___default___(94x94).png"), m_hoverPlayButton(":/images/video___btn_play___hover___(94x94).png"), m_clickedPlayButton(":/images/video___btn_play___clicked___(94x94).png"), m_selImage(&m_defPlayButton) { m_noPreviewImg = QImage(":/images/fvd_banner.png"); setMouseTracking(true); setStyleSheet("background: black;"); #ifndef DEVELOPER_OPENGL setAlignment(Qt::AlignCenter); #endif m_cursorTimer.setInterval(1000); connect(&m_cursorTimer, &QTimer::timeout, this, &VideoWidget::onCursorTimer); m_cursorTimer.start(); } VideoWidget::~VideoWidget() { if (auto videoPlayerWidgetInstance = VideoPlayerWidgetInstance()) { videoPlayerWidgetInstance->m_videoWidget = nullptr; } } void VideoWidget::setDefaultPreviewPicture() { m_startImgButton = m_noPreviewImg; m_pictureSize = m_startImgButton.size(); VideoPlayerWidgetInstance()->updateLayout(); m_fromImage = m_startImgButton; showPicture(m_startImgButton.scaled(width(), height() + HEIGHT_FIX, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); setContentsMargins(0, 0, 0, 0); } QPixmap VideoWidget::drawPreview(const QImage& fromImage) { Q_ASSERT(m_selImage); if (VideoPlayerWidgetInstance()->state() == VideoPlayerWidget::InitialState) { QPixmap preview_with_button(fromImage.width(), fromImage.height()); QPainter painter(&preview_with_button); painter.drawImage(0, 0, fromImage); painter.drawImage(fromImage.width() / 2 - m_selImage->width() / 2, fromImage.height() / 2 - m_selImage->height() / 2, *m_selImage); painter.end(); return preview_with_button; } return QPixmap::fromImage(fromImage); } void VideoWidget::hidePlayButton() { showPicture(m_startImgButton.scaled(width(), height() + HEIGHT_FIX, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } void VideoWidget::getImageFinished(const QImage& image) { m_startImgButton = m_fromImage = image.isNull() ? m_noPreviewImg : image; m_pictureSize = m_startImgButton.size(); if (!isFullScreen()) { // This will set image to layout VideoPlayerWidgetInstance()->updateLayout(); } } void VideoWidget::showElements() { m_lastMouseTime = QDateTime::currentMSecsSinceEpoch(); setCursor(Qt::ArrowCursor); videoControlWidget()->show(); } void VideoWidget::hideElements() { setCursor(Qt::BlankCursor); videoControlWidget()->hide(); } void VideoWidget::keyPressEvent(QKeyEvent* event) { showElements(); // Full screen exiting if (((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && (event->modifiers() & Qt::AltModifier)) || event->key() == Qt::Key_Escape) { if (isFullScreen()) { fullScreen(false); } } } void VideoWidget::mousePressEvent(QMouseEvent* event) { showElements(); if (event->button() == Qt::LeftButton && VideoPlayerWidgetInstance()->state() == VideoPlayerWidget::InitialState && pointInButton(event->pos())) { m_isMousePressed = true; m_selImage = &m_clickedPlayButton; m_startImgButton = m_fromImage; m_pictureSize = m_startImgButton.size(); showPicture(drawPreview(m_startImgButton.scaled(width(), height() + HEIGHT_FIX, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); } } void VideoWidget::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { showElements(); VideoPlayerWidget::VideoState state = VideoPlayerWidgetInstance()->state(); if (state == VideoPlayerWidget::Paused || state == VideoPlayerWidget::Playing) { VideoPlayerWidgetInstance()->playPauseButtonAction(); } else if (m_isMousePressed) { m_isMousePressed = false; m_selImage = pointInButton(event->pos()) ? &m_hoverPlayButton : &m_defPlayButton; m_startImgButton = m_fromImage; m_pictureSize = m_startImgButton.size(); if (m_selImage == &m_hoverPlayButton && state == VideoPlayerWidget::InitialState) { VideoPlayerWidgetInstance()->playPauseButtonAction(); } } } else if (event->button() == Qt::MiddleButton) { if (isFullScreen()) { fullScreen(false); } else { dockWidget()->setVisibilityState(CustomDockWidget::FullScreen); } } } void VideoWidget::mouseMoveEvent(QMouseEvent* event) { showElements(); if (VideoPlayerWidgetInstance()->state() == VideoPlayerWidget::InitialState) { QImage* selImage = m_isMousePressed ? &m_clickedPlayButton : (pointInButton(event->pos()) ? &m_hoverPlayButton : &m_defPlayButton); if (m_selImage != selImage) { m_selImage = selImage; m_startImgButton = m_fromImage; m_pictureSize = m_startImgButton.size(); showPicture(drawPreview(m_startImgButton.scaled(width(), height() + HEIGHT_FIX, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); } } } void VideoWidget::wheelEvent(QWheelEvent* event) { showElements(); VideoPlayerWidgetInstance()->wheelEvent(event); } bool VideoWidget::pointInButton(const QPoint& point) { QPoint centerPoint(width() / 2, height() / 2); if (point.x() >= centerPoint.x() - m_playBtnRadius && point.x() <= centerPoint.x() + m_playBtnRadius && point.y() >= centerPoint.y() - m_playBtnRadius && point.y() <= centerPoint.y() + m_playBtnRadius) { return std::hypot(centerPoint.x() - point.x(), centerPoint.y() - point.y()) < m_playBtnRadius; } return false; } void VideoWidget::updatePlayButton() { if (VideoPlayerWidgetInstance()->state() == VideoPlayerWidget::InitialState) { m_selImage = pointInButton(mapFromGlobal(QCursor::pos())) ? &m_hoverPlayButton : &m_defPlayButton; } } void VideoWidget::fullScreen(bool isEnable) { if (isEnable) { setWindowFlags(Qt::Window); setContentsMargins(0, 0, 0, 0); #ifdef Q_OS_LINUX Q_ASSERT(!m_resizeIndicator); m_resizeIndicator = true; #endif showFullScreen(); #ifndef Q_OS_LINUX fullScreenProcess(); #endif } else { setWindowFlags(Qt::SubWindow); showNormal(); VideoPlayerWidgetInstance()->updateLayout(); emit leaveFullScreen(); QWidget* videoControl = videoControlWidget(); videoControl->setParent(getPlayer(), Qt::SubWindow); videoControl->lower(); videoControl->show(); } } void VideoWidget::fullScreenProcess() { if (VideoPlayerWidget::InitialState == VideoPlayerWidgetInstance()->state()) { updatePlayButton(); //setPreviewPicture(VideoPlayerWidgetInstance()->entity()); } QWidget* videoControl = videoControlWidget(); videoControl->setParent(nullptr, Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint); videoControl->setAttribute(Qt::WA_TranslucentBackground); videoControl->move(this->width() / 2 - videoControl->width() / 2, this->height() - videoControl->height() - 20); videoControl->show(); //auto* spinner = findChild("bufferingSpinner"); //Q_ASSERT(spinner); //spinner->resize(this->width(), this->height()); } void VideoWidget::resizeEvent(QResizeEvent* event) { #ifdef Q_OS_LINUX if (m_resizeIndicator) { fullScreenProcess(); m_resizeIndicator = false; } #endif // FIXME: OpenGL full support #ifdef DEVELOPER_OPENGL OpenGLDisplay::resizeEvent(event); #endif } #ifndef DEVELOPER_OPENGL void VideoWidget::currentDisplay(unsigned int generation) { WidgetDisplay::currentDisplay(generation); m_originalFrame = *pixmap(); } #endif void VideoWidget::onCursorTimer() { if (isFullScreen() && (QDateTime::currentMSecsSinceEpoch() - m_lastMouseTime > 2000)) { hideElements(); } } VideoPlayerWidget* VideoWidget::VideoPlayerWidgetInstance() { return static_cast(parent()); } ================================================ FILE: QtPlayer/videowidget.h ================================================ #pragma once #include #include #include #ifdef DEVELOPER_OPENGL #include "opengldisplay.h" #else #include "widgetdisplay.h" #endif class VideoPlayerWidget; // FIXME: OpenGL full support #ifdef DEVELOPER_OPENGL class VideoWidget : public OpenGLDisplay #else class VideoWidget : public WidgetDisplay #endif { Q_OBJECT Q_PROPERTY(QImage m_noPreviewImg READ noPreviewImage WRITE setNoPreviewImage); public: explicit VideoWidget(VideoPlayerWidget* parent = nullptr); ~VideoWidget() override; void setDefaultPreviewPicture(); QSize getPictureSize() const { return m_pictureSize; } QPixmap originalFrame() const { return m_originalFrame; } QImage startImageButton() const { return m_startImgButton; } QImage noPreviewImage() const { return m_noPreviewImg; } void setNoPreviewImage(const QImage& noImage) { m_noPreviewImg = noImage; } QPixmap drawPreview(const QImage& fromImage); void hidePlayButton(); void updatePlayButton(); protected: void keyPressEvent(QKeyEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void wheelEvent(QWheelEvent* event) override; void resizeEvent(QResizeEvent* event) override; VideoPlayerWidget* VideoPlayerWidgetInstance(); QImage m_startImgButton; QImage m_noPreviewImg; QPixmap m_originalFrame; private: bool m_playIndicator{false}; QSize m_pictureSize; QImage m_defPlayButton, m_hoverPlayButton, m_clickedPlayButton; QImage* m_selImage; QImage m_fromImage; bool m_isMousePressed{false}; const int m_playBtnRadius{29}; #ifdef Q_OS_LINUX bool m_resizeIndicator = false; #endif qint64 m_lastMouseTime{0}; QTimer m_cursorTimer; bool pointInButton(const QPoint& point); void showElements(); void hideElements(); public Q_SLOTS: void fullScreen(bool isEnable = true); protected Q_SLOTS: #ifndef DEVELOPER_OPENGL virtual void currentDisplay(unsigned int generation) override; #endif private Q_SLOTS: void getImageFinished(const QImage& image); void onCursorTimer(); void fullScreenProcess(); Q_SIGNALS: void leaveFullScreen(); void mouseClicked(); }; ================================================ FILE: QtPlayer/volumeprogressbar.cpp ================================================ #include "volumeprogressbar.h" #include VolumeProgressBar::VolumeProgressBar(QWidget* parent) : QProgressBar(parent), m_borderBrush(QColor(101, 105, 108)), m_backBrush(QColor(66, 66, 66)), m_fillBorderBrush(QColor(176, 180, 183)), m_fillBrush(QColor(255, 255, 255)) { } VolumeProgressBar::~VolumeProgressBar() = default; void VolumeProgressBar::paintEvent(QPaintEvent* event) { Q_UNUSED(event) QPainter painter(this); painter.fillRect(0, 0, width(), height(), m_borderBrush); painter.fillRect(1, 1, width() - 2, height() - 2, m_backBrush); int filledWidth = static_cast(width() / 100.0 * value()); painter.fillRect(0, 0, filledWidth, height(), m_fillBorderBrush); if (filledWidth > 1) { painter.fillRect(1, 1, filledWidth - 2, height() - 2, m_fillBrush); } } ================================================ FILE: QtPlayer/volumeprogressbar.h ================================================ #pragma once #include class VolumeProgressBar : public QProgressBar { Q_OBJECT public: VolumeProgressBar(QWidget* parent); ~VolumeProgressBar() override; protected: void paintEvent(QPaintEvent* event) override; private: QBrush m_borderBrush, m_backBrush; QBrush m_fillBorderBrush, m_fillBrush; }; ================================================ FILE: QtPlayer/widgetdisplay.cpp ================================================ #include "widgetdisplay.h" #include #include WidgetDisplay::WidgetDisplay(QWidget* parent) : QLabel(parent) { setScaledContents(true); connect(this, &WidgetDisplay::display, this, &WidgetDisplay::currentDisplay); } void WidgetDisplay::currentDisplay(unsigned int generation) { m_display = QPixmap::fromImage(m_image); setPixmap(m_display); finishedDisplayingFrame(generation); } void WidgetDisplay::showPicture(const QImage& picture) { showPicture(QPixmap::fromImage(picture)); } void WidgetDisplay::showPicture(const QPixmap& picture) { setPixmap(picture); } void WidgetDisplay::updateFrame(IFrameDecoder* decoder, unsigned int generation) { FrameRenderingData data; if (!decoder->getFrameRenderingData(&data)) { return; } m_aspectRatio = float(data.height) / data.width; m_image = QImage(data.image[0], data.width, data.height, data.pitch[0], QImage::Format_RGB888); } void WidgetDisplay::drawFrame(IFrameDecoder* decoder, unsigned int generation) { emit display(generation); } void WidgetDisplay::decoderClosing() { } ================================================ FILE: QtPlayer/widgetdisplay.h ================================================ #pragma once #include "videodisplay.h" #include #include #include class WidgetDisplay : public QLabel, public VideoDisplay { Q_OBJECT public: WidgetDisplay(QWidget* parent = nullptr); ~WidgetDisplay() override = default; void showPicture(const QImage& picture) override; void showPicture(const QPixmap& picture) override; // decoder->finishedDisplayingFrame() must be called void updateFrame(IFrameDecoder* decoder, unsigned int generation) override; void drawFrame(IFrameDecoder* decoder, unsigned int generation) override; void decoderClosing() override; float aspectRatio() const { return m_aspectRatio; } protected: QImage m_image; QPixmap m_display; float m_aspectRatio { 0.75F }; protected slots: virtual void currentDisplay(unsigned int generation); signals: void display(unsigned int generation); }; ================================================ FILE: README.md ================================================ # ℉ℲmpegPlayer A simple FFmpeg based player. The player core is generic and made with multiplatformity in mind. UI / video / audio layer is MFC/Win32 specific. It turns out that there is no need to use multimedia libraries. There is also a Qt based demo example included. It offers: - Basic Playback Controls: Play/Pause, Stop. - Next/Previous Frame: Step through the video one frame at a time during pause. - Speed Change: Increase or decrease the playback speed without altering the pitch of the audio. - Separate Video and Audio Inputs: Ability to load and play video and audio from separate sources. - Audio Track Selection: Choose between different audio tracks if available. - Fragment Selection for Export: Mark in and out points to select a part of the video for exporting. - Repeated Playing: Loop the entire video/playlist or selected fragment continuously. - Subtitles: Load and display subtitle files in various formats. - Super Resolution: Enhance the resolution of the video using upscaling techniques. - Codec Support: Compatibility with a wide range of video and audio codecs. - Streaming Support: Ability to stream video from online sources. [Semi transparent, click through full screen mode introduced.](https://bit.ly/2JLTbQn) It is invokable by holding ctrl+shift while pressing full screen button. ## Getting Started These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. ### Prerequisites - Visual Studio 2017 or higher. - vcpkg. ### Installing Be sure to download git submodules. To get a development env running: Install vcpkg from https://github.com/Microsoft/vcpkg. ``` .\vcpkg integrate install ``` Install Boost, FFmpeg, OpenCV etc. Details can be found in .github/workflows/msbuild.yml. Create ./Directory.Build.props file in the project folder. It contents depend on you environment, for example: ``` 10.0.22621.0 v4.6.2 v143 ``` YouTube view support is turned on by default. It can be turned off by commenting define YOUTUBE_EXPERIMENT in YouTuber.cpp. A special mode has been implemented: Direct‑IP no‑SNI Host‑Override CDN‑Pinning Mode. You can do without SNI by using the IP address in the URL and the Host header to identify the content. The matching Python version has to be installed for the accessory DLLs to be accessible, except for embedded Python coming with installation. In any case Pytubefix now requires Node.js installed. Just in case: "In fact in boost-python, the default behavior is that even when debug boost libraries are created, these libraries are linked to the release pythonX.dll/lib - by intention, according to the docs." https://github.com/pybind/pybind11/issues/1295 Tiny demos here: https://www.youtube.com/watch?v=dySA4yEGdEc https://www.youtube.com/watch?v=t5iW2ZsEzrA Tip: hold Ctrl+Shift while submitting File Open dialog to choose a separate audio file. It works for the file opening from the Windows Explorer as well. Please take into account specific Windows 10 behavior while opening Internet shortcuts: https://community.spiceworks.com/topic/1968971-opening-web-links-downloading-1-item-to-zcrksihu You can avoid this by dragging and dropping them. Note that the FFmpeg patch speeds up HEVC decoding without GPU support by ~10%: ![image](https://user-images.githubusercontent.com/11851670/171165625-3a111046-672c-4a75-8184-c91fde994e00.png) ### 🎬 Video Conversion Script Generation This feature generates and runs a batch script that converts selected video files into a format compatible with basic players. The script adapts dynamically based on playback settings and optional media inputs. #### 🔧 Controlled via Menu Options: File → Convert Videos into Compatible Format Triggers the generation and running of a conversion script using FFmpeg. The script includes commands to re-encode or copy video, audio, and subtitle streams based on compatibility and user preferences. File → Autoplay When enabled, the script processes a sequence of video files for automatic conversion. If combined with Looping, the entire sequence is included. If Looping is disabled, only the current and following files are processed. File → Looping When Autoplay is disabled, this option includes both the current file and its predecessors in the conversion script. When Autoplay is enabled, it loops through the entire detected sequence of files. Audio / Video → Open Audio File... Allows users to specify a separate audio file to be merged with the video during conversion. If provided, the script maps video from the original files and audio from the separate files. Audio / Video → Open Subtitles File... Enables users to attach external subtitle files. These are converted to UTF-8 encoding using ToUTF8.exe and saved alongside the converted videos. #### 🛠️ Conversion Details: Uses FFmpeg for media processing. Selects codecs based on compatibility: libx264 for video re-encoding if needed aac for audio if separate or incompatible Copies subtitle streams when available Outputs converted files to the specified folder, preserving original filenames. ### Bonus tip Playing YouTube videos in browsers may result in poor performance on slow hardware. Assign a keyboard shortcut to the FFmpeg player by editing its shortcut. Hover your mouse over the YouTube link in Firefox and bring up the shortcut. A player pop-up window will appear, starting the video playback. The same can be achieved in Chrome with some tweaking. [Start Chrome with this flag: --force-renderer-accessibility](https://www.chromium.org/developers/design-documents/accessibility/) and / or [set up IAccessible2 COM proxy stub DLL](https://github.com/aliakseis/IAccessible2Proxy). ![redline](https://user-images.githubusercontent.com/11851670/184552270-73cb8ba4-31f7-47f2-9f50-2b4ceae601e7.gif) ================================================ FILE: Setup/Setup.vdproj ================================================ "DeployProject" { "VSVersion" = "3:800" "ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}" "IsWebType" = "8:FALSE" "ProjectName" = "8:Setup" "LanguageId" = "3:1033" "CodePage" = "3:1252" "UILanguageId" = "3:1033" "SccProjectName" = "8:" "SccLocalPath" = "8:" "SccAuxPath" = "8:" "SccProvider" = "8:" "BackwardsCompatibleGUIDGeneration" = "8:TRUE" "Hierarchy" { "Entry" { "MsmKey" = "8:_02EFEE03BFE64741B0ABE34B02FC32B5" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_04D9482C98764C51AD4E4D730BAEBE29" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_13D05759FE0A4D95A0231F4083F018C4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_195C5150B8964F18BE6B37EC32F0A6A2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1ABC4268EB654842AFAAD1EC6895A7BA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1BB4844D47E14A15B89CF19C542161BB" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1E65B5DFF0DC498E88CA9D3A7C275D76" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_1F243CD6EC02496B996FBE37FD926E0C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_264B785039E04289B2A9C0F511AD4551" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_277BDF4042FB4CACAB6E1AB94081D4DA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_28ED1968ACAD4AAC8B67914CE9F9DA96" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3262A9A7818C4E61A47966A1AC635C9D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3287D5542CEB463FBCF4C232B67F1661" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_432363CD76D0480AA7084E8FF9F2BE71" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4E1B2E066177453BAA56F6A639DA8C91" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5248F3DEC0F84A5EA1E49E4228FC507F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5661D94E79A44B658C8B5D3D6144F5EA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5A15AAA6BD1C4EB5AF34273674A620C9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5AEE0FDF644E421AA8CE0C416041E22E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5F83FC91573A48E4942BBFA02E16470E" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_6B047B7A63CC46B895F0CF653C4202FD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_773E956ED5734071898B809DF7A09F5C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_78A6F9F7FFB14C2681C7884E42D8F58A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7BBE6F8C77DD46679E86F836865E5268" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7CFE85030EA24C6DB5A69D7E9D4B9870" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_7EAF2BD4D7C6410AA00673E7A387C976" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_81363E05C5EF41ACA5813F69543E574D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_854599C408B14066937C40F97C68BD97" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_8E7646220BFC46EEBA194DECA90226DA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_966194489C5E48A59EDB67DA6102C198" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_96D017BD77B34DDDABD27B278D0313EF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_99FE9F86BECE469DAF1A2E8A801A82E1" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9FE3E71BA9214645891A4C52B44EC846" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A4B43CDF9CA54C19AF7D23DB37A100AC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B046520A3F94442E894F11443DE3C515" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BB3F2FE2B1344C3A89B5586F5E476566" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CFF418461B324683A7ACE56113EC015D" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D67C2908BF6B4AB2806D3E5701BDF09A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E36BE16F84CF4B1F8F13230F495EB066" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E6740841F93C44CCB118D19E99E2C1D6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FA750DBCBC084FE991EDD407C9629097" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } } "Configurations" { "Debug" { "DisplayName" = "8:Debug" "IsDebugOnly" = "11:TRUE" "IsReleaseOnly" = "11:FALSE" "OutputFilename" = "8:Debug\\FFmpegPlayerSetup.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" "Compression" = "3:2" "SignOutput" = "11:FALSE" "CertificateFile" = "8:" "PrivateKeyFile" = "8:" "TimeStampServer" = "8:" "InstallerBootstrapper" = "3:2" "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" { "Enabled" = "11:TRUE" "PromptEnabled" = "11:TRUE" "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" "Items" { "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.5" { "Name" = "8:Microsoft .NET Framework 4.5 (x86 and x64)" "ProductCode" = "8:.NETFramework,Version=v4.5" } } } } "Release" { "DisplayName" = "8:Release" "IsDebugOnly" = "11:FALSE" "IsReleaseOnly" = "11:TRUE" "OutputFilename" = "8:Release\\FFmpegPlayerSetup.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" "Compression" = "3:2" "SignOutput" = "11:FALSE" "CertificateFile" = "8:" "PrivateKeyFile" = "8:" "TimeStampServer" = "8:" "InstallerBootstrapper" = "3:2" "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" { "Enabled" = "11:TRUE" "PromptEnabled" = "11:TRUE" "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" "Items" { } } } } "Deployable" { "CustomAction" { } "DefaultFeature" { "Name" = "8:DefaultFeature" "Title" = "8:" "Description" = "8:" } "ExternalPersistence" { "LaunchCondition" { } } "File" { "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_04D9482C98764C51AD4E4D730BAEBE29" { "SourcePath" = "8:C:\\python-embed-win32\\_sqlite3.pyd" "TargetName" = "8:_sqlite3.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_195C5150B8964F18BE6B37EC32F0A6A2" { "SourcePath" = "8:C:\\python-embed-win32\\_ssl.pyd" "TargetName" = "8:_ssl.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1ABC4268EB654842AFAAD1EC6895A7BA" { "SourcePath" = "8:C:\\python-embed-win32\\_uuid.pyd" "TargetName" = "8:_uuid.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1BB4844D47E14A15B89CF19C542161BB" { "SourcePath" = "8:..\\edit_git_subst_cfg.cmd" "TargetName" = "8:edit_git_subst_cfg.cmd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1E65B5DFF0DC498E88CA9D3A7C275D76" { "SourcePath" = "8:C:\\python-embed-win32\\_hashlib.pyd" "TargetName" = "8:_hashlib.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1F243CD6EC02496B996FBE37FD926E0C" { "SourcePath" = "8:C:\\python-embed-win32\\_zoneinfo.pyd" "TargetName" = "8:_zoneinfo.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_264B785039E04289B2A9C0F511AD4551" { "SourcePath" = "8:C:\\python-embed-win32\\_elementtree.pyd" "TargetName" = "8:_elementtree.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_277BDF4042FB4CACAB6E1AB94081D4DA" { "SourcePath" = "8:C:\\python-embed-win32\\unicodedata.pyd" "TargetName" = "8:unicodedata.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_28ED1968ACAD4AAC8B67914CE9F9DA96" { "SourcePath" = "8:C:\\python-embed-win32\\python.cat" "TargetName" = "8:python.cat" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7818C4E61A47966A1AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\ffmpeg.exe" "TargetName" = "8:ffmpeg.exe" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7828C4E61A47966A2AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\avutil-56.dll" "TargetName" = "8:avutil-56.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7838C4E61A47966A3AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\swscale-5.dll" "TargetName" = "8:swscale-5.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7848C4E61A47966A4AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\swresample-3.dll" "TargetName" = "8:swresample-3.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7858C4E61A47966A5AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\postproc-55.dll" "TargetName" = "8:postproc-55.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7868C4E61A47966A6AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\avresample-4.dll" "TargetName" = "8:avresample-4.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7878C4E61A47966A7AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\avcodec-58.dll" "TargetName" = "8:avcodec-58.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7888C4E61A47966A8AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\avformat-58.dll" "TargetName" = "8:avformat-58.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A7898C4E61A47966A9AC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\avfilter-7.dll" "TargetName" = "8:avfilter-7.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A78A8C4E61A47966AAAC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\avdevice-58.dll" "TargetName" = "8:avdevice-58.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A78B8C4E61A47966ABAC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\libx264-157.dll" "TargetName" = "8:libx264-157.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A78C8C4E61A47966ACAC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\webp.dll" "TargetName" = "8:webp.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3262A9A78D8C4E61A47966ADAC635C9D" { "SourcePath" = "8:C:\\vcpkg\\installed\\x86-windows\\tools\\ffmpeg\\zlib1.dll" "TargetName" = "8:zlib1.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3287D5542CEB463FBCF4C232B67F1661" { "SourcePath" = "8:C:\\python-embed-win32\\_decimal.pyd" "TargetName" = "8:_decimal.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_432363CD76D0480AA7084E8FF9F2BE71" { "SourcePath" = "8:..\\remove_youtube_transcript_api.cmd" "TargetName" = "8:remove_youtube_transcript_api.cmd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5248F3DEC0F84A5EA1E49E4228FC507F" { "SourcePath" = "8:C:\\python-embed-win32\\python39.zip" "TargetName" = "8:python39.zip" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5661D94E79A44B658C8B5D3D6144F5EA" { "SourcePath" = "8:C:\\python-embed-win32\\python.exe" "TargetName" = "8:python.exe" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5A15AAA6BD1C4EB5AF34273674A620C9" { "SourcePath" = "8:C:\\python-embed-win32\\pythonw.exe" "TargetName" = "8:pythonw.exe" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5AEE0FDF644E421AA8CE0C416041E22E" { "SourcePath" = "8:C:\\python-embed-win32\\_msi.pyd" "TargetName" = "8:_msi.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5F83FC91573A48E4942BBFA02E16470E" { "SourcePath" = "8:C:\\python-embed-win32\\winsound.pyd" "TargetName" = "8:winsound.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6B047B7A63CC46B895F0CF653C4202FD" { "SourcePath" = "8:C:\\python-embed-win32\\_overlapped.pyd" "TargetName" = "8:_overlapped.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_773E956ED5734071898B809DF7A09F5C" { "SourcePath" = "8:C:\\python-embed-win32\\pyexpat.pyd" "TargetName" = "8:pyexpat.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_78A6F9F7FFB14C2681C7884E42D8F58A" { "SourcePath" = "8:C:\\python-embed-win32\\_lzma.pyd" "TargetName" = "8:_lzma.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7BBE6F8C77DD46679E86F836865E5268" { "SourcePath" = "8:..\\Player\\res\\Player.ico" "TargetName" = "8:Player.ico" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7CFE85030EA24C6DB5A69D7E9D4B9870" { "SourcePath" = "8:C:\\python-embed-win32\\_bz2.pyd" "TargetName" = "8:_bz2.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7EAF2BD4D7C6410AA00673E7A387C976" { "SourcePath" = "8:C:\\python-embed-win32\\libcrypto-1_1.dll" "TargetName" = "8:libcrypto-1_1.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_81363E05C5EF41ACA5813F69543E574D" { "SourcePath" = "8:C:\\python-embed-win32\\sqlite3.dll" "TargetName" = "8:sqlite3.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_854599C408B14066937C40F97C68BD97" { "SourcePath" = "8:C:\\python-embed-win32\\select.pyd" "TargetName" = "8:select.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8E7646220BFC46EEBA194DECA90226DA" { "SourcePath" = "8:C:\\python-embed-win32\\python39._pth" "TargetName" = "8:python39._pth" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_966194489C5E48A59EDB67DA6102C198" { "SourcePath" = "8:C:\\python-embed-win32\\_ctypes.pyd" "TargetName" = "8:_ctypes.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_96D017BD77B34DDDABD27B278D0313EF" { "SourcePath" = "8:C:\\python-embed-win32\\libffi-7.dll" "TargetName" = "8:libffi-7.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_99FE9F86BECE469DAF1A2E8A801A82E1" { "SourcePath" = "8:C:\\python-embed-win32\\_asyncio.pyd" "TargetName" = "8:_asyncio.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9FE3E71BA9214645891A4C52B44EC846" { "SourcePath" = "8:C:\\python-embed-win32\\libssl-1_1.dll" "TargetName" = "8:libssl-1_1.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A4B43CDF9CA54C19AF7D23DB37A100AC" { "SourcePath" = "8:..\\remove_pytube.cmd" "TargetName" = "8:remove_pytube.cmd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B046520A3F94442E894F11443DE3C515" { "SourcePath" = "8:C:\\python-embed-win32\\_socket.pyd" "TargetName" = "8:_socket.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BB3F2FE2B1344C3A89B5586F5E476566" { "SourcePath" = "8:C:\\python-embed-win32\\LICENSE.txt" "TargetName" = "8:LICENSE.txt" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CFF418461B324683A7ACE56113EC015D" { "SourcePath" = "8:C:\\python-embed-win32\\python3.dll" "TargetName" = "8:python3.dll" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D67C2908BF6B4AB2806D3E5701BDF09A" { "SourcePath" = "8:C:\\python-embed-win32\\_multiprocessing.pyd" "TargetName" = "8:_multiprocessing.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E6740841F93C44CCB118D19E99E2C1D6" { "SourcePath" = "8:C:\\python-embed-win32\\_queue.pyd" "TargetName" = "8:_queue.pyd" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } } "FileType" { } "Folder" { "{1525181F-901A-416C-8A58-119130FE478E}:_164318547FB24CA28F6F77C529476D36" { "Name" = "8:#1916" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:DesktopFolder" "Folders" { } } "{1525181F-901A-416C-8A58-119130FE478E}:_575FB8B12F0B4A8FA84C53016FE10EE5" { "Name" = "8:#1919" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:ProgramMenuFolder" "Folders" { "{9EF0B969-E518-4E46-987F-47570745A589}:_9B328E46D8A9465692C2C0A26ECD0EB4" { "Name" = "8:FFmpeg Player" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_E74CB00121364B60BD4599770E8BB2F0" "Folders" { } } } } "{3C67513D-01DD-4637-8A68-80971EB9504F}:_B8C7CD6341EA4A8BAFE1F90F54D378CA" { "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]" "Name" = "8:#1925" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:TARGETDIR" "Folders" { } } } "LaunchCondition" { } "Locator" { } "MsiBootstrapper" { "LangId" = "3:1033" "RequiresElevation" = "11:FALSE" } "Product" { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:FFmpeg Player" "ProductCode" = "8:{75A04463-09D1-4BE7-A045-58CACA8F9664}" "PackageCode" = "8:{5FB1158C-B95D-4B33-BC3A-0B88305C2B3B}" "UpgradeCode" = "8:{0D785376-AABE-42D5-B21F-916C345075C2}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:FALSE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:FALSE" "ProductVersion" = "8:0.1.60" "Manufacturer" = "8:aliakseis" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" "Title" = "8:FFmpeg Player Setup" "Subject" = "8:" "ARPCONTACT" = "8:aliakseis" "Keywords" = "8:" "ARPCOMMENTS" = "8:" "ARPURLINFOABOUT" = "8:" "ARPPRODUCTICON" = "8:" "ARPIconIndex" = "3:0" "SearchPath" = "8:" "UseSystemSearchPath" = "11:TRUE" "TargetPlatform" = "3:0" "PreBuildEvent" = "8:" "PostBuildEvent" = "8:" "RunPostBuildEvent" = "3:0" } "Registry" { "HKLM" { "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_8B53D0A75414407DAC4C298784EF3141" { "Name" = "8:Software" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_D5C8CECBFFAF422CA3F468589A3F86F1" { "Name" = "8:[Manufacturer]" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { } "Values" { } } } "Values" { } } } } "HKCU" { "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_CE9477C0C34D4D29B75118BAE38E083D" { "Name" = "8:Software" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_5AD054C81E184E23B61586D130BF43E2" { "Name" = "8:[Manufacturer]" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { } "Values" { } } } "Values" { } } } } "HKCR" { "Keys" { } } "HKU" { "Keys" { } } "HKPU" { "Keys" { } } } "Sequences" { } "Shortcut" { "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_0279D2BC938B460AB0B3840652A35B5E" { "Name" = "8:Remove pytube" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:7" "IconIndex" = "3:0" "Transitive" = "11:FALSE" "Target" = "8:_A4B43CDF9CA54C19AF7D23DB37A100AC" "Folder" = "8:_9B328E46D8A9465692C2C0A26ECD0EB4" "WorkingFolder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Icon" = "8:" "Feature" = "8:" } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_029CAA7D4EB448C98C4D939B2F49B1AE" { "Name" = "8:FFmpeg Player" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" "IconIndex" = "3:0" "Transitive" = "11:FALSE" "Target" = "8:_FA750DBCBC084FE991EDD407C9629097" "Folder" = "8:_9B328E46D8A9465692C2C0A26ECD0EB4" "WorkingFolder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Icon" = "8:_7BBE6F8C77DD46679E86F836865E5268" "Feature" = "8:" } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_39657F46CF514780A9D5D8B756C09F30" { "Name" = "8:Remove youtube_transcript_api" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:7" "IconIndex" = "3:0" "Transitive" = "11:FALSE" "Target" = "8:_432363CD76D0480AA7084E8FF9F2BE71" "Folder" = "8:_9B328E46D8A9465692C2C0A26ECD0EB4" "WorkingFolder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Icon" = "8:" "Feature" = "8:" } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_7927A90BD084447D927AF769176382C4" { "Name" = "8:Modify git_subst.cfg" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:7" "IconIndex" = "3:0" "Transitive" = "11:FALSE" "Target" = "8:_1BB4844D47E14A15B89CF19C542161BB" "Folder" = "8:_9B328E46D8A9465692C2C0A26ECD0EB4" "WorkingFolder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Icon" = "8:" "Feature" = "8:" } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_868418E8894F4D2F9641A497BCEE6D47" { "Name" = "8:FFmpeg Player" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" "IconIndex" = "3:0" "Transitive" = "11:FALSE" "Target" = "8:_FA750DBCBC084FE991EDD407C9629097" "Folder" = "8:_164318547FB24CA28F6F77C529476D36" "WorkingFolder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Icon" = "8:_7BBE6F8C77DD46679E86F836865E5268" "Feature" = "8:" } } "UserInterface" { "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_393FF30E69B34CD8BD878D4DD6D8EFA3" { "Name" = "8:#1901" "Sequence" = "3:1" "Attributes" = "3:2" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_043AA40592BF4398847515376E58417A" { "Sequence" = "3:100" "DisplayName" = "8:Progress" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdProgressDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "ShowProgress" { "Name" = "8:ShowProgress" "DisplayName" = "8:#1009" "Description" = "8:#1109" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_5A4E5B83027E4E98829E4F6C0062EFE4" { "Name" = "8:#1902" "Sequence" = "3:2" "Attributes" = "3:3" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_2D89A43D8D1D46D2A91B0B0A5680B5FE" { "Sequence" = "3:100" "DisplayName" = "8:Finished" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminFinishedDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_7772B3E764094DD0AB14C513B0CA0143" { "Name" = "8:#1901" "Sequence" = "3:2" "Attributes" = "3:2" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_E267556BF8874F83A64C71B33B0D327A" { "Sequence" = "3:100" "DisplayName" = "8:Progress" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminProgressDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "ShowProgress" { "Name" = "8:ShowProgress" "DisplayName" = "8:#1009" "Description" = "8:#1109" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_95F2869C2D0E48FB970C95D59E7A999E" { "Name" = "8:#1902" "Sequence" = "3:1" "Attributes" = "3:3" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_8144A416E6DD44D59FCA783CDD83C11A" { "Sequence" = "3:100" "DisplayName" = "8:Finished" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdFinishedDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "UpdateText" { "Name" = "8:UpdateText" "DisplayName" = "8:#1058" "Description" = "8:#1158" "Type" = "3:15" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1258" "DefaultValue" = "8:#1258" "UsePlugInResources" = "11:TRUE" } } } } } "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_A5AA2C6D0AF24A8692A8DBC2C610F558" { "UseDynamicProperties" = "11:FALSE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdUserInterface.wim" } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_C50ADF40726F4E1FBEA40BC383EC61E2" { "Name" = "8:#1900" "Sequence" = "3:2" "Attributes" = "3:1" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_2A72F1DAA40242A7B1401E58C2E2FD05" { "Sequence" = "3:300" "DisplayName" = "8:Confirm Installation" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminConfirmDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_5E783BE669C84563892F59A2111A831F" { "Sequence" = "3:100" "DisplayName" = "8:Welcome" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminWelcomeDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "CopyrightWarning" { "Name" = "8:CopyrightWarning" "DisplayName" = "8:#1002" "Description" = "8:#1102" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:2" "Value" = "8:" "DefaultValue" = "8:#1202" "UsePlugInResources" = "11:TRUE" } "Welcome" { "Name" = "8:Welcome" "DisplayName" = "8:#1003" "Description" = "8:#1103" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1203" "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_B113448BB15A4F0A973E12015C5F0960" { "Sequence" = "3:200" "DisplayName" = "8:Installation Folder" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminFolderDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_E7EAECAC199E486B82068B8BFE02B183" { "Name" = "8:#1900" "Sequence" = "3:1" "Attributes" = "3:1" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_3A13CD611BCD4060ADF44B3B2BCC21C3" { "Sequence" = "3:300" "DisplayName" = "8:Confirm Installation" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdConfirmDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_A5266A82833A4A9A89F20785556069A0" { "Sequence" = "3:100" "DisplayName" = "8:Welcome" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdWelcomeDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "CopyrightWarning" { "Name" = "8:CopyrightWarning" "DisplayName" = "8:#1002" "Description" = "8:#1102" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:2" "Value" = "8:" "DefaultValue" = "8:#1202" "UsePlugInResources" = "11:TRUE" } "Welcome" { "Name" = "8:Welcome" "DisplayName" = "8:#1003" "Description" = "8:#1103" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1203" "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_BAFBB03D526A404DA170E80641D3DAE0" { "Sequence" = "3:200" "DisplayName" = "8:Installation Folder" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdFolderDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "InstallAllUsersVisible" { "Name" = "8:InstallAllUsersVisible" "DisplayName" = "8:#1059" "Description" = "8:#1159" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_FEDCE1090B054055A6CB8FE90EB522D6" { "UseDynamicProperties" = "11:FALSE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdBasicDialogs.wim" } } "MergeModule" { "{CEE29DC0-9FBA-4B99-8D47-5BC643D9B626}:_4E1B2E066177453BAA56F6A639DA8C91" { "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Redist\\MSVC\\14.16.27012\\MergeModules\\Microsoft_VC141_MFC_x86.msm" "Properties" { } "LanguageId" = "3:0" "Exclude" = "11:FALSE" "Folder" = "8:" "Feature" = "8:" "IsolateTo" = "8:" } "{CEE29DC0-9FBA-4B99-8D47-5BC643D9B626}:_E36BE16F84CF4B1F8F13230F495EB066" { "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Redist\\MSVC\\14.16.27012\\MergeModules\\Microsoft_VC141_CRT_x86.msm" "Properties" { } "LanguageId" = "3:0" "Exclude" = "11:FALSE" "Folder" = "8:" "Feature" = "8:" "IsolateTo" = "8:" } } "ProjectOutput" { "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_02EFEE03BFE64741B0ABE34B02FC32B5" { "SourcePath" = "8:..\\Release\\HttpDownload.exe" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" "ProjectOutputGroupRegister" = "3:1" "OutputConfiguration" = "8:" "OutputGroupCanonicalName" = "8:Built" "OutputProjectGuid" = "8:{A4113679-4736-494B-B8D2-3C35B34E5491}" "ShowKeyOutput" = "11:TRUE" "ExcludeFilters" { } } "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_13D05759FE0A4D95A0231F4083F018C4" { "SourcePath" = "8:..\\Release\\ToUTF8.exe" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" "ProjectOutputGroupRegister" = "3:1" "OutputConfiguration" = "8:" "OutputGroupCanonicalName" = "8:Built" "OutputProjectGuid" = "8:{F082EF32-A1D0-48B9-9AF1-DC56178EDE92}" "ShowKeyOutput" = "11:TRUE" "ExcludeFilters" { } } "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_C9AF4EE415C543558DFD791707994B89" { "SourcePath" = "8:" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" "ProjectOutputGroupRegister" = "3:1" "OutputConfiguration" = "8:" "OutputGroupCanonicalName" = "8:ContentFiles" "OutputProjectGuid" = "8:{BC1BC9F1-893D-4715-818A-F37F74EB5710}" "ShowKeyOutput" = "11:TRUE" "ExcludeFilters" { } } "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_FA750DBCBC084FE991EDD407C9629097" { "SourcePath" = "8:..\\Release\\Player.exe" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_B8C7CD6341EA4A8BAFE1F90F54D378CA" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" "ProjectOutputGroupRegister" = "3:1" "OutputConfiguration" = "8:" "OutputGroupCanonicalName" = "8:Built" "OutputProjectGuid" = "8:{C90196CD-7354-4ED4-BFC0-E51A7E1CBE3C}" "ShowKeyOutput" = "11:TRUE" "ExcludeFilters" { } } } } } ================================================ FILE: ThirdParty/include/cmdline/cmdline.hpp ================================================ /* Copyright (c) 2009, Hideyuki Tanaka All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace cmdline { namespace detail { template struct lexical_cast_t { static Target cast(const Source &arg) { return static_cast(arg); } }; template struct lexical_cast_t { static std::string cast(const Source &arg) { return std::to_string(arg); } }; template struct lexical_cast_t { static Target cast(const std::string &arg) { static std::istringstream iss; Target ret{}; iss.str(arg); iss >> ret; iss.clear(); return ret; } }; template ::value> * = nullptr> Target lexical_cast(const Source &arg) { return lexical_cast_t::cast(arg); } template ::value> * = nullptr> Target lexical_cast(const Source &arg) { return arg; } template ::value> * = nullptr> std::string readable_typename() { return "Integer"; } template ::value> * = nullptr> std::string readable_typename() { return "Floating-point"; } template ::value> * = nullptr> std::string readable_typename() { return "String"; } template std::string default_value(T def) { return detail::lexical_cast(def); } } // detail //----- template struct default_reader { T operator()(const std::string &str) { return detail::lexical_cast(str); } }; template struct range_reader { range_reader(const T &low, const T &high) : low(low), high(high) {} T operator()(const std::string &s) const { T ret = default_reader()(s); if (!(ret >= low && ret <= high)) throw std::exception(); return ret; } private: T low, high; }; template range_reader range(const T &low, const T &high) { return range_reader(low, high); } template struct oneof_reader { oneof_reader(std::initializer_list list) : alt(list) {} T operator()(const std::string &s) { T ret = default_reader()(s); if (std::find(alt.begin(), alt.end(), ret) == alt.end()) throw std::exception(); return ret; } private: std::vector alt; }; template oneof_reader oneof(T a1, Args... args) { return oneof_reader{a1, args...}; } template auto custom_reader(Checker &&checker) { return [checker](const std::string &s) -> Target { Target ret = default_reader()(s); if (!checker(ret)) throw std::exception(); return ret; }; } //----- class parser { public: void add(const std::string &name, char short_name = 0, const std::string &desc = std::string{}) { if (options.find(name) != options.end()) { std::cerr << "multiple definition: " + name << '\n'; std::exit(1); } options.emplace(name, std::make_shared(name, short_name, desc)); ordered.emplace_back(options[name]); } template > void add(const std::string &name, char short_name = 0, const std::string &desc = std::string{}, bool need = true, const T def = T(), F reader = F()) { if (options.find(name) != options.end()) { std::cerr << "multiple definition: " + name << '\n'; std::exit(1); } options.emplace(name, std::make_shared>(name, short_name, need, def, desc, reader)); ordered.emplace_back(options[name]); } void footer(const std::string &f) { ftr = f; } void set_program_name(const std::string &name) { prog_name = name; } bool exist(const std::string &name) const { if (options.find(name) == options.end()) { std::cerr << "there is no flag: --" + name << '\n' << usage(); std::exit(1); } return options.find(name)->second->has_set(); } template const T &get(const std::string &name) const { if (options.find(name) == options.end()) { std::cerr << "there is no flag: --" + name << '\n' << usage(); std::exit(1); } auto p = std::dynamic_pointer_cast>(options.find(name)->second); if (p == nullptr) { std::cerr << "type mismatch flag '" + name + "'" << '\n' << usage(); std::exit(1); } return p->get(); } const std::vector &rest() const { return others; } bool parse(const std::string &arg) { std::vector args; std::string buf; bool in_quote = false; for (std::size_t i = 0; i < arg.length(); i++) { if (arg[i] == '\"') { in_quote = !in_quote; continue; } if (arg[i] == ' ' && !in_quote) { args.emplace_back(buf); buf = ""; continue; } if (arg[i] == '\\') { i++; if (i >= arg.length()) { errors.emplace_back("unexpected occurrence of '\\' at end of string"); return false; } } buf += arg[i]; } if (in_quote) { errors.emplace_back("quote is not closed"); return false; } if (buf.length() > 0) args.emplace_back(buf); for (std::size_t i = 0; i < args.size(); i++) std::cout << "\"" << args[i] << "\"" << std::endl; return parse(args); } bool parse(const std::vector &args) { int argc = static_cast(args.size()); std::vector argv(argc); for (int i = 0; i < argc; i++) argv[i] = args[i].c_str(); return parse(argc, &argv[0]); } bool parse(int argc, const char *const argv[]) { errors.clear(); others.clear(); if (argc < 1) { errors.emplace_back("argument number must be longer than 0"); return false; } if (prog_name.empty()) prog_name = argv[0]; std::unordered_map lookup; for (auto p = options.begin(); p != options.end(); p++) { if (p->first.length() == 0) continue; char initial = p->second->short_name(); if (initial) { if (lookup.find(initial) != lookup.end()) { lookup[initial].clear(); errors.emplace_back(std::string("short option '") + initial + "' is ambiguous"); return false; } else lookup[initial] = p->first; } } for (int i = 1; i < argc; i++) { if (std::strncmp(argv[i], "--", 2) == 0) { const char *p = std::strchr(argv[i] + 2, '='); if (p) { set_option(std::string{argv[i] + 2, p}, std::string{p + 1}); } else { std::string name(argv[i] + 2); if (options.find(name) == options.end()) { errors.emplace_back("undefined option: --" + name); } else if (options[name]->has_value()) { if (i + 1 >= argc) { errors.emplace_back("option needs value: --" + name); } else { set_option(name, argv[++i]); } } else { set_option(name); } } } else if (std::strncmp(argv[i], "-", 1) == 0) { for (int j = 1; argv[i][j]; j++) { if (lookup.find(argv[i][j]) == lookup.end()) { errors.emplace_back(std::string("undefined short option: -") + argv[i][j]); } else { std::string name = lookup[argv[i][j]]; if (!argv[i][j + 1] && i + 1 < argc && options[name]->has_value()) { set_option(name, argv[++i]); break; } else { set_option(name); } } } } else { others.emplace_back(argv[i]); } } if (errors.empty()) { for (auto p = options.begin(); p != options.end(); p++) if (!p->second->valid()) errors.emplace_back("need option: --" + p->first); return errors.empty(); } return false; } void parse_check(const std::string &arg) { if (options.find("help") == options.end()) add("help", '?', "print this message"); check(parse(arg)); } void parse_check(const std::vector &args) { if (options.find("help") == options.end()) add("help", '?', "print this message"); check(parse(args)); } void parse_check(int argc, char *argv[]) { if (options.find("help") == options.end()) add("help", '?', "print this message"); check(parse(argc, argv)); } std::string error() const { std::ostringstream os; auto b = std::begin(errors), e = std::end(errors); if (b != e) { std::copy(b, std::prev(e), std::ostream_iterator(os, "\n")); b = std::prev(e); } if (b != e) { os << *b; } return os.str(); } std::string usage() const { std::ostringstream oss; oss << "usage: " << prog_name << " "; for (std::size_t i = 0; i < ordered.size(); i++) { if (ordered[i]->must()) oss << ordered[i]->short_description() << " "; } oss << "[options] ... " << ftr << std::endl; oss << "options:" << std::endl; std::size_t max_width = 0; for (std::size_t i = 0; i < ordered.size(); i++) { max_width = std::max(max_width, ordered[i]->name().length()); } for (std::size_t i = 0; i < ordered.size(); i++) { if (ordered[i]->short_name()) { oss << " -" << ordered[i]->short_name() << ", "; } else { oss << " "; } oss << "--" << ordered[i]->name(); for (std::size_t j = ordered[i]->name().length(); j < max_width + 4; j++) oss << ' '; oss << ordered[i]->description() << std::endl; } return oss.str(); } private: void check(bool ok) { if (exist("help")) { std::cout << usage() << std::endl; std::exit(0); } if (!ok) { std::cerr << error() << '\n' << usage(); std::exit(1); } } void set_option(const std::string &name) { if (options.find(name) == options.end()) { errors.emplace_back("undefined option: --" + name); return; } if (!options[name]->set()) { errors.emplace_back("option needs value: --" + name); return; } } void set_option(const std::string &name, const std::string &value) { if (options.find(name) == options.end()) { errors.emplace_back("undefined option: --" + name); return; } if (!options[name]->set(value)) { errors.emplace_back("option value is invalid: --" + name + "=" + value); return; } } class option_base { public: virtual ~option_base() {} virtual bool has_value() const = 0; virtual bool set() = 0; virtual bool set(const std::string &value) = 0; virtual bool has_set() const = 0; virtual bool valid() const = 0; virtual bool must() const = 0; virtual const std::string &name() const = 0; virtual char short_name() const = 0; virtual const std::string &description() const = 0; virtual std::string short_description() const = 0; }; class option_without_value : public option_base { public: option_without_value(const std::string &name, char short_name, const std::string &desc) : nam(name), snam(short_name), has(false) { this->desc = format_description(desc); } ~option_without_value() {} bool has_value() const { return false; } bool set() { has = true; return true; } bool set(const std::string &) { return false; } bool has_set() const { return has; } bool valid() const { return true; } bool must() const { return false; } const std::string &name() const { return nam; } char short_name() const { return snam; } const std::string &description() const { return desc; } std::string short_description() const { return "--" + nam; } protected: std::string format_description(const std::string &desc) { std::size_t size = desc.size(); std::size_t width = 60; std::string tmp = "\n "; for (std::size_t i = 0; i < size; i += width) tmp += (desc.substr(i, width > size - i ? size - i : width) + "\n "); return tmp; } private: std::string nam; char snam; std::string desc; bool has; }; template class option_with_value : public option_base { public: option_with_value(const std::string &name, char short_name, bool need, const T &def, const std::string &desc) : nam(name), snam(short_name), need(need), has(false), def(def), actual(def) { this->desc = format_description(desc); } ~option_with_value() {} const T &get() const { return actual; } bool has_value() const { return true; } bool set() { return false; } bool set(const std::string &value) { try { actual = read(value); has = true; } catch (const std::exception) { return false; } return true; } bool has_set() const { return has; } bool valid() const { if (need && !has) return false; return true; } bool must() const { return need; } const std::string &name() const { return nam; } char short_name() const { return snam; } const std::string &description() const { return desc; } std::string short_description() const { return "--" + nam + "=" + detail::readable_typename(); } protected: std::string format_description(const std::string &desc) { std::size_t size = desc.size(); std::size_t width = 60; std::string tmp = "\n "; for (std::size_t i = 0; i < size; i += width) tmp += (desc.substr(i, width > size - i ? size - i : width) + "\n "); return tmp + " (" + detail::readable_typename() + (need ? "" : " [=" + detail::default_value(def) + "]") + ")\n"; } virtual T read(const std::string &s) = 0; std::string nam; char snam; bool need; std::string desc; bool has; T def; T actual; }; template class option_with_value_with_reader : public option_with_value { public: option_with_value_with_reader(const std::string &name, char short_name, bool need, const T def, const std::string &desc, F reader) : option_with_value(name, short_name, need, def, desc), reader(reader) {} private: T read(const std::string &s) { return reader(s); } F reader; }; private: std::unordered_map> options; std::vector> ordered; std::string ftr; std::string prog_name; std::vector others; std::vector errors; }; } // cmdline ================================================ FILE: ThirdParty/include/ini17/ini17.hpp ================================================ #include #include #include #include #include #include #include #include #include #include #include namespace ini17 { namespace detail { template struct is_string : std::integral_constant> || std::is_same_v> || std::is_same_v> || std::is_same_v>> {}; template inline constexpr bool is_string_v = is_string::value; template inline constexpr bool not_string_v = !is_string_v; template inline T fromStringCast(const std::string &arg) { static std::istringstream iss; T ret{}; iss.str(arg); iss >> ret; iss.clear(); return ret; } template <> inline std::string fromStringCast(const std::string &arg) { return arg; } template > * = nullptr> inline std::string toStringCast(const T &arg) { return std::to_string(arg); } template > * = nullptr> inline std::string toStringCast(const std::string &arg) { return arg; } enum class TokenType { Section, Key, Value, Equal, Bracket }; struct Token { TokenType type; std::string value; std::size_t line; Token(TokenType type, std::string_view value, std::size_t line) : type(type), value(value), line(line) {} }; } // namespace detail using KeyValueType = std::unordered_map; class Section { private: class KeyValueEditer { public: KeyValueEditer(std::string &valueReference) : valueReference(valueReference) {} template void operator=(const T &value) { valueReference = detail::toStringCast(value); } template operator T() const { return detail::fromStringCast(valueReference); } operator const std::string &() const { return valueReference; } private: std::string &valueReference; }; public: Section(std::string_view name, KeyValueType kv) : name(name), kv(std::move(kv)) {} Section(std::string_view name) : Section(name, KeyValueType{}) {} Section(KeyValueType kv) : Section(std::string_view{}, std::move(kv)) {} Section() : Section(std::string_view{}, KeyValueType{}) {} void setName(std::string_view name) { this->name = name; } std::string_view getName() const { return name; } const KeyValueType &getKeyValueMap() const { return kv; } template std::optional get(std::string_view key) const { auto vit = kv.find(key.data()); if (vit == kv.end()) return std::nullopt; return detail::fromStringCast(vit->second); } template bool set(std::string_view key, const T &value) { auto vit = kv.find(key.data()); if (vit == kv.end()) return false; vit->second.assign(detail::toStringCast(value)); return true; } template bool add(std::string_view key, const T &value) { return kv.emplace(key, detail::toStringCast(value)).second; } KeyValueEditer operator[](std::string_view key) { auto vit = kv.find(key.data()); if (vit != kv.end()) return vit->second; return kv.emplace(key, std::string{}).first->second; } private: std::string name; KeyValueType kv; }; class Parser { public: template std::optional get(std::string_view name) const { if constexpr (std::is_same_v) { auto sit = result.find(name.data()); if (sit == result.end()) return std::nullopt; return Section{name, sit->second}; } else { return get(defaultSectionName, name); } } template std::optional get(std::string_view section, std::string_view key) const { auto sit = result.find(section.data()); if (sit == result.end()) return std::nullopt; auto vit = sit->second.find(key.data()); if (vit == sit->second.end()) return std::nullopt; return detail::fromStringCast(vit->second); } bool parseFile(std::string_view filePath) { std::string contents; std::ifstream file(filePath.data(), std::ios::in | std::ios::ate); if (!file.is_open()) return false; contents.resize(file.tellg()); file.seekg(0, std::ios::beg); file.read(&contents[0], contents.size()); file.close(); return parse(contents); } bool parse(std::string_view src) { if (!result.empty()) result.clear(); auto tokens = tokenize(src); if (!tokens.has_value()) { pushError("Failed to parse source file."); return false; } analyze(*tokens); return true; } const std::vector &error() const { return errors; } void setDefaultSectionName(std::string_view section) { defaultSectionName = section; } std::string_view getDefaultSectionName() const { return defaultSectionName; } private: template void pushError(std::size_t line, Token token) { errors.emplace_back("error: " + std::to_string(line) + ": unexcept token: " + token); } void pushError(std::string_view err) { errors.emplace_back(err); } private: std::optional> tokenize(std::string_view src) { std::vector tokens; auto length = src.size(); std::string_view::size_type count = 0; std::size_t line = 1; auto boundaryCheck = [this, length](std::size_t current) -> bool { if (current >= length) { pushError("The source file cannot be resolved"); return false; } return true; }; while (count < length) { char c = src[count]; if (c == '\n') { line++; count++; continue; } if (c == '#' || c == ';') { do c = src[++count]; while (c != '\n' && count < length); count++; line++; continue; } if (c == '[') { tokens.emplace_back(detail::TokenType::Bracket, "[", line); std::string section; while ((c = src[++count]) != ']' && !std::isspace(c) && count < length) section.push_back(c); if (!boundaryCheck(count)) return std::nullopt; while (std::isspace(c)) { if (c == '\n') { pushError(line, c); return std::nullopt; } c = src[++count]; if (!boundaryCheck(count)) return std::nullopt; } if (c != ']') { pushError(line, c); return std::nullopt; } tokens.emplace_back(detail::TokenType::Section, section, line); tokens.emplace_back(detail::TokenType::Bracket, "]", line); count++; continue; } if (std::isgraph(c)) { std::string key{c}; while ((c = src[++count]) != '=' && !std::isspace(c) && count < length) key.push_back(c); if (!boundaryCheck(count)) return std::nullopt; while (std::isspace(c)) { if (c == '\n') { pushError(line, c); return std::nullopt; } c = src[++count]; if (!boundaryCheck(count)) return std::nullopt; } if (c != '=') { pushError(line, c); return std::nullopt; } tokens.emplace_back(detail::TokenType::Key, key, line); tokens.emplace_back(detail::TokenType::Equal, "=", line); while (std::isspace(c = src[++count])) { if (!boundaryCheck(count)) return std::nullopt; } if (std::isgraph(c)) { std::string value{c}; for (;;) { while ((c = src[++count]) != '\n' && !std::isspace(c) && count < length) value.push_back(c); if (!boundaryCheck(count)) return std::nullopt; if (c != '\n') { std::string buf{c}; do { buf.push_back(c = src[++count]); if (!boundaryCheck(count)) return std::nullopt; } while (c != '\n' && std::isspace(c)); if (c != '\n') value.append(buf); else break; } else break; }; tokens.emplace_back(detail::TokenType::Value, value, line); line++; count++; continue; } } count++; } return std::make_optional(tokens); } void analyze(const std::vector &tokens) { auto length = tokens.size(); std::vector::size_type count = 0; ResultType::iterator it; auto boundaryCheck = [this, length](std::size_t current) -> bool { if (current >= length) { pushError("The token list cannot be resolved"); return false; } return true; }; if (length && tokens.front().type != detail::TokenType::Bracket) { it = result.emplace(defaultSectionName, KeyValueType{}).first; } while (count < length) { if (tokens[count].type == detail::TokenType::Bracket) { if (!boundaryCheck(count + 2)) return; auto &lBracketToken = tokens[count]; auto §ionToken = tokens[count + 1]; auto &rBracketToken = tokens[count + 2]; if (sectionToken.type != detail::TokenType::Section || rBracketToken.type != detail::TokenType::Bracket) { pushError(lBracketToken.line, lBracketToken.value); return; } it = result.emplace(sectionToken.value, KeyValueType{}).first; count += 3; continue; } if (tokens[count].type == detail::TokenType::Key) { if (!boundaryCheck(count + 2)) return; auto &keyToken = tokens[count]; auto &equalToken = tokens[count + 1]; auto &valueToken = tokens[count + 2]; if (equalToken.type != detail::TokenType::Equal || valueToken.type != detail::TokenType::Value) { pushError(keyToken.line, keyToken.value); return; } auto &&ret = it->second.emplace(keyToken.value, valueToken.value); if (!ret.second) pushError("duplicate key: " + tokens[count].value + ", in section: " + it->first); count += 3; continue; } count++; } } private: using ResultType = std::unordered_map; ResultType result; std::vector errors; std::string defaultSectionName = "global"; }; class Generator { public: template std::enable_if_t>...>> push(Sections &&...args) { (sections.emplace_back(std::forward(args)), ...); } template std::enable_if_t>>...>> push(Args &&...args) { sections.emplace_back(std::forward(args)...); } void clear() noexcept { sections.clear(); } void setHeader(std::string_view msg) { header = msg; } void setFooter(std::string_view msg) { footer = msg; } bool generateFile(std::string_view filePath) const { std::ofstream file(filePath.data()); if (!file.is_open()) return false; file << generate(); file.close(); return true; } std::string generate() const { std::string ret; if (!header.empty()) { ret.push_back(';'); ret.append(header).append("\n\n"); } for (auto &§ion : sections) { auto &&kvMap = section.getKeyValueMap(); auto name = section.getName(); if (!name.empty()) { ret.push_back('['); ret.append(name); ret.push_back(']'); ret.push_back('\n'); } for (auto &&kv : kvMap) { ret.append(kv.first).append(" = ").append(kv.second).push_back('\n'); } ret.push_back('\n'); } if (!footer.empty()) { ret.push_back(';'); ret.append(footer); ret.push_back('\n'); } return ret; } private: std::string header; std::string footer; std::list
sections; }; } // namespace ini17 ================================================ FILE: ThirdParty/include/opencl/CL/opencl.hpp ================================================ // // Copyright (c) 2008-2020 The Khronos Group Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // /*! \file * * \brief C++ bindings for OpenCL 1.0, OpenCL 1.1, OpenCL 1.2, * OpenCL 2.0, OpenCL 2.1, OpenCL 2.2, and OpenCL 3.0. * \author Lee Howes and Bruce Merry * * Derived from the OpenCL 1.x C++ bindings written by * Benedict R. Gaster, Laurent Morichetti and Lee Howes * With additions and fixes from: * Brian Cole, March 3rd 2010 and April 2012 * Matt Gruenke, April 2012. * Bruce Merry, February 2013. * Tom Deakin and Simon McIntosh-Smith, July 2013 * James Price, 2015- * \version 2.2.0 * \date 2019-09-18 * * Optional extension support * * cl_ext_device_fission * #define CL_HPP_USE_CL_DEVICE_FISSION * cl_khr_d3d10_sharing * #define CL_HPP_USE_DX_INTEROP * cl_khr_sub_groups * #define CL_HPP_USE_CL_SUB_GROUPS_KHR * cl_khr_image2d_from_buffer * #define CL_HPP_USE_CL_IMAGE2D_FROM_BUFFER_KHR * * Doxygen documentation for this header is available here: * * http://khronosgroup.github.io/OpenCL-CLHPP/ * * The latest version of this header can be found on the GitHub releases page: * * https://github.com/KhronosGroup/OpenCL-CLHPP/releases * * Bugs and patches can be submitted to the GitHub repository: * * https://github.com/KhronosGroup/OpenCL-CLHPP */ /*! \mainpage * \section intro Introduction * For many large applications C++ is the language of choice and so it seems * reasonable to define C++ bindings for OpenCL. * * The interface is contained with a single C++ header file \em opencl.hpp and all * definitions are contained within the namespace \em cl. There is no additional * requirement to include \em cl.h and to use either the C++ or original C * bindings; it is enough to simply include \em opencl.hpp. * * The bindings themselves are lightweight and correspond closely to the * underlying C API. Using the C++ bindings introduces no additional execution * overhead. * * There are numerous compatibility, portability and memory management * fixes in the new header as well as additional OpenCL 2.0 features. * As a result the header is not directly backward compatible and for this * reason we release it as opencl.hpp rather than a new version of cl.hpp. * * * \section compatibility Compatibility * Due to the evolution of the underlying OpenCL API the 2.0 C++ bindings * include an updated approach to defining supported feature versions * and the range of valid underlying OpenCL runtime versions supported. * * The combination of preprocessor macros CL_HPP_TARGET_OPENCL_VERSION and * CL_HPP_MINIMUM_OPENCL_VERSION control this range. These are three digit * decimal values representing OpenCL runime versions. The default for * the target is 200, representing OpenCL 2.0 and the minimum is also * defined as 200. These settings would use 2.0 API calls only. * If backward compatibility with a 1.2 runtime is required, the minimum * version may be set to 120. * * Note that this is a compile-time setting, and so affects linking against * a particular SDK version rather than the versioning of the loaded runtime. * * The earlier versions of the header included basic vector and string * classes based loosely on STL versions. These were difficult to * maintain and very rarely used. For the 2.0 header we now assume * the presence of the standard library unless requested otherwise. * We use std::array, std::vector, std::shared_ptr and std::string * throughout to safely manage memory and reduce the chance of a * recurrance of earlier memory management bugs. * * These classes are used through typedefs in the cl namespace: * cl::array, cl::vector, cl::pointer and cl::string. * In addition cl::allocate_pointer forwards to std::allocate_shared * by default. * In all cases these standard library classes can be replaced with * custom interface-compatible versions using the CL_HPP_NO_STD_ARRAY, * CL_HPP_NO_STD_VECTOR, CL_HPP_NO_STD_UNIQUE_PTR and * CL_HPP_NO_STD_STRING macros. * * The OpenCL 1.x versions of the C++ bindings included a size_t wrapper * class to interface with kernel enqueue. This caused unpleasant interactions * with the standard size_t declaration and led to namespacing bugs. * In the 2.0 version we have replaced this with a std::array-based interface. * However, the old behaviour can be regained for backward compatibility * using the CL_HPP_ENABLE_SIZE_T_COMPATIBILITY macro. * * Finally, the program construction interface used a clumsy vector-of-pairs * design in the earlier versions. We have replaced that with a cleaner * vector-of-vectors and vector-of-strings design. However, for backward * compatibility old behaviour can be regained with the * CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY macro. * * In OpenCL 2.0 OpenCL C is not entirely backward compatibility with * earlier versions. As a result a flag must be passed to the OpenCL C * compiled to request OpenCL 2.0 compilation of kernels with 1.2 as * the default in the absence of the flag. * In some cases the C++ bindings automatically compile code for ease. * For those cases the compilation defaults to OpenCL C 2.0. * If this is not wanted, the CL_HPP_CL_1_2_DEFAULT_BUILD macro may * be specified to assume 1.2 compilation. * If more fine-grained decisions on a per-kernel bases are required * then explicit build operations that take the flag should be used. * * * \section parameterization Parameters * This header may be parameterized by a set of preprocessor macros. * * - CL_HPP_TARGET_OPENCL_VERSION * * Defines the target OpenCL runtime version to build the header * against. Defaults to 200, representing OpenCL 2.0. * * - CL_HPP_NO_STD_STRING * * Do not use the standard library string class. cl::string is not * defined and may be defined by the user before opencl.hpp is * included. * * - CL_HPP_NO_STD_VECTOR * * Do not use the standard library vector class. cl::vector is not * defined and may be defined by the user before opencl.hpp is * included. * * - CL_HPP_NO_STD_ARRAY * * Do not use the standard library array class. cl::array is not * defined and may be defined by the user before opencl.hpp is * included. * * - CL_HPP_NO_STD_UNIQUE_PTR * * Do not use the standard library unique_ptr class. cl::pointer and * the cl::allocate_pointer functions are not defined and may be * defined by the user before opencl.hpp is included. * * - CL_HPP_ENABLE_DEVICE_FISSION * * Enables device fission for OpenCL 1.2 platforms. * * - CL_HPP_ENABLE_EXCEPTIONS * * Enable exceptions for use in the C++ bindings header. This is the * preferred error handling mechanism but is not required. * * - CL_HPP_ENABLE_SIZE_T_COMPATIBILITY * * Backward compatibility option to support cl.hpp-style size_t * class. Replaces the updated std::array derived version and * removal of size_t from the namespace. Note that in this case the * new size_t class is placed in the cl::compatibility namespace and * thus requires an additional using declaration for direct backward * compatibility. * * - CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY * * Enable older vector of pairs interface for construction of * programs. * * - CL_HPP_CL_1_2_DEFAULT_BUILD * * Default to OpenCL C 1.2 compilation rather than OpenCL C 2.0 * applies to use of cl::Program construction and other program * build variants. * * - CL_HPP_USE_CL_SUB_GROUPS_KHR * * Enable the cl_khr_subgroups extension. * * - CL_HPP_USE_IL_KHR * * Enable the cl_khr_il_program extension. * * * \section example Example * * The following example shows a general use case for the C++ * bindings, including support for the optional exception feature and * also the supplied vector and string classes, see following sections for * decriptions of these features. * * \code #define CL_HPP_ENABLE_EXCEPTIONS #define CL_HPP_TARGET_OPENCL_VERSION 200 #include #include #include #include #include const int numElements = 32; int main(void) { // Filter for a 2.0 platform and set it as the default std::vector platforms; cl::Platform::get(&platforms); cl::Platform plat; for (auto &p : platforms) { std::string platver = p.getInfo(); if (platver.find("OpenCL 2.") != std::string::npos) { plat = p; } } if (plat() == 0) { std::cout << "No OpenCL 2.0 platform found."; return -1; } cl::Platform newP = cl::Platform::setDefault(plat); if (newP != plat) { std::cout << "Error setting default platform."; return -1; } // Use C++11 raw string literals for kernel source code std::string kernel1{R"CLC( global int globalA; kernel void updateGlobal() { globalA = 75; } )CLC"}; std::string kernel2{R"CLC( typedef struct { global int *bar; } Foo; kernel void vectorAdd(global const Foo* aNum, global const int *inputA, global const int *inputB, global int *output, int val, write_only pipe int outPipe, queue_t childQueue) { output[get_global_id(0)] = inputA[get_global_id(0)] + inputB[get_global_id(0)] + val + *(aNum->bar); write_pipe(outPipe, &val); queue_t default_queue = get_default_queue(); ndrange_t ndrange = ndrange_1D(get_global_size(0)/2, get_global_size(0)/2); // Have a child kernel write into third quarter of output enqueue_kernel(default_queue, CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange, ^{ output[get_global_size(0)*2 + get_global_id(0)] = inputA[get_global_size(0)*2 + get_global_id(0)] + inputB[get_global_size(0)*2 + get_global_id(0)] + globalA; }); // Have a child kernel write into last quarter of output enqueue_kernel(childQueue, CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange, ^{ output[get_global_size(0)*3 + get_global_id(0)] = inputA[get_global_size(0)*3 + get_global_id(0)] + inputB[get_global_size(0)*3 + get_global_id(0)] + globalA + 2; }); } )CLC"}; // New simpler string interface style std::vector programStrings {kernel1, kernel2}; cl::Program vectorAddProgram(programStrings); try { vectorAddProgram.build("-cl-std=CL2.0"); } catch (...) { // Print build info for all devices cl_int buildErr = CL_SUCCESS; auto buildInfo = vectorAddProgram.getBuildInfo(&buildErr); for (auto &pair : buildInfo) { std::cerr << pair.second << std::endl << std::endl; } return 1; } typedef struct { int *bar; } Foo; // Get and run kernel that initializes the program-scope global // A test for kernels that take no arguments auto program2Kernel = cl::KernelFunctor<>(vectorAddProgram, "updateGlobal"); program2Kernel( cl::EnqueueArgs( cl::NDRange(1))); ////////////////// // SVM allocations auto anSVMInt = cl::allocate_svm>(); *anSVMInt = 5; cl::SVMAllocator>> svmAllocReadOnly; auto fooPointer = cl::allocate_pointer(svmAllocReadOnly); fooPointer->bar = anSVMInt.get(); cl::SVMAllocator> svmAlloc; std::vector>> inputA(numElements, 1, svmAlloc); cl::coarse_svm_vector inputB(numElements, 2, svmAlloc); // ////////////// // Traditional cl_mem allocations std::vector output(numElements, 0xdeadbeef); cl::Buffer outputBuffer(begin(output), end(output), false); cl::Pipe aPipe(sizeof(cl_int), numElements / 2); // Default command queue, also passed in as a parameter cl::DeviceCommandQueue defaultDeviceQueue = cl::DeviceCommandQueue::makeDefault( cl::Context::getDefault(), cl::Device::getDefault()); auto vectorAddKernel = cl::KernelFunctor< decltype(fooPointer)&, int*, cl::coarse_svm_vector&, cl::Buffer, int, cl::Pipe&, cl::DeviceCommandQueue >(vectorAddProgram, "vectorAdd"); // Ensure that the additional SVM pointer is available to the kernel // This one was not passed as a parameter vectorAddKernel.setSVMPointers(anSVMInt); // Hand control of coarse allocations to runtime cl::enqueueUnmapSVM(anSVMInt); cl::enqueueUnmapSVM(fooPointer); cl::unmapSVM(inputB); cl::unmapSVM(output2); cl_int error; vectorAddKernel( cl::EnqueueArgs( cl::NDRange(numElements/2), cl::NDRange(numElements/2)), fooPointer, inputA.data(), inputB, outputBuffer, 3, aPipe, defaultDeviceQueue, error ); cl::copy(outputBuffer, begin(output), end(output)); // Grab the SVM output vector using a map cl::mapSVM(output2); cl::Device d = cl::Device::getDefault(); std::cout << "Output:\n"; for (int i = 1; i < numElements; ++i) { std::cout << "\t" << output[i] << "\n"; } std::cout << "\n\n"; return 0; } * * \endcode * */ #ifndef CL_HPP_ #define CL_HPP_ /* Handle deprecated preprocessor definitions. In each case, we only check for * the old name if the new name is not defined, so that user code can define * both and hence work with either version of the bindings. */ #if !defined(CL_HPP_USE_DX_INTEROP) && defined(USE_DX_INTEROP) # pragma message("opencl.hpp: USE_DX_INTEROP is deprecated. Define CL_HPP_USE_DX_INTEROP instead") # define CL_HPP_USE_DX_INTEROP #endif #if !defined(CL_HPP_USE_CL_DEVICE_FISSION) && defined(USE_CL_DEVICE_FISSION) # pragma message("opencl.hpp: USE_CL_DEVICE_FISSION is deprecated. Define CL_HPP_USE_CL_DEVICE_FISSION instead") # define CL_HPP_USE_CL_DEVICE_FISSION #endif #if !defined(CL_HPP_ENABLE_EXCEPTIONS) && defined(__CL_ENABLE_EXCEPTIONS) # pragma message("opencl.hpp: __CL_ENABLE_EXCEPTIONS is deprecated. Define CL_HPP_ENABLE_EXCEPTIONS instead") # define CL_HPP_ENABLE_EXCEPTIONS #endif #if !defined(CL_HPP_NO_STD_VECTOR) && defined(__NO_STD_VECTOR) # pragma message("opencl.hpp: __NO_STD_VECTOR is deprecated. Define CL_HPP_NO_STD_VECTOR instead") # define CL_HPP_NO_STD_VECTOR #endif #if !defined(CL_HPP_NO_STD_STRING) && defined(__NO_STD_STRING) # pragma message("opencl.hpp: __NO_STD_STRING is deprecated. Define CL_HPP_NO_STD_STRING instead") # define CL_HPP_NO_STD_STRING #endif #if defined(VECTOR_CLASS) # pragma message("opencl.hpp: VECTOR_CLASS is deprecated. Alias cl::vector instead") #endif #if defined(STRING_CLASS) # pragma message("opencl.hpp: STRING_CLASS is deprecated. Alias cl::string instead.") #endif #if !defined(CL_HPP_USER_OVERRIDE_ERROR_STRINGS) && defined(__CL_USER_OVERRIDE_ERROR_STRINGS) # pragma message("opencl.hpp: __CL_USER_OVERRIDE_ERROR_STRINGS is deprecated. Define CL_HPP_USER_OVERRIDE_ERROR_STRINGS instead") # define CL_HPP_USER_OVERRIDE_ERROR_STRINGS #endif /* Warn about features that are no longer supported */ #if defined(__USE_DEV_VECTOR) # pragma message("opencl.hpp: __USE_DEV_VECTOR is no longer supported. Expect compilation errors") #endif #if defined(__USE_DEV_STRING) # pragma message("opencl.hpp: __USE_DEV_STRING is no longer supported. Expect compilation errors") #endif /* Detect which version to target */ #if !defined(CL_HPP_TARGET_OPENCL_VERSION) # pragma message("opencl.hpp: CL_HPP_TARGET_OPENCL_VERSION is not defined. It will default to 300 (OpenCL 3.0)") # define CL_HPP_TARGET_OPENCL_VERSION 300 #endif #if CL_HPP_TARGET_OPENCL_VERSION != 100 && \ CL_HPP_TARGET_OPENCL_VERSION != 110 && \ CL_HPP_TARGET_OPENCL_VERSION != 120 && \ CL_HPP_TARGET_OPENCL_VERSION != 200 && \ CL_HPP_TARGET_OPENCL_VERSION != 210 && \ CL_HPP_TARGET_OPENCL_VERSION != 220 && \ CL_HPP_TARGET_OPENCL_VERSION != 300 # pragma message("opencl.hpp: CL_HPP_TARGET_OPENCL_VERSION is not a valid value (100, 110, 120, 200, 210, 220 or 300). It will be set to 300 (OpenCL 3.0).") # undef CL_HPP_TARGET_OPENCL_VERSION # define CL_HPP_TARGET_OPENCL_VERSION 300 #endif /* Forward target OpenCL version to C headers if necessary */ #if defined(CL_TARGET_OPENCL_VERSION) /* Warn if prior definition of CL_TARGET_OPENCL_VERSION is lower than * requested C++ bindings version */ #if CL_TARGET_OPENCL_VERSION < CL_HPP_TARGET_OPENCL_VERSION # pragma message("CL_TARGET_OPENCL_VERSION is already defined as is lower than CL_HPP_TARGET_OPENCL_VERSION") #endif #else # define CL_TARGET_OPENCL_VERSION CL_HPP_TARGET_OPENCL_VERSION #endif #if !defined(CL_HPP_MINIMUM_OPENCL_VERSION) # define CL_HPP_MINIMUM_OPENCL_VERSION 200 #endif #if CL_HPP_MINIMUM_OPENCL_VERSION != 100 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 110 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 120 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 200 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 210 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 220 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 300 # pragma message("opencl.hpp: CL_HPP_MINIMUM_OPENCL_VERSION is not a valid value (100, 110, 120, 200, 210, 220 or 300). It will be set to 100") # undef CL_HPP_MINIMUM_OPENCL_VERSION # define CL_HPP_MINIMUM_OPENCL_VERSION 100 #endif #if CL_HPP_MINIMUM_OPENCL_VERSION > CL_HPP_TARGET_OPENCL_VERSION # error "CL_HPP_MINIMUM_OPENCL_VERSION must not be greater than CL_HPP_TARGET_OPENCL_VERSION" #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 100 && !defined(CL_USE_DEPRECATED_OPENCL_1_0_APIS) # define CL_USE_DEPRECATED_OPENCL_1_0_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 110 && !defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) # define CL_USE_DEPRECATED_OPENCL_1_1_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 120 && !defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) # define CL_USE_DEPRECATED_OPENCL_1_2_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 200 && !defined(CL_USE_DEPRECATED_OPENCL_2_0_APIS) # define CL_USE_DEPRECATED_OPENCL_2_0_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 210 && !defined(CL_USE_DEPRECATED_OPENCL_2_1_APIS) # define CL_USE_DEPRECATED_OPENCL_2_1_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 220 && !defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS) # define CL_USE_DEPRECATED_OPENCL_2_2_APIS #endif #ifdef _WIN32 #include #if defined(CL_HPP_USE_DX_INTEROP) #include #include #endif #endif // _WIN32 #if defined(_MSC_VER) #include #endif // _MSC_VER // Check for a valid C++ version // Need to do both tests here because for some reason __cplusplus is not // updated in visual studio #if (!defined(_MSC_VER) && __cplusplus < 201103L) || (defined(_MSC_VER) && _MSC_VER < 1700) #error Visual studio 2013 or another C++11-supporting compiler required #endif // #if defined(CL_HPP_USE_CL_DEVICE_FISSION) || defined(CL_HPP_USE_CL_SUB_GROUPS_KHR) #include #endif #if defined(__APPLE__) || defined(__MACOSX) #include #else #include #endif // !__APPLE__ #if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L ) #define CL_HPP_NOEXCEPT_ noexcept #else #define CL_HPP_NOEXCEPT_ #endif #if __cplusplus >= 201703L # define CL_HPP_DEFINE_STATIC_MEMBER_ inline #elif defined(_MSC_VER) # define CL_HPP_DEFINE_STATIC_MEMBER_ __declspec(selectany) #elif defined(__MINGW32__) # define CL_HPP_DEFINE_STATIC_MEMBER_ __attribute__((selectany)) #else # define CL_HPP_DEFINE_STATIC_MEMBER_ __attribute__((weak)) #endif // !_MSC_VER // Define deprecated prefixes and suffixes to ensure compilation // in case they are not pre-defined #if !defined(CL_API_PREFIX__VERSION_1_1_DEPRECATED) #define CL_API_PREFIX__VERSION_1_1_DEPRECATED #endif // #if !defined(CL_API_PREFIX__VERSION_1_1_DEPRECATED) #if !defined(CL_API_SUFFIX__VERSION_1_1_DEPRECATED) #define CL_API_SUFFIX__VERSION_1_1_DEPRECATED #endif // #if !defined(CL_API_PREFIX__VERSION_1_1_DEPRECATED) #if !defined(CL_API_PREFIX__VERSION_1_2_DEPRECATED) #define CL_API_PREFIX__VERSION_1_2_DEPRECATED #endif // #if !defined(CL_API_PREFIX__VERSION_1_2_DEPRECATED) #if !defined(CL_API_SUFFIX__VERSION_1_2_DEPRECATED) #define CL_API_SUFFIX__VERSION_1_2_DEPRECATED #endif // #if !defined(CL_API_PREFIX__VERSION_1_2_DEPRECATED) #if !defined(CL_CALLBACK) #define CL_CALLBACK #endif //CL_CALLBACK #include #include #include #include #include #include // Define a size_type to represent a correctly resolved size_t #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) namespace cl { using size_type = ::size_t; } // namespace cl #else // #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) namespace cl { using size_type = size_t; } // namespace cl #endif // #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) #if defined(CL_HPP_ENABLE_EXCEPTIONS) #include #endif // #if defined(CL_HPP_ENABLE_EXCEPTIONS) #if !defined(CL_HPP_NO_STD_VECTOR) #include namespace cl { template < class T, class Alloc = std::allocator > using vector = std::vector; } // namespace cl #endif // #if !defined(CL_HPP_NO_STD_VECTOR) #if !defined(CL_HPP_NO_STD_STRING) #include namespace cl { using string = std::string; } // namespace cl #endif // #if !defined(CL_HPP_NO_STD_STRING) #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if !defined(CL_HPP_NO_STD_UNIQUE_PTR) #include namespace cl { // Replace unique_ptr and allocate_pointer for internal use // to allow user to replace them template using pointer = std::unique_ptr; } // namespace cl #endif #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if !defined(CL_HPP_NO_STD_ARRAY) #include namespace cl { template < class T, size_type N > using array = std::array; } // namespace cl #endif // #if !defined(CL_HPP_NO_STD_ARRAY) // Define size_type appropriately to allow backward-compatibility // use of the old size_t interface class #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) namespace cl { namespace compatibility { /*! \brief class used to interface between C++ and * OpenCL C calls that require arrays of size_t values, whose * size is known statically. */ template class size_t { private: size_type data_[N]; public: //! \brief Initialize size_t to all 0s size_t() { for (int i = 0; i < N; ++i) { data_[i] = 0; } } size_t(const array &rhs) { for (int i = 0; i < N; ++i) { data_[i] = rhs[i]; } } size_type& operator[](int index) { return data_[index]; } const size_type& operator[](int index) const { return data_[index]; } //! \brief Conversion operator to T*. operator size_type* () { return data_; } //! \brief Conversion operator to const T*. operator const size_type* () const { return data_; } operator array() const { array ret; for (int i = 0; i < N; ++i) { ret[i] = data_[i]; } return ret; } }; } // namespace compatibility template using size_t = compatibility::size_t; } // namespace cl #endif // #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) // Helper alias to avoid confusing the macros namespace cl { namespace detail { using size_t_array = array; } // namespace detail } // namespace cl /*! \namespace cl * * \brief The OpenCL C++ bindings are defined within this namespace. * */ namespace cl { class Memory; #define CL_HPP_INIT_CL_EXT_FCN_PTR_(name) \ if (!pfn_##name) { \ pfn_##name = (PFN_##name) \ clGetExtensionFunctionAddress(#name); \ if (!pfn_##name) { \ } \ } #define CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, name) \ if (!pfn_##name) { \ pfn_##name = (PFN_##name) \ clGetExtensionFunctionAddressForPlatform(platform, #name); \ if (!pfn_##name) { \ } \ } class Program; class Device; class Context; class CommandQueue; class DeviceCommandQueue; class Memory; class Buffer; class Pipe; #if defined(CL_HPP_ENABLE_EXCEPTIONS) /*! \brief Exception class * * This may be thrown by API functions when CL_HPP_ENABLE_EXCEPTIONS is defined. */ class Error : public std::exception { private: cl_int err_; const char * errStr_; public: /*! \brief Create a new CL error exception for a given error code * and corresponding message. * * \param err error code value. * * \param errStr a descriptive string that must remain in scope until * handling of the exception has concluded. If set, it * will be returned by what(). */ Error(cl_int err, const char * errStr = NULL) : err_(err), errStr_(errStr) {} ~Error() throw() {} /*! \brief Get error string associated with exception * * \return A memory pointer to the error message string. */ virtual const char * what() const throw () { if (errStr_ == NULL) { return "empty"; } else { return errStr_; } } /*! \brief Get error code associated with exception * * \return The error code. */ cl_int err(void) const { return err_; } }; #define CL_HPP_ERR_STR_(x) #x #else #define CL_HPP_ERR_STR_(x) NULL #endif // CL_HPP_ENABLE_EXCEPTIONS namespace detail { #if defined(CL_HPP_ENABLE_EXCEPTIONS) static inline cl_int errHandler ( cl_int err, const char * errStr = NULL) { if (err != CL_SUCCESS) { throw Error(err, errStr); } return err; } #else static inline cl_int errHandler (cl_int err, const char * errStr = NULL) { (void) errStr; // suppress unused variable warning return err; } #endif // CL_HPP_ENABLE_EXCEPTIONS } //! \cond DOXYGEN_DETAIL #if !defined(CL_HPP_USER_OVERRIDE_ERROR_STRINGS) #define __GET_DEVICE_INFO_ERR CL_HPP_ERR_STR_(clGetDeviceInfo) #define __GET_PLATFORM_INFO_ERR CL_HPP_ERR_STR_(clGetPlatformInfo) #define __GET_DEVICE_IDS_ERR CL_HPP_ERR_STR_(clGetDeviceIDs) #define __GET_PLATFORM_IDS_ERR CL_HPP_ERR_STR_(clGetPlatformIDs) #define __GET_CONTEXT_INFO_ERR CL_HPP_ERR_STR_(clGetContextInfo) #define __GET_EVENT_INFO_ERR CL_HPP_ERR_STR_(clGetEventInfo) #define __GET_EVENT_PROFILE_INFO_ERR CL_HPP_ERR_STR_(clGetEventProfileInfo) #define __GET_MEM_OBJECT_INFO_ERR CL_HPP_ERR_STR_(clGetMemObjectInfo) #define __GET_IMAGE_INFO_ERR CL_HPP_ERR_STR_(clGetImageInfo) #define __GET_SAMPLER_INFO_ERR CL_HPP_ERR_STR_(clGetSamplerInfo) #define __GET_KERNEL_INFO_ERR CL_HPP_ERR_STR_(clGetKernelInfo) #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __GET_KERNEL_ARG_INFO_ERR CL_HPP_ERR_STR_(clGetKernelArgInfo) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #define __GET_KERNEL_SUB_GROUP_INFO_ERR CL_HPP_ERR_STR_(clGetKernelSubGroupInfo) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #define __GET_KERNEL_WORK_GROUP_INFO_ERR CL_HPP_ERR_STR_(clGetKernelWorkGroupInfo) #define __GET_PROGRAM_INFO_ERR CL_HPP_ERR_STR_(clGetProgramInfo) #define __GET_PROGRAM_BUILD_INFO_ERR CL_HPP_ERR_STR_(clGetProgramBuildInfo) #define __GET_COMMAND_QUEUE_INFO_ERR CL_HPP_ERR_STR_(clGetCommandQueueInfo) #define __CREATE_CONTEXT_ERR CL_HPP_ERR_STR_(clCreateContext) #define __CREATE_CONTEXT_FROM_TYPE_ERR CL_HPP_ERR_STR_(clCreateContextFromType) #define __GET_SUPPORTED_IMAGE_FORMATS_ERR CL_HPP_ERR_STR_(clGetSupportedImageFormats) #define __CREATE_BUFFER_ERR CL_HPP_ERR_STR_(clCreateBuffer) #define __COPY_ERR CL_HPP_ERR_STR_(cl::copy) #define __CREATE_SUBBUFFER_ERR CL_HPP_ERR_STR_(clCreateSubBuffer) #define __CREATE_GL_BUFFER_ERR CL_HPP_ERR_STR_(clCreateFromGLBuffer) #define __CREATE_GL_RENDER_BUFFER_ERR CL_HPP_ERR_STR_(clCreateFromGLBuffer) #define __GET_GL_OBJECT_INFO_ERR CL_HPP_ERR_STR_(clGetGLObjectInfo) #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __CREATE_IMAGE_ERR CL_HPP_ERR_STR_(clCreateImage) #define __CREATE_GL_TEXTURE_ERR CL_HPP_ERR_STR_(clCreateFromGLTexture) #define __IMAGE_DIMENSION_ERR CL_HPP_ERR_STR_(Incorrect image dimensions) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR CL_HPP_ERR_STR_(clSetMemObjectDestructorCallback) #define __CREATE_USER_EVENT_ERR CL_HPP_ERR_STR_(clCreateUserEvent) #define __SET_USER_EVENT_STATUS_ERR CL_HPP_ERR_STR_(clSetUserEventStatus) #define __SET_EVENT_CALLBACK_ERR CL_HPP_ERR_STR_(clSetEventCallback) #define __WAIT_FOR_EVENTS_ERR CL_HPP_ERR_STR_(clWaitForEvents) #define __CREATE_KERNEL_ERR CL_HPP_ERR_STR_(clCreateKernel) #define __SET_KERNEL_ARGS_ERR CL_HPP_ERR_STR_(clSetKernelArg) #define __CREATE_PROGRAM_WITH_SOURCE_ERR CL_HPP_ERR_STR_(clCreateProgramWithSource) #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #define __CREATE_PROGRAM_WITH_IL_ERR CL_HPP_ERR_STR_(clCreateProgramWithIL) #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #define __CREATE_PROGRAM_WITH_BINARY_ERR CL_HPP_ERR_STR_(clCreateProgramWithBinary) #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __CREATE_PROGRAM_WITH_IL_ERR CL_HPP_ERR_STR_(clCreateProgramWithIL) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR CL_HPP_ERR_STR_(clCreateProgramWithBuiltInKernels) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __BUILD_PROGRAM_ERR CL_HPP_ERR_STR_(clBuildProgram) #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __COMPILE_PROGRAM_ERR CL_HPP_ERR_STR_(clCompileProgram) #define __LINK_PROGRAM_ERR CL_HPP_ERR_STR_(clLinkProgram) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __CREATE_KERNELS_IN_PROGRAM_ERR CL_HPP_ERR_STR_(clCreateKernelsInProgram) #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #define __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR CL_HPP_ERR_STR_(clCreateCommandQueueWithProperties) #define __CREATE_SAMPLER_WITH_PROPERTIES_ERR CL_HPP_ERR_STR_(clCreateSamplerWithProperties) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #define __SET_COMMAND_QUEUE_PROPERTY_ERR CL_HPP_ERR_STR_(clSetCommandQueueProperty) #define __ENQUEUE_READ_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueReadBuffer) #define __ENQUEUE_READ_BUFFER_RECT_ERR CL_HPP_ERR_STR_(clEnqueueReadBufferRect) #define __ENQUEUE_WRITE_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueWriteBuffer) #define __ENQUEUE_WRITE_BUFFER_RECT_ERR CL_HPP_ERR_STR_(clEnqueueWriteBufferRect) #define __ENQEUE_COPY_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueCopyBuffer) #define __ENQEUE_COPY_BUFFER_RECT_ERR CL_HPP_ERR_STR_(clEnqueueCopyBufferRect) #define __ENQUEUE_FILL_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueFillBuffer) #define __ENQUEUE_READ_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueReadImage) #define __ENQUEUE_WRITE_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueWriteImage) #define __ENQUEUE_COPY_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueCopyImage) #define __ENQUEUE_FILL_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueFillImage) #define __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueCopyImageToBuffer) #define __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueCopyBufferToImage) #define __ENQUEUE_MAP_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueMapBuffer) #define __ENQUEUE_MAP_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueMapImage) #define __ENQUEUE_UNMAP_MEM_OBJECT_ERR CL_HPP_ERR_STR_(clEnqueueUnMapMemObject) #define __ENQUEUE_NDRANGE_KERNEL_ERR CL_HPP_ERR_STR_(clEnqueueNDRangeKernel) #define __ENQUEUE_NATIVE_KERNEL CL_HPP_ERR_STR_(clEnqueueNativeKernel) #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __ENQUEUE_MIGRATE_MEM_OBJECTS_ERR CL_HPP_ERR_STR_(clEnqueueMigrateMemObjects) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __ENQUEUE_MIGRATE_SVM_ERR CL_HPP_ERR_STR_(clEnqueueSVMMigrateMem) #define __SET_DEFAULT_DEVICE_COMMAND_QUEUE_ERR CL_HPP_ERR_STR_(clSetDefaultDeviceCommandQueue) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __ENQUEUE_ACQUIRE_GL_ERR CL_HPP_ERR_STR_(clEnqueueAcquireGLObjects) #define __ENQUEUE_RELEASE_GL_ERR CL_HPP_ERR_STR_(clEnqueueReleaseGLObjects) #define __CREATE_PIPE_ERR CL_HPP_ERR_STR_(clCreatePipe) #define __GET_PIPE_INFO_ERR CL_HPP_ERR_STR_(clGetPipeInfo) #define __RETAIN_ERR CL_HPP_ERR_STR_(Retain Object) #define __RELEASE_ERR CL_HPP_ERR_STR_(Release Object) #define __FLUSH_ERR CL_HPP_ERR_STR_(clFlush) #define __FINISH_ERR CL_HPP_ERR_STR_(clFinish) #define __VECTOR_CAPACITY_ERR CL_HPP_ERR_STR_(Vector capacity error) #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __GET_HOST_TIMER_ERR CL_HPP_ERR_STR_(clGetHostTimer) #define __GET_DEVICE_AND_HOST_TIMER_ERR CL_HPP_ERR_STR_(clGetDeviceAndHostTimer) #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 220 #define __SET_PROGRAM_RELEASE_CALLBACK_ERR CL_HPP_ERR_STR_(clSetProgramReleaseCallback) #define __SET_PROGRAM_SPECIALIZATION_CONSTANT_ERR CL_HPP_ERR_STR_(clSetProgramSpecializationConstant) #endif /** * CL 1.2 version that uses device fission. */ #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __CREATE_SUB_DEVICES_ERR CL_HPP_ERR_STR_(clCreateSubDevices) #else #define __CREATE_SUB_DEVICES_ERR CL_HPP_ERR_STR_(clCreateSubDevicesEXT) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) #define __ENQUEUE_MARKER_ERR CL_HPP_ERR_STR_(clEnqueueMarker) #define __ENQUEUE_WAIT_FOR_EVENTS_ERR CL_HPP_ERR_STR_(clEnqueueWaitForEvents) #define __ENQUEUE_BARRIER_ERR CL_HPP_ERR_STR_(clEnqueueBarrier) #define __UNLOAD_COMPILER_ERR CL_HPP_ERR_STR_(clUnloadCompiler) #define __CREATE_GL_TEXTURE_2D_ERR CL_HPP_ERR_STR_(clCreateFromGLTexture2D) #define __CREATE_GL_TEXTURE_3D_ERR CL_HPP_ERR_STR_(clCreateFromGLTexture3D) #define __CREATE_IMAGE2D_ERR CL_HPP_ERR_STR_(clCreateImage2D) #define __CREATE_IMAGE3D_ERR CL_HPP_ERR_STR_(clCreateImage3D) #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /** * Deprecated APIs for 2.0 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) #define __CREATE_COMMAND_QUEUE_ERR CL_HPP_ERR_STR_(clCreateCommandQueue) #define __ENQUEUE_TASK_ERR CL_HPP_ERR_STR_(clEnqueueTask) #define __CREATE_SAMPLER_ERR CL_HPP_ERR_STR_(clCreateSampler) #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /** * CL 1.2 marker and barrier commands */ #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __ENQUEUE_MARKER_WAIT_LIST_ERR CL_HPP_ERR_STR_(clEnqueueMarkerWithWaitList) #define __ENQUEUE_BARRIER_WAIT_LIST_ERR CL_HPP_ERR_STR_(clEnqueueBarrierWithWaitList) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __CLONE_KERNEL_ERR CL_HPP_ERR_STR_(clCloneKernel) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #endif // CL_HPP_USER_OVERRIDE_ERROR_STRINGS //! \endcond namespace detail { // Generic getInfoHelper. The final parameter is used to guide overload // resolution: the actual parameter passed is an int, which makes this // a worse conversion sequence than a specialization that declares the // parameter as an int. template inline cl_int getInfoHelper(Functor f, cl_uint name, T* param, long) { return f(name, sizeof(T), param, NULL); } // Specialized for getInfo // Assumes that the output vector was correctly resized on the way in template inline cl_int getInfoHelper(Func f, cl_uint name, vector>* param, int) { if (name != CL_PROGRAM_BINARIES) { return CL_INVALID_VALUE; } if (param) { // Create array of pointers, calculate total size and pass pointer array in size_type numBinaries = param->size(); vector binariesPointers(numBinaries); for (size_type i = 0; i < numBinaries; ++i) { binariesPointers[i] = (*param)[i].data(); } cl_int err = f(name, numBinaries * sizeof(unsigned char*), binariesPointers.data(), NULL); if (err != CL_SUCCESS) { return err; } } return CL_SUCCESS; } // Specialized getInfoHelper for vector params template inline cl_int getInfoHelper(Func f, cl_uint name, vector* param, long) { size_type required; cl_int err = f(name, 0, NULL, &required); if (err != CL_SUCCESS) { return err; } const size_type elements = required / sizeof(T); // Temporary to avoid changing param on an error vector localData(elements); err = f(name, required, localData.data(), NULL); if (err != CL_SUCCESS) { return err; } if (param) { *param = std::move(localData); } return CL_SUCCESS; } /* Specialization for reference-counted types. This depends on the * existence of Wrapper::cl_type, and none of the other types having the * cl_type member. Note that simplify specifying the parameter as Wrapper * does not work, because when using a derived type (e.g. Context) the generic * template will provide a better match. */ template inline cl_int getInfoHelper( Func f, cl_uint name, vector* param, int, typename T::cl_type = 0) { size_type required; cl_int err = f(name, 0, NULL, &required); if (err != CL_SUCCESS) { return err; } const size_type elements = required / sizeof(typename T::cl_type); vector value(elements); err = f(name, required, value.data(), NULL); if (err != CL_SUCCESS) { return err; } if (param) { // Assign to convert CL type to T for each element param->resize(elements); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < elements; i++) { (*param)[i] = T(value[i], true); } } return CL_SUCCESS; } // Specialized GetInfoHelper for string params template inline cl_int getInfoHelper(Func f, cl_uint name, string* param, long) { size_type required; cl_int err = f(name, 0, NULL, &required); if (err != CL_SUCCESS) { return err; } // std::string has a constant data member // a char vector does not if (required > 0) { vector value(required); err = f(name, required, value.data(), NULL); if (err != CL_SUCCESS) { return err; } if (param) { param->assign(begin(value), prev(end(value))); } } else if (param) { param->assign(""); } return CL_SUCCESS; } // Specialized GetInfoHelper for clsize_t params template inline cl_int getInfoHelper(Func f, cl_uint name, array* param, long) { size_type required; cl_int err = f(name, 0, NULL, &required); if (err != CL_SUCCESS) { return err; } size_type elements = required / sizeof(size_type); vector value(elements, 0); err = f(name, required, value.data(), NULL); if (err != CL_SUCCESS) { return err; } // Bound the copy with N to prevent overruns // if passed N > than the amount copied if (elements > N) { elements = N; } for (size_type i = 0; i < elements; ++i) { (*param)[i] = value[i]; } return CL_SUCCESS; } template struct ReferenceHandler; /* Specialization for reference-counted types. This depends on the * existence of Wrapper::cl_type, and none of the other types having the * cl_type member. Note that simplify specifying the parameter as Wrapper * does not work, because when using a derived type (e.g. Context) the generic * template will provide a better match. */ template inline cl_int getInfoHelper(Func f, cl_uint name, T* param, int, typename T::cl_type = 0) { typename T::cl_type value; cl_int err = f(name, sizeof(value), &value, NULL); if (err != CL_SUCCESS) { return err; } *param = value; if (value != NULL) { err = param->retain(); if (err != CL_SUCCESS) { return err; } } return CL_SUCCESS; } #define CL_HPP_PARAM_NAME_INFO_1_0_(F) \ F(cl_platform_info, CL_PLATFORM_PROFILE, string) \ F(cl_platform_info, CL_PLATFORM_VERSION, string) \ F(cl_platform_info, CL_PLATFORM_NAME, string) \ F(cl_platform_info, CL_PLATFORM_VENDOR, string) \ F(cl_platform_info, CL_PLATFORM_EXTENSIONS, string) \ \ F(cl_device_info, CL_DEVICE_TYPE, cl_device_type) \ F(cl_device_info, CL_DEVICE_VENDOR_ID, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WORK_GROUP_SIZE, size_type) \ F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_SIZES, cl::vector) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint) \ F(cl_device_info, CL_DEVICE_ADDRESS_BITS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_READ_IMAGE_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_MEM_ALLOC_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_WIDTH, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_HEIGHT, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_WIDTH, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_HEIGHT, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_DEPTH, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_MAX_PARAMETER_SIZE, size_type) \ F(cl_device_info, CL_DEVICE_MAX_SAMPLERS, cl_uint) \ F(cl_device_info, CL_DEVICE_MEM_BASE_ADDR_ALIGN, cl_uint) \ F(cl_device_info, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_SINGLE_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_DOUBLE_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_HALF_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, cl_device_mem_cache_type) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, cl_uint)\ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_MAX_CONSTANT_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_LOCAL_MEM_TYPE, cl_device_local_mem_type) \ F(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_ERROR_CORRECTION_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_PROFILING_TIMER_RESOLUTION, size_type) \ F(cl_device_info, CL_DEVICE_ENDIAN_LITTLE, cl_bool) \ F(cl_device_info, CL_DEVICE_AVAILABLE, cl_bool) \ F(cl_device_info, CL_DEVICE_COMPILER_AVAILABLE, cl_bool) \ F(cl_device_info, CL_DEVICE_EXECUTION_CAPABILITIES, cl_device_exec_capabilities) \ F(cl_device_info, CL_DEVICE_PLATFORM, cl_platform_id) \ F(cl_device_info, CL_DEVICE_NAME, string) \ F(cl_device_info, CL_DEVICE_VENDOR, string) \ F(cl_device_info, CL_DRIVER_VERSION, string) \ F(cl_device_info, CL_DEVICE_PROFILE, string) \ F(cl_device_info, CL_DEVICE_VERSION, string) \ F(cl_device_info, CL_DEVICE_EXTENSIONS, string) \ \ F(cl_context_info, CL_CONTEXT_REFERENCE_COUNT, cl_uint) \ F(cl_context_info, CL_CONTEXT_DEVICES, cl::vector) \ F(cl_context_info, CL_CONTEXT_PROPERTIES, cl::vector) \ \ F(cl_event_info, CL_EVENT_COMMAND_QUEUE, cl::CommandQueue) \ F(cl_event_info, CL_EVENT_COMMAND_TYPE, cl_command_type) \ F(cl_event_info, CL_EVENT_REFERENCE_COUNT, cl_uint) \ F(cl_event_info, CL_EVENT_COMMAND_EXECUTION_STATUS, cl_int) \ \ F(cl_profiling_info, CL_PROFILING_COMMAND_QUEUED, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_SUBMIT, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_START, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_END, cl_ulong) \ \ F(cl_mem_info, CL_MEM_TYPE, cl_mem_object_type) \ F(cl_mem_info, CL_MEM_FLAGS, cl_mem_flags) \ F(cl_mem_info, CL_MEM_SIZE, size_type) \ F(cl_mem_info, CL_MEM_HOST_PTR, void*) \ F(cl_mem_info, CL_MEM_MAP_COUNT, cl_uint) \ F(cl_mem_info, CL_MEM_REFERENCE_COUNT, cl_uint) \ F(cl_mem_info, CL_MEM_CONTEXT, cl::Context) \ \ F(cl_image_info, CL_IMAGE_FORMAT, cl_image_format) \ F(cl_image_info, CL_IMAGE_ELEMENT_SIZE, size_type) \ F(cl_image_info, CL_IMAGE_ROW_PITCH, size_type) \ F(cl_image_info, CL_IMAGE_SLICE_PITCH, size_type) \ F(cl_image_info, CL_IMAGE_WIDTH, size_type) \ F(cl_image_info, CL_IMAGE_HEIGHT, size_type) \ F(cl_image_info, CL_IMAGE_DEPTH, size_type) \ \ F(cl_sampler_info, CL_SAMPLER_REFERENCE_COUNT, cl_uint) \ F(cl_sampler_info, CL_SAMPLER_CONTEXT, cl::Context) \ F(cl_sampler_info, CL_SAMPLER_NORMALIZED_COORDS, cl_bool) \ F(cl_sampler_info, CL_SAMPLER_ADDRESSING_MODE, cl_addressing_mode) \ F(cl_sampler_info, CL_SAMPLER_FILTER_MODE, cl_filter_mode) \ \ F(cl_program_info, CL_PROGRAM_REFERENCE_COUNT, cl_uint) \ F(cl_program_info, CL_PROGRAM_CONTEXT, cl::Context) \ F(cl_program_info, CL_PROGRAM_NUM_DEVICES, cl_uint) \ F(cl_program_info, CL_PROGRAM_DEVICES, cl::vector) \ F(cl_program_info, CL_PROGRAM_SOURCE, string) \ F(cl_program_info, CL_PROGRAM_BINARY_SIZES, cl::vector) \ F(cl_program_info, CL_PROGRAM_BINARIES, cl::vector>) \ \ F(cl_program_build_info, CL_PROGRAM_BUILD_STATUS, cl_build_status) \ F(cl_program_build_info, CL_PROGRAM_BUILD_OPTIONS, string) \ F(cl_program_build_info, CL_PROGRAM_BUILD_LOG, string) \ \ F(cl_kernel_info, CL_KERNEL_FUNCTION_NAME, string) \ F(cl_kernel_info, CL_KERNEL_NUM_ARGS, cl_uint) \ F(cl_kernel_info, CL_KERNEL_REFERENCE_COUNT, cl_uint) \ F(cl_kernel_info, CL_KERNEL_CONTEXT, cl::Context) \ F(cl_kernel_info, CL_KERNEL_PROGRAM, cl::Program) \ \ F(cl_kernel_work_group_info, CL_KERNEL_WORK_GROUP_SIZE, size_type) \ F(cl_kernel_work_group_info, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, cl::detail::size_t_array) \ F(cl_kernel_work_group_info, CL_KERNEL_LOCAL_MEM_SIZE, cl_ulong) \ \ F(cl_command_queue_info, CL_QUEUE_CONTEXT, cl::Context) \ F(cl_command_queue_info, CL_QUEUE_DEVICE, cl::Device) \ F(cl_command_queue_info, CL_QUEUE_REFERENCE_COUNT, cl_uint) \ F(cl_command_queue_info, CL_QUEUE_PROPERTIES, cl_command_queue_properties) #define CL_HPP_PARAM_NAME_INFO_1_1_(F) \ F(cl_context_info, CL_CONTEXT_NUM_DEVICES, cl_uint)\ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, cl_uint) \ F(cl_device_info, CL_DEVICE_OPENCL_C_VERSION, string) \ \ F(cl_mem_info, CL_MEM_ASSOCIATED_MEMOBJECT, cl::Memory) \ F(cl_mem_info, CL_MEM_OFFSET, size_type) \ \ F(cl_kernel_work_group_info, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, size_type) \ F(cl_kernel_work_group_info, CL_KERNEL_PRIVATE_MEM_SIZE, cl_ulong) \ \ F(cl_event_info, CL_EVENT_CONTEXT, cl::Context) #define CL_HPP_PARAM_NAME_INFO_1_2_(F) \ F(cl_program_info, CL_PROGRAM_NUM_KERNELS, size_type) \ F(cl_program_info, CL_PROGRAM_KERNEL_NAMES, string) \ \ F(cl_program_build_info, CL_PROGRAM_BINARY_TYPE, cl_program_binary_type) \ \ F(cl_kernel_info, CL_KERNEL_ATTRIBUTES, string) \ \ F(cl_kernel_arg_info, CL_KERNEL_ARG_ADDRESS_QUALIFIER, cl_kernel_arg_address_qualifier) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_ACCESS_QUALIFIER, cl_kernel_arg_access_qualifier) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_TYPE_NAME, string) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_NAME, string) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_TYPE_QUALIFIER, cl_kernel_arg_type_qualifier) \ \ F(cl_kernel_work_group_info, CL_KERNEL_GLOBAL_WORK_SIZE, cl::detail::size_t_array) \ \ F(cl_device_info, CL_DEVICE_LINKER_AVAILABLE, cl_bool) \ F(cl_device_info, CL_DEVICE_IMAGE_MAX_BUFFER_SIZE, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE_MAX_ARRAY_SIZE, size_type) \ F(cl_device_info, CL_DEVICE_PARENT_DEVICE, cl::Device) \ F(cl_device_info, CL_DEVICE_PARTITION_MAX_SUB_DEVICES, cl_uint) \ F(cl_device_info, CL_DEVICE_PARTITION_PROPERTIES, cl::vector) \ F(cl_device_info, CL_DEVICE_PARTITION_TYPE, cl::vector) \ F(cl_device_info, CL_DEVICE_REFERENCE_COUNT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_INTEROP_USER_SYNC, cl_bool) \ F(cl_device_info, CL_DEVICE_PARTITION_AFFINITY_DOMAIN, cl_device_affinity_domain) \ F(cl_device_info, CL_DEVICE_BUILT_IN_KERNELS, string) \ F(cl_device_info, CL_DEVICE_PRINTF_BUFFER_SIZE, size_type) \ \ F(cl_image_info, CL_IMAGE_ARRAY_SIZE, size_type) \ F(cl_image_info, CL_IMAGE_NUM_MIP_LEVELS, cl_uint) \ F(cl_image_info, CL_IMAGE_NUM_SAMPLES, cl_uint) #define CL_HPP_PARAM_NAME_INFO_2_0_(F) \ F(cl_device_info, CL_DEVICE_QUEUE_ON_HOST_PROPERTIES, cl_command_queue_properties) \ F(cl_device_info, CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES, cl_command_queue_properties) \ F(cl_device_info, CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_ON_DEVICE_QUEUES, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_ON_DEVICE_EVENTS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_PIPE_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS, cl_uint) \ F(cl_device_info, CL_DEVICE_PIPE_MAX_PACKET_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_SVM_CAPABILITIES, cl_device_svm_capabilities) \ F(cl_device_info, CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_IMAGE_PITCH_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS, cl_uint ) \ F(cl_device_info, CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE, size_type ) \ F(cl_device_info, CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE, size_type ) \ F(cl_profiling_info, CL_PROFILING_COMMAND_COMPLETE, cl_ulong) \ F(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM, cl_bool) \ F(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_SVM_PTRS, void**) \ F(cl_command_queue_info, CL_QUEUE_SIZE, cl_uint) \ F(cl_mem_info, CL_MEM_USES_SVM_POINTER, cl_bool) \ F(cl_program_build_info, CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE, size_type) \ F(cl_pipe_info, CL_PIPE_PACKET_SIZE, cl_uint) \ F(cl_pipe_info, CL_PIPE_MAX_PACKETS, cl_uint) #define CL_HPP_PARAM_NAME_INFO_SUBGROUP_KHR_(F) \ F(cl_kernel_sub_group_info, CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE_KHR, size_type) \ F(cl_kernel_sub_group_info, CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE_KHR, size_type) #define CL_HPP_PARAM_NAME_INFO_IL_KHR_(F) \ F(cl_device_info, CL_DEVICE_IL_VERSION_KHR, string) \ F(cl_program_info, CL_PROGRAM_IL_KHR, cl::vector) #define CL_HPP_PARAM_NAME_INFO_2_1_(F) \ F(cl_platform_info, CL_PLATFORM_HOST_TIMER_RESOLUTION, cl_ulong) \ F(cl_program_info, CL_PROGRAM_IL, cl::vector) \ F(cl_device_info, CL_DEVICE_MAX_NUM_SUB_GROUPS, cl_uint) \ F(cl_device_info, CL_DEVICE_IL_VERSION, string) \ F(cl_device_info, CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS, cl_bool) \ F(cl_command_queue_info, CL_QUEUE_DEVICE_DEFAULT, cl::DeviceCommandQueue) \ F(cl_kernel_sub_group_info, CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE, size_type) \ F(cl_kernel_sub_group_info, CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE, size_type) \ F(cl_kernel_sub_group_info, CL_KERNEL_LOCAL_SIZE_FOR_SUB_GROUP_COUNT, cl::detail::size_t_array) \ F(cl_kernel_sub_group_info, CL_KERNEL_MAX_NUM_SUB_GROUPS, size_type) \ F(cl_kernel_sub_group_info, CL_KERNEL_COMPILE_NUM_SUB_GROUPS, size_type) #define CL_HPP_PARAM_NAME_INFO_2_2_(F) \ F(cl_program_info, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT, cl_bool) \ F(cl_program_info, CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT, cl_bool) #define CL_HPP_PARAM_NAME_DEVICE_FISSION_(F) \ F(cl_device_info, CL_DEVICE_PARENT_DEVICE_EXT, cl_device_id) \ F(cl_device_info, CL_DEVICE_PARTITION_TYPES_EXT, cl::vector) \ F(cl_device_info, CL_DEVICE_AFFINITY_DOMAINS_EXT, cl::vector) \ F(cl_device_info, CL_DEVICE_REFERENCE_COUNT_EXT , cl_uint) \ F(cl_device_info, CL_DEVICE_PARTITION_STYLE_EXT, cl::vector) #define CL_HPP_PARAM_NAME_CL_KHR_EXTENDED_VERSIONING_CL3_SHARED_(F) \ F(cl_platform_info, CL_PLATFORM_NUMERIC_VERSION_KHR, cl_version_khr) \ F(cl_platform_info, CL_PLATFORM_EXTENSIONS_WITH_VERSION_KHR, cl::vector) \ \ F(cl_device_info, CL_DEVICE_NUMERIC_VERSION_KHR, cl_version_khr) \ F(cl_device_info, CL_DEVICE_EXTENSIONS_WITH_VERSION_KHR, cl::vector) \ F(cl_device_info, CL_DEVICE_ILS_WITH_VERSION_KHR, cl::vector) \ F(cl_device_info, CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION_KHR, cl::vector) #define CL_HPP_PARAM_NAME_CL_KHR_EXTENDED_VERSIONING_KHRONLY_(F) \ F(cl_device_info, CL_DEVICE_OPENCL_C_NUMERIC_VERSION_KHR, cl_version_khr) #define CL_HPP_PARAM_NAME_INFO_3_0_(F) \ F(cl_platform_info, CL_PLATFORM_NUMERIC_VERSION, cl_version) \ F(cl_platform_info, CL_PLATFORM_EXTENSIONS_WITH_VERSION, cl::vector) \ \ F(cl_device_info, CL_DEVICE_NUMERIC_VERSION, cl_version) \ F(cl_device_info, CL_DEVICE_EXTENSIONS_WITH_VERSION, cl::vector) \ F(cl_device_info, CL_DEVICE_ILS_WITH_VERSION, cl::vector) \ F(cl_device_info, CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION, cl::vector) \ F(cl_device_info, CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES, cl_device_atomic_capabilities) \ F(cl_device_info, CL_DEVICE_ATOMIC_FENCE_CAPABILITIES, cl_device_atomic_capabilities) \ F(cl_device_info, CL_DEVICE_NON_UNIFORM_WORK_GROUP_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_OPENCL_C_ALL_VERSIONS, cl::vector) \ F(cl_device_info, CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, size_type) \ F(cl_device_info, CL_DEVICE_WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_GENERIC_ADDRESS_SPACE_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_OPENCL_C_FEATURES, cl::vector) \ F(cl_device_info, CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES, cl_device_device_enqueue_capabilities) \ F(cl_device_info, CL_DEVICE_PIPE_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_LATEST_CONFORMANCE_VERSION_PASSED, string) \ \ F(cl_command_queue_info, CL_QUEUE_PROPERTIES_ARRAY, cl::vector) \ F(cl_mem_info, CL_MEM_PROPERTIES, cl::vector) \ F(cl_pipe_info, CL_PIPE_PROPERTIES, cl::vector) \ F(cl_sampler_info, CL_SAMPLER_PROPERTIES, cl::vector) template struct param_traits {}; #define CL_HPP_DECLARE_PARAM_TRAITS_(token, param_name, T) \ struct token; \ template<> \ struct param_traits \ { \ enum { value = param_name }; \ typedef T param_type; \ }; CL_HPP_PARAM_NAME_INFO_1_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #if CL_HPP_TARGET_OPENCL_VERSION >= 110 CL_HPP_PARAM_NAME_INFO_1_1_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 CL_HPP_PARAM_NAME_INFO_1_2_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 200 CL_HPP_PARAM_NAME_INFO_2_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 CL_HPP_PARAM_NAME_INFO_2_1_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #if CL_HPP_TARGET_OPENCL_VERSION >= 220 CL_HPP_PARAM_NAME_INFO_2_2_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 220 #if CL_HPP_TARGET_OPENCL_VERSION >= 300 CL_HPP_PARAM_NAME_INFO_3_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 300 #if defined(CL_HPP_USE_CL_SUB_GROUPS_KHR) && CL_HPP_TARGET_OPENCL_VERSION < 210 CL_HPP_PARAM_NAME_INFO_SUBGROUP_KHR_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // #if defined(CL_HPP_USE_CL_SUB_GROUPS_KHR) && CL_HPP_TARGET_OPENCL_VERSION < 210 #if defined(CL_HPP_USE_IL_KHR) CL_HPP_PARAM_NAME_INFO_IL_KHR_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // #if defined(CL_HPP_USE_IL_KHR) // Flags deprecated in OpenCL 2.0 #define CL_HPP_PARAM_NAME_INFO_1_0_DEPRECATED_IN_2_0_(F) \ F(cl_device_info, CL_DEVICE_QUEUE_PROPERTIES, cl_command_queue_properties) #define CL_HPP_PARAM_NAME_INFO_1_1_DEPRECATED_IN_2_0_(F) \ F(cl_device_info, CL_DEVICE_HOST_UNIFIED_MEMORY, cl_bool) #define CL_HPP_PARAM_NAME_INFO_1_2_DEPRECATED_IN_2_0_(F) \ F(cl_image_info, CL_IMAGE_BUFFER, cl::Buffer) // Include deprecated query flags based on versions // Only include deprecated 1.0 flags if 2.0 not active as there is an enum clash #if CL_HPP_TARGET_OPENCL_VERSION > 100 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 && CL_HPP_TARGET_OPENCL_VERSION < 200 CL_HPP_PARAM_NAME_INFO_1_0_DEPRECATED_IN_2_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 110 #if CL_HPP_TARGET_OPENCL_VERSION > 110 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 CL_HPP_PARAM_NAME_INFO_1_1_DEPRECATED_IN_2_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 120 #if CL_HPP_TARGET_OPENCL_VERSION > 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 CL_HPP_PARAM_NAME_INFO_1_2_DEPRECATED_IN_2_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 #if defined(CL_HPP_USE_CL_DEVICE_FISSION) CL_HPP_PARAM_NAME_DEVICE_FISSION_(CL_HPP_DECLARE_PARAM_TRAITS_); #endif // CL_HPP_USE_CL_DEVICE_FISSION #if defined(cl_khr_extended_versioning) #if CL_HPP_TARGET_OPENCL_VERSION < 300 CL_HPP_PARAM_NAME_CL_KHR_EXTENDED_VERSIONING_CL3_SHARED_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION < 300 CL_HPP_PARAM_NAME_CL_KHR_EXTENDED_VERSIONING_KHRONLY_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // cl_khr_extended_versioning #if defined(cl_khr_device_uuid) using uuid_array = array; using luid_array = array; CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_UUID_KHR, uuid_array) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DRIVER_UUID_KHR, uuid_array) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_LUID_VALID_KHR, cl_bool) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_LUID_KHR, luid_array) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_NODE_MASK_KHR, cl_uint) #endif #if defined(cl_khr_pci_bus_info) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_PCI_BUS_INFO_KHR, cl_device_pci_bus_info_khr) #endif #if defined(cl_khr_integer_dot_product) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_INTEGER_DOT_PRODUCT_CAPABILITIES_KHR, cl_device_integer_dot_product_capabilities_khr) #endif #ifdef CL_PLATFORM_ICD_SUFFIX_KHR CL_HPP_DECLARE_PARAM_TRAITS_(cl_platform_info, CL_PLATFORM_ICD_SUFFIX_KHR, string) #endif #ifdef CL_DEVICE_PROFILING_TIMER_OFFSET_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_PROFILING_TIMER_OFFSET_AMD, cl_ulong) #endif #ifdef CL_DEVICE_GLOBAL_FREE_MEMORY_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GLOBAL_FREE_MEMORY_AMD, vector) #endif #ifdef CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD, cl_uint) #endif #ifdef CL_DEVICE_SIMD_WIDTH_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SIMD_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_WAVEFRONT_WIDTH_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_WAVEFRONT_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD, cl_uint) #endif #ifdef CL_DEVICE_LOCAL_MEM_BANKS_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_LOCAL_MEM_BANKS_AMD, cl_uint) #endif #ifdef CL_DEVICE_COMPUTE_UNITS_BITFIELD_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_COMPUTE_UNITS_BITFIELD_ARM, cl_ulong) #endif #ifdef CL_DEVICE_JOB_SLOTS_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_JOB_SLOTS_ARM, cl_uint) #endif #ifdef CL_DEVICE_SCHEDULING_CONTROLS_CAPABILITIES_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SCHEDULING_CONTROLS_CAPABILITIES_ARM, cl_bitfield) #endif #ifdef CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_ARM, cl_uint) #endif #ifdef CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_MODIFIER_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_MODIFIER_ARM, cl_int) #endif #ifdef CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV, cl_uint) #endif #ifdef CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV, cl_uint) #endif #ifdef CL_DEVICE_REGISTERS_PER_BLOCK_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_REGISTERS_PER_BLOCK_NV, cl_uint) #endif #ifdef CL_DEVICE_WARP_SIZE_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_WARP_SIZE_NV, cl_uint) #endif #ifdef CL_DEVICE_GPU_OVERLAP_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GPU_OVERLAP_NV, cl_bool) #endif #ifdef CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV, cl_bool) #endif #ifdef CL_DEVICE_INTEGRATED_MEMORY_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_INTEGRATED_MEMORY_NV, cl_bool) #endif // Convenience functions template inline cl_int getInfo(Func f, cl_uint name, T* param) { return getInfoHelper(f, name, param, 0); } template struct GetInfoFunctor0 { Func f_; const Arg0& arg0_; cl_int operator ()( cl_uint param, size_type size, void* value, size_type* size_ret) { return f_(arg0_, param, size, value, size_ret); } }; template struct GetInfoFunctor1 { Func f_; const Arg0& arg0_; const Arg1& arg1_; cl_int operator ()( cl_uint param, size_type size, void* value, size_type* size_ret) { return f_(arg0_, arg1_, param, size, value, size_ret); } }; template inline cl_int getInfo(Func f, const Arg0& arg0, cl_uint name, T* param) { GetInfoFunctor0 f0 = { f, arg0 }; return getInfoHelper(f0, name, param, 0); } template inline cl_int getInfo(Func f, const Arg0& arg0, const Arg1& arg1, cl_uint name, T* param) { GetInfoFunctor1 f0 = { f, arg0, arg1 }; return getInfoHelper(f0, name, param, 0); } template struct ReferenceHandler { }; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * OpenCL 1.2 devices do have retain/release. */ template <> struct ReferenceHandler { /** * Retain the device. * \param device A valid device created using createSubDevices * \return * CL_SUCCESS if the function executed successfully. * CL_INVALID_DEVICE if device was not a valid subdevice * CL_OUT_OF_RESOURCES * CL_OUT_OF_HOST_MEMORY */ static cl_int retain(cl_device_id device) { return ::clRetainDevice(device); } /** * Retain the device. * \param device A valid device created using createSubDevices * \return * CL_SUCCESS if the function executed successfully. * CL_INVALID_DEVICE if device was not a valid subdevice * CL_OUT_OF_RESOURCES * CL_OUT_OF_HOST_MEMORY */ static cl_int release(cl_device_id device) { return ::clReleaseDevice(device); } }; #else // CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * OpenCL 1.1 devices do not have retain/release. */ template <> struct ReferenceHandler { // cl_device_id does not have retain(). static cl_int retain(cl_device_id) { return CL_SUCCESS; } // cl_device_id does not have release(). static cl_int release(cl_device_id) { return CL_SUCCESS; } }; #endif // ! (CL_HPP_TARGET_OPENCL_VERSION >= 120) template <> struct ReferenceHandler { // cl_platform_id does not have retain(). static cl_int retain(cl_platform_id) { return CL_SUCCESS; } // cl_platform_id does not have release(). static cl_int release(cl_platform_id) { return CL_SUCCESS; } }; template <> struct ReferenceHandler { static cl_int retain(cl_context context) { return ::clRetainContext(context); } static cl_int release(cl_context context) { return ::clReleaseContext(context); } }; template <> struct ReferenceHandler { static cl_int retain(cl_command_queue queue) { return ::clRetainCommandQueue(queue); } static cl_int release(cl_command_queue queue) { return ::clReleaseCommandQueue(queue); } }; template <> struct ReferenceHandler { static cl_int retain(cl_mem memory) { return ::clRetainMemObject(memory); } static cl_int release(cl_mem memory) { return ::clReleaseMemObject(memory); } }; template <> struct ReferenceHandler { static cl_int retain(cl_sampler sampler) { return ::clRetainSampler(sampler); } static cl_int release(cl_sampler sampler) { return ::clReleaseSampler(sampler); } }; template <> struct ReferenceHandler { static cl_int retain(cl_program program) { return ::clRetainProgram(program); } static cl_int release(cl_program program) { return ::clReleaseProgram(program); } }; template <> struct ReferenceHandler { static cl_int retain(cl_kernel kernel) { return ::clRetainKernel(kernel); } static cl_int release(cl_kernel kernel) { return ::clReleaseKernel(kernel); } }; template <> struct ReferenceHandler { static cl_int retain(cl_event event) { return ::clRetainEvent(event); } static cl_int release(cl_event event) { return ::clReleaseEvent(event); } }; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 120 // Extracts version number with major in the upper 16 bits, minor in the lower 16 static cl_uint getVersion(const vector &versionInfo) { int highVersion = 0; int lowVersion = 0; int index = 7; while(versionInfo[index] != '.' ) { highVersion *= 10; highVersion += versionInfo[index]-'0'; ++index; } ++index; while(versionInfo[index] != ' ' && versionInfo[index] != '\0') { lowVersion *= 10; lowVersion += versionInfo[index]-'0'; ++index; } return (highVersion << 16) | lowVersion; } static cl_uint getPlatformVersion(cl_platform_id platform) { size_type size = 0; clGetPlatformInfo(platform, CL_PLATFORM_VERSION, 0, NULL, &size); vector versionInfo(size); clGetPlatformInfo(platform, CL_PLATFORM_VERSION, size, versionInfo.data(), &size); return getVersion(versionInfo); } static cl_uint getDevicePlatformVersion(cl_device_id device) { cl_platform_id platform; clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(platform), &platform, NULL); return getPlatformVersion(platform); } static cl_uint getContextPlatformVersion(cl_context context) { // The platform cannot be queried directly, so we first have to grab a // device and obtain its context size_type size = 0; clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &size); if (size == 0) return 0; vector devices(size/sizeof(cl_device_id)); clGetContextInfo(context, CL_CONTEXT_DEVICES, size, devices.data(), NULL); return getDevicePlatformVersion(devices[0]); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 120 template class Wrapper { public: typedef T cl_type; protected: cl_type object_; public: Wrapper() : object_(NULL) { } Wrapper(const cl_type &obj, bool retainObject) : object_(obj) { if (retainObject) { detail::errHandler(retain(), __RETAIN_ERR); } } ~Wrapper() { if (object_ != NULL) { release(); } } Wrapper(const Wrapper& rhs) { object_ = rhs.object_; detail::errHandler(retain(), __RETAIN_ERR); } Wrapper(Wrapper&& rhs) CL_HPP_NOEXCEPT_ { object_ = rhs.object_; rhs.object_ = NULL; } Wrapper& operator = (const Wrapper& rhs) { if (this != &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs.object_; detail::errHandler(retain(), __RETAIN_ERR); } return *this; } Wrapper& operator = (Wrapper&& rhs) { if (this != &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs.object_; rhs.object_ = NULL; } return *this; } Wrapper& operator = (const cl_type &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs; return *this; } const cl_type& operator ()() const { return object_; } cl_type& operator ()() { return object_; } cl_type get() const { return object_; } protected: template friend inline cl_int getInfoHelper(Func, cl_uint, U*, int, typename U::cl_type); cl_int retain() const { if (object_ != nullptr) { return ReferenceHandler::retain(object_); } else { return CL_SUCCESS; } } cl_int release() const { if (object_ != nullptr) { return ReferenceHandler::release(object_); } else { return CL_SUCCESS; } } }; template <> class Wrapper { public: typedef cl_device_id cl_type; protected: cl_type object_; bool referenceCountable_; static bool isReferenceCountable(cl_device_id device) { bool retVal = false; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 if (device != NULL) { int version = getDevicePlatformVersion(device); if(version > ((1 << 16) + 1)) { retVal = true; } } #else // CL_HPP_MINIMUM_OPENCL_VERSION < 120 retVal = true; #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 120 #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 return retVal; } public: Wrapper() : object_(NULL), referenceCountable_(false) { } Wrapper(const cl_type &obj, bool retainObject) : object_(obj), referenceCountable_(false) { referenceCountable_ = isReferenceCountable(obj); if (retainObject) { detail::errHandler(retain(), __RETAIN_ERR); } } ~Wrapper() { release(); } Wrapper(const Wrapper& rhs) { object_ = rhs.object_; referenceCountable_ = isReferenceCountable(object_); detail::errHandler(retain(), __RETAIN_ERR); } Wrapper(Wrapper&& rhs) CL_HPP_NOEXCEPT_ { object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; rhs.object_ = NULL; rhs.referenceCountable_ = false; } Wrapper& operator = (const Wrapper& rhs) { if (this != &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; detail::errHandler(retain(), __RETAIN_ERR); } return *this; } Wrapper& operator = (Wrapper&& rhs) { if (this != &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; rhs.object_ = NULL; rhs.referenceCountable_ = false; } return *this; } Wrapper& operator = (const cl_type &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs; referenceCountable_ = isReferenceCountable(object_); return *this; } const cl_type& operator ()() const { return object_; } cl_type& operator ()() { return object_; } cl_type get() const { return object_; } protected: template friend inline cl_int getInfoHelper(Func, cl_uint, U*, int, typename U::cl_type); template friend inline cl_int getInfoHelper(Func, cl_uint, vector*, int, typename U::cl_type); cl_int retain() const { if( object_ != nullptr && referenceCountable_ ) { return ReferenceHandler::retain(object_); } else { return CL_SUCCESS; } } cl_int release() const { if (object_ != nullptr && referenceCountable_) { return ReferenceHandler::release(object_); } else { return CL_SUCCESS; } } }; template inline bool operator==(const Wrapper &lhs, const Wrapper &rhs) { return lhs() == rhs(); } template inline bool operator!=(const Wrapper &lhs, const Wrapper &rhs) { return !operator==(lhs, rhs); } } // namespace detail //! \endcond using BuildLogType = vector::param_type>>; #if defined(CL_HPP_ENABLE_EXCEPTIONS) /** * Exception class for build errors to carry build info */ class BuildError : public Error { private: BuildLogType buildLogs; public: BuildError(cl_int err, const char * errStr, const BuildLogType &vec) : Error(err, errStr), buildLogs(vec) { } BuildLogType getBuildLog() const { return buildLogs; } }; namespace detail { static inline cl_int buildErrHandler( cl_int err, const char * errStr, const BuildLogType &buildLogs) { if (err != CL_SUCCESS) { throw BuildError(err, errStr, buildLogs); } return err; } } // namespace detail #else namespace detail { static inline cl_int buildErrHandler( cl_int err, const char * errStr, const BuildLogType &buildLogs) { (void)buildLogs; // suppress unused variable warning (void)errStr; return err; } } // namespace detail #endif // #if defined(CL_HPP_ENABLE_EXCEPTIONS) /*! \stuct ImageFormat * \brief Adds constructors and member functions for cl_image_format. * * \see cl_image_format */ struct ImageFormat : public cl_image_format { //! \brief Default constructor - performs no initialization. ImageFormat(){} //! \brief Initializing constructor. ImageFormat(cl_channel_order order, cl_channel_type type) { image_channel_order = order; image_channel_data_type = type; } //! \brief Copy constructor. ImageFormat(const ImageFormat &other) { *this = other; } //! \brief Assignment operator. ImageFormat& operator = (const ImageFormat& rhs) { if (this != &rhs) { this->image_channel_data_type = rhs.image_channel_data_type; this->image_channel_order = rhs.image_channel_order; } return *this; } }; /*! \brief Class interface for cl_device_id. * * \note Copies of these objects are inexpensive, since they don't 'own' * any underlying resources or data structures. * * \see cl_device_id */ class Device : public detail::Wrapper { private: static std::once_flag default_initialized_; static Device default_; static cl_int default_error_; /*! \brief Create the default context. * * This sets @c default_ and @c default_error_. It does not throw * @c cl::Error. */ static void makeDefault(); /*! \brief Create the default platform from a provided platform. * * This sets @c default_. It does not throw * @c cl::Error. */ static void makeDefaultProvided(const Device &p) { default_ = p; } public: #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Reset the default. * * This sets @c default_ to an empty value to support cleanup in * the unit test framework. * This function is not thread safe. */ static void unitTestClearDefault() { default_ = Device(); } #endif // #ifdef CL_HPP_UNIT_TEST_ENABLE //! \brief Default constructor - initializes to NULL. Device() : detail::Wrapper() { } /*! \brief Constructor from cl_device_id. * * This simply copies the device ID value, which is an inexpensive operation. */ explicit Device(const cl_device_id &device, bool retainObject = false) : detail::Wrapper(device, retainObject) { } /*! \brief Returns the first device on the default context. * * \see Context::getDefault() */ static Device getDefault( cl_int *errResult = NULL) { std::call_once(default_initialized_, makeDefault); detail::errHandler(default_error_); if (errResult != NULL) { *errResult = default_error_; } return default_; } /** * Modify the default device to be used by * subsequent operations. * Will only set the default if no default was previously created. * @return updated default device. * Should be compared to the passed value to ensure that it was updated. */ static Device setDefault(const Device &default_device) { std::call_once(default_initialized_, makeDefaultProvided, std::cref(default_device)); detail::errHandler(default_error_); return default_; } /*! \brief Assignment operator from cl_device_id. * * This simply copies the device ID value, which is an inexpensive operation. */ Device& operator = (const cl_device_id& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Device(const Device& dev) : detail::Wrapper(dev) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Device& operator = (const Device &dev) { detail::Wrapper::operator=(dev); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Device(Device&& dev) CL_HPP_NOEXCEPT_ : detail::Wrapper(std::move(dev)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Device& operator = (Device &&dev) { detail::Wrapper::operator=(std::move(dev)); return *this; } //! \brief Wrapper for clGetDeviceInfo(). template cl_int getInfo(cl_device_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetDeviceInfo, object_, name, param), __GET_DEVICE_INFO_ERR); } //! \brief Wrapper for clGetDeviceInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_device_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Return the current value of the host clock as seen by the device. * The resolution of the device timer may be queried with the * CL_DEVICE_PROFILING_TIMER_RESOLUTION query. * @return The host timer value. */ cl_ulong getHostTimer(cl_int *error = nullptr) { cl_ulong retVal = 0; cl_int err = clGetHostTimer(this->get(), &retVal); detail::errHandler( err, __GET_HOST_TIMER_ERR); if (error) { *error = err; } return retVal; } /** * Return a synchronized pair of host and device timestamps as seen by device. * Use to correlate the clocks and get the host timer only using getHostTimer * as a lower cost mechanism in between calls. * The resolution of the host timer may be queried with the * CL_PLATFORM_HOST_TIMER_RESOLUTION query. * The resolution of the device timer may be queried with the * CL_DEVICE_PROFILING_TIMER_RESOLUTION query. * @return A pair of (device timer, host timer) timer values. */ std::pair getDeviceAndHostTimer(cl_int *error = nullptr) { std::pair retVal; cl_int err = clGetDeviceAndHostTimer(this->get(), &(retVal.first), &(retVal.second)); detail::errHandler( err, __GET_DEVICE_AND_HOST_TIMER_ERR); if (error) { *error = err; } return retVal; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * CL 1.2 version */ #if CL_HPP_TARGET_OPENCL_VERSION >= 120 //! \brief Wrapper for clCreateSubDevices(). cl_int createSubDevices( const cl_device_partition_property * properties, vector* devices) { cl_uint n = 0; cl_int err = clCreateSubDevices(object_, properties, 0, NULL, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES_ERR); } vector ids(n); err = clCreateSubDevices(object_, properties, n, ids.data(), NULL); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES_ERR); } // Cannot trivially assign because we need to capture intermediates // with safe construction if (devices) { devices->resize(ids.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < ids.size(); i++) { // We do not need to retain because this device is being created // by the runtime (*devices)[i] = Device(ids[i], false); } } return CL_SUCCESS; } #elif defined(CL_HPP_USE_CL_DEVICE_FISSION) /** * CL 1.1 version that uses device fission extension. */ cl_int createSubDevices( const cl_device_partition_property_ext * properties, vector* devices) { typedef CL_API_ENTRY cl_int ( CL_API_CALL * PFN_clCreateSubDevicesEXT)( cl_device_id /*in_device*/, const cl_device_partition_property_ext * /* properties */, cl_uint /*num_entries*/, cl_device_id * /*out_devices*/, cl_uint * /*num_devices*/ ) CL_API_SUFFIX__VERSION_1_1; static PFN_clCreateSubDevicesEXT pfn_clCreateSubDevicesEXT = NULL; CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateSubDevicesEXT); cl_uint n = 0; cl_int err = pfn_clCreateSubDevicesEXT(object_, properties, 0, NULL, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES_ERR); } vector ids(n); err = pfn_clCreateSubDevicesEXT(object_, properties, n, ids.data(), NULL); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES_ERR); } // Cannot trivially assign because we need to capture intermediates // with safe construction if (devices) { devices->resize(ids.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < ids.size(); i++) { // We do not need to retain because this device is being created // by the runtime (*devices)[i] = Device(ids[i], false); } } return CL_SUCCESS; } #endif // defined(CL_HPP_USE_CL_DEVICE_FISSION) }; CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag Device::default_initialized_; CL_HPP_DEFINE_STATIC_MEMBER_ Device Device::default_; CL_HPP_DEFINE_STATIC_MEMBER_ cl_int Device::default_error_ = CL_SUCCESS; /*! \brief Class interface for cl_platform_id. * * \note Copies of these objects are inexpensive, since they don't 'own' * any underlying resources or data structures. * * \see cl_platform_id */ class Platform : public detail::Wrapper { private: static std::once_flag default_initialized_; static Platform default_; static cl_int default_error_; /*! \brief Create the default context. * * This sets @c default_ and @c default_error_. It does not throw * @c cl::Error. */ static void makeDefault() { /* Throwing an exception from a call_once invocation does not do * what we wish, so we catch it and save the error. */ #if defined(CL_HPP_ENABLE_EXCEPTIONS) try #endif { // If default wasn't passed ,generate one // Otherwise set it cl_uint n = 0; cl_int err = ::clGetPlatformIDs(0, NULL, &n); if (err != CL_SUCCESS) { default_error_ = err; return; } if (n == 0) { default_error_ = CL_INVALID_PLATFORM; return; } vector ids(n); err = ::clGetPlatformIDs(n, ids.data(), NULL); if (err != CL_SUCCESS) { default_error_ = err; return; } default_ = Platform(ids[0]); } #if defined(CL_HPP_ENABLE_EXCEPTIONS) catch (cl::Error &e) { default_error_ = e.err(); } #endif } /*! \brief Create the default platform from a provided platform. * * This sets @c default_. It does not throw * @c cl::Error. */ static void makeDefaultProvided(const Platform &p) { default_ = p; } public: #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Reset the default. * * This sets @c default_ to an empty value to support cleanup in * the unit test framework. * This function is not thread safe. */ static void unitTestClearDefault() { default_ = Platform(); } #endif // #ifdef CL_HPP_UNIT_TEST_ENABLE //! \brief Default constructor - initializes to NULL. Platform() : detail::Wrapper() { } /*! \brief Constructor from cl_platform_id. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * This simply copies the platform ID value, which is an inexpensive operation. */ explicit Platform(const cl_platform_id &platform, bool retainObject = false) : detail::Wrapper(platform, retainObject) { } /*! \brief Assignment operator from cl_platform_id. * * This simply copies the platform ID value, which is an inexpensive operation. */ Platform& operator = (const cl_platform_id& rhs) { detail::Wrapper::operator=(rhs); return *this; } static Platform getDefault( cl_int *errResult = NULL) { std::call_once(default_initialized_, makeDefault); detail::errHandler(default_error_); if (errResult != NULL) { *errResult = default_error_; } return default_; } /** * Modify the default platform to be used by * subsequent operations. * Will only set the default if no default was previously created. * @return updated default platform. * Should be compared to the passed value to ensure that it was updated. */ static Platform setDefault(const Platform &default_platform) { std::call_once(default_initialized_, makeDefaultProvided, std::cref(default_platform)); detail::errHandler(default_error_); return default_; } //! \brief Wrapper for clGetPlatformInfo(). template cl_int getInfo(cl_platform_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetPlatformInfo, object_, name, param), __GET_PLATFORM_INFO_ERR); } //! \brief Wrapper for clGetPlatformInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_platform_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } /*! \brief Gets a list of devices for this platform. * * Wraps clGetDeviceIDs(). */ cl_int getDevices( cl_device_type type, vector* devices) const { cl_uint n = 0; if( devices == NULL ) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_DEVICE_IDS_ERR); } cl_int err = ::clGetDeviceIDs(object_, type, 0, NULL, &n); if (err != CL_SUCCESS && err != CL_DEVICE_NOT_FOUND) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } vector ids(n); if (n>0) { err = ::clGetDeviceIDs(object_, type, n, ids.data(), NULL); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } } // Cannot trivially assign because we need to capture intermediates // with safe construction // We must retain things we obtain from the API to avoid releasing // API-owned objects. if (devices) { devices->resize(ids.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < ids.size(); i++) { (*devices)[i] = Device(ids[i], true); } } return CL_SUCCESS; } #if defined(CL_HPP_USE_DX_INTEROP) /*! \brief Get the list of available D3D10 devices. * * \param d3d_device_source. * * \param d3d_object. * * \param d3d_device_set. * * \param devices returns a vector of OpenCL D3D10 devices found. The cl::Device * values returned in devices can be used to identify a specific OpenCL * device. If \a devices argument is NULL, this argument is ignored. * * \return One of the following values: * - CL_SUCCESS if the function is executed successfully. * * The application can query specific capabilities of the OpenCL device(s) * returned by cl::getDevices. This can be used by the application to * determine which device(s) to use. * * \note In the case that exceptions are enabled and a return value * other than CL_SUCCESS is generated, then cl::Error exception is * generated. */ cl_int getDevices( cl_d3d10_device_source_khr d3d_device_source, void * d3d_object, cl_d3d10_device_set_khr d3d_device_set, vector* devices) const { typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clGetDeviceIDsFromD3D10KHR)( cl_platform_id platform, cl_d3d10_device_source_khr d3d_device_source, void * d3d_object, cl_d3d10_device_set_khr d3d_device_set, cl_uint num_entries, cl_device_id * devices, cl_uint* num_devices); if( devices == NULL ) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_DEVICE_IDS_ERR); } static PFN_clGetDeviceIDsFromD3D10KHR pfn_clGetDeviceIDsFromD3D10KHR = NULL; CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(object_, clGetDeviceIDsFromD3D10KHR); cl_uint n = 0; cl_int err = pfn_clGetDeviceIDsFromD3D10KHR( object_, d3d_device_source, d3d_object, d3d_device_set, 0, NULL, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } vector ids(n); err = pfn_clGetDeviceIDsFromD3D10KHR( object_, d3d_device_source, d3d_object, d3d_device_set, n, ids.data(), NULL); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } // Cannot trivially assign because we need to capture intermediates // with safe construction // We must retain things we obtain from the API to avoid releasing // API-owned objects. if (devices) { devices->resize(ids.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < ids.size(); i++) { (*devices)[i] = Device(ids[i], true); } } return CL_SUCCESS; } #endif /*! \brief Gets a list of available platforms. * * Wraps clGetPlatformIDs(). */ static cl_int get( vector* platforms) { cl_uint n = 0; if( platforms == NULL ) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_PLATFORM_IDS_ERR); } cl_int err = ::clGetPlatformIDs(0, NULL, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); } vector ids(n); err = ::clGetPlatformIDs(n, ids.data(), NULL); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); } if (platforms) { platforms->resize(ids.size()); // Platforms don't reference count for (size_type i = 0; i < ids.size(); i++) { (*platforms)[i] = Platform(ids[i]); } } return CL_SUCCESS; } /*! \brief Gets the first available platform. * * Wraps clGetPlatformIDs(), returning the first result. */ static cl_int get( Platform * platform) { cl_int err; Platform default_platform = Platform::getDefault(&err); if (platform) { *platform = default_platform; } return err; } /*! \brief Gets the first available platform, returning it by value. * * \return Returns a valid platform if one is available. * If no platform is available will return a null platform. * Throws an exception if no platforms are available * or an error condition occurs. * Wraps clGetPlatformIDs(), returning the first result. */ static Platform get( cl_int * errResult = NULL) { cl_int err; Platform default_platform = Platform::getDefault(&err); if (errResult) { *errResult = err; } return default_platform; } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 //! \brief Wrapper for clUnloadCompiler(). cl_int unloadCompiler() { return ::clUnloadPlatformCompiler(object_); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 }; // class Platform CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag Platform::default_initialized_; CL_HPP_DEFINE_STATIC_MEMBER_ Platform Platform::default_; CL_HPP_DEFINE_STATIC_MEMBER_ cl_int Platform::default_error_ = CL_SUCCESS; /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /** * Unload the OpenCL compiler. * \note Deprecated for OpenCL 1.2. Use Platform::unloadCompiler instead. */ inline CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int UnloadCompiler() CL_API_SUFFIX__VERSION_1_1_DEPRECATED; inline cl_int UnloadCompiler() { return ::clUnloadCompiler(); } #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /*! \brief Class interface for cl_context. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_context as the original. For details, see * clRetainContext() and clReleaseContext(). * * \see cl_context */ class Context : public detail::Wrapper { private: static std::once_flag default_initialized_; static Context default_; static cl_int default_error_; /*! \brief Create the default context from the default device type in the default platform. * * This sets @c default_ and @c default_error_. It does not throw * @c cl::Error. */ static void makeDefault() { /* Throwing an exception from a call_once invocation does not do * what we wish, so we catch it and save the error. */ #if defined(CL_HPP_ENABLE_EXCEPTIONS) try #endif { #if !defined(__APPLE__) && !defined(__MACOS) const Platform &p = Platform::getDefault(); cl_platform_id defaultPlatform = p(); cl_context_properties properties[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)defaultPlatform, 0 }; #else // #if !defined(__APPLE__) && !defined(__MACOS) cl_context_properties *properties = nullptr; #endif // #if !defined(__APPLE__) && !defined(__MACOS) default_ = Context( CL_DEVICE_TYPE_DEFAULT, properties, NULL, NULL, &default_error_); } #if defined(CL_HPP_ENABLE_EXCEPTIONS) catch (cl::Error &e) { default_error_ = e.err(); } #endif } /*! \brief Create the default context from a provided Context. * * This sets @c default_. It does not throw * @c cl::Error. */ static void makeDefaultProvided(const Context &c) { default_ = c; } public: #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Reset the default. * * This sets @c default_ to an empty value to support cleanup in * the unit test framework. * This function is not thread safe. */ static void unitTestClearDefault() { default_ = Context(); } #endif // #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Constructs a context including a list of specified devices. * * Wraps clCreateContext(). */ Context( const vector& devices, const cl_context_properties* properties = NULL, void (CL_CALLBACK * notifyFptr)( const char *, const void *, size_type, void *) = NULL, void* data = NULL, cl_int* err = NULL) { cl_int error; size_type numDevices = devices.size(); vector deviceIDs(numDevices); for( size_type deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex ) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } object_ = ::clCreateContext( properties, (cl_uint) numDevices, deviceIDs.data(), notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (err != NULL) { *err = error; } } /*! \brief Constructs a context including a specific device. * * Wraps clCreateContext(). */ Context( const Device& device, const cl_context_properties* properties = NULL, void (CL_CALLBACK * notifyFptr)( const char *, const void *, size_type, void *) = NULL, void* data = NULL, cl_int* err = NULL) { cl_int error; cl_device_id deviceID = device(); object_ = ::clCreateContext( properties, 1, &deviceID, notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (err != NULL) { *err = error; } } /*! \brief Constructs a context including all or a subset of devices of a specified type. * * Wraps clCreateContextFromType(). */ Context( cl_device_type type, const cl_context_properties* properties = NULL, void (CL_CALLBACK * notifyFptr)( const char *, const void *, size_type, void *) = NULL, void* data = NULL, cl_int* err = NULL) { cl_int error; #if !defined(__APPLE__) && !defined(__MACOS) cl_context_properties prop[4] = {CL_CONTEXT_PLATFORM, 0, 0, 0 }; if (properties == NULL) { // Get a valid platform ID as we cannot send in a blank one vector platforms; error = Platform::get(&platforms); if (error != CL_SUCCESS) { detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != NULL) { *err = error; } return; } // Check the platforms we found for a device of our specified type cl_context_properties platform_id = 0; for (unsigned int i = 0; i < platforms.size(); i++) { vector devices; #if defined(CL_HPP_ENABLE_EXCEPTIONS) try { #endif error = platforms[i].getDevices(type, &devices); #if defined(CL_HPP_ENABLE_EXCEPTIONS) } catch (cl::Error& e) { error = e.err(); } // Catch if exceptions are enabled as we don't want to exit if first platform has no devices of type // We do error checking next anyway, and can throw there if needed #endif // Only squash CL_SUCCESS and CL_DEVICE_NOT_FOUND if (error != CL_SUCCESS && error != CL_DEVICE_NOT_FOUND) { detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != NULL) { *err = error; } } if (devices.size() > 0) { platform_id = (cl_context_properties)platforms[i](); break; } } if (platform_id == 0) { detail::errHandler(CL_DEVICE_NOT_FOUND, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != NULL) { *err = CL_DEVICE_NOT_FOUND; } return; } prop[1] = platform_id; properties = &prop[0]; } #endif object_ = ::clCreateContextFromType( properties, type, notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != NULL) { *err = error; } } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Context(const Context& ctx) : detail::Wrapper(ctx) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Context& operator = (const Context &ctx) { detail::Wrapper::operator=(ctx); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Context(Context&& ctx) CL_HPP_NOEXCEPT_ : detail::Wrapper(std::move(ctx)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Context& operator = (Context &&ctx) { detail::Wrapper::operator=(std::move(ctx)); return *this; } /*! \brief Returns a singleton context including all devices of CL_DEVICE_TYPE_DEFAULT. * * \note All calls to this function return the same cl_context as the first. */ static Context getDefault(cl_int * err = NULL) { std::call_once(default_initialized_, makeDefault); detail::errHandler(default_error_); if (err != NULL) { *err = default_error_; } return default_; } /** * Modify the default context to be used by * subsequent operations. * Will only set the default if no default was previously created. * @return updated default context. * Should be compared to the passed value to ensure that it was updated. */ static Context setDefault(const Context &default_context) { std::call_once(default_initialized_, makeDefaultProvided, std::cref(default_context)); detail::errHandler(default_error_); return default_; } //! \brief Default constructor - initializes to NULL. Context() : detail::Wrapper() { } /*! \brief Constructor from cl_context - takes ownership. * * This effectively transfers ownership of a refcount on the cl_context * into the new Context object. */ explicit Context(const cl_context& context, bool retainObject = false) : detail::Wrapper(context, retainObject) { } /*! \brief Assignment operator from cl_context - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseContext() on the value previously held by this instance. */ Context& operator = (const cl_context& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetContextInfo(). template cl_int getInfo(cl_context_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetContextInfo, object_, name, param), __GET_CONTEXT_INFO_ERR); } //! \brief Wrapper for clGetContextInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_context_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } /*! \brief Gets a list of supported image formats. * * Wraps clGetSupportedImageFormats(). */ cl_int getSupportedImageFormats( cl_mem_flags flags, cl_mem_object_type type, vector* formats) const { cl_uint numEntries; if (!formats) { return CL_SUCCESS; } cl_int err = ::clGetSupportedImageFormats( object_, flags, type, 0, NULL, &numEntries); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); } if (numEntries > 0) { vector value(numEntries); err = ::clGetSupportedImageFormats( object_, flags, type, numEntries, (cl_image_format*)value.data(), NULL); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); } formats->assign(begin(value), end(value)); } else { // If no values are being returned, ensure an empty vector comes back formats->clear(); } return CL_SUCCESS; } }; inline void Device::makeDefault() { /* Throwing an exception from a call_once invocation does not do * what we wish, so we catch it and save the error. */ #if defined(CL_HPP_ENABLE_EXCEPTIONS) try #endif { cl_int error = 0; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { default_error_ = error; } else { default_ = context.getInfo()[0]; default_error_ = CL_SUCCESS; } } #if defined(CL_HPP_ENABLE_EXCEPTIONS) catch (cl::Error &e) { default_error_ = e.err(); } #endif } CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag Context::default_initialized_; CL_HPP_DEFINE_STATIC_MEMBER_ Context Context::default_; CL_HPP_DEFINE_STATIC_MEMBER_ cl_int Context::default_error_ = CL_SUCCESS; /*! \brief Class interface for cl_event. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_event as the original. For details, see * clRetainEvent() and clReleaseEvent(). * * \see cl_event */ class Event : public detail::Wrapper { public: //! \brief Default constructor - initializes to NULL. Event() : detail::Wrapper() { } /*! \brief Constructor from cl_event - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * This effectively transfers ownership of a refcount on the cl_event * into the new Event object. */ explicit Event(const cl_event& event, bool retainObject = false) : detail::Wrapper(event, retainObject) { } /*! \brief Assignment operator from cl_event - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseEvent() on the value previously held by this instance. */ Event& operator = (const cl_event& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetEventInfo(). template cl_int getInfo(cl_event_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetEventInfo, object_, name, param), __GET_EVENT_INFO_ERR); } //! \brief Wrapper for clGetEventInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_event_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } //! \brief Wrapper for clGetEventProfilingInfo(). template cl_int getProfilingInfo(cl_profiling_info name, T* param) const { return detail::errHandler(detail::getInfo( &::clGetEventProfilingInfo, object_, name, param), __GET_EVENT_PROFILE_INFO_ERR); } //! \brief Wrapper for clGetEventProfilingInfo() that returns by value. template typename detail::param_traits::param_type getProfilingInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_profiling_info, name>::param_type param; cl_int result = getProfilingInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } /*! \brief Blocks the calling thread until this event completes. * * Wraps clWaitForEvents(). */ cl_int wait() const { return detail::errHandler( ::clWaitForEvents(1, &object_), __WAIT_FOR_EVENTS_ERR); } #if CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Registers a user callback function for a specific command execution status. * * Wraps clSetEventCallback(). */ cl_int setCallback( cl_int type, void (CL_CALLBACK * pfn_notify)(cl_event, cl_int, void *), void * user_data = NULL) { return detail::errHandler( ::clSetEventCallback( object_, type, pfn_notify, user_data), __SET_EVENT_CALLBACK_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Blocks the calling thread until every event specified is complete. * * Wraps clWaitForEvents(). */ static cl_int waitForEvents(const vector& events) { return detail::errHandler( ::clWaitForEvents( (cl_uint) events.size(), (events.size() > 0) ? (cl_event*)&events.front() : NULL), __WAIT_FOR_EVENTS_ERR); } }; #if CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Class interface for user events (a subset of cl_event's). * * See Event for details about copy semantics, etc. */ class UserEvent : public Event { public: /*! \brief Constructs a user event on a given context. * * Wraps clCreateUserEvent(). */ UserEvent( const Context& context, cl_int * err = NULL) { cl_int error; object_ = ::clCreateUserEvent( context(), &error); detail::errHandler(error, __CREATE_USER_EVENT_ERR); if (err != NULL) { *err = error; } } //! \brief Default constructor - initializes to NULL. UserEvent() : Event() { } /*! \brief Sets the execution status of a user event object. * * Wraps clSetUserEventStatus(). */ cl_int setStatus(cl_int status) { return detail::errHandler( ::clSetUserEventStatus(object_,status), __SET_USER_EVENT_STATUS_ERR); } }; #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Blocks the calling thread until every event specified is complete. * * Wraps clWaitForEvents(). */ inline static cl_int WaitForEvents(const vector& events) { return detail::errHandler( ::clWaitForEvents( (cl_uint) events.size(), (events.size() > 0) ? (cl_event*)&events.front() : NULL), __WAIT_FOR_EVENTS_ERR); } /*! \brief Class interface for cl_mem. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_mem as the original. For details, see * clRetainMemObject() and clReleaseMemObject(). * * \see cl_mem */ class Memory : public detail::Wrapper { public: //! \brief Default constructor - initializes to NULL. Memory() : detail::Wrapper() { } /*! \brief Constructor from cl_mem - takes ownership. * * Optionally transfer ownership of a refcount on the cl_mem * into the new Memory object. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * * See Memory for further details. */ explicit Memory(const cl_mem& memory, bool retainObject) : detail::Wrapper(memory, retainObject) { } /*! \brief Assignment operator from cl_mem - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseMemObject() on the value previously held by this instance. */ Memory& operator = (const cl_mem& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Memory(const Memory& mem) : detail::Wrapper(mem) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Memory& operator = (const Memory &mem) { detail::Wrapper::operator=(mem); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Memory(Memory&& mem) CL_HPP_NOEXCEPT_ : detail::Wrapper(std::move(mem)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Memory& operator = (Memory &&mem) { detail::Wrapper::operator=(std::move(mem)); return *this; } //! \brief Wrapper for clGetMemObjectInfo(). template cl_int getInfo(cl_mem_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetMemObjectInfo, object_, name, param), __GET_MEM_OBJECT_INFO_ERR); } //! \brief Wrapper for clGetMemObjectInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_mem_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } #if CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Registers a callback function to be called when the memory object * is no longer needed. * * Wraps clSetMemObjectDestructorCallback(). * * Repeated calls to this function, for a given cl_mem value, will append * to the list of functions called (in reverse order) when memory object's * resources are freed and the memory object is deleted. * * \note * The registered callbacks are associated with the underlying cl_mem * value - not the Memory class instance. */ cl_int setDestructorCallback( void (CL_CALLBACK * pfn_notify)(cl_mem, void *), void * user_data = NULL) { return detail::errHandler( ::clSetMemObjectDestructorCallback( object_, pfn_notify, user_data), __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 }; // Pre-declare copy functions class Buffer; template< typename IteratorType > cl_int copy( IteratorType startIterator, IteratorType endIterator, cl::Buffer &buffer ); template< typename IteratorType > cl_int copy( const cl::Buffer &buffer, IteratorType startIterator, IteratorType endIterator ); template< typename IteratorType > cl_int copy( const CommandQueue &queue, IteratorType startIterator, IteratorType endIterator, cl::Buffer &buffer ); template< typename IteratorType > cl_int copy( const CommandQueue &queue, const cl::Buffer &buffer, IteratorType startIterator, IteratorType endIterator ); #if CL_HPP_TARGET_OPENCL_VERSION >= 200 namespace detail { class SVMTraitNull { public: static cl_svm_mem_flags getSVMMemFlags() { return 0; } }; } // namespace detail template class SVMTraitReadWrite { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_READ_WRITE | Trait::getSVMMemFlags(); } }; template class SVMTraitReadOnly { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_READ_ONLY | Trait::getSVMMemFlags(); } }; template class SVMTraitWriteOnly { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_WRITE_ONLY | Trait::getSVMMemFlags(); } }; template> class SVMTraitCoarse { public: static cl_svm_mem_flags getSVMMemFlags() { return Trait::getSVMMemFlags(); } }; template> class SVMTraitFine { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_SVM_FINE_GRAIN_BUFFER | Trait::getSVMMemFlags(); } }; template> class SVMTraitAtomic { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_SVM_FINE_GRAIN_BUFFER | CL_MEM_SVM_ATOMICS | Trait::getSVMMemFlags(); } }; // Pre-declare SVM map function template inline cl_int enqueueMapSVM( T* ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events = NULL, Event* event = NULL); /** * STL-like allocator class for managing SVM objects provided for convenience. * * Note that while this behaves like an allocator for the purposes of constructing vectors and similar objects, * care must be taken when using with smart pointers. * The allocator should not be used to construct a unique_ptr if we are using coarse-grained SVM mode because * the coarse-grained management behaviour would behave incorrectly with respect to reference counting. * * Instead the allocator embeds a Deleter which may be used with unique_ptr and is used * with the allocate_shared and allocate_ptr supplied operations. */ template class SVMAllocator { private: Context context_; public: typedef T value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; template struct rebind { typedef SVMAllocator other; }; template friend class SVMAllocator; SVMAllocator() : context_(Context::getDefault()) { } explicit SVMAllocator(cl::Context context) : context_(context) { } SVMAllocator(const SVMAllocator &other) : context_(other.context_) { } template SVMAllocator(const SVMAllocator &other) : context_(other.context_) { } ~SVMAllocator() { } pointer address(reference r) CL_HPP_NOEXCEPT_ { return std::addressof(r); } const_pointer address(const_reference r) CL_HPP_NOEXCEPT_ { return std::addressof(r); } /** * Allocate an SVM pointer. * * If the allocator is coarse-grained, this will take ownership to allow * containers to correctly construct data in place. */ pointer allocate( size_type size, typename cl::SVMAllocator::const_pointer = 0) { // Allocate memory with default alignment matching the size of the type void* voidPointer = clSVMAlloc( context_(), SVMTrait::getSVMMemFlags(), size*sizeof(T), 0); pointer retValue = reinterpret_cast( voidPointer); #if defined(CL_HPP_ENABLE_EXCEPTIONS) if (!retValue) { std::bad_alloc excep; throw excep; } #endif // #if defined(CL_HPP_ENABLE_EXCEPTIONS) // If allocation was coarse-grained then map it if (!(SVMTrait::getSVMMemFlags() & CL_MEM_SVM_FINE_GRAIN_BUFFER)) { cl_int err = enqueueMapSVM(retValue, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, size*sizeof(T)); if (err != CL_SUCCESS) { std::bad_alloc excep; throw excep; } } // If exceptions disabled, return null pointer from allocator return retValue; } void deallocate(pointer p, size_type) { clSVMFree(context_(), p); } /** * Return the maximum possible allocation size. * This is the minimum of the maximum sizes of all devices in the context. */ size_type max_size() const CL_HPP_NOEXCEPT_ { size_type maxSize = std::numeric_limits::max() / sizeof(T); for (const Device &d : context_.getInfo()) { maxSize = std::min( maxSize, static_cast(d.getInfo())); } return maxSize; } template< class U, class... Args > void construct(U* p, Args&&... args) { new(p)T(args...); } template< class U > void destroy(U* p) { p->~U(); } /** * Returns true if the contexts match. */ inline bool operator==(SVMAllocator const& rhs) { return (context_==rhs.context_); } inline bool operator!=(SVMAllocator const& a) { return !operator==(a); } }; // class SVMAllocator return cl::pointer(tmp, detail::Deleter{alloc, copies}); template class SVMAllocator { public: typedef void value_type; typedef value_type* pointer; typedef const value_type* const_pointer; template struct rebind { typedef SVMAllocator other; }; template friend class SVMAllocator; }; #if !defined(CL_HPP_NO_STD_UNIQUE_PTR) namespace detail { template class Deleter { private: Alloc alloc_; size_type copies_; public: typedef typename std::allocator_traits::pointer pointer; Deleter(const Alloc &alloc, size_type copies) : alloc_{ alloc }, copies_{ copies } { } void operator()(pointer ptr) const { Alloc tmpAlloc{ alloc_ }; std::allocator_traits::destroy(tmpAlloc, std::addressof(*ptr)); std::allocator_traits::deallocate(tmpAlloc, ptr, copies_); } }; } // namespace detail /** * Allocation operation compatible with std::allocate_ptr. * Creates a unique_ptr by default. * This requirement is to ensure that the control block is not * allocated in memory inaccessible to the host. */ template cl::pointer> allocate_pointer(const Alloc &alloc_, Args&&... args) { Alloc alloc(alloc_); static const size_type copies = 1; // Ensure that creation of the management block and the // object are dealt with separately such that we only provide a deleter T* tmp = std::allocator_traits::allocate(alloc, copies); if (!tmp) { std::bad_alloc excep; throw excep; } try { std::allocator_traits::construct( alloc, std::addressof(*tmp), std::forward(args)...); return cl::pointer>(tmp, detail::Deleter{alloc, copies}); } catch (std::bad_alloc& b) { std::allocator_traits::deallocate(alloc, tmp, copies); throw; } } template< class T, class SVMTrait, class... Args > cl::pointer>> allocate_svm(Args... args) { SVMAllocator alloc; return cl::allocate_pointer(alloc, args...); } template< class T, class SVMTrait, class... Args > cl::pointer>> allocate_svm(const cl::Context &c, Args... args) { SVMAllocator alloc(c); return cl::allocate_pointer(alloc, args...); } #endif // #if !defined(CL_HPP_NO_STD_UNIQUE_PTR) /*! \brief Vector alias to simplify contruction of coarse-grained SVM containers. * */ template < class T > using coarse_svm_vector = vector>>; /*! \brief Vector alias to simplify contruction of fine-grained SVM containers. * */ template < class T > using fine_svm_vector = vector>>; /*! \brief Vector alias to simplify contruction of fine-grained SVM containers that support platform atomics. * */ template < class T > using atomic_svm_vector = vector>>; #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief Class interface for Buffer Memory Objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Buffer : public Memory { public: /*! \brief Constructs a Buffer in a specified context. * * Wraps clCreateBuffer(). * * \param host_ptr Storage to be used if the CL_MEM_USE_HOST_PTR flag was * specified. Note alignment & exclusivity requirements. */ Buffer( const Context& context, cl_mem_flags flags, size_type size, void* host_ptr = NULL, cl_int* err = NULL) { cl_int error; object_ = ::clCreateBuffer(context(), flags, size, host_ptr, &error); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } } /*! \brief Constructs a Buffer in the default context. * * Wraps clCreateBuffer(). * * \param host_ptr Storage to be used if the CL_MEM_USE_HOST_PTR flag was * specified. Note alignment & exclusivity requirements. * * \see Context::getDefault() */ Buffer( cl_mem_flags flags, size_type size, void* host_ptr = NULL, cl_int* err = NULL) { cl_int error; Context context = Context::getDefault(err); object_ = ::clCreateBuffer(context(), flags, size, host_ptr, &error); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } } /*! * \brief Construct a Buffer from a host container via iterators. * IteratorType must be random access. * If useHostPtr is specified iterators must represent contiguous data. */ template< typename IteratorType > Buffer( IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = NULL) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if( readOnly ) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if( useHostPtr ) { flags |= CL_MEM_USE_HOST_PTR; } size_type size = sizeof(DataType)*(endIterator - startIterator); Context context = Context::getDefault(err); if( useHostPtr ) { object_ = ::clCreateBuffer(context(), flags, size, static_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, 0, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } if( !useHostPtr ) { error = cl::copy(startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } } } /*! * \brief Construct a Buffer from a host container via iterators using a specified context. * IteratorType must be random access. * If useHostPtr is specified iterators must represent contiguous data. */ template< typename IteratorType > Buffer(const Context &context, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = NULL); /*! * \brief Construct a Buffer from a host container via iterators using a specified queue. * If useHostPtr is specified iterators must be random access. */ template< typename IteratorType > Buffer(const CommandQueue &queue, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = NULL); //! \brief Default constructor - initializes to NULL. Buffer() : Memory() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with earlier versions. * * See Memory for further details. */ explicit Buffer(const cl_mem& buffer, bool retainObject = false) : Memory(buffer, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Buffer& operator = (const cl_mem& rhs) { Memory::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Buffer(const Buffer& buf) : Memory(buf) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Buffer& operator = (const Buffer &buf) { Memory::operator=(buf); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Buffer(Buffer&& buf) CL_HPP_NOEXCEPT_ : Memory(std::move(buf)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Buffer& operator = (Buffer &&buf) { Memory::operator=(std::move(buf)); return *this; } #if CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Creates a new buffer object from this. * * Wraps clCreateSubBuffer(). */ Buffer createSubBuffer( cl_mem_flags flags, cl_buffer_create_type buffer_create_type, const void * buffer_create_info, cl_int * err = NULL) { Buffer result; cl_int error; result.object_ = ::clCreateSubBuffer( object_, flags, buffer_create_type, buffer_create_info, &error); detail::errHandler(error, __CREATE_SUBBUFFER_ERR); if (err != NULL) { *err = error; } return result; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 }; #if defined (CL_HPP_USE_DX_INTEROP) /*! \brief Class interface for creating OpenCL buffers from ID3D10Buffer's. * * This is provided to facilitate interoperability with Direct3D. * * See Memory for details about copy semantics, etc. * * \see Memory */ class BufferD3D10 : public Buffer { public: /*! \brief Constructs a BufferD3D10, in a specified context, from a * given ID3D10Buffer. * * Wraps clCreateFromD3D10BufferKHR(). */ BufferD3D10( const Context& context, cl_mem_flags flags, ID3D10Buffer* bufobj, cl_int * err = NULL) : pfn_clCreateFromD3D10BufferKHR(nullptr) { typedef CL_API_ENTRY cl_mem (CL_API_CALL *PFN_clCreateFromD3D10BufferKHR)( cl_context context, cl_mem_flags flags, ID3D10Buffer* buffer, cl_int* errcode_ret); PFN_clCreateFromD3D10BufferKHR pfn_clCreateFromD3D10BufferKHR; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 vector props = context.getInfo(); cl_platform platform = -1; for( int i = 0; i < props.size(); ++i ) { if( props[i] == CL_CONTEXT_PLATFORM ) { platform = props[i+1]; } } CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCreateFromD3D10BufferKHR); #elif CL_HPP_TARGET_OPENCL_VERSION >= 110 CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateFromD3D10BufferKHR); #endif cl_int error; object_ = pfn_clCreateFromD3D10BufferKHR( context(), flags, bufobj, &error); detail::errHandler(error, __CREATE_GL_BUFFER_ERR); if (err != NULL) { *err = error; } } //! \brief Default constructor - initializes to NULL. BufferD3D10() : Buffer() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit BufferD3D10(const cl_mem& buffer, bool retainObject = false) : Buffer(buffer, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ BufferD3D10& operator = (const cl_mem& rhs) { Buffer::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ BufferD3D10(const BufferD3D10& buf) : Buffer(buf) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ BufferD3D10& operator = (const BufferD3D10 &buf) { Buffer::operator=(buf); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ BufferD3D10(BufferD3D10&& buf) CL_HPP_NOEXCEPT_ : Buffer(std::move(buf)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ BufferD3D10& operator = (BufferD3D10 &&buf) { Buffer::operator=(std::move(buf)); return *this; } }; #endif /*! \brief Class interface for GL Buffer Memory Objects. * * This is provided to facilitate interoperability with OpenGL. * * See Memory for details about copy semantics, etc. * * \see Memory */ class BufferGL : public Buffer { public: /*! \brief Constructs a BufferGL in a specified context, from a given * GL buffer. * * Wraps clCreateFromGLBuffer(). */ BufferGL( const Context& context, cl_mem_flags flags, cl_GLuint bufobj, cl_int * err = NULL) { cl_int error; object_ = ::clCreateFromGLBuffer( context(), flags, bufobj, &error); detail::errHandler(error, __CREATE_GL_BUFFER_ERR); if (err != NULL) { *err = error; } } //! \brief Default constructor - initializes to NULL. BufferGL() : Buffer() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit BufferGL(const cl_mem& buffer, bool retainObject = false) : Buffer(buffer, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ BufferGL& operator = (const cl_mem& rhs) { Buffer::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ BufferGL(const BufferGL& buf) : Buffer(buf) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ BufferGL& operator = (const BufferGL &buf) { Buffer::operator=(buf); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ BufferGL(BufferGL&& buf) CL_HPP_NOEXCEPT_ : Buffer(std::move(buf)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ BufferGL& operator = (BufferGL &&buf) { Buffer::operator=(std::move(buf)); return *this; } //! \brief Wrapper for clGetGLObjectInfo(). cl_int getObjectInfo( cl_gl_object_type *type, cl_GLuint * gl_object_name) { return detail::errHandler( ::clGetGLObjectInfo(object_,type,gl_object_name), __GET_GL_OBJECT_INFO_ERR); } }; /*! \brief Class interface for GL Render Buffer Memory Objects. * * This is provided to facilitate interoperability with OpenGL. * * See Memory for details about copy semantics, etc. * * \see Memory */ class BufferRenderGL : public Buffer { public: /*! \brief Constructs a BufferRenderGL in a specified context, from a given * GL Renderbuffer. * * Wraps clCreateFromGLRenderbuffer(). */ BufferRenderGL( const Context& context, cl_mem_flags flags, cl_GLuint bufobj, cl_int * err = NULL) { cl_int error; object_ = ::clCreateFromGLRenderbuffer( context(), flags, bufobj, &error); detail::errHandler(error, __CREATE_GL_RENDER_BUFFER_ERR); if (err != NULL) { *err = error; } } //! \brief Default constructor - initializes to NULL. BufferRenderGL() : Buffer() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit BufferRenderGL(const cl_mem& buffer, bool retainObject = false) : Buffer(buffer, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ BufferRenderGL& operator = (const cl_mem& rhs) { Buffer::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ BufferRenderGL(const BufferRenderGL& buf) : Buffer(buf) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ BufferRenderGL& operator = (const BufferRenderGL &buf) { Buffer::operator=(buf); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ BufferRenderGL(BufferRenderGL&& buf) CL_HPP_NOEXCEPT_ : Buffer(std::move(buf)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ BufferRenderGL& operator = (BufferRenderGL &&buf) { Buffer::operator=(std::move(buf)); return *this; } //! \brief Wrapper for clGetGLObjectInfo(). cl_int getObjectInfo( cl_gl_object_type *type, cl_GLuint * gl_object_name) { return detail::errHandler( ::clGetGLObjectInfo(object_,type,gl_object_name), __GET_GL_OBJECT_INFO_ERR); } }; /*! \brief C++ base class for Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image : public Memory { protected: //! \brief Default constructor - initializes to NULL. Image() : Memory() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image(const cl_mem& image, bool retainObject = false) : Memory(image, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image& operator = (const cl_mem& rhs) { Memory::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image(const Image& img) : Memory(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image& operator = (const Image &img) { Memory::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image(Image&& img) CL_HPP_NOEXCEPT_ : Memory(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image& operator = (Image &&img) { Memory::operator=(std::move(img)); return *this; } public: //! \brief Wrapper for clGetImageInfo(). template cl_int getImageInfo(cl_image_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetImageInfo, object_, name, param), __GET_IMAGE_INFO_ERR); } //! \brief Wrapper for clGetImageInfo() that returns by value. template typename detail::param_traits::param_type getImageInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_image_info, name>::param_type param; cl_int result = getImageInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } }; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \brief Class interface for 1D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image1D : public Image { public: /*! \brief Constructs a 1D Image in a specified context. * * Wraps clCreateImage(). */ Image1D( const Context& context, cl_mem_flags flags, ImageFormat format, size_type width, void* host_ptr = NULL, cl_int* err = NULL) { cl_int error; cl_image_desc desc = {0}; desc.image_type = CL_MEM_OBJECT_IMAGE1D; desc.image_width = width; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != NULL) { *err = error; } } //! \brief Default constructor - initializes to NULL. Image1D() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image1D(const cl_mem& image1D, bool retainObject = false) : Image(image1D, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image1D& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image1D(const Image1D& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image1D& operator = (const Image1D &img) { Image::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image1D(Image1D&& img) CL_HPP_NOEXCEPT_ : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image1D& operator = (Image1D &&img) { Image::operator=(std::move(img)); return *this; } }; /*! \class Image1DBuffer * \brief Image interface for 1D buffer images. */ class Image1DBuffer : public Image { public: Image1DBuffer( const Context& context, cl_mem_flags flags, ImageFormat format, size_type width, const Buffer &buffer, cl_int* err = NULL) { cl_int error; cl_image_desc desc = {0}; desc.image_type = CL_MEM_OBJECT_IMAGE1D_BUFFER; desc.image_width = width; desc.buffer = buffer(); object_ = ::clCreateImage( context(), flags, &format, &desc, NULL, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != NULL) { *err = error; } } Image1DBuffer() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image1DBuffer(const cl_mem& image1D, bool retainObject = false) : Image(image1D, retainObject) { } Image1DBuffer& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image1DBuffer(const Image1DBuffer& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image1DBuffer& operator = (const Image1DBuffer &img) { Image::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image1DBuffer(Image1DBuffer&& img) CL_HPP_NOEXCEPT_ : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image1DBuffer& operator = (Image1DBuffer &&img) { Image::operator=(std::move(img)); return *this; } }; /*! \class Image1DArray * \brief Image interface for arrays of 1D images. */ class Image1DArray : public Image { public: Image1DArray( const Context& context, cl_mem_flags flags, ImageFormat format, size_type arraySize, size_type width, size_type rowPitch, void* host_ptr = NULL, cl_int* err = NULL) { cl_int error; cl_image_desc desc = {0}; desc.image_type = CL_MEM_OBJECT_IMAGE1D_ARRAY; desc.image_width = width; desc.image_array_size = arraySize; desc.image_row_pitch = rowPitch; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != NULL) { *err = error; } } Image1DArray() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image1DArray(const cl_mem& imageArray, bool retainObject = false) : Image(imageArray, retainObject) { } Image1DArray& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image1DArray(const Image1DArray& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image1DArray& operator = (const Image1DArray &img) { Image::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image1DArray(Image1DArray&& img) CL_HPP_NOEXCEPT_ : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image1DArray& operator = (Image1DArray &&img) { Image::operator=(std::move(img)); return *this; } }; #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \brief Class interface for 2D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image2D : public Image { public: /*! \brief Constructs a 2D Image in a specified context. * * Wraps clCreateImage(). */ Image2D( const Context& context, cl_mem_flags flags, ImageFormat format, size_type width, size_type height, size_type row_pitch = 0, void* host_ptr = NULL, cl_int* err = NULL) { cl_int error; bool useCreateImage; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 120 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useCreateImage = (version >= 0x10002); // OpenCL 1.2 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 120 useCreateImage = true; #else useCreateImage = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 120 if (useCreateImage) { cl_image_desc desc = {0}; desc.image_type = CL_MEM_OBJECT_IMAGE2D; desc.image_width = width; desc.image_height = height; desc.image_row_pitch = row_pitch; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 if (!useCreateImage) { object_ = ::clCreateImage2D( context(), flags,&format, width, height, row_pitch, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE2D_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 120 } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 || defined(CL_HPP_USE_CL_IMAGE2D_FROM_BUFFER_KHR) /*! \brief Constructs a 2D Image from a buffer. * \note This will share storage with the underlying buffer. * * Wraps clCreateImage(). */ Image2D( const Context& context, ImageFormat format, const Buffer &sourceBuffer, size_type width, size_type height, size_type row_pitch = 0, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = {0}; desc.image_type = CL_MEM_OBJECT_IMAGE2D; desc.image_width = width; desc.image_height = height; desc.image_row_pitch = row_pitch; desc.buffer = sourceBuffer(); object_ = ::clCreateImage( context(), 0, // flags inherited from buffer &format, &desc, nullptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } #endif //#if CL_HPP_TARGET_OPENCL_VERSION >= 200 || defined(CL_HPP_USE_CL_IMAGE2D_FROM_BUFFER_KHR) #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief Constructs a 2D Image from an image. * \note This will share storage with the underlying image but may * reinterpret the channel order and type. * * The image will be created matching with a descriptor matching the source. * * \param order is the channel order to reinterpret the image data as. * The channel order may differ as described in the OpenCL * 2.0 API specification. * * Wraps clCreateImage(). */ Image2D( const Context& context, cl_channel_order order, const Image &sourceImage, cl_int* err = nullptr) { cl_int error; // Descriptor fields have to match source image size_type sourceWidth = sourceImage.getImageInfo(); size_type sourceHeight = sourceImage.getImageInfo(); size_type sourceRowPitch = sourceImage.getImageInfo(); cl_uint sourceNumMIPLevels = sourceImage.getImageInfo(); cl_uint sourceNumSamples = sourceImage.getImageInfo(); cl_image_format sourceFormat = sourceImage.getImageInfo(); // Update only the channel order. // Channel format inherited from source. sourceFormat.image_channel_order = order; cl_image_desc desc = {0}; desc.image_type = CL_MEM_OBJECT_IMAGE2D; desc.image_width = sourceWidth; desc.image_height = sourceHeight; desc.image_row_pitch = sourceRowPitch; desc.num_mip_levels = sourceNumMIPLevels; desc.num_samples = sourceNumSamples; desc.buffer = sourceImage(); object_ = ::clCreateImage( context(), 0, // flags should be inherited from mem_object &sourceFormat, &desc, nullptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } #endif //#if CL_HPP_TARGET_OPENCL_VERSION >= 200 //! \brief Default constructor - initializes to NULL. Image2D() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image2D(const cl_mem& image2D, bool retainObject = false) : Image(image2D, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image2D& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image2D(const Image2D& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image2D& operator = (const Image2D &img) { Image::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image2D(Image2D&& img) CL_HPP_NOEXCEPT_ : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image2D& operator = (Image2D &&img) { Image::operator=(std::move(img)); return *this; } }; #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /*! \brief Class interface for GL 2D Image Memory objects. * * This is provided to facilitate interoperability with OpenGL. * * See Memory for details about copy semantics, etc. * * \see Memory * \note Deprecated for OpenCL 1.2. Please use ImageGL instead. */ class CL_API_PREFIX__VERSION_1_1_DEPRECATED Image2DGL : public Image2D { public: /*! \brief Constructs an Image2DGL in a specified context, from a given * GL Texture. * * Wraps clCreateFromGLTexture2D(). */ Image2DGL( const Context& context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, cl_GLuint texobj, cl_int * err = NULL) { cl_int error; object_ = ::clCreateFromGLTexture2D( context(), flags, target, miplevel, texobj, &error); detail::errHandler(error, __CREATE_GL_TEXTURE_2D_ERR); if (err != NULL) { *err = error; } } //! \brief Default constructor - initializes to NULL. Image2DGL() : Image2D() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image2DGL(const cl_mem& image, bool retainObject = false) : Image2D(image, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. *c * See Memory for further details. */ Image2DGL& operator = (const cl_mem& rhs) { Image2D::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image2DGL(const Image2DGL& img) : Image2D(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image2DGL& operator = (const Image2DGL &img) { Image2D::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image2DGL(Image2DGL&& img) CL_HPP_NOEXCEPT_ : Image2D(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image2DGL& operator = (Image2DGL &&img) { Image2D::operator=(std::move(img)); return *this; } } CL_API_SUFFIX__VERSION_1_1_DEPRECATED; #endif // CL_USE_DEPRECATED_OPENCL_1_1_APIS #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \class Image2DArray * \brief Image interface for arrays of 2D images. */ class Image2DArray : public Image { public: Image2DArray( const Context& context, cl_mem_flags flags, ImageFormat format, size_type arraySize, size_type width, size_type height, size_type rowPitch, size_type slicePitch, void* host_ptr = NULL, cl_int* err = NULL) { cl_int error; cl_image_desc desc = {0}; desc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; desc.image_width = width; desc.image_height = height; desc.image_array_size = arraySize; desc.image_row_pitch = rowPitch; desc.image_slice_pitch = slicePitch; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != NULL) { *err = error; } } Image2DArray() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image2DArray(const cl_mem& imageArray, bool retainObject = false) : Image(imageArray, retainObject) { } Image2DArray& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image2DArray(const Image2DArray& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image2DArray& operator = (const Image2DArray &img) { Image::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image2DArray(Image2DArray&& img) CL_HPP_NOEXCEPT_ : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image2DArray& operator = (Image2DArray &&img) { Image::operator=(std::move(img)); return *this; } }; #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \brief Class interface for 3D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image3D : public Image { public: /*! \brief Constructs a 3D Image in a specified context. * * Wraps clCreateImage(). */ Image3D( const Context& context, cl_mem_flags flags, ImageFormat format, size_type width, size_type height, size_type depth, size_type row_pitch = 0, size_type slice_pitch = 0, void* host_ptr = NULL, cl_int* err = NULL) { cl_int error; bool useCreateImage; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 120 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useCreateImage = (version >= 0x10002); // OpenCL 1.2 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 120 useCreateImage = true; #else useCreateImage = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 120 if (useCreateImage) { cl_image_desc desc = {0}; desc.image_type = CL_MEM_OBJECT_IMAGE3D; desc.image_width = width; desc.image_height = height; desc.image_depth = depth; desc.image_row_pitch = row_pitch; desc.image_slice_pitch = slice_pitch; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 if (!useCreateImage) { object_ = ::clCreateImage3D( context(), flags, &format, width, height, depth, row_pitch, slice_pitch, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE3D_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 120 } //! \brief Default constructor - initializes to NULL. Image3D() : Image() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image3D(const cl_mem& image3D, bool retainObject = false) : Image(image3D, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image3D& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image3D(const Image3D& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image3D& operator = (const Image3D &img) { Image::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image3D(Image3D&& img) CL_HPP_NOEXCEPT_ : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image3D& operator = (Image3D &&img) { Image::operator=(std::move(img)); return *this; } }; #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /*! \brief Class interface for GL 3D Image Memory objects. * * This is provided to facilitate interoperability with OpenGL. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image3DGL : public Image3D { public: /*! \brief Constructs an Image3DGL in a specified context, from a given * GL Texture. * * Wraps clCreateFromGLTexture3D(). */ Image3DGL( const Context& context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, cl_GLuint texobj, cl_int * err = NULL) { cl_int error; object_ = ::clCreateFromGLTexture3D( context(), flags, target, miplevel, texobj, &error); detail::errHandler(error, __CREATE_GL_TEXTURE_3D_ERR); if (err != NULL) { *err = error; } } //! \brief Default constructor - initializes to NULL. Image3DGL() : Image3D() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image3DGL(const cl_mem& image, bool retainObject = false) : Image3D(image, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image3DGL& operator = (const cl_mem& rhs) { Image3D::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Image3DGL(const Image3DGL& img) : Image3D(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Image3DGL& operator = (const Image3DGL &img) { Image3D::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Image3DGL(Image3DGL&& img) CL_HPP_NOEXCEPT_ : Image3D(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Image3DGL& operator = (Image3DGL &&img) { Image3D::operator=(std::move(img)); return *this; } }; #endif // CL_USE_DEPRECATED_OPENCL_1_1_APIS #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \class ImageGL * \brief general image interface for GL interop. * We abstract the 2D and 3D GL images into a single instance here * that wraps all GL sourced images on the grounds that setup information * was performed by OpenCL anyway. */ class ImageGL : public Image { public: ImageGL( const Context& context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, cl_GLuint texobj, cl_int * err = NULL) { cl_int error; object_ = ::clCreateFromGLTexture( context(), flags, target, miplevel, texobj, &error); detail::errHandler(error, __CREATE_GL_TEXTURE_ERR); if (err != NULL) { *err = error; } } ImageGL() : Image() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit ImageGL(const cl_mem& image, bool retainObject = false) : Image(image, retainObject) { } ImageGL& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ ImageGL(const ImageGL& img) : Image(img) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ ImageGL& operator = (const ImageGL &img) { Image::operator=(img); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ ImageGL(ImageGL&& img) CL_HPP_NOEXCEPT_ : Image(std::move(img)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ ImageGL& operator = (ImageGL &&img) { Image::operator=(std::move(img)); return *this; } }; #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief Class interface for Pipe Memory Objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Pipe : public Memory { public: /*! \brief Constructs a Pipe in a specified context. * * Wraps clCreatePipe(). * @param context Context in which to create the pipe. * @param flags Bitfield. Only CL_MEM_READ_WRITE and CL_MEM_HOST_NO_ACCESS are valid. * @param packet_size Size in bytes of a single packet of the pipe. * @param max_packets Number of packets that may be stored in the pipe. * */ Pipe( const Context& context, cl_uint packet_size, cl_uint max_packets, cl_int* err = NULL) { cl_int error; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS; object_ = ::clCreatePipe(context(), flags, packet_size, max_packets, nullptr, &error); detail::errHandler(error, __CREATE_PIPE_ERR); if (err != NULL) { *err = error; } } /*! \brief Constructs a Pipe in a the default context. * * Wraps clCreatePipe(). * @param flags Bitfield. Only CL_MEM_READ_WRITE and CL_MEM_HOST_NO_ACCESS are valid. * @param packet_size Size in bytes of a single packet of the pipe. * @param max_packets Number of packets that may be stored in the pipe. * */ Pipe( cl_uint packet_size, cl_uint max_packets, cl_int* err = NULL) { cl_int error; Context context = Context::getDefault(err); cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS; object_ = ::clCreatePipe(context(), flags, packet_size, max_packets, nullptr, &error); detail::errHandler(error, __CREATE_PIPE_ERR); if (err != NULL) { *err = error; } } //! \brief Default constructor - initializes to NULL. Pipe() : Memory() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with earlier versions. * * See Memory for further details. */ explicit Pipe(const cl_mem& pipe, bool retainObject = false) : Memory(pipe, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Pipe& operator = (const cl_mem& rhs) { Memory::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Pipe(const Pipe& pipe) : Memory(pipe) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Pipe& operator = (const Pipe &pipe) { Memory::operator=(pipe); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Pipe(Pipe&& pipe) CL_HPP_NOEXCEPT_ : Memory(std::move(pipe)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Pipe& operator = (Pipe &&pipe) { Memory::operator=(std::move(pipe)); return *this; } //! \brief Wrapper for clGetMemObjectInfo(). template cl_int getInfo(cl_pipe_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetPipeInfo, object_, name, param), __GET_PIPE_INFO_ERR); } //! \brief Wrapper for clGetMemObjectInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_pipe_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } }; // class Pipe #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief Class interface for cl_sampler. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_sampler as the original. For details, see * clRetainSampler() and clReleaseSampler(). * * \see cl_sampler */ class Sampler : public detail::Wrapper { public: //! \brief Default constructor - initializes to NULL. Sampler() { } /*! \brief Constructs a Sampler in a specified context. * * Wraps clCreateSampler(). */ Sampler( const Context& context, cl_bool normalized_coords, cl_addressing_mode addressing_mode, cl_filter_mode filter_mode, cl_int* err = NULL) { cl_int error; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 cl_sampler_properties sampler_properties[] = { CL_SAMPLER_NORMALIZED_COORDS, normalized_coords, CL_SAMPLER_ADDRESSING_MODE, addressing_mode, CL_SAMPLER_FILTER_MODE, filter_mode, 0 }; object_ = ::clCreateSamplerWithProperties( context(), sampler_properties, &error); detail::errHandler(error, __CREATE_SAMPLER_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } #else object_ = ::clCreateSampler( context(), normalized_coords, addressing_mode, filter_mode, &error); detail::errHandler(error, __CREATE_SAMPLER_ERR); if (err != NULL) { *err = error; } #endif } /*! \brief Constructor from cl_sampler - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * This effectively transfers ownership of a refcount on the cl_sampler * into the new Sampler object. */ explicit Sampler(const cl_sampler& sampler, bool retainObject = false) : detail::Wrapper(sampler, retainObject) { } /*! \brief Assignment operator from cl_sampler - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseSampler() on the value previously held by this instance. */ Sampler& operator = (const cl_sampler& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Sampler(const Sampler& sam) : detail::Wrapper(sam) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Sampler& operator = (const Sampler &sam) { detail::Wrapper::operator=(sam); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Sampler(Sampler&& sam) CL_HPP_NOEXCEPT_ : detail::Wrapper(std::move(sam)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Sampler& operator = (Sampler &&sam) { detail::Wrapper::operator=(std::move(sam)); return *this; } //! \brief Wrapper for clGetSamplerInfo(). template cl_int getInfo(cl_sampler_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetSamplerInfo, object_, name, param), __GET_SAMPLER_INFO_ERR); } //! \brief Wrapper for clGetSamplerInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_sampler_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } }; class Program; class CommandQueue; class DeviceCommandQueue; class Kernel; //! \brief Class interface for specifying NDRange values. class NDRange { private: size_type sizes_[3]; cl_uint dimensions_; public: //! \brief Default constructor - resulting range has zero dimensions. NDRange() : dimensions_(0) { sizes_[0] = 0; sizes_[1] = 0; sizes_[2] = 0; } //! \brief Constructs one-dimensional range. NDRange(size_type size0) : dimensions_(1) { sizes_[0] = size0; sizes_[1] = 1; sizes_[2] = 1; } //! \brief Constructs two-dimensional range. NDRange(size_type size0, size_type size1) : dimensions_(2) { sizes_[0] = size0; sizes_[1] = size1; sizes_[2] = 1; } //! \brief Constructs three-dimensional range. NDRange(size_type size0, size_type size1, size_type size2) : dimensions_(3) { sizes_[0] = size0; sizes_[1] = size1; sizes_[2] = size2; } /*! \brief Conversion operator to const size_type *. * * \returns a pointer to the size of the first dimension. */ operator const size_type*() const { return sizes_; } //! \brief Queries the number of dimensions in the range. size_type dimensions() const { return dimensions_; } //! \brief Returns the size of the object in bytes based on the // runtime number of dimensions size_type size() const { return dimensions_*sizeof(size_type); } size_type* get() { return sizes_; } const size_type* get() const { return sizes_; } }; //! \brief A zero-dimensional range. static const NDRange NullRange; //! \brief Local address wrapper for use with Kernel::setArg struct LocalSpaceArg { size_type size_; }; namespace detail { template struct KernelArgumentHandler; // Enable for objects that are not subclasses of memory // Pointers, constants etc template struct KernelArgumentHandler::value>::type> { static size_type size(const T&) { return sizeof(T); } static const T* ptr(const T& value) { return &value; } }; // Enable for subclasses of memory where we want to get a reference to the cl_mem out // and pass that in for safety template struct KernelArgumentHandler::value>::type> { static size_type size(const T&) { return sizeof(cl_mem); } static const cl_mem* ptr(const T& value) { return &(value()); } }; // Specialization for DeviceCommandQueue defined later template <> struct KernelArgumentHandler { static size_type size(const LocalSpaceArg& value) { return value.size_; } static const void* ptr(const LocalSpaceArg&) { return NULL; } }; } //! \endcond /*! Local * \brief Helper function for generating LocalSpaceArg objects. */ inline LocalSpaceArg Local(size_type size) { LocalSpaceArg ret = { size }; return ret; } /*! \brief Class interface for cl_kernel. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_kernel as the original. For details, see * clRetainKernel() and clReleaseKernel(). * * \see cl_kernel */ class Kernel : public detail::Wrapper { public: inline Kernel(const Program& program, const char* name, cl_int* err = NULL); //! \brief Default constructor - initializes to NULL. Kernel() { } /*! \brief Constructor from cl_kernel - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * This effectively transfers ownership of a refcount on the cl_kernel * into the new Kernel object. */ explicit Kernel(const cl_kernel& kernel, bool retainObject = false) : detail::Wrapper(kernel, retainObject) { } /*! \brief Assignment operator from cl_kernel - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseKernel() on the value previously held by this instance. */ Kernel& operator = (const cl_kernel& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Kernel(const Kernel& kernel) : detail::Wrapper(kernel) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Kernel& operator = (const Kernel &kernel) { detail::Wrapper::operator=(kernel); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Kernel(Kernel&& kernel) CL_HPP_NOEXCEPT_ : detail::Wrapper(std::move(kernel)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Kernel& operator = (Kernel &&kernel) { detail::Wrapper::operator=(std::move(kernel)); return *this; } template cl_int getInfo(cl_kernel_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetKernelInfo, object_, name, param), __GET_KERNEL_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_kernel_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 template cl_int getArgInfo(cl_uint argIndex, cl_kernel_arg_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetKernelArgInfo, object_, argIndex, name, param), __GET_KERNEL_ARG_INFO_ERR); } template typename detail::param_traits::param_type getArgInfo(cl_uint argIndex, cl_int* err = NULL) const { typename detail::param_traits< detail::cl_kernel_arg_info, name>::param_type param; cl_int result = getArgInfo(argIndex, name, ¶m); if (err != NULL) { *err = result; } return param; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 template cl_int getWorkGroupInfo( const Device& device, cl_kernel_work_group_info name, T* param) const { return detail::errHandler( detail::getInfo( &::clGetKernelWorkGroupInfo, object_, device(), name, param), __GET_KERNEL_WORK_GROUP_INFO_ERR); } template typename detail::param_traits::param_type getWorkGroupInfo(const Device& device, cl_int* err = NULL) const { typename detail::param_traits< detail::cl_kernel_work_group_info, name>::param_type param; cl_int result = getWorkGroupInfo(device, name, ¶m); if (err != NULL) { *err = result; } return param; } #if (CL_HPP_TARGET_OPENCL_VERSION >= 200 && defined(CL_HPP_USE_CL_SUB_GROUPS_KHR)) || CL_HPP_TARGET_OPENCL_VERSION >= 210 cl_int getSubGroupInfo(const cl::Device &dev, cl_kernel_sub_group_info name, const cl::NDRange &range, size_type* param) const { #if CL_HPP_TARGET_OPENCL_VERSION >= 210 return detail::errHandler( clGetKernelSubGroupInfo(object_, dev(), name, range.size(), range.get(), sizeof(size_type), param, nullptr), __GET_KERNEL_SUB_GROUP_INFO_ERR); #else // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 typedef clGetKernelSubGroupInfoKHR_fn PFN_clGetKernelSubGroupInfoKHR; static PFN_clGetKernelSubGroupInfoKHR pfn_clGetKernelSubGroupInfoKHR = NULL; CL_HPP_INIT_CL_EXT_FCN_PTR_(clGetKernelSubGroupInfoKHR); return detail::errHandler( pfn_clGetKernelSubGroupInfoKHR(object_, dev(), name, range.size(), range.get(), sizeof(size_type), param, nullptr), __GET_KERNEL_SUB_GROUP_INFO_ERR); #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 } template size_type getSubGroupInfo(const cl::Device &dev, const cl::NDRange &range, cl_int* err = NULL) const { size_type param; cl_int result = getSubGroupInfo(dev, name, range, ¶m); if (err != NULL) { *err = result; } return param; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief setArg overload taking a shared_ptr type */ template cl_int setArg(cl_uint index, const cl::pointer &argPtr) { return detail::errHandler( ::clSetKernelArgSVMPointer(object_, index, argPtr.get()), __SET_KERNEL_ARGS_ERR); } /*! \brief setArg overload taking a vector type. */ template cl_int setArg(cl_uint index, const cl::vector &argPtr) { return detail::errHandler( ::clSetKernelArgSVMPointer(object_, index, argPtr.data()), __SET_KERNEL_ARGS_ERR); } /*! \brief setArg overload taking a pointer type */ template typename std::enable_if::value, cl_int>::type setArg(cl_uint index, const T argPtr) { return detail::errHandler( ::clSetKernelArgSVMPointer(object_, index, argPtr), __SET_KERNEL_ARGS_ERR); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief setArg overload taking a POD type */ template typename std::enable_if::value, cl_int>::type setArg(cl_uint index, const T &value) { return detail::errHandler( ::clSetKernelArg( object_, index, detail::KernelArgumentHandler::size(value), detail::KernelArgumentHandler::ptr(value)), __SET_KERNEL_ARGS_ERR); } cl_int setArg(cl_uint index, size_type size, const void* argPtr) { return detail::errHandler( ::clSetKernelArg(object_, index, size, argPtr), __SET_KERNEL_ARGS_ERR); } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! * Specify a vector of SVM pointers that the kernel may access in * addition to its arguments. */ cl_int setSVMPointers(const vector &pointerList) { return detail::errHandler( ::clSetKernelExecInfo( object_, CL_KERNEL_EXEC_INFO_SVM_PTRS, sizeof(void*)*pointerList.size(), pointerList.data())); } /*! * Specify a std::array of SVM pointers that the kernel may access in * addition to its arguments. */ template cl_int setSVMPointers(const std::array &pointerList) { return detail::errHandler( ::clSetKernelExecInfo( object_, CL_KERNEL_EXEC_INFO_SVM_PTRS, sizeof(void*)*pointerList.size(), pointerList.data())); } /*! \brief Enable fine-grained system SVM. * * \note It is only possible to enable fine-grained system SVM if all devices * in the context associated with kernel support it. * * \param svmEnabled True if fine-grained system SVM is requested. False otherwise. * \return CL_SUCCESS if the function was executed succesfully. CL_INVALID_OPERATION * if no devices in the context support fine-grained system SVM. * * \see clSetKernelExecInfo */ cl_int enableFineGrainedSystemSVM(bool svmEnabled) { cl_bool svmEnabled_ = svmEnabled ? CL_TRUE : CL_FALSE; return detail::errHandler( ::clSetKernelExecInfo( object_, CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM, sizeof(cl_bool), &svmEnabled_ ) ); } template void setSVMPointersHelper(std::array &pointerList, const pointer &t0, const pointer &t1, Ts & ... ts) { pointerList[index] = static_cast(t0.get()); setSVMPointersHelper(pointerList, t1, ts...); } template typename std::enable_if::value, void>::type setSVMPointersHelper(std::array &pointerList, T0 t0, T1 t1, Ts... ts) { pointerList[index] = static_cast(t0); setSVMPointersHelper(pointerList, t1, ts...); } template void setSVMPointersHelper(std::array &pointerList, const pointer &t0) { pointerList[index] = static_cast(t0.get()); } template typename std::enable_if::value, void>::type setSVMPointersHelper(std::array &pointerList, T0 t0) { pointerList[index] = static_cast(t0); } template cl_int setSVMPointers(const T0 &t0, Ts & ... ts) { std::array pointerList; setSVMPointersHelper<0, 1 + sizeof...(Ts)>(pointerList, t0, ts...); return detail::errHandler( ::clSetKernelExecInfo( object_, CL_KERNEL_EXEC_INFO_SVM_PTRS, sizeof(void*)*(1 + sizeof...(Ts)), pointerList.data())); } template cl_int setExecInfo(cl_kernel_exec_info param_name, const T& val) { return detail::errHandler( ::clSetKernelExecInfo( object_, param_name, sizeof(T), &val)); } template cl_int setExecInfo(typename detail::param_traits::param_type& val) { return setExecInfo(name, val); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Make a deep copy of the kernel object including its arguments. * @return A new kernel object with internal state entirely separate from that * of the original but with any arguments set on the original intact. */ Kernel clone() { cl_int error; Kernel retValue(clCloneKernel(this->get(), &error)); detail::errHandler(error, __CLONE_KERNEL_ERR); return retValue; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 }; /*! \class Program * \brief Program interface that implements cl_program. */ class Program : public detail::Wrapper { public: #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) typedef vector> Binaries; typedef vector Sources; #else // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) typedef vector > Binaries; typedef vector > Sources; #endif // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) Program( const string& source, bool build = false, cl_int* err = NULL) { cl_int error; const char * strings = source.c_str(); const size_type length = source.size(); Context context = Context::getDefault(err); object_ = ::clCreateProgramWithSource( context(), (cl_uint)1, &strings, &length, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, NULL, #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) "-cl-std=CL2.0", #else "", #endif // #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) NULL, NULL); detail::buildErrHandler(error, __BUILD_PROGRAM_ERR, getBuildInfo()); } if (err != NULL) { *err = error; } } Program( const Context& context, const string& source, bool build = false, cl_int* err = NULL) { cl_int error; const char * strings = source.c_str(); const size_type length = source.size(); object_ = ::clCreateProgramWithSource( context(), (cl_uint)1, &strings, &length, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, NULL, #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) "-cl-std=CL2.0", #else "", #endif // #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) NULL, NULL); detail::buildErrHandler(error, __BUILD_PROGRAM_ERR, getBuildInfo()); } if (err != NULL) { *err = error; } } /** * Create a program from a vector of source strings and the default context. * Does not compile or link the program. */ Program( const Sources& sources, cl_int* err = NULL) { cl_int error; Context context = Context::getDefault(err); const size_type n = (size_type)sources.size(); vector lengths(n); vector strings(n); for (size_type i = 0; i < n; ++i) { #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) strings[i] = sources[(int)i].data(); lengths[i] = sources[(int)i].length(); #else // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) strings[i] = sources[(int)i].first; lengths[i] = sources[(int)i].second; #endif // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) } object_ = ::clCreateProgramWithSource( context(), (cl_uint)n, strings.data(), lengths.data(), &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (err != NULL) { *err = error; } } /** * Create a program from a vector of source strings and a provided context. * Does not compile or link the program. */ Program( const Context& context, const Sources& sources, cl_int* err = NULL) { cl_int error; const size_type n = (size_type)sources.size(); vector lengths(n); vector strings(n); for (size_type i = 0; i < n; ++i) { #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) strings[i] = sources[(int)i].data(); lengths[i] = sources[(int)i].length(); #else // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) strings[i] = sources[(int)i].first; lengths[i] = sources[(int)i].second; #endif // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) } object_ = ::clCreateProgramWithSource( context(), (cl_uint)n, strings.data(), lengths.data(), &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (err != NULL) { *err = error; } } #if CL_HPP_TARGET_OPENCL_VERSION >= 210 || (CL_HPP_TARGET_OPENCL_VERSION==200 && defined(CL_HPP_USE_IL_KHR)) /** * Program constructor to allow construction of program from SPIR-V or another IL. * Valid for either OpenCL >= 2.1 or when CL_HPP_USE_IL_KHR is defined. */ Program( const vector& IL, bool build = false, cl_int* err = NULL) { cl_int error; Context context = Context::getDefault(err); #if CL_HPP_TARGET_OPENCL_VERSION >= 210 object_ = ::clCreateProgramWithIL( context(), static_cast(IL.data()), IL.size(), &error); #else // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 typedef clCreateProgramWithILKHR_fn PFN_clCreateProgramWithILKHR; static PFN_clCreateProgramWithILKHR pfn_clCreateProgramWithILKHR = NULL; CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateProgramWithILKHR); return detail::errHandler( pfn_clCreateProgramWithILKHR( context(), static_cast(IL.data()), IL.size(), &error); #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 detail::errHandler(error, __CREATE_PROGRAM_WITH_IL_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, NULL, #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) "-cl-std=CL2.0", #else "", #endif // #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) NULL, NULL); detail::buildErrHandler(error, __BUILD_PROGRAM_ERR, getBuildInfo()); } if (err != NULL) { *err = error; } } /** * Program constructor to allow construction of program from SPIR-V or another IL * for a specific context. * Valid for either OpenCL >= 2.1 or when CL_HPP_USE_IL_KHR is defined. */ Program( const Context& context, const vector& IL, bool build = false, cl_int* err = NULL) { cl_int error; #if CL_HPP_TARGET_OPENCL_VERSION >= 210 object_ = ::clCreateProgramWithIL( context(), static_cast(IL.data()), IL.size(), &error); #else // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 typedef clCreateProgramWithILKHR_fn PFN_clCreateProgramWithILKHR; static PFN_clCreateProgramWithILKHR pfn_clCreateProgramWithILKHR = NULL; CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateProgramWithILKHR); return detail::errHandler( pfn_clCreateProgramWithILKHR( context(), static_cast(IL.data()), IL.size(), &error); #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 detail::errHandler(error, __CREATE_PROGRAM_WITH_IL_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, NULL, #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) "-cl-std=CL2.0", #else "", #endif // #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) NULL, NULL); detail::buildErrHandler(error, __BUILD_PROGRAM_ERR, getBuildInfo()); } if (err != NULL) { *err = error; } } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Construct a program object from a list of devices and a per-device list of binaries. * \param context A valid OpenCL context in which to construct the program. * \param devices A vector of OpenCL device objects for which the program will be created. * \param binaries A vector of pairs of a pointer to a binary object and its length. * \param binaryStatus An optional vector that on completion will be resized to * match the size of binaries and filled with values to specify if each binary * was successfully loaded. * Set to CL_SUCCESS if the binary was successfully loaded. * Set to CL_INVALID_VALUE if the length is 0 or the binary pointer is NULL. * Set to CL_INVALID_BINARY if the binary provided is not valid for the matching device. * \param err if non-NULL will be set to CL_SUCCESS on successful operation or one of the following errors: * CL_INVALID_CONTEXT if context is not a valid context. * CL_INVALID_VALUE if the length of devices is zero; or if the length of binaries does not match the length of devices; * or if any entry in binaries is NULL or has length 0. * CL_INVALID_DEVICE if OpenCL devices listed in devices are not in the list of devices associated with context. * CL_INVALID_BINARY if an invalid program binary was encountered for any device. binaryStatus will return specific status for each device. * CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required by the OpenCL implementation on the host. */ Program( const Context& context, const vector& devices, const Binaries& binaries, vector* binaryStatus = NULL, cl_int* err = NULL) { cl_int error; const size_type numDevices = devices.size(); // Catch size mismatch early and return if(binaries.size() != numDevices) { error = CL_INVALID_VALUE; detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); if (err != NULL) { *err = error; } return; } vector lengths(numDevices); vector images(numDevices); #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) for (size_type i = 0; i < numDevices; ++i) { images[i] = binaries[i].data(); lengths[i] = binaries[(int)i].size(); } #else // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) for (size_type i = 0; i < numDevices; ++i) { images[i] = (const unsigned char*)binaries[i].first; lengths[i] = binaries[(int)i].second; } #endif // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) vector deviceIDs(numDevices); for( size_type deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex ) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } if(binaryStatus) { binaryStatus->resize(numDevices); } object_ = ::clCreateProgramWithBinary( context(), (cl_uint) devices.size(), deviceIDs.data(), lengths.data(), images.data(), (binaryStatus != NULL && numDevices > 0) ? &binaryStatus->front() : NULL, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); if (err != NULL) { *err = error; } } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Create program using builtin kernels. * \param kernelNames Semi-colon separated list of builtin kernel names */ Program( const Context& context, const vector& devices, const string& kernelNames, cl_int* err = NULL) { cl_int error; size_type numDevices = devices.size(); vector deviceIDs(numDevices); for( size_type deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex ) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } object_ = ::clCreateProgramWithBuiltInKernels( context(), (cl_uint) devices.size(), deviceIDs.data(), kernelNames.c_str(), &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 Program() { } /*! \brief Constructor from cl_program - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. */ explicit Program(const cl_program& program, bool retainObject = false) : detail::Wrapper(program, retainObject) { } Program& operator = (const cl_program& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ Program(const Program& program) : detail::Wrapper(program) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ Program& operator = (const Program &program) { detail::Wrapper::operator=(program); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ Program(Program&& program) CL_HPP_NOEXCEPT_ : detail::Wrapper(std::move(program)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ Program& operator = (Program &&program) { detail::Wrapper::operator=(std::move(program)); return *this; } cl_int build( const vector& devices, const char* options = NULL, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, void* data = NULL) const { size_type numDevices = devices.size(); vector deviceIDs(numDevices); for( size_type deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex ) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } cl_int buildError = ::clBuildProgram( object_, (cl_uint) devices.size(), deviceIDs.data(), options, notifyFptr, data); return detail::buildErrHandler(buildError, __BUILD_PROGRAM_ERR, getBuildInfo()); } cl_int build( const Device& device, const char* options = NULL, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, void* data = NULL) const { cl_device_id deviceID = device(); cl_int buildError = ::clBuildProgram( object_, 1, &deviceID, options, notifyFptr, data); BuildLogType buildLog(1); buildLog.push_back(std::make_pair(device, getBuildInfo(device))); return detail::buildErrHandler(buildError, __BUILD_PROGRAM_ERR, buildLog); } cl_int build( const char* options = NULL, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, void* data = NULL) const { cl_int buildError = ::clBuildProgram( object_, 0, NULL, options, notifyFptr, data); return detail::buildErrHandler(buildError, __BUILD_PROGRAM_ERR, getBuildInfo()); } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_int compile( const char* options = NULL, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, void* data = NULL) const { cl_int error = ::clCompileProgram( object_, 0, NULL, options, 0, NULL, NULL, notifyFptr, data); return detail::buildErrHandler(error, __COMPILE_PROGRAM_ERR, getBuildInfo()); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 template cl_int getInfo(cl_program_info name, T* param) const { return detail::errHandler( detail::getInfo(&::clGetProgramInfo, object_, name, param), __GET_PROGRAM_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_program_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } template cl_int getBuildInfo( const Device& device, cl_program_build_info name, T* param) const { return detail::errHandler( detail::getInfo( &::clGetProgramBuildInfo, object_, device(), name, param), __GET_PROGRAM_BUILD_INFO_ERR); } template typename detail::param_traits::param_type getBuildInfo(const Device& device, cl_int* err = NULL) const { typename detail::param_traits< detail::cl_program_build_info, name>::param_type param; cl_int result = getBuildInfo(device, name, ¶m); if (err != NULL) { *err = result; } return param; } /** * Build info function that returns a vector of device/info pairs for the specified * info type and for all devices in the program. * On an error reading the info for any device, an empty vector of info will be returned. */ template vector::param_type>> getBuildInfo(cl_int *err = NULL) const { cl_int result = CL_SUCCESS; auto devs = getInfo(&result); vector::param_type>> devInfo; // If there was an initial error from getInfo return the error if (result != CL_SUCCESS) { if (err != NULL) { *err = result; } return devInfo; } for (const cl::Device &d : devs) { typename detail::param_traits< detail::cl_program_build_info, name>::param_type param; result = getBuildInfo(d, name, ¶m); devInfo.push_back( std::pair::param_type> (d, param)); if (result != CL_SUCCESS) { // On error, leave the loop and return the error code break; } } if (err != NULL) { *err = result; } if (result != CL_SUCCESS) { devInfo.clear(); } return devInfo; } cl_int createKernels(vector* kernels) { cl_uint numKernels; cl_int err = ::clCreateKernelsInProgram(object_, 0, NULL, &numKernels); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); } vector value(numKernels); err = ::clCreateKernelsInProgram( object_, numKernels, value.data(), NULL); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); } if (kernels) { kernels->resize(value.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < value.size(); i++) { // We do not need to retain because this kernel is being created // by the runtime (*kernels)[i] = Kernel(value[i], false); } } return CL_SUCCESS; } #if CL_HPP_TARGET_OPENCL_VERSION >= 220 #if defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS) /*! \brief Registers a callback function to be called when destructors for * program scope global variables are complete and before the * program is released. * * Wraps clSetProgramReleaseCallback(). * * Each call to this function registers the specified user callback function * on a callback stack associated with program. The registered user callback * functions are called in the reverse order in which they were registered. */ CL_API_PREFIX__VERSION_2_2_DEPRECATED cl_int setReleaseCallback( void (CL_CALLBACK * pfn_notify)(cl_program program, void * user_data), void * user_data = NULL) CL_API_SUFFIX__VERSION_2_2_DEPRECATED { return detail::errHandler( ::clSetProgramReleaseCallback( object_, pfn_notify, user_data), __SET_PROGRAM_RELEASE_CALLBACK_ERR); } #endif // #if defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS) /*! \brief Sets a SPIR-V specialization constant. * * Wraps clSetProgramSpecializationConstant(). */ template typename std::enable_if::value, cl_int>::type setSpecializationConstant(cl_uint index, const T &value) { return detail::errHandler( ::clSetProgramSpecializationConstant( object_, index, sizeof(value), &value), __SET_PROGRAM_SPECIALIZATION_CONSTANT_ERR); } /*! \brief Sets a SPIR-V specialization constant. * * Wraps clSetProgramSpecializationConstant(). */ cl_int setSpecializationConstant(cl_uint index, size_type size, const void* value) { return detail::errHandler( ::clSetProgramSpecializationConstant( object_, index, size, value), __SET_PROGRAM_SPECIALIZATION_CONSTANT_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 220 }; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 inline Program linkProgram( Program input1, Program input2, const char* options = NULL, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, void* data = NULL, cl_int* err = NULL) { cl_int error_local = CL_SUCCESS; cl_program programs[2] = { input1(), input2() }; Context ctx = input1.getInfo(&error_local); if(error_local!=CL_SUCCESS) { detail::errHandler(error_local, __LINK_PROGRAM_ERR); } cl_program prog = ::clLinkProgram( ctx(), 0, NULL, options, 2, programs, notifyFptr, data, &error_local); detail::errHandler(error_local,__COMPILE_PROGRAM_ERR); if (err != NULL) { *err = error_local; } return Program(prog); } inline Program linkProgram( vector inputPrograms, const char* options = NULL, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, void* data = NULL, cl_int* err = NULL) { cl_int error_local = CL_SUCCESS; vector programs(inputPrograms.size()); for (unsigned int i = 0; i < inputPrograms.size(); i++) { programs[i] = inputPrograms[i](); } Context ctx; if(inputPrograms.size() > 0) { ctx = inputPrograms[0].getInfo(&error_local); if(error_local!=CL_SUCCESS) { detail::errHandler(error_local, __LINK_PROGRAM_ERR); } } cl_program prog = ::clLinkProgram( ctx(), 0, NULL, options, (cl_uint)inputPrograms.size(), programs.data(), notifyFptr, data, &error_local); detail::errHandler(error_local,__COMPILE_PROGRAM_ERR); if (err != NULL) { *err = error_local; } return Program(prog, false); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 // Template specialization for CL_PROGRAM_BINARIES template <> inline cl_int cl::Program::getInfo(cl_program_info name, vector>* param) const { if (name != CL_PROGRAM_BINARIES) { return CL_INVALID_VALUE; } if (param) { // Resize the parameter array appropriately for each allocation // and pass down to the helper vector sizes = getInfo(); size_type numBinaries = sizes.size(); // Resize the parameter array and constituent arrays param->resize(numBinaries); for (size_type i = 0; i < numBinaries; ++i) { (*param)[i].resize(sizes[i]); } return detail::errHandler( detail::getInfo(&::clGetProgramInfo, object_, name, param), __GET_PROGRAM_INFO_ERR); } return CL_SUCCESS; } template<> inline vector> cl::Program::getInfo(cl_int* err) const { vector> binariesVectors; cl_int result = getInfo(CL_PROGRAM_BINARIES, &binariesVectors); if (err != NULL) { *err = result; } return binariesVectors; } #if CL_HPP_TARGET_OPENCL_VERSION >= 220 // Template specialization for clSetProgramSpecializationConstant template <> inline cl_int cl::Program::setSpecializationConstant(cl_uint index, const bool &value) { cl_uchar ucValue = value ? CL_UCHAR_MAX : 0; return detail::errHandler( ::clSetProgramSpecializationConstant( object_, index, sizeof(ucValue), &ucValue), __SET_PROGRAM_SPECIALIZATION_CONSTANT_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 220 inline Kernel::Kernel(const Program& program, const char* name, cl_int* err) { cl_int error; object_ = ::clCreateKernel(program(), name, &error); detail::errHandler(error, __CREATE_KERNEL_ERR); if (err != NULL) { *err = error; } } enum class QueueProperties : cl_command_queue_properties { None = 0, Profiling = CL_QUEUE_PROFILING_ENABLE, OutOfOrder = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, }; inline QueueProperties operator|(QueueProperties lhs, QueueProperties rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } /*! \class CommandQueue * \brief CommandQueue interface for cl_command_queue. */ class CommandQueue : public detail::Wrapper { private: static std::once_flag default_initialized_; static CommandQueue default_; static cl_int default_error_; /*! \brief Create the default command queue returned by @ref getDefault. * * It sets default_error_ to indicate success or failure. It does not throw * @c cl::Error. */ static void makeDefault() { /* We don't want to throw an error from this function, so we have to * catch and set the error flag. */ #if defined(CL_HPP_ENABLE_EXCEPTIONS) try #endif { int error; Context context = Context::getDefault(&error); if (error != CL_SUCCESS) { default_error_ = error; } else { Device device = Device::getDefault(); default_ = CommandQueue(context, device, 0, &default_error_); } } #if defined(CL_HPP_ENABLE_EXCEPTIONS) catch (cl::Error &e) { default_error_ = e.err(); } #endif } /*! \brief Create the default command queue. * * This sets @c default_. It does not throw * @c cl::Error. */ static void makeDefaultProvided(const CommandQueue &c) { default_ = c; } public: #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Reset the default. * * This sets @c default_ to an empty value to support cleanup in * the unit test framework. * This function is not thread safe. */ static void unitTestClearDefault() { default_ = CommandQueue(); } #endif // #ifdef CL_HPP_UNIT_TEST_ENABLE /*! * \brief Constructs a CommandQueue based on passed properties. * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ CommandQueue( cl_command_queue_properties properties, cl_int* err = NULL) { cl_int error; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != NULL) { *err = error; } } else { Device device = context.getInfo()[0]; bool useWithProperties; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; if ((properties & CL_QUEUE_ON_DEVICE) == 0) { object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); } else { error = CL_INVALID_QUEUE_PROPERTIES; } detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), device(), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } } /*! * \brief Constructs a CommandQueue based on passed properties. * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ CommandQueue( QueueProperties properties, cl_int* err = NULL) { cl_int error; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != NULL) { *err = error; } } else { Device device = context.getInfo()[0]; bool useWithProperties; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, static_cast(properties), 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), device(), static_cast(properties), &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } } /*! * \brief Constructs a CommandQueue for an implementation defined device in the given context * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ explicit CommandQueue( const Context& context, cl_command_queue_properties properties = 0, cl_int* err = NULL) { cl_int error; bool useWithProperties; vector devices; error = context.getInfo(CL_CONTEXT_DEVICES, &devices); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != NULL) { *err = error; } return; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; if ((properties & CL_QUEUE_ON_DEVICE) == 0) { object_ = ::clCreateCommandQueueWithProperties( context(), devices[0](), queue_properties, &error); } else { error = CL_INVALID_QUEUE_PROPERTIES; } detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), devices[0](), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } /*! * \brief Constructs a CommandQueue for an implementation defined device in the given context * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ explicit CommandQueue( const Context& context, QueueProperties properties, cl_int* err = NULL) { cl_int error; bool useWithProperties; vector devices; error = context.getInfo(CL_CONTEXT_DEVICES, &devices); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != NULL) { *err = error; } return; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, static_cast(properties), 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), devices[0](), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), devices[0](), static_cast(properties), &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } /*! * \brief Constructs a CommandQueue for a passed device and context * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ CommandQueue( const Context& context, const Device& device, cl_command_queue_properties properties = 0, cl_int* err = NULL) { cl_int error; bool useWithProperties; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), device(), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } /*! * \brief Constructs a CommandQueue for a passed device and context * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ CommandQueue( const Context& context, const Device& device, QueueProperties properties, cl_int* err = NULL) { cl_int error; bool useWithProperties; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, static_cast(properties), 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), device(), static_cast(properties), &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != NULL) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } static CommandQueue getDefault(cl_int * err = NULL) { std::call_once(default_initialized_, makeDefault); #if CL_HPP_TARGET_OPENCL_VERSION >= 200 detail::errHandler(default_error_, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); #else // CL_HPP_TARGET_OPENCL_VERSION >= 200 detail::errHandler(default_error_, __CREATE_COMMAND_QUEUE_ERR); #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 if (err != NULL) { *err = default_error_; } return default_; } /** * Modify the default command queue to be used by * subsequent operations. * Will only set the default if no default was previously created. * @return updated default command queue. * Should be compared to the passed value to ensure that it was updated. */ static CommandQueue setDefault(const CommandQueue &default_queue) { std::call_once(default_initialized_, makeDefaultProvided, std::cref(default_queue)); detail::errHandler(default_error_); return default_; } CommandQueue() { } /*! \brief Constructor from cl_command_queue - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. */ explicit CommandQueue(const cl_command_queue& commandQueue, bool retainObject = false) : detail::Wrapper(commandQueue, retainObject) { } CommandQueue& operator = (const cl_command_queue& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ CommandQueue(const CommandQueue& queue) : detail::Wrapper(queue) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ CommandQueue& operator = (const CommandQueue &queue) { detail::Wrapper::operator=(queue); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ CommandQueue(CommandQueue&& queue) CL_HPP_NOEXCEPT_ : detail::Wrapper(std::move(queue)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ CommandQueue& operator = (CommandQueue &&queue) { detail::Wrapper::operator=(std::move(queue)); return *this; } template cl_int getInfo(cl_command_queue_info name, T* param) const { return detail::errHandler( detail::getInfo( &::clGetCommandQueueInfo, object_, name, param), __GET_COMMAND_QUEUE_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_command_queue_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } cl_int enqueueReadBuffer( const Buffer& buffer, cl_bool blocking, size_type offset, size_type size, void* ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadBuffer( object_, buffer(), blocking, offset, size, ptr, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_READ_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteBuffer( const Buffer& buffer, cl_bool blocking, size_type offset, size_type size, const void* ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteBuffer( object_, buffer(), blocking, offset, size, ptr, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_WRITE_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBuffer( const Buffer& src, const Buffer& dst, size_type src_offset, size_type dst_offset, size_type size, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBuffer( object_, src(), dst(), src_offset, dst_offset, size, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQEUE_COPY_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #if CL_HPP_TARGET_OPENCL_VERSION >= 110 cl_int enqueueReadBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, void *ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadBufferRect( object_, buffer(), blocking, buffer_offset.data(), host_offset.data(), region.data(), buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_READ_BUFFER_RECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, const void *ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteBufferRect( object_, buffer(), blocking, buffer_offset.data(), host_offset.data(), region.data(), buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_WRITE_BUFFER_RECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBufferRect( const Buffer& src, const Buffer& dst, const array& src_origin, const array& dst_origin, const array& region, size_type src_row_pitch, size_type src_slice_pitch, size_type dst_row_pitch, size_type dst_slice_pitch, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBufferRect( object_, src(), dst(), src_origin.data(), dst_origin.data(), region.data(), src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQEUE_COPY_BUFFER_RECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Enqueue a command to fill a buffer object with a pattern * of a given size. The pattern is specified as a vector type. * \tparam PatternType The datatype of the pattern field. * The pattern type must be an accepted OpenCL data type. * \tparam offset Is the offset in bytes into the buffer at * which to start filling. This must be a multiple of * the pattern size. * \tparam size Is the size in bytes of the region to fill. * This must be a multiple of the pattern size. */ template cl_int enqueueFillBuffer( const Buffer& buffer, PatternType pattern, size_type offset, size_type size, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillBuffer( object_, buffer(), static_cast(&pattern), sizeof(PatternType), offset, size, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_FILL_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_int enqueueReadImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, void* ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadImage( object_, image(), blocking, origin.data(), region.data(), row_pitch, slice_pitch, ptr, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_READ_IMAGE_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, const void* ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteImage( object_, image(), blocking, origin.data(), region.data(), row_pitch, slice_pitch, ptr, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_WRITE_IMAGE_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyImage( const Image& src, const Image& dst, const array& src_origin, const array& dst_origin, const array& region, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyImage( object_, src(), dst(), src_origin.data(), dst_origin.data(), region.data(), (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_COPY_IMAGE_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Enqueue a command to fill an image object with a specified color. * \param fillColor is the color to use to fill the image. * This is a four component RGBA floating-point color value if * the image channel data type is not an unnormalized signed or * unsigned data type. */ cl_int enqueueFillImage( const Image& image, cl_float4 fillColor, const array& origin, const array& region, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillImage( object_, image(), static_cast(&fillColor), origin.data(), region.data(), (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_FILL_IMAGE_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueue a command to fill an image object with a specified color. * \param fillColor is the color to use to fill the image. * This is a four component RGBA signed integer color value if * the image channel data type is an unnormalized signed integer * type. */ cl_int enqueueFillImage( const Image& image, cl_int4 fillColor, const array& origin, const array& region, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillImage( object_, image(), static_cast(&fillColor), origin.data(), region.data(), (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_FILL_IMAGE_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueue a command to fill an image object with a specified color. * \param fillColor is the color to use to fill the image. * This is a four component RGBA unsigned integer color value if * the image channel data type is an unnormalized unsigned integer * type. */ cl_int enqueueFillImage( const Image& image, cl_uint4 fillColor, const array& origin, const array& region, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillImage( object_, image(), static_cast(&fillColor), origin.data(), region.data(), (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_FILL_IMAGE_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_int enqueueCopyImageToBuffer( const Image& src, const Buffer& dst, const array& src_origin, const array& region, size_type dst_offset, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyImageToBuffer( object_, src(), dst(), src_origin.data(), region.data(), dst_offset, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBufferToImage( const Buffer& src, const Image& dst, size_type src_offset, const array& dst_origin, const array& region, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBufferToImage( object_, src(), dst(), src_offset, dst_origin.data(), region.data(), (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } void* enqueueMapBuffer( const Buffer& buffer, cl_bool blocking, cl_map_flags flags, size_type offset, size_type size, const vector* events = NULL, Event* event = NULL, cl_int* err = NULL) const { cl_event tmp; cl_int error; void * result = ::clEnqueueMapBuffer( object_, buffer(), blocking, flags, offset, size, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL, &error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != NULL) { *err = error; } if (event != NULL && error == CL_SUCCESS) *event = tmp; return result; } void* enqueueMapImage( const Image& buffer, cl_bool blocking, cl_map_flags flags, const array& origin, const array& region, size_type * row_pitch, size_type * slice_pitch, const vector* events = NULL, Event* event = NULL, cl_int* err = NULL) const { cl_event tmp; cl_int error; void * result = ::clEnqueueMapImage( object_, buffer(), blocking, flags, origin.data(), region.data(), row_pitch, slice_pitch, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL, &error); detail::errHandler(error, __ENQUEUE_MAP_IMAGE_ERR); if (err != NULL) { *err = error; } if (event != NULL && error == CL_SUCCESS) *event = tmp; return result; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Enqueues a command that will allow the host to update a region of a coarse-grained SVM buffer. * This variant takes a raw SVM pointer. */ template cl_int enqueueMapSVM( T* ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler(::clEnqueueSVMMap( object_, blocking, flags, static_cast(ptr), size, (events != NULL) ? (cl_uint)events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_MAP_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will allow the host to update a region of a coarse-grained SVM buffer. * This variant takes a cl::pointer instance. */ template cl_int enqueueMapSVM( cl::pointer &ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler(::clEnqueueSVMMap( object_, blocking, flags, static_cast(ptr.get()), size, (events != NULL) ? (cl_uint)events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_MAP_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will allow the host to update a region of a coarse-grained SVM buffer. * This variant takes a cl::vector instance. */ template cl_int enqueueMapSVM( cl::vector &container, cl_bool blocking, cl_map_flags flags, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler(::clEnqueueSVMMap( object_, blocking, flags, static_cast(container.data()), container.size()*sizeof(T), (events != NULL) ? (cl_uint)events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_MAP_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 cl_int enqueueUnmapMemObject( const Memory& memory, void* mapped_ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueUnmapMemObject( object_, memory(), mapped_ptr, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Enqueues a command that will release a coarse-grained SVM buffer back to the OpenCL runtime. * This variant takes a raw SVM pointer. */ template cl_int enqueueUnmapSVM( T* ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueSVMUnmap( object_, static_cast(ptr), (events != NULL) ? (cl_uint)events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will release a coarse-grained SVM buffer back to the OpenCL runtime. * This variant takes a cl::pointer instance. */ template cl_int enqueueUnmapSVM( cl::pointer &ptr, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueSVMUnmap( object_, static_cast(ptr.get()), (events != NULL) ? (cl_uint)events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will release a coarse-grained SVM buffer back to the OpenCL runtime. * This variant takes a cl::vector instance. */ template cl_int enqueueUnmapSVM( cl::vector &container, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueSVMUnmap( object_, static_cast(container.data()), (events != NULL) ? (cl_uint)events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Enqueues a marker command which waits for either a list of events to complete, * or all previously enqueued commands to complete. * * Enqueues a marker command which waits for either a list of events to complete, * or if the list is empty it waits for all commands previously enqueued in command_queue * to complete before it completes. This command returns an event which can be waited on, * i.e. this event can be waited on to insure that all events either in the event_wait_list * or all previously enqueued commands, queued before this command to command_queue, * have completed. */ cl_int enqueueMarkerWithWaitList( const vector *events = 0, Event *event = 0) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueMarkerWithWaitList( object_, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_MARKER_WAIT_LIST_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * A synchronization point that enqueues a barrier operation. * * Enqueues a barrier command which waits for either a list of events to complete, * or if the list is empty it waits for all commands previously enqueued in command_queue * to complete before it completes. This command blocks command execution, that is, any * following commands enqueued after it do not execute until it completes. This command * returns an event which can be waited on, i.e. this event can be waited on to insure that * all events either in the event_wait_list or all previously enqueued commands, queued * before this command to command_queue, have completed. */ cl_int enqueueBarrierWithWaitList( const vector *events = 0, Event *event = 0) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueBarrierWithWaitList( object_, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_BARRIER_WAIT_LIST_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command to indicate with which device a set of memory objects * should be associated. */ cl_int enqueueMigrateMemObjects( const vector &memObjects, cl_mem_migration_flags flags, const vector* events = NULL, Event* event = NULL ) const { cl_event tmp; vector localMemObjects(memObjects.size()); for( int i = 0; i < (int)memObjects.size(); ++i ) { localMemObjects[i] = memObjects[i](); } cl_int err = detail::errHandler( ::clEnqueueMigrateMemObjects( object_, (cl_uint)memObjects.size(), localMemObjects.data(), flags, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Enqueues a command that will allow the host associate ranges within a set of * SVM allocations with a device. * @param sizes - The length from each pointer to migrate. */ template cl_int enqueueMigrateSVM( const cl::vector &svmRawPointers, const cl::vector &sizes, cl_mem_migration_flags flags = 0, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler(::clEnqueueSVMMigrateMem( object_, svmRawPointers.size(), static_cast(svmRawPointers.data()), sizes.data(), // array of sizes not passed flags, (events != NULL) ? (cl_uint)events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_MIGRATE_SVM_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will allow the host associate a set of SVM allocations with * a device. */ template cl_int enqueueMigrateSVM( const cl::vector &svmRawPointers, cl_mem_migration_flags flags = 0, const vector* events = NULL, Event* event = NULL) const { return enqueueMigrateSVM(svmRawPointers, cl::vector(svmRawPointers.size()), flags, events, event); } /** * Enqueues a command that will allow the host associate ranges within a set of * SVM allocations with a device. * @param sizes - The length from each pointer to migrate. */ template cl_int enqueueMigrateSVM( const cl::vector> &svmPointers, const cl::vector &sizes, cl_mem_migration_flags flags = 0, const vector* events = NULL, Event* event = NULL) const { cl::vector svmRawPointers; svmRawPointers.reserve(svmPointers.size()); for (auto p : svmPointers) { svmRawPointers.push_back(static_cast(p.get())); } return enqueueMigrateSVM(svmRawPointers, sizes, flags, events, event); } /** * Enqueues a command that will allow the host associate a set of SVM allocations with * a device. */ template cl_int enqueueMigrateSVM( const cl::vector> &svmPointers, cl_mem_migration_flags flags = 0, const vector* events = NULL, Event* event = NULL) const { return enqueueMigrateSVM(svmPointers, cl::vector(svmPointers.size()), flags, events, event); } /** * Enqueues a command that will allow the host associate ranges within a set of * SVM allocations with a device. * @param sizes - The length from the beginning of each container to migrate. */ template cl_int enqueueMigrateSVM( const cl::vector> &svmContainers, const cl::vector &sizes, cl_mem_migration_flags flags = 0, const vector* events = NULL, Event* event = NULL) const { cl::vector svmRawPointers; svmRawPointers.reserve(svmContainers.size()); for (auto p : svmContainers) { svmRawPointers.push_back(static_cast(p.data())); } return enqueueMigrateSVM(svmRawPointers, sizes, flags, events, event); } /** * Enqueues a command that will allow the host associate a set of SVM allocations with * a device. */ template cl_int enqueueMigrateSVM( const cl::vector> &svmContainers, cl_mem_migration_flags flags = 0, const vector* events = NULL, Event* event = NULL) const { return enqueueMigrateSVM(svmContainers, cl::vector(svmContainers.size()), flags, events, event); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 cl_int enqueueNDRangeKernel( const Kernel& kernel, const NDRange& offset, const NDRange& global, const NDRange& local = NullRange, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueNDRangeKernel( object_, kernel(), (cl_uint) global.dimensions(), offset.dimensions() != 0 ? (const size_type*) offset : NULL, (const size_type*) global, local.dimensions() != 0 ? (const size_type*) local : NULL, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_NDRANGE_KERNEL_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #if defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) CL_API_PREFIX__VERSION_1_2_DEPRECATED cl_int enqueueTask( const Kernel& kernel, const vector* events = NULL, Event* event = NULL) const CL_API_SUFFIX__VERSION_1_2_DEPRECATED { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueTask( object_, kernel(), (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_TASK_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) cl_int enqueueNativeKernel( void (CL_CALLBACK *userFptr)(void *), std::pair args, const vector* mem_objects = NULL, const vector* mem_locs = NULL, const vector* events = NULL, Event* event = NULL) const { size_type elements = 0; if (mem_objects != NULL) { elements = mem_objects->size(); } vector mems(elements); for (unsigned int i = 0; i < elements; i++) { mems[i] = ((*mem_objects)[i])(); } cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueNativeKernel( object_, userFptr, args.first, args.second, (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, mems.data(), (mem_locs != NULL && mem_locs->size() > 0) ? (const void **) &mem_locs->front() : NULL, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_NATIVE_KERNEL); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueMarker(Event* event = NULL) const CL_API_SUFFIX__VERSION_1_1_DEPRECATED { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueMarker( object_, (event != NULL) ? &tmp : NULL), __ENQUEUE_MARKER_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueWaitForEvents(const vector& events) const CL_API_SUFFIX__VERSION_1_1_DEPRECATED { return detail::errHandler( ::clEnqueueWaitForEvents( object_, (cl_uint) events.size(), events.size() > 0 ? (const cl_event*) &events.front() : NULL), __ENQUEUE_WAIT_FOR_EVENTS_ERR); } #endif // defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) cl_int enqueueAcquireGLObjects( const vector* mem_objects = NULL, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueAcquireGLObjects( object_, (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, (mem_objects != NULL && mem_objects->size() > 0) ? (const cl_mem *) &mem_objects->front(): NULL, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_ACQUIRE_GL_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReleaseGLObjects( const vector* mem_objects = NULL, const vector* events = NULL, Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReleaseGLObjects( object_, (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, (mem_objects != NULL && mem_objects->size() > 0) ? (const cl_mem *) &mem_objects->front(): NULL, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_RELEASE_GL_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #if defined (CL_HPP_USE_DX_INTEROP) typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueAcquireD3D10ObjectsKHR)( cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueReleaseD3D10ObjectsKHR)( cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); cl_int enqueueAcquireD3D10Objects( const vector* mem_objects = NULL, const vector* events = NULL, Event* event = NULL) const { static PFN_clEnqueueAcquireD3D10ObjectsKHR pfn_clEnqueueAcquireD3D10ObjectsKHR = NULL; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_context context = getInfo(); cl::Device device(getInfo()); cl_platform_id platform = device.getInfo(); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueAcquireD3D10ObjectsKHR); #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 110 CL_HPP_INIT_CL_EXT_FCN_PTR_(clEnqueueAcquireD3D10ObjectsKHR); #endif cl_event tmp; cl_int err = detail::errHandler( pfn_clEnqueueAcquireD3D10ObjectsKHR( object_, (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, (mem_objects != NULL && mem_objects->size() > 0) ? (const cl_mem *) &mem_objects->front(): NULL, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_ACQUIRE_GL_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReleaseD3D10Objects( const vector* mem_objects = NULL, const vector* events = NULL, Event* event = NULL) const { static PFN_clEnqueueReleaseD3D10ObjectsKHR pfn_clEnqueueReleaseD3D10ObjectsKHR = NULL; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_context context = getInfo(); cl::Device device(getInfo()); cl_platform_id platform = device.getInfo(); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueReleaseD3D10ObjectsKHR); #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 110 CL_HPP_INIT_CL_EXT_FCN_PTR_(clEnqueueReleaseD3D10ObjectsKHR); #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 cl_event tmp; cl_int err = detail::errHandler( pfn_clEnqueueReleaseD3D10ObjectsKHR( object_, (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, (mem_objects != NULL && mem_objects->size() > 0) ? (const cl_mem *) &mem_objects->front(): NULL, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_RELEASE_GL_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #endif /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueBarrier() const CL_API_SUFFIX__VERSION_1_1_DEPRECATED { return detail::errHandler( ::clEnqueueBarrier(object_), __ENQUEUE_BARRIER_ERR); } #endif // CL_USE_DEPRECATED_OPENCL_1_1_APIS cl_int flush() const { return detail::errHandler(::clFlush(object_), __FLUSH_ERR); } cl_int finish() const { return detail::errHandler(::clFinish(object_), __FINISH_ERR); } }; // CommandQueue CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag CommandQueue::default_initialized_; CL_HPP_DEFINE_STATIC_MEMBER_ CommandQueue CommandQueue::default_; CL_HPP_DEFINE_STATIC_MEMBER_ cl_int CommandQueue::default_error_ = CL_SUCCESS; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 enum class DeviceQueueProperties : cl_command_queue_properties { None = 0, Profiling = CL_QUEUE_PROFILING_ENABLE, }; inline DeviceQueueProperties operator|(DeviceQueueProperties lhs, DeviceQueueProperties rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } /*! \class DeviceCommandQueue * \brief DeviceCommandQueue interface for device cl_command_queues. */ class DeviceCommandQueue : public detail::Wrapper { public: /*! * Trivial empty constructor to create a null queue. */ DeviceCommandQueue() { } /*! * Default construct device command queue on default context and device */ DeviceCommandQueue(DeviceQueueProperties properties, cl_int* err = NULL) { cl_int error; cl::Context context = cl::Context::getDefault(); cl::Device device = cl::Device::getDefault(); cl_command_queue_properties mergedProperties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | static_cast(properties); cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, mergedProperties, 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } /*! * Create a device command queue for a specified device in the passed context. */ DeviceCommandQueue( const Context& context, const Device& device, DeviceQueueProperties properties = DeviceQueueProperties::None, cl_int* err = NULL) { cl_int error; cl_command_queue_properties mergedProperties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | static_cast(properties); cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, mergedProperties, 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } /*! * Create a device command queue for a specified device in the passed context. */ DeviceCommandQueue( const Context& context, const Device& device, cl_uint queueSize, DeviceQueueProperties properties = DeviceQueueProperties::None, cl_int* err = NULL) { cl_int error; cl_command_queue_properties mergedProperties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | static_cast(properties); cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, mergedProperties, CL_QUEUE_SIZE, queueSize, 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } } /*! \brief Constructor from cl_command_queue - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. */ explicit DeviceCommandQueue(const cl_command_queue& commandQueue, bool retainObject = false) : detail::Wrapper(commandQueue, retainObject) { } DeviceCommandQueue& operator = (const cl_command_queue& rhs) { detail::Wrapper::operator=(rhs); return *this; } /*! \brief Copy constructor to forward copy to the superclass correctly. * Required for MSVC. */ DeviceCommandQueue(const DeviceCommandQueue& queue) : detail::Wrapper(queue) {} /*! \brief Copy assignment to forward copy to the superclass correctly. * Required for MSVC. */ DeviceCommandQueue& operator = (const DeviceCommandQueue &queue) { detail::Wrapper::operator=(queue); return *this; } /*! \brief Move constructor to forward move to the superclass correctly. * Required for MSVC. */ DeviceCommandQueue(DeviceCommandQueue&& queue) CL_HPP_NOEXCEPT_ : detail::Wrapper(std::move(queue)) {} /*! \brief Move assignment to forward move to the superclass correctly. * Required for MSVC. */ DeviceCommandQueue& operator = (DeviceCommandQueue &&queue) { detail::Wrapper::operator=(std::move(queue)); return *this; } template cl_int getInfo(cl_command_queue_info name, T* param) const { return detail::errHandler( detail::getInfo( &::clGetCommandQueueInfo, object_, name, param), __GET_COMMAND_QUEUE_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = NULL) const { typename detail::param_traits< detail::cl_command_queue_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != NULL) { *err = result; } return param; } /*! * Create a new default device command queue for the default device, * in the default context and of the default size. * If there is already a default queue for the specified device this * function will return the pre-existing queue. */ static DeviceCommandQueue makeDefault( cl_int *err = nullptr) { cl_int error; cl::Context context = cl::Context::getDefault(); cl::Device device = cl::Device::getDefault(); cl_command_queue_properties properties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | CL_QUEUE_ON_DEVICE_DEFAULT; cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; DeviceCommandQueue deviceQueue( ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error)); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } return deviceQueue; } /*! * Create a new default device command queue for the specified device * and of the default size. * If there is already a default queue for the specified device this * function will return the pre-existing queue. */ static DeviceCommandQueue makeDefault( const Context &context, const Device &device, cl_int *err = nullptr) { cl_int error; cl_command_queue_properties properties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | CL_QUEUE_ON_DEVICE_DEFAULT; cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; DeviceCommandQueue deviceQueue( ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error)); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } return deviceQueue; } /*! * Create a new default device command queue for the specified device * and of the requested size in bytes. * If there is already a default queue for the specified device this * function will return the pre-existing queue. */ static DeviceCommandQueue makeDefault( const Context &context, const Device &device, cl_uint queueSize, cl_int *err = nullptr) { cl_int error; cl_command_queue_properties properties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | CL_QUEUE_ON_DEVICE_DEFAULT; cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, CL_QUEUE_SIZE, queueSize, 0 }; DeviceCommandQueue deviceQueue( ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error)); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != NULL) { *err = error; } return deviceQueue; } #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /*! * Modify the default device command queue to be used for subsequent kernels. * This can update the default command queue for a device repeatedly to account * for kernels that rely on the default. * @return updated default device command queue. */ static DeviceCommandQueue updateDefault(const Context &context, const Device &device, const DeviceCommandQueue &default_queue, cl_int *err = nullptr) { cl_int error; error = clSetDefaultDeviceCommandQueue(context.get(), device.get(), default_queue.get()); detail::errHandler(error, __SET_DEFAULT_DEVICE_COMMAND_QUEUE_ERR); if (err != NULL) { *err = error; } return default_queue; } /*! * Return the current default command queue for the specified command queue */ static DeviceCommandQueue getDefault(const CommandQueue &queue, cl_int * err = NULL) { return queue.getInfo(err); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 }; // DeviceCommandQueue namespace detail { // Specialization for device command queue template <> struct KernelArgumentHandler { static size_type size(const cl::DeviceCommandQueue&) { return sizeof(cl_command_queue); } static const cl_command_queue* ptr(const cl::DeviceCommandQueue& value) { return &(value()); } }; } // namespace detail #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 template< typename IteratorType > Buffer::Buffer( const Context &context, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr, cl_int* err) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if( readOnly ) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if( useHostPtr ) { flags |= CL_MEM_USE_HOST_PTR; } size_type size = sizeof(DataType)*(endIterator - startIterator); if( useHostPtr ) { object_ = ::clCreateBuffer(context(), flags, size, static_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, 0, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } if( !useHostPtr ) { CommandQueue queue(context, 0, &error); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } error = cl::copy(queue, startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } } } template< typename IteratorType > Buffer::Buffer( const CommandQueue &queue, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr, cl_int* err) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if (readOnly) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if (useHostPtr) { flags |= CL_MEM_USE_HOST_PTR; } size_type size = sizeof(DataType)*(endIterator - startIterator); Context context = queue.getInfo(); if (useHostPtr) { object_ = ::clCreateBuffer(context(), flags, size, static_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, 0, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } if (!useHostPtr) { error = cl::copy(queue, startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != NULL) { *err = error; } } } inline cl_int enqueueReadBuffer( const Buffer& buffer, cl_bool blocking, size_type offset, size_type size, void* ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadBuffer(buffer, blocking, offset, size, ptr, events, event); } inline cl_int enqueueWriteBuffer( const Buffer& buffer, cl_bool blocking, size_type offset, size_type size, const void* ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteBuffer(buffer, blocking, offset, size, ptr, events, event); } inline void* enqueueMapBuffer( const Buffer& buffer, cl_bool blocking, cl_map_flags flags, size_type offset, size_type size, const vector* events = NULL, Event* event = NULL, cl_int* err = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != NULL) { *err = error; } void * result = ::clEnqueueMapBuffer( queue(), buffer(), blocking, flags, offset, size, (events != NULL) ? (cl_uint) events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, (cl_event*) event, &error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != NULL) { *err = error; } return result; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Enqueues to the default queue a command that will allow the host to * update a region of a coarse-grained SVM buffer. * This variant takes a raw SVM pointer. */ template inline cl_int enqueueMapSVM( T* ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events, Event* event) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); } return queue.enqueueMapSVM( ptr, blocking, flags, size, events, event); } /** * Enqueues to the default queue a command that will allow the host to * update a region of a coarse-grained SVM buffer. * This variant takes a cl::pointer instance. */ template inline cl_int enqueueMapSVM( cl::pointer ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); } return queue.enqueueMapSVM( ptr, blocking, flags, size, events, event); } /** * Enqueues to the default queue a command that will allow the host to * update a region of a coarse-grained SVM buffer. * This variant takes a cl::vector instance. */ template inline cl_int enqueueMapSVM( cl::vector container, cl_bool blocking, cl_map_flags flags, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); } return queue.enqueueMapSVM( container, blocking, flags, events, event); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 inline cl_int enqueueUnmapMemObject( const Memory& memory, void* mapped_ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (error != CL_SUCCESS) { return error; } cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueUnmapMemObject( queue(), memory(), mapped_ptr, (events != NULL) ? (cl_uint)events->size() : 0, (events != NULL && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : NULL), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != NULL && err == CL_SUCCESS) *event = tmp; return err; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Enqueues to the default queue a command that will release a coarse-grained * SVM buffer back to the OpenCL runtime. * This variant takes a raw SVM pointer. */ template inline cl_int enqueueUnmapSVM( T* ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } return detail::errHandler(queue.enqueueUnmapSVM(ptr, events, event), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } /** * Enqueues to the default queue a command that will release a coarse-grained * SVM buffer back to the OpenCL runtime. * This variant takes a cl::pointer instance. */ template inline cl_int enqueueUnmapSVM( cl::pointer &ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } return detail::errHandler(queue.enqueueUnmapSVM(ptr, events, event), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } /** * Enqueues to the default queue a command that will release a coarse-grained * SVM buffer back to the OpenCL runtime. * This variant takes a cl::vector instance. */ template inline cl_int enqueueUnmapSVM( cl::vector &container, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } return detail::errHandler(queue.enqueueUnmapSVM(container, events, event), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 inline cl_int enqueueCopyBuffer( const Buffer& src, const Buffer& dst, size_type src_offset, size_type dst_offset, size_type size, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBuffer(src, dst, src_offset, dst_offset, size, events, event); } /** * Blocking copy operation between iterators and a buffer. * Host to Device. * Uses default command queue. */ template< typename IteratorType > inline cl_int copy( IteratorType startIterator, IteratorType endIterator, cl::Buffer &buffer ) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) return error; return cl::copy(queue, startIterator, endIterator, buffer); } /** * Blocking copy operation between iterators and a buffer. * Device to Host. * Uses default command queue. */ template< typename IteratorType > inline cl_int copy( const cl::Buffer &buffer, IteratorType startIterator, IteratorType endIterator ) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) return error; return cl::copy(queue, buffer, startIterator, endIterator); } /** * Blocking copy operation between iterators and a buffer. * Host to Device. * Uses specified queue. */ template< typename IteratorType > inline cl_int copy( const CommandQueue &queue, IteratorType startIterator, IteratorType endIterator, cl::Buffer &buffer ) { typedef typename std::iterator_traits::value_type DataType; cl_int error; size_type length = endIterator-startIterator; size_type byteLength = length*sizeof(DataType); DataType *pointer = static_cast(queue.enqueueMapBuffer(buffer, CL_TRUE, CL_MAP_WRITE, 0, byteLength, 0, 0, &error)); // if exceptions enabled, enqueueMapBuffer will throw if( error != CL_SUCCESS ) { return error; } #if defined(_MSC_VER) std::copy( startIterator, endIterator, stdext::checked_array_iterator( pointer, length)); #else std::copy(startIterator, endIterator, pointer); #endif Event endEvent; error = queue.enqueueUnmapMemObject(buffer, pointer, 0, &endEvent); // if exceptions enabled, enqueueUnmapMemObject will throw if( error != CL_SUCCESS ) { return error; } endEvent.wait(); return CL_SUCCESS; } /** * Blocking copy operation between iterators and a buffer. * Device to Host. * Uses specified queue. */ template< typename IteratorType > inline cl_int copy( const CommandQueue &queue, const cl::Buffer &buffer, IteratorType startIterator, IteratorType endIterator ) { typedef typename std::iterator_traits::value_type DataType; cl_int error; size_type length = endIterator-startIterator; size_type byteLength = length*sizeof(DataType); DataType *pointer = static_cast(queue.enqueueMapBuffer(buffer, CL_TRUE, CL_MAP_READ, 0, byteLength, 0, 0, &error)); // if exceptions enabled, enqueueMapBuffer will throw if( error != CL_SUCCESS ) { return error; } std::copy(pointer, pointer + length, startIterator); Event endEvent; error = queue.enqueueUnmapMemObject(buffer, pointer, 0, &endEvent); // if exceptions enabled, enqueueUnmapMemObject will throw if( error != CL_SUCCESS ) { return error; } endEvent.wait(); return CL_SUCCESS; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Blocking SVM map operation - performs a blocking map underneath. */ template inline cl_int mapSVM(cl::vector &container) { return enqueueMapSVM(container, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE); } /** * Blocking SVM map operation - performs a blocking map underneath. */ template inline cl_int unmapSVM(cl::vector &container) { return enqueueUnmapSVM(container); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 110 inline cl_int enqueueReadBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, void *ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadBufferRect( buffer, blocking, buffer_offset, host_offset, region, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } inline cl_int enqueueWriteBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, const void *ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteBufferRect( buffer, blocking, buffer_offset, host_offset, region, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } inline cl_int enqueueCopyBufferRect( const Buffer& src, const Buffer& dst, const array& src_origin, const array& dst_origin, const array& region, size_type src_row_pitch, size_type src_slice_pitch, size_type dst_row_pitch, size_type dst_slice_pitch, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBufferRect( src, dst, src_origin, dst_origin, region, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, events, event); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 inline cl_int enqueueReadImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, void* ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadImage( image, blocking, origin, region, row_pitch, slice_pitch, ptr, events, event); } inline cl_int enqueueWriteImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, const void* ptr, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteImage( image, blocking, origin, region, row_pitch, slice_pitch, ptr, events, event); } inline cl_int enqueueCopyImage( const Image& src, const Image& dst, const array& src_origin, const array& dst_origin, const array& region, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyImage( src, dst, src_origin, dst_origin, region, events, event); } inline cl_int enqueueCopyImageToBuffer( const Image& src, const Buffer& dst, const array& src_origin, const array& region, size_type dst_offset, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyImageToBuffer( src, dst, src_origin, region, dst_offset, events, event); } inline cl_int enqueueCopyBufferToImage( const Buffer& src, const Image& dst, size_type src_offset, const array& dst_origin, const array& region, const vector* events = NULL, Event* event = NULL) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBufferToImage( src, dst, src_offset, dst_origin, region, events, event); } inline cl_int flush(void) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.flush(); } inline cl_int finish(void) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.finish(); } class EnqueueArgs { private: CommandQueue queue_; const NDRange offset_; const NDRange global_; const NDRange local_; vector events_; template friend class KernelFunctor; public: EnqueueArgs(NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange) { } EnqueueArgs(NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local) { } EnqueueArgs(NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local) { } EnqueueArgs(Event e, NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange) { events_.push_back(e); } EnqueueArgs(Event e, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(Event e, NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(const vector &events, NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange), events_(events) { } EnqueueArgs(const vector &events, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local), events_(events) { } EnqueueArgs(const vector &events, NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local), events_(events) { } EnqueueArgs(CommandQueue &queue, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange) { } EnqueueArgs(CommandQueue &queue, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local) { } EnqueueArgs(CommandQueue &queue, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local) { } EnqueueArgs(CommandQueue &queue, Event e, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange) { events_.push_back(e); } EnqueueArgs(CommandQueue &queue, Event e, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(CommandQueue &queue, Event e, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(CommandQueue &queue, const vector &events, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange), events_(events) { } EnqueueArgs(CommandQueue &queue, const vector &events, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local), events_(events) { } EnqueueArgs(CommandQueue &queue, const vector &events, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local), events_(events) { } }; //---------------------------------------------------------------------------------------------- /** * Type safe kernel functor. * */ template class KernelFunctor { private: Kernel kernel_; template void setArgs(T0&& t0, T1s&&... t1s) { kernel_.setArg(index, t0); setArgs(std::forward(t1s)...); } template void setArgs(T0&& t0) { kernel_.setArg(index, t0); } template void setArgs() { } public: KernelFunctor(Kernel kernel) : kernel_(kernel) {} KernelFunctor( const Program& program, const string name, cl_int * err = NULL) : kernel_(program, name.c_str(), err) {} //! \brief Return type of the functor typedef Event result_type; /** * Enqueue kernel. * @param args Launch parameters of the kernel. * @param t0... List of kernel arguments based on the template type of the functor. */ Event operator() ( const EnqueueArgs& args, Ts... ts) { Event event; setArgs<0>(std::forward(ts)...); args.queue_.enqueueNDRangeKernel( kernel_, args.offset_, args.global_, args.local_, &args.events_, &event); return event; } /** * Enqueue kernel with support for error code. * @param args Launch parameters of the kernel. * @param t0... List of kernel arguments based on the template type of the functor. * @param error Out parameter returning the error code from the execution. */ Event operator() ( const EnqueueArgs& args, Ts... ts, cl_int &error) { Event event; setArgs<0>(std::forward(ts)...); error = args.queue_.enqueueNDRangeKernel( kernel_, args.offset_, args.global_, args.local_, &args.events_, &event); return event; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 cl_int setSVMPointers(const vector &pointerList) { return kernel_.setSVMPointers(pointerList); } template cl_int setSVMPointers(const T0 &t0, T1s &... ts) { return kernel_.setSVMPointers(t0, ts...); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 Kernel getKernel() { return kernel_; } }; namespace compatibility { /** * Backward compatibility class to ensure that cl.hpp code works with opencl.hpp. * Please use KernelFunctor directly. */ template struct make_kernel { typedef KernelFunctor FunctorType; FunctorType functor_; make_kernel( const Program& program, const string name, cl_int * err = NULL) : functor_(FunctorType(program, name, err)) {} make_kernel( const Kernel kernel) : functor_(FunctorType(kernel)) {} //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, Ts...); Event operator()( const EnqueueArgs& enqueueArgs, Ts... args) { return functor_( enqueueArgs, args...); } }; } // namespace compatibility //---------------------------------------------------------------------------------------------------------------------- #undef CL_HPP_ERR_STR_ #if !defined(CL_HPP_USER_OVERRIDE_ERROR_STRINGS) #undef __GET_DEVICE_INFO_ERR #undef __GET_PLATFORM_INFO_ERR #undef __GET_DEVICE_IDS_ERR #undef __GET_PLATFORM_IDS_ERR #undef __GET_CONTEXT_INFO_ERR #undef __GET_EVENT_INFO_ERR #undef __GET_EVENT_PROFILE_INFO_ERR #undef __GET_MEM_OBJECT_INFO_ERR #undef __GET_IMAGE_INFO_ERR #undef __GET_SAMPLER_INFO_ERR #undef __GET_KERNEL_INFO_ERR #undef __GET_KERNEL_ARG_INFO_ERR #undef __GET_KERNEL_SUB_GROUP_INFO_ERR #undef __GET_KERNEL_WORK_GROUP_INFO_ERR #undef __GET_PROGRAM_INFO_ERR #undef __GET_PROGRAM_BUILD_INFO_ERR #undef __GET_COMMAND_QUEUE_INFO_ERR #undef __CREATE_CONTEXT_ERR #undef __CREATE_CONTEXT_FROM_TYPE_ERR #undef __GET_SUPPORTED_IMAGE_FORMATS_ERR #undef __CREATE_BUFFER_ERR #undef __COPY_ERR #undef __CREATE_SUBBUFFER_ERR #undef __CREATE_GL_BUFFER_ERR #undef __CREATE_GL_RENDER_BUFFER_ERR #undef __GET_GL_OBJECT_INFO_ERR #undef __CREATE_IMAGE_ERR #undef __CREATE_GL_TEXTURE_ERR #undef __IMAGE_DIMENSION_ERR #undef __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR #undef __CREATE_USER_EVENT_ERR #undef __SET_USER_EVENT_STATUS_ERR #undef __SET_EVENT_CALLBACK_ERR #undef __WAIT_FOR_EVENTS_ERR #undef __CREATE_KERNEL_ERR #undef __SET_KERNEL_ARGS_ERR #undef __CREATE_PROGRAM_WITH_SOURCE_ERR #undef __CREATE_PROGRAM_WITH_IL_ERR #undef __CREATE_PROGRAM_WITH_BINARY_ERR #undef __CREATE_PROGRAM_WITH_IL_ERR #undef __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR #undef __BUILD_PROGRAM_ERR #undef __COMPILE_PROGRAM_ERR #undef __LINK_PROGRAM_ERR #undef __CREATE_KERNELS_IN_PROGRAM_ERR #undef __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR #undef __CREATE_SAMPLER_WITH_PROPERTIES_ERR #undef __SET_COMMAND_QUEUE_PROPERTY_ERR #undef __ENQUEUE_READ_BUFFER_ERR #undef __ENQUEUE_READ_BUFFER_RECT_ERR #undef __ENQUEUE_WRITE_BUFFER_ERR #undef __ENQUEUE_WRITE_BUFFER_RECT_ERR #undef __ENQEUE_COPY_BUFFER_ERR #undef __ENQEUE_COPY_BUFFER_RECT_ERR #undef __ENQUEUE_FILL_BUFFER_ERR #undef __ENQUEUE_READ_IMAGE_ERR #undef __ENQUEUE_WRITE_IMAGE_ERR #undef __ENQUEUE_COPY_IMAGE_ERR #undef __ENQUEUE_FILL_IMAGE_ERR #undef __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR #undef __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR #undef __ENQUEUE_MAP_BUFFER_ERR #undef __ENQUEUE_MAP_IMAGE_ERR #undef __ENQUEUE_UNMAP_MEM_OBJECT_ERR #undef __ENQUEUE_NDRANGE_KERNEL_ERR #undef __ENQUEUE_NATIVE_KERNEL #undef __ENQUEUE_MIGRATE_MEM_OBJECTS_ERR #undef __ENQUEUE_MIGRATE_SVM_ERR #undef __ENQUEUE_ACQUIRE_GL_ERR #undef __ENQUEUE_RELEASE_GL_ERR #undef __CREATE_PIPE_ERR #undef __GET_PIPE_INFO_ERR #undef __RETAIN_ERR #undef __RELEASE_ERR #undef __FLUSH_ERR #undef __FINISH_ERR #undef __VECTOR_CAPACITY_ERR #undef __CREATE_SUB_DEVICES_ERR #undef __CREATE_SUB_DEVICES_ERR #undef __ENQUEUE_MARKER_ERR #undef __ENQUEUE_WAIT_FOR_EVENTS_ERR #undef __ENQUEUE_BARRIER_ERR #undef __UNLOAD_COMPILER_ERR #undef __CREATE_GL_TEXTURE_2D_ERR #undef __CREATE_GL_TEXTURE_3D_ERR #undef __CREATE_IMAGE2D_ERR #undef __CREATE_IMAGE3D_ERR #undef __CREATE_COMMAND_QUEUE_ERR #undef __ENQUEUE_TASK_ERR #undef __CREATE_SAMPLER_ERR #undef __ENQUEUE_MARKER_WAIT_LIST_ERR #undef __ENQUEUE_BARRIER_WAIT_LIST_ERR #undef __CLONE_KERNEL_ERR #undef __GET_HOST_TIMER_ERR #undef __GET_DEVICE_AND_HOST_TIMER_ERR #endif //CL_HPP_USER_OVERRIDE_ERROR_STRINGS // Extensions #undef CL_HPP_INIT_CL_EXT_FCN_PTR_ #undef CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_ #if defined(CL_HPP_USE_CL_DEVICE_FISSION) #undef CL_HPP_PARAM_NAME_DEVICE_FISSION_ #endif // CL_HPP_USE_CL_DEVICE_FISSION #undef CL_HPP_NOEXCEPT_ #undef CL_HPP_DEFINE_STATIC_MEMBER_ } // namespace cl #endif // CL_HPP_ ================================================ FILE: ToUTF8/ToUTF8.cpp ================================================ #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include // Helper: Convert wide string to UTF-8 std::string. std::string wstringToUtf8(const std::wstring& wstr) { if (wstr.empty()) return std::string(); // Determine the size needed for the UTF-8 string. int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), NULL, 0, NULL, NULL); std::string result(size_needed, 0); WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), &result[0], size_needed, NULL, NULL); return result; } // Check if text is valid UTF-8 (provided earlier). bool IsTextUtf8(const std::vector& text) { auto Src = text.begin(); const auto SrcEnd = text.end(); while (Src != SrcEnd) { int C = *(Src++); int HighOne = 0; // Number of leftmost '1' bits. for (int Mask = 0x80; Mask != 0 && (C & Mask) != 0; Mask >>= 1) HighOne++; if (HighOne == 1 || HighOne > 4) return false; while (--HighOne > 0) if (Src == SrcEnd || (*(Src++) & 0xc0) != 0x80) return false; } return true; } // Enumeration of supported encodings. enum class Encoding { Unknown, UTF8BOM, UTF8, UTF16LE, UTF16BE, UTF16, ANSI }; // Read the entire file into a vector of char. std::vector readFile(const wchar_t* filename) { std::ifstream in(filename, std::ios::binary); if (!in) { std::cerr << "Error opening input file: " << filename << std::endl; exit(EXIT_FAILURE); } return std::vector(std::istreambuf_iterator(in), std::istreambuf_iterator()); } // Detect encoding based on BOM; if no BOM, use IsTextUtf8 to decide if UTF-8. Encoding detectEncoding(const std::vector& buffer) { if (buffer.size() >= 3 && static_cast(buffer[0]) == 0xEF && static_cast(buffer[1]) == 0xBB && static_cast(buffer[2]) == 0xBF) { return Encoding::UTF8BOM; } if (buffer.size() >= 2 && static_cast(buffer[0]) == 0xFF && static_cast(buffer[1]) == 0xFE) { return Encoding::UTF16LE; } if (buffer.size() >= 2 && static_cast(buffer[0]) == 0xFE && static_cast(buffer[1]) == 0xFF) { return Encoding::UTF16BE; } // No BOM: if valid UTF-8, assume UTF-8; otherwise ANSI. if (IsTextUtf8(buffer)) return Encoding::UTF8; // Otherwise, use IsTextUnicode to get a hint if it might be UTF-16 BOOL isUnicode = FALSE; // Note: You can pass in a pointer to a flag if you need additional output. if (IsTextUnicode(buffer.data(), static_cast(buffer.size()), &isUnicode) && isUnicode) { // For simplicity, assume little-endian if Unicode is detected. return Encoding::UTF16; } return Encoding::ANSI; } // Convert input buffer to a wide string. std::wstring convertToWideString(const std::vector& buffer, Encoding enc) { if (enc == Encoding::UTF8BOM || enc == Encoding::UTF8) { int bomSize = (enc == Encoding::UTF8BOM) ? 3 : 0; int wideSize = MultiByteToWideChar(CP_UTF8, 0, buffer.data() + bomSize, static_cast(buffer.size() - bomSize), NULL, 0); std::wstring wstr(wideSize, L'\0'); MultiByteToWideChar(CP_UTF8, 0, buffer.data() + bomSize, static_cast(buffer.size() - bomSize), &wstr[0], wideSize); return wstr; } else if (enc == Encoding::UTF16LE) { int wcharCount = (static_cast(buffer.size()) - 2) / 2; const wchar_t* start = reinterpret_cast(buffer.data() + 2); return std::wstring(start, wcharCount); } else if (enc == Encoding::UTF16) { int wcharCount = static_cast(buffer.size()) / 2; const wchar_t* start = reinterpret_cast(buffer.data()); return std::wstring(start, wcharCount); } else if (enc == Encoding::UTF16BE) { int wcharCount = (static_cast(buffer.size()) - 2) / 2; std::wstring wstr; wstr.resize(wcharCount); const unsigned char* data = reinterpret_cast(buffer.data() + 2); for (int i = 0; i < wcharCount; i++) { wstr[i] = static_cast(data[2 * i + 1] | (data[2 * i] << 8)); } return wstr; } else { // ANSI int wideSize = MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast(buffer.size()), NULL, 0); std::wstring wstr(wideSize, L'\0'); MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast(buffer.size()), &wstr[0], wideSize); return wstr; } } // Convert a wide string to a UTF-8 std::string. std::string convertWideToUTF8(const std::wstring& wstr) { int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), NULL, 0, NULL, NULL); std::string result(size_needed, '\0'); WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), &result[0], size_needed, NULL, NULL); return result; } // Main function using a wide-character entry point. int wmain(int argc, wchar_t* argv[]) { if (argc < 3) { std::wcerr << L"Usage: ToUTF8.exe " << std::endl; return EXIT_FAILURE; } // Read the entire input file. std::vector buffer = readFile(argv[1]); // Detect encoding. Encoding encoding = detectEncoding(buffer); std::wcout << L"Detected encoding: "; switch (encoding) { case Encoding::UTF8BOM: std::wcout << L"UTF-8 BOM"; break; case Encoding::UTF8: std::wcout << L"UTF-8"; break; case Encoding::UTF16LE: std::wcout << L"UTF-16 LE"; break; case Encoding::UTF16: std::wcout << L"UTF-16"; break; case Encoding::UTF16BE: std::wcout << L"UTF-16 BE"; break; case Encoding::ANSI: std::wcout << L"ANSI"; break; default: std::wcout << L"Unknown"; break; } std::wcout << std::endl; // Convert the input file content to a wide string. std::wstring wideContent = convertToWideString(buffer, encoding); // Convert the wide string to UTF-8. std::string utf8Content = convertWideToUTF8(wideContent); // Write the UTF-8 output to the output file, and write the UTF-8 BOM first. std::ofstream outFile(argv[2], std::ios::binary); if (!outFile) { std::wcerr << L"Error creating output file: " << argv[2] << std::endl; return EXIT_FAILURE; } // Write UTF-8 BOM: 0xEF, 0xBB, 0xBF. const unsigned char bom[3] = { 0xEF, 0xBB, 0xBF }; outFile.write(reinterpret_cast(bom), sizeof(bom)); // Write the converted UTF-8 text. outFile.write(utf8Content.data(), utf8Content.size()); outFile.close(); std::cout << "Conversion to UTF-8 with BOM completed successfully." << std::endl; return EXIT_SUCCESS; } ================================================ FILE: ToUTF8/ToUTF8.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {f082ef32-a1d0-48b9-9af1-dc56178ede92} ToUTF8 Application true Unicode Application false true Unicode Application true Unicode Application false true Unicode Level3 true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true ================================================ FILE: ToUTF8/ToUTF8.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files ================================================ FILE: audio/AudioPitchDecorator.cpp ================================================ #include "AudioPitchDecorator.h" #include "smbPitchShift.h" #include #include AudioPitchDecorator::AudioPitchDecorator(std::unique_ptr player , std::function getPitchShift) : m_player(std::move(player)) , m_getPitchShift(std::move(getPitchShift)) { } AudioPitchDecorator::~AudioPitchDecorator() = default; void AudioPitchDecorator::SetCallback(IAudioPlayerCallback * callback) { m_player->SetCallback(callback); } void AudioPitchDecorator::InitializeThread() { m_player->InitializeThread(); } void AudioPitchDecorator::DeinitializeThread() { m_player->DeinitializeThread(); } void AudioPitchDecorator::WaveOutReset() { m_player->WaveOutReset(); for (auto& v : m_smbPitchShifts) v.reset(); } void AudioPitchDecorator::Close() { m_player->Close(); } bool AudioPitchDecorator::Open(int bytesPerSample, int channels, int * samplesPerSec) { if (!m_player->Open(bytesPerSample, channels, samplesPerSec)) return false; m_bytesPerSample = bytesPerSample; m_samplesPerSec = *samplesPerSec; m_smbPitchShifts.resize(channels); for (auto& v : m_smbPitchShifts) v.reset(); return true; } void AudioPitchDecorator::SetVolume(double volume) { m_player->SetVolume(volume); } double AudioPitchDecorator::GetVolume() const { return m_player->GetVolume(); } void AudioPitchDecorator::WaveOutPause() { m_player->WaveOutPause(); } void AudioPitchDecorator::WaveOutRestart() { m_player->WaveOutRestart(); } bool AudioPitchDecorator::WriteAudio(uint8_t * write_data, int64_t write_size) { const auto pitchShift = m_getPitchShift(); if (pitchShift != 1. && m_bytesPerSample == 2) { const auto numSamples = (write_size / m_bytesPerSample) / m_smbPitchShifts.size(); if (m_buffer.size() < numSamples) m_buffer.resize(numSamples); int16_t* const intData = (int16_t*)write_data; for (int i = 0; i < m_smbPitchShifts.size(); ++i) { for (size_t j = 0; j < numSamples; ++j) { m_buffer[j] = intData[j * m_smbPitchShifts.size() + i] / 32768.; } m_smbPitchShifts[i].smbPitchShift( pitchShift, numSamples, 4096, 16, m_samplesPerSec, m_buffer.data(), m_buffer.data()); for (size_t j = 0; j < numSamples; ++j) { // decrease level to avoid clipping distortions intData[j * m_smbPitchShifts.size() + i] = std::clamp(m_buffer[j], -2.f, 2.f) * (32767. / 2); } } } else { for (auto& v : m_smbPitchShifts) v.reset(); } return m_player->WriteAudio(write_data, write_size); } ================================================ FILE: audio/AudioPitchDecorator.h ================================================ #pragma once #include "../video/audioplayer.h" #include #include #include class CSmbPitchShift; class AudioPitchDecorator : public IAudioPlayer { public: AudioPitchDecorator(std::unique_ptr player, std::function getPitchShift); ~AudioPitchDecorator(); AudioPitchDecorator(const AudioPitchDecorator&) = delete; AudioPitchDecorator& operator =(const AudioPitchDecorator&) = delete; // Inherited via IAudioPlayer void SetCallback(IAudioPlayerCallback * callback) override; void InitializeThread() override; void DeinitializeThread() override; void WaveOutReset() override; void Close() override; bool Open(int bytesPerSample, int channels, int * samplesPerSec) override; void SetVolume(double volume) override; double GetVolume() const override; void WaveOutPause() override; void WaveOutRestart() override; bool WriteAudio(uint8_t * write_data, int64_t write_size) override; private: std::unique_ptr m_player; std::function m_getPitchShift; std::vector m_smbPitchShifts; std::vector m_buffer; int m_bytesPerSample{}; int m_samplesPerSec{}; }; ================================================ FILE: audio/AudioPlayerImpl.cpp ================================================ #include "AudioPlayerImpl.h" #include namespace { enum { BLOCK_SIZE = 4096, BLOCK_COUNT = 4, }; WAVEHDR* allocateBlocks(int size, int count) { const size_t totalBufferSize = (sizeof(WAVEHDR) + size) * count; // allocate memory for the entire set in one step char* buffer = (char*) calloc(totalBufferSize, 1); // and set up the pointers to each bit WAVEHDR* blocks = (WAVEHDR*)buffer; buffer += sizeof(WAVEHDR) * count; for (int i = 0; i < count; ++i) { blocks[i].dwBufferLength = size; blocks[i].lpData = buffer; buffer += size; } return blocks; } void freeBlocks(WAVEHDR* blockArray) { free(blockArray); } } // namespace AudioPlayerImpl::AudioPlayerImpl() : m_callback(nullptr) , m_waveOutput(NULL) , m_waveBlocks(nullptr) , m_evtHasFreeBlocks(CreateEvent(NULL, FALSE, FALSE, NULL)) , m_bytesPerSecond(0) { } AudioPlayerImpl::~AudioPlayerImpl() { CloseHandle(m_evtHasFreeBlocks); } void AudioPlayerImpl::InitializeThread() { ATLTRACE("Old audio thread priority = %d\n", GetThreadPriority(GetCurrentThread())); ATLVERIFY(SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)); } void AudioPlayerImpl::DeinitializeThread() { } void AudioPlayerImpl::WaveOutReset() { waveOutReset(m_waveOutput); } void AudioPlayerImpl::Close() { if (m_waveOutput != NULL) { waveOutReset(m_waveOutput); waveOutClose(m_waveOutput); } if (m_waveBlocks) { // unprepare any blocks that are still prepared for (int i = 0; i < m_waveFreeBlockCount; ++i) { if (m_waveBlocks[i].dwFlags & WHDR_PREPARED) { waveOutUnprepareHeader(m_waveOutput, &m_waveBlocks[i], sizeof(WAVEHDR)); } } freeBlocks(m_waveBlocks); } m_waveOutput = NULL; m_waveBlocks = nullptr; m_waveFreeBlockCount = BLOCK_COUNT; ResetEvent(m_evtHasFreeBlocks); m_waveCurrentBlock = 0; } bool AudioPlayerImpl::Open(int bytesPerSample, int channels, int* samplesPerSec) { m_waveBlocks = allocateBlocks(BLOCK_SIZE, BLOCK_COUNT); m_waveFreeBlockCount = BLOCK_COUNT; m_waveCurrentBlock = 0; WAVEFORMATEX waveFormat = {}; waveFormat.wBitsPerSample = bytesPerSample << 3; waveFormat.nSamplesPerSec = *samplesPerSec; waveFormat.nChannels = channels; waveFormat.cbSize = 0; // size of _extra_ info waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nBlockAlign = (waveFormat.wBitsPerSample >> 3) * waveFormat.nChannels; waveFormat.nAvgBytesPerSec = waveFormat.nBlockAlign * waveFormat.nSamplesPerSec; m_bytesPerSecond = waveFormat.nAvgBytesPerSec; ATLTRACE("Bits per sample = %d\n", waveFormat.wBitsPerSample); ATLTRACE("Samples per second = %d\n", waveFormat.nSamplesPerSec); ATLTRACE("Channels = %d\n", waveFormat.nChannels); ATLTRACE("Block align = %d\n", waveFormat.nBlockAlign); ATLTRACE("Average bit rate = %d\n", waveFormat.nAvgBytesPerSec); if (waveOutOpen( &m_waveOutput, WAVE_MAPPER, &waveFormat, (DWORD_PTR)waveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION ) != MMSYSERR_NOERROR) { ATLTRACE("Unable to open WAVE_MAPPER device.\n"); return false; } return true; } void AudioPlayerImpl::SetVolume(double volume) { WORD vol = (WORD)(volume * 0xFFFF); // Left and right channels waveOutSetVolume(m_waveOutput, (DWORD)(vol << 16 | vol & 0xFFFF)); } double AudioPlayerImpl::GetVolume() const { const double point = 1. / 0xFFFF; DWORD volume = 0; waveOutGetVolume(m_waveOutput, &volume); return (volume & 0xFFFF) * point; } void AudioPlayerImpl::WaveOutPause() { waveOutPause(m_waveOutput); } void AudioPlayerImpl::WaveOutRestart() { waveOutRestart(m_waveOutput); } bool AudioPlayerImpl::WriteAudio(uint8_t* write_data, int64_t write_size) { if (!m_waveOutput) return false; WAVEHDR* current = &m_waveBlocks[m_waveCurrentBlock]; while (write_size > 0) { // fill block partially and wait for the next writeAudio call if (write_size < (int)(BLOCK_SIZE - current->dwUser)) { memcpy(current->lpData + current->dwUser, write_data, (size_t)write_size); current->dwUser += (DWORD_PTR)write_size; break; } // fill block completely and play it const int remain = BLOCK_SIZE - current->dwUser; memcpy(current->lpData + current->dwUser, write_data, remain); write_size -= remain; write_data += remain; ATLASSERT(current->dwBufferLength == BLOCK_SIZE); if (current->dwFlags & WHDR_PREPARED || waveOutPrepareHeader(m_waveOutput, current, sizeof(WAVEHDR)) == MMSYSERR_NOERROR) { if (-1 == InterlockedDecrement(&m_waveFreeBlockCount)) { WaitForSingleObject(m_evtHasFreeBlocks, INFINITE); } MMRESULT isOutWritten = waveOutWrite(m_waveOutput, current, sizeof(WAVEHDR)); if (isOutWritten != MMSYSERR_NOERROR) { InterlockedIncrement(&m_waveFreeBlockCount); } } // point to the next block ++m_waveCurrentBlock; m_waveCurrentBlock %= BLOCK_COUNT; current = &m_waveBlocks[m_waveCurrentBlock]; current->dwUser = 0; } return true; } ////////////////////////////////////////////////////////////////////////////// void CALLBACK AudioPlayerImpl::waveOutProc(HWAVEOUT, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR, DWORD_PTR) { // ignore calls that occur due to openining and closing the device. if (uMsg == WOM_DONE) { // pointer to free block counter auto waveArgs = reinterpret_cast(dwInstance); if (0 == InterlockedIncrement(&waveArgs->m_waveFreeBlockCount)) { SetEvent(waveArgs->m_evtHasFreeBlocks); } // count audio pts const double frame_clock = (double)BLOCK_SIZE / waveArgs->m_bytesPerSecond; waveArgs->m_callback->AppendFrameClock(frame_clock); } } ================================================ FILE: audio/AudioPlayerImpl.h ================================================ #pragma once #include "../video/audioplayer.h" #include #include class AudioPlayerImpl : public IAudioPlayer { public: AudioPlayerImpl(); ~AudioPlayerImpl() override; AudioPlayerImpl(const AudioPlayerImpl&) = delete; AudioPlayerImpl& operator=(const AudioPlayerImpl&) = delete; void SetCallback(IAudioPlayerCallback* callback) override { m_callback = callback; } void InitializeThread() override; void DeinitializeThread() override; void WaveOutReset() override; void Close() override; bool Open(int bytesPerSample, int channels, int* samplesPerSec) override; void SetVolume(double volume) override; double GetVolume() const override; void WaveOutPause() override; void WaveOutRestart() override; bool WriteAudio(uint8_t* write_data, int64_t write_size) override; private: IAudioPlayerCallback* m_callback; HWAVEOUT m_waveOutput; static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR); WAVEHDR* m_waveBlocks; volatile long m_waveFreeBlockCount {}; HANDLE m_evtHasFreeBlocks; int m_waveCurrentBlock {}; int m_bytesPerSecond; }; ================================================ FILE: audio/AudioPlayerWasapi.cpp ================================================ #include "AudioPlayerWasapi.h" #include #include #include #include #include namespace { HMODULE GetAvrtHandle() { static HMODULE avrtHandle = LoadLibrary(_T("Avrt.dll")); return avrtHandle; } } // https://chromium.googlesource.com/chromium/src/media/+/master/audio/win/core_audio_util_win.cc namespace CoreAudioUtil { CComPtr CreateDeviceEnumeratorInternal( bool allow_reinitialize) { CComPtr device_enumerator; HRESULT hr = device_enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER); if (hr == CO_E_NOTINITIALIZED && allow_reinitialize) { ATLTRACE("CoCreateInstance fails with CO_E_NOTINITIALIZED\n"); // We have seen crashes which indicates that this method can in fact // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party // modules. Calling CoInitializeEx is an attempt to resolve the reported // issues. See http://crbug.com/378465 for details. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (SUCCEEDED(hr)) { hr = device_enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER); } } return device_enumerator; } CComPtr CreateDeviceEnumerator() { //ASSERT(IsSupported()); auto device_enumerator = CreateDeviceEnumeratorInternal(true); ATLASSERT(device_enumerator); return device_enumerator; } CComPtr CreateDefaultDevice(EDataFlow data_flow, ERole role) { //ASSERT(IsSupported()); CComPtr endpoint_device; // Create the IMMDeviceEnumerator interface. auto device_enumerator = CreateDeviceEnumerator(); if (!device_enumerator) return endpoint_device; // Retrieve the default audio endpoint for the specified data-flow // direction and role. HRESULT hr = device_enumerator->GetDefaultAudioEndpoint( data_flow, role, &endpoint_device); if (FAILED(hr)) { ATLTRACE("IMMDeviceEnumerator::GetDefaultAudioEndpoint: %x\n", hr); return endpoint_device; } // Verify that the audio endpoint device is active, i.e., that the audio // adapter that connects to the endpoint device is present and enabled. DWORD state = DEVICE_STATE_DISABLED; hr = endpoint_device->GetState(&state); if (SUCCEEDED(hr)) { if (!(state & DEVICE_STATE_ACTIVE)) { ATLTRACE("Selected endpoint device is not active\n"); endpoint_device.Release(); } } return endpoint_device; } CComPtr CreateDevice( const std::string& device_id) { //ASSERT(IsSupported()); CComPtr endpoint_device; // Create the IMMDeviceEnumerator interface. auto device_enumerator = CreateDeviceEnumerator(); if (!device_enumerator) return endpoint_device; // Retrieve an audio device specified by an endpoint device-identification // string. HRESULT hr = device_enumerator->GetDevice(CAtlStringW(device_id.c_str()), &endpoint_device); if (FAILED(hr)) { ATLTRACE("IMMDeviceEnumerator::GetDevice: %x\n", hr); } return endpoint_device; } CComPtr CreateClient( IMMDevice* audio_device) { //ASSERT(IsSupported()); // Creates and activates an IAudioClient COM object given the selected // endpoint device. CComPtr audio_client; HRESULT hr = audio_device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&audio_client); if (FAILED(hr)) { ATLTRACE("IMMDevice::Activate: %x\n", hr); } return audio_client; } CComPtr CreateDefaultClient( EDataFlow data_flow, ERole role) { //ASSERT(IsSupported()); auto default_device = CreateDefaultDevice(data_flow, role); if (!default_device) return {}; return CreateClient(default_device); } CComPtr CreateClient( const std::string& device_id, EDataFlow data_flow, ERole role) { if (device_id.empty()) return CreateDefaultClient(data_flow, role); auto device = CreateDevice(device_id); if (!device) return {}; return CreateClient(device); } HRESULT GetSharedModeMixFormat( IAudioClient* client, WAVEFORMATPCMEX* format) { //ASSERT(IsSupported()); CComHeapPtr format_pcmex; HRESULT hr = client->GetMixFormat( reinterpret_cast(&format_pcmex)); if (FAILED(hr)) return hr; size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; ATLASSERT(bytes == sizeof(WAVEFORMATPCMEX)); memcpy(format, format_pcmex, bytes); return hr; } DWORD GetChannelConfig(const std::string& device_id, EDataFlow data_flow) { auto client = CreateClient(device_id, data_flow, eConsole); WAVEFORMATPCMEX format = { 0 }; if (!client || FAILED(GetSharedModeMixFormat(client, &format))) return 0; return format.dwChannelMask; } HRESULT SharedModeInitialize( IAudioClient* client, const WAVEFORMATPCMEX* format, HANDLE event_handle, unsigned int* endpoint_buffer_size, const GUID* session_guid) { //ASSERT(IsSupported()); // Use default flags (i.e, dont set AUDCLNT_STREAMFLAGS_NOPERSIST) to // ensure that the volume level and muting state for a rendering session // are persistent across system restarts. The volume level and muting // state for a capture session are never persistent. DWORD stream_flags = 0; // Enable event-driven streaming if a valid event handle is provided. // After the stream starts, the audio engine will signal the event handle // to notify the client each time a buffer becomes ready to process. // Event-driven buffering is supported for both rendering and capturing. // Both shared-mode and exclusive-mode streams can use event-driven buffering. bool use_event = (event_handle != NULL && event_handle != INVALID_HANDLE_VALUE); if (use_event) stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; ATLTRACE("stream_flags: 0x%x\n", stream_flags); // Initialize the shared mode client for minimal delay. HRESULT hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, stream_flags, 100 * 10000, 0, reinterpret_cast(format), session_guid); if (FAILED(hr)) { ATLTRACE("IAudioClient::Initialize: %x\n", hr); return hr; } if (use_event) { hr = client->SetEventHandle(event_handle); if (FAILED(hr)) { ATLTRACE("IAudioClient::SetEventHandle: %x\n", hr); return hr; } } UINT32 buffer_size_in_frames = 0; hr = client->GetBufferSize(&buffer_size_in_frames); if (FAILED(hr)) { ATLTRACE("IAudioClient::GetBufferSize: %x\n", hr); return hr; } *endpoint_buffer_size = buffer_size_in_frames; ATLTRACE("endpoint buffer size: %d\n", buffer_size_in_frames); return hr; } CComPtr CreateRenderClient( IAudioClient* client) { //ASSERT(IsSupported()); // Get access to the IAudioRenderClient interface. This interface // enables us to write output data to a rendering endpoint buffer. CComPtr audio_render_client; HRESULT hr = client->GetService(__uuidof(IAudioRenderClient), (void**) &audio_render_client); if (FAILED(hr)) { ATLTRACE("IAudioClient::GetService: %x\n", hr); return CComPtr(); } return audio_render_client; } } // namespace CoreAudioUtil const GUID kCommunicationsSessionId = { 0xbe39af4f, 0x87c, 0x423f, { 0x93, 0x3, 0x23, 0x4e, 0xc1, 0xe5, 0xb8, 0xee } }; AudioPlayerWasapi::AudioPlayerWasapi() : m_callback(nullptr) , m_hAudioSamplesRenderEvent(CreateEvent(NULL, FALSE, FALSE, NULL)) , m_BufferSize(0) , m_FrameSize(0) , m_samplesPerSec(0) , m_mmcssHandle(NULL) , m_coUnitialize(false) { } AudioPlayerWasapi::~AudioPlayerWasapi() { CloseHandle(m_hAudioSamplesRenderEvent); } bool AudioPlayerWasapi::Open(int bytesPerSample, int channels, int* samplesPerSec) { const ERole device_role_ = eConsole; // Will be set to true if we ended up opening the default communications device. const bool communications_device = (device_role_ == eCommunications); // Create an IAudioClient interface for the default rendering IMMDevice. auto audio_client = CoreAudioUtil::CreateDefaultClient(eRender, device_role_); if (!audio_client) return false; WAVEFORMATPCMEX format_ = {}; HRESULT hr = CoreAudioUtil::GetSharedModeMixFormat(audio_client, &format_); if (FAILED(hr)) return false; if (format_.Format.nSamplesPerSec != 0) { *samplesPerSec = format_.Format.nSamplesPerSec; } // Begin with the WAVEFORMATEX structure that specifies the basic format. WAVEFORMATEX* format = &format_.Format; format->wFormatTag = WAVE_FORMAT_EXTENSIBLE; format->nChannels = channels; format->nSamplesPerSec = *samplesPerSec; format->wBitsPerSample = bytesPerSample << 3; format->nBlockAlign = (format->wBitsPerSample / 8) * format->nChannels; format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign; format->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); m_FrameSize = format->nBlockAlign; m_samplesPerSec = *samplesPerSec; // Add the parts which are unique to WAVE_FORMAT_EXTENSIBLE. format_.Samples.wValidBitsPerSample = bytesPerSample << 3; //params.bits_per_sample(); //format_.dwChannelMask = CoreAudioUtil::GetChannelConfig(std::string(), eRender); format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; // Initialize the audio stream between the client and the device in shared // mode and using event-driven buffer handling. hr = CoreAudioUtil::SharedModeInitialize( audio_client, &format_, m_hAudioSamplesRenderEvent, &m_BufferSize, communications_device ? &kCommunicationsSessionId : NULL); if (FAILED(hr)) return false; // Create an IAudioRenderClient client for an initialized IAudioClient. // The IAudioRenderClient interface enables us to write output data to // a rendering endpoint buffer. auto audio_render_client = CoreAudioUtil::CreateRenderClient(audio_client); if (!audio_render_client) return false; // Store valid COM interfaces. m_AudioClient = audio_client; m_RenderClient = audio_render_client; hr = m_AudioClient->GetService(__uuidof(ISimpleAudioVolume), (void**)&m_SimpleAudioVolume); m_AudioClient->Start(); return true; } bool AudioPlayerWasapi::WriteAudio(uint8_t* write_data, int64_t write_size) { if (!m_AudioClient || !m_RenderClient) { return false; } while (write_size > 0) { // We need to provide the next buffer of samples to the audio renderer. // We want to find out how much of the buffer *isn't* available (is padding). UINT32 padding; HRESULT hr = m_AudioClient->GetCurrentPadding(&padding); if (FAILED(hr)) { ATLTRACE("Unable to get padding: %x\n", hr); return false; } UINT32 framesAvailable = m_BufferSize - padding; if (framesAvailable != 0) { const auto remain = min(write_size, framesAvailable * m_FrameSize); UINT32 framesToWrite = UINT32(remain / m_FrameSize); BYTE *pData; hr = m_RenderClient->GetBuffer(framesToWrite, &pData); if (SUCCEEDED(hr)) { // Copy data from the render buffer to the output buffer and bump our render pointer. memcpy(pData, write_data, framesToWrite * m_FrameSize); hr = m_RenderClient->ReleaseBuffer(framesToWrite, 0); if (!SUCCEEDED(hr)) { ATLTRACE("Unable to release buffer: %x\n", hr); } // count audio pts const double frame_clock = (double)framesToWrite / m_samplesPerSec; m_callback->AppendFrameClock(frame_clock); } else { ATLTRACE("Unable to get buffer: %x\n", hr); } write_size -= remain; write_data += remain; } if (write_size > 0) { WaitForSingleObject(m_hAudioSamplesRenderEvent, INFINITE); } } return true; } #define AVRT_FUNC_PTR(name) \ static decltype(name)* name##Ptr = (decltype(name)*)GetProcAddress(avrtHandle, #name) void AudioPlayerWasapi::InitializeThread() { HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); m_coUnitialize = (S_OK == hr); if (FAILED(hr)) { ATLTRACE("Unable to initialize COM in render thread: %x\n", hr); } ATLTRACE("Old audio thread priority = %d\n", GetThreadPriority(GetCurrentThread())); ATLVERIFY(SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)); if (auto avrtHandle = GetAvrtHandle()) { DWORD mmcssTaskIndex = 0; AVRT_FUNC_PTR(AvSetMmThreadCharacteristicsW); if (AvSetMmThreadCharacteristicsWPtr) { m_mmcssHandle = AvSetMmThreadCharacteristicsWPtr(L"Pro Audio", &mmcssTaskIndex); if (m_mmcssHandle == NULL) { ATLTRACE("Unable to enable MMCSS on render thread: %d\n", GetLastError()); } else { AVRT_FUNC_PTR(AvSetMmThreadPriority); if (AvSetMmThreadPriorityPtr) ATLVERIFY(AvSetMmThreadPriorityPtr(m_mmcssHandle, AVRT_PRIORITY_CRITICAL)); } } } } void AudioPlayerWasapi::DeinitializeThread() { if (m_mmcssHandle) { if (auto avrtHandle = GetAvrtHandle()) { AVRT_FUNC_PTR(AvRevertMmThreadCharacteristics); if (AvRevertMmThreadCharacteristicsPtr) { ATLVERIFY(AvRevertMmThreadCharacteristicsPtr(m_mmcssHandle)); m_mmcssHandle = NULL; } } } if (m_coUnitialize) { CoUninitialize(); m_coUnitialize = false; } } #undef AVRT_FUNC_PTR void AudioPlayerWasapi::WaveOutReset() { if (m_AudioClient) { m_AudioClient->Reset(); } } void AudioPlayerWasapi::Close() { m_SimpleAudioVolume.Release(); m_RenderClient.Release(); m_AudioClient.Release(); } void AudioPlayerWasapi::SetVolume(double volume) { if (m_SimpleAudioVolume) { m_SimpleAudioVolume->SetMasterVolume((float)volume, NULL); } } double AudioPlayerWasapi::GetVolume() const { float result = 1.; if (m_SimpleAudioVolume) { m_SimpleAudioVolume->GetMasterVolume(&result); } return result; } void AudioPlayerWasapi::WaveOutPause() { if (m_AudioClient) { m_AudioClient->Stop(); } } void AudioPlayerWasapi::WaveOutRestart() { if (m_AudioClient) { m_AudioClient->Start(); } } ================================================ FILE: audio/AudioPlayerWasapi.h ================================================ #pragma once #include "../video/audioplayer.h" #include struct IAudioClient; struct IAudioRenderClient; struct ISimpleAudioVolume; class AudioPlayerWasapi : public IAudioPlayer { public: AudioPlayerWasapi(); ~AudioPlayerWasapi() override; AudioPlayerWasapi(const AudioPlayerWasapi&) = delete; AudioPlayerWasapi& operator=(const AudioPlayerWasapi&) = delete; void SetCallback(IAudioPlayerCallback* callback) override { m_callback = callback; } void InitializeThread() override; void DeinitializeThread() override; void WaveOutReset() override; void Close() override; bool Open(int bytesPerSample, int channels, int* samplesPerSec) override; void SetVolume(double volume) override; double GetVolume() const override; void WaveOutPause() override; void WaveOutRestart() override; bool WriteAudio(uint8_t* write_data, int64_t write_size) override; private: IAudioPlayerCallback* m_callback; HANDLE m_hAudioSamplesRenderEvent; CComPtr m_AudioClient; CComPtr m_RenderClient; CComPtr m_SimpleAudioVolume; unsigned int m_BufferSize; unsigned int m_FrameSize; unsigned int m_samplesPerSec; HANDLE m_mmcssHandle; bool m_coUnitialize; }; ================================================ FILE: audio/ReadMe.txt ================================================ ======================================================================== STATIC LIBRARY : audio Project Overview ======================================================================== AppWizard has created this audio library project for you. No source files were created as part of your project. audio.vcxproj This is the main project file for VC++ projects generated using an Application Wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations, and project features selected with the Application Wizard. audio.vcxproj.filters This is the filters file for VC++ projects generated using an Application Wizard. It contains information about the association between the files in your project and the filters. This association is used in the IDE to show grouping of files with similar extensions under a specific node (for e.g. ".cpp" files are associated with the "Source Files" filter). ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" comments to indicate parts of the source code you should add to or customize. ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: audio/audio.vcxproj ================================================  Debug Win32 Release Win32 Debug x64 Release x64 {8B955995-B5EC-41F0-940A-48A6F17BCBB8} Win32Proj audio StaticLibrary true Unicode StaticLibrary false true Unicode StaticLibrary true Unicode StaticLibrary false true Unicode Level3 Disabled _USE_MATH_DEFINES;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true /std:c++latest /D_HAS_CXX17=1 %(AdditionalOptions) Fast Windows Level3 Disabled _USE_MATH_DEFINES;_DEBUG;_LIB;%(PreprocessorDefinitions) true /std:c++latest /D_HAS_CXX17=1 %(AdditionalOptions) Fast Windows Level3 MaxSpeed true true _USE_MATH_DEFINES;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true /std:c++latest /D_HAS_CXX17=1 %(AdditionalOptions) Fast Windows true true Level3 MaxSpeed true true _USE_MATH_DEFINES;NDEBUG;_LIB;%(PreprocessorDefinitions) true /std:c++latest /D_HAS_CXX17=1 %(AdditionalOptions) Fast Windows true true ================================================ FILE: audio/audio.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files ================================================ FILE: audio/smbPitchShift.cpp ================================================ /**************************************************************************** * * NAME: smbPitchShift.cpp * VERSION: 1.2 * HOME URL: http://blogs.zynaptiq.com/bernsee * KNOWN BUGS: none * * SYNOPSIS: Routine for doing pitch shifting while maintaining * duration using the Short Time Fourier Transform. * * DESCRIPTION: The routine takes a pitchShift factor value which is between 0.5 * (one octave down) and 2. (one octave up). A value of exactly 1 does not change * the pitch. numSampsToProcess tells the routine how many samples in indata[0... * numSampsToProcess-1] should be pitch shifted and moved to outdata[0 ... * numSampsToProcess-1]. The two buffers can be identical (ie. it can process the * data in-place). fftFrameSize defines the FFT frame size used for the * processing. Typical values are 1024, 2048 and 4096. It may be any value <= * MAX_FRAME_LENGTH but it MUST be a power of 2. osamp is the STFT * oversampling factor which also determines the overlap between adjacent STFT * frames. It should at least be 4 for moderate scaling ratios. A value of 32 is * recommended for best quality. sampleRate takes the sample rate for the signal * in unit Hz, ie. 44100 for 44.1 kHz audio. The data passed to the routine in * indata[] should be in the range [-1.0, 1.0), which is also the output range * for the data, make sure you scale the data accordingly (for 16bit signed integers * you would have to divide (and multiply) by 32768). * * COPYRIGHT 1999-2015 Stephan M. Bernsee * * The Wide Open License (WOL) * * Permission to use, copy, modify, distribute and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice and this license appear in all source copies. * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF * ANY KIND. See http://www.dspguru.com/wol.htm for more information. * *****************************************************************************/ #include "smbPitchShift.h" #include #include #include #include #include #include #include namespace { void smbFft(float *fftBuffer, long fftFrameSize, long sign) /* FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse) Fills fftBuffer[0...2*fftFrameSize-1] with the Fourier transform of the time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes and returns the cosine and sine parts in an interleaved manner, ie. fftBuffer[0] = cosPart[0], fftBuffer[1] = sinPart[0], asf. fftFrameSize must be a power of 2. It expects a complex input signal (see footnote 2), ie. when working with 'common' audio signals our input signal has to be passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform of the frequencies of interest is in fftBuffer[0...fftFrameSize]. */ { const auto number = 2 * fftFrameSize - 2; for (long i = 2, j = 0; i < number; i += 2) { for (long bitm = fftFrameSize; bitm != 1; bitm >>= 1) { if (j & bitm) j &= ~bitm; else { j |= bitm; break; } } if (i < j) { auto p1 = fftBuffer+i; auto p2 = fftBuffer+j; auto temp = *p1; *(p1++) = *p2; *(p2++) = temp; temp = *p1; *p1 = *p2; *p2 = temp; } } for (long k = 0, le = 2; k < (long)(log(fftFrameSize)/log(2.)+.5); k++) { le <<= 1; const auto le2 = le>>1; __declspec(align(8)) struct { float r, i; } u{ 1.0, 0.0 }; const float arg = M_PI / (le2>>1); const float wr = cos(arg); const float wi = sign*sin(arg); for (long j = 0; j < le2; j += 2) { auto p1r = fftBuffer+j; auto p2r = p1r+le2; __m128 u_ = _mm_castpd_ps(_mm_movedup_pd(_mm_load_sd((const double*)&u))); __m128 ldup = _mm_moveldup_ps(u_); __m128 hdup = _mm_movehdup_ps(u_); long i = j; for (; i < 2*fftFrameSize - le; i += le * 2) { __m128 p2 = _mm_loadh_pi(_mm_castpd_ps(_mm_load_sd((const double*)p2r)), (const __m64*)(p2r + le)); __m128 part1 = _mm_mul_ps(p2, ldup); __m128 part2 = _mm_mul_ps(p2, hdup); part2 = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(part2), _MM_SHUFFLE(2, 3, 0, 1))); __m128 t = _mm_addsub_ps(part1, part2); __m128 p1 = _mm_loadh_pi(_mm_castpd_ps(_mm_load_sd((const double*)p1r)), (const __m64*)(p1r + le)); __m128 buffer = _mm_sub_ps(p1, t); _mm_storeh_pi((__m64*)(p2r + le), buffer); _mm_storel_pi((__m64*)p2r, buffer); buffer = _mm_add_ps(p1, t); _mm_storeh_pi((__m64*)(p1r + le), buffer); _mm_storel_pi((__m64*)p1r, buffer); p1r += le * 2; p2r += le * 2; } if (i < 2 * fftFrameSize) { const auto p1i = p1r + 1; const auto p2i = p2r + 1; const float tr = *p2r * u.r - *p2i * u.i; const float ti = *p2r * u.i + *p2i * u.r; *p2r = *p1r - tr; *p2i = *p1i - ti; *p1r += tr; *p1i += ti; } const float tr = u.r*wr - u.i*wi; u.i = u.r*wi + u.i*wr; u.r = tr; } } } // ----------------------------------------------------------------------------------------------------------------- /* 12/12/02, smb PLEASE NOTE: There have been some reports on domain errors when the atan2() function was used as in the above code. Usually, a domain error should not interrupt the program flow (maybe except in Debug mode) but rather be handled "silently" and a global variable should be set according to this error. However, on some occasions people ran into this kind of scenario, so a replacement atan2() function is provided here. If you are experiencing domain errors and your program stops, simply replace all instances of atan2() with calls to the smbAtan2() function below. */ // Approximation was taken from: // http://www-labs.iro.umontreal.ca/~mignotte/IFT2425/Documents/EfficientApproximationArctgFunction.pdf // // |Error = fast_atan2(y, x) - atan2f(y, x)| < 0.00468 rad // // Octants: // pi/2 // ` 3 | 2 / // ` | / // 4 ` | / 1 // pi -----+----- 0 // 5 / | ` 8 // / | ` // / 6 | 7 ` // 3pi/2 template T CopySign(T v, T x) { return (x >= 0) ? v : -v; } double smbAtan2(double y, double x) { constexpr double scaling_constant = 0.28086; if (x == 0.) { // Special case atan2(0.0, 0.0) = 0.0 if (y == 0.) { return 0.; } // x is zero so we are either at pi/2 for (y > 0) or -pi/2 for (y < 0) return CopySign(M_PI_2, y); } // Calculate quotient of y and x const auto div = y / x; // Determine in which octants we can be, if |y| is smaller than |x| (|div|<1) // then we are either in 1,4,5 or 8 else we are in 2,3,6 or 7. if (fabs(div) < 1.) { // We are in 1,4,5 or 8 const auto atan = div / (1. + scaling_constant * div * div); // If we are in 4 or 5 we need to add pi or -pi respectively if (x < 0.) { return CopySign(M_PI, y) + atan; } return atan; } // We are in 2,3,6 or 7 return CopySign(M_PI_2, y) - div / (div * div + scaling_constant); } //double smbAtan2(double x, double y) //{ // double signx; // if (x > 0.) signx = 1.; // else signx = -1.; // // if (x == 0.) return 0.; // if (y == 0.) return signx * M_PI / 2.; // // return atan2(x, y); //} } // namespace // ----------------------------------------------------------------------------------------------------------------- void CSmbPitchShift::smbPitchShift(float pitchShift, long numSampsToProcess, long fftFrameSize, long osamp, float sampleRate, float *indata, float *outdata) /* Routine smbPitchShift(). See top of file for explanation Purpose: doing pitch shifting while maintaining duration using the Short Time Fourier Transform. Author: (c)1999-2015 Stephan M. Bernsee */ { /* set up some handy variables */ const long fftFrameSize2 = fftFrameSize/2; const long stepSize = fftFrameSize/osamp; const double freqPerBin = sampleRate/(double)fftFrameSize; const double expct = 2.*M_PI*(double)stepSize/(double)fftFrameSize; const long inFifoLatency = fftFrameSize-stepSize; if (!gRover) gRover = inFifoLatency; /* initialize our static arrays */ if (!gInit) { memset(gInFIFO, 0, MAX_FRAME_LENGTH*sizeof(float)); memset(gOutFIFO, 0, MAX_FRAME_LENGTH*sizeof(float)); memset(gFFTworksp, 0, 2*MAX_FRAME_LENGTH*sizeof(float)); memset(gLastPhase, 0, (MAX_FRAME_LENGTH/2+1)*sizeof(float)); memset(gSumPhase, 0, (MAX_FRAME_LENGTH/2+1)*sizeof(float)); memset(gOutputAccum, 0, 2*MAX_FRAME_LENGTH*sizeof(float)); memset(gAnaFreq, 0, MAX_FRAME_LENGTH*sizeof(float)); memset(gAnaMagn, 0, MAX_FRAME_LENGTH*sizeof(float)); memset(gErrors, 0, MAX_FRAME_LENGTH * sizeof(float)); gInit = true; } auto window = std::make_unique(fftFrameSize); for (long k = 0; k < fftFrameSize; k++) { window[k] = -.5*cos(2.*M_PI*(double)k / (double)fftFrameSize) + .5; } /* main processing loop */ for (long i = 0; i < numSampsToProcess; i++){ /* As long as we have not yet collected enough data just read in */ gInFIFO[gRover] = indata[i]; outdata[i] = gOutFIFO[gRover-inFifoLatency]; gRover++; /* now we have enough data for processing */ if (gRover >= fftFrameSize) { gRover = inFifoLatency; /* do windowing and re,im interleave */ for (long k = 0; k < fftFrameSize;k++) { gFFTworksp[2*k] = gInFIFO[k] * window[k]; gFFTworksp[2*k+1] = 0.; } /* ***************** ANALYSIS ******************* */ /* do transform */ smbFft(gFFTworksp, fftFrameSize, -1); /* this is the analysis step */ for (long k = 0; k <= fftFrameSize2; k++) { /* de-interlace FFT buffer */ const auto real = gFFTworksp[2*k]; const auto imag = gFFTworksp[2*k+1]; /* compute magnitude and phase */ const auto magn = 2.*hypotf(real, imag); const auto phase = smbAtan2(imag,real); /* compute phase difference */ double tmp = phase - gLastPhase[k]; gLastPhase[k] = phase; /* subtract expected phase difference */ tmp -= (double)k*expct; /* map delta phase into +/- Pi interval */ /* get deviation from bin frequency from the +/- Pi interval */ tmp /= (2.*M_PI); tmp = osamp * (tmp - floor(tmp + 0.5)); // faster than round /* compute the k-th partials' true frequency */ tmp = (k + tmp) * freqPerBin; /* store magnitude and true frequency in analysis arrays */ gAnaMagn[k] = magn; gAnaFreq[k] = tmp; } /* ***************** PROCESSING ******************* */ /* this does the actual pitch shifting */ memset(gSynMagn, 0, fftFrameSize*sizeof(float)); memset(gSynFreq, 0, fftFrameSize*sizeof(float)); for (long k = 0; k <= fftFrameSize2; k++) { //const long index = k*pitchShift; const auto originalIndex = k*pitchShift + gErrors[k]; const long index = originalIndex; gErrors[k] = originalIndex - index; if (index <= fftFrameSize2) { const bool useSynFreq = gSynMagn[index] < gAnaMagn[k]; gSynMagn[index] += gAnaMagn[k]; if (useSynFreq) { gSynFreq[index] = gAnaFreq[k] * pitchShift; } } } /* ***************** SYNTHESIS ******************* */ /* this is the synthesis step */ for (long k = 0; k <= fftFrameSize2; k++) { /* get magnitude and true frequency from synthesis arrays */ const auto magn = gSynMagn[k]; double tmp = gSynFreq[k]; /* get bin deviation from freq deviation */ tmp /= freqPerBin; /* subtract bin mid frequency */ tmp -= k; /* take osamp into account */ tmp = 2.*M_PI*tmp/osamp; /* add the overlap phase advance back in */ tmp += (double)k*expct; /* accumulate delta phase to get bin phase */ gSumPhase[k] += tmp; const auto phase = gSumPhase[k]; /* get real and imag part and re-interleave */ gFFTworksp[2*k] = magn*cosf(phase); gFFTworksp[2*k+1] = magn*sinf(phase); } /* zero negative frequencies */ for (long k = fftFrameSize+2; k < 2*fftFrameSize; k++) gFFTworksp[k] = 0.; /* do inverse transform */ smbFft(gFFTworksp, fftFrameSize, 1); /* do windowing and add to output accumulator */ for(long k=0; k < fftFrameSize; k++) { gOutputAccum[k] += 2. * window[k] * gFFTworksp[2*k]/(fftFrameSize2*osamp); } for (long k = 0; k < stepSize; k++) gOutFIFO[k] = gOutputAccum[k]; /* shift accumulator */ memmove(gOutputAccum, gOutputAccum+stepSize, fftFrameSize*sizeof(float)); /* move input FIFO */ for (long k = 0; k < inFifoLatency; k++) gInFIFO[k] = gInFIFO[k+stepSize]; } } } ================================================ FILE: audio/smbPitchShift.h ================================================ #pragma once // http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/ class CSmbPitchShift { enum { MAX_FRAME_LENGTH = 8192 }; public: void reset() { gRover = 0; gInit = false; } void smbPitchShift(float pitchShift, long numSampsToProcess, long fftFrameSize, long osamp, float sampleRate, float *indata, float *outdata); private: float gInFIFO[MAX_FRAME_LENGTH]; float gOutFIFO[MAX_FRAME_LENGTH]; float gFFTworksp[2 * MAX_FRAME_LENGTH]; float gLastPhase[MAX_FRAME_LENGTH / 2 + 1]; float gSumPhase[MAX_FRAME_LENGTH / 2 + 1]; float gOutputAccum[2 * MAX_FRAME_LENGTH]; float gAnaFreq[MAX_FRAME_LENGTH]; float gAnaMagn[MAX_FRAME_LENGTH]; float gSynFreq[MAX_FRAME_LENGTH]; float gSynMagn[MAX_FRAME_LENGTH]; float gErrors[MAX_FRAME_LENGTH]; long gRover = 0; bool gInit = false; }; ================================================ FILE: core/ac_export.h ================================================ #ifndef AC_EXPORT_H #define AC_EXPORT_H #ifdef AC_STATIC_DEFINE # define AC_EXPORT # define AC_NO_EXPORT #else # ifndef AC_EXPORT # ifdef Anime4KCPPCore_EXPORTS /* We are building this library */ # define AC_EXPORT # else /* We are using this library */ # define AC_EXPORT # endif # endif # ifndef AC_NO_EXPORT # define AC_NO_EXPORT # endif #endif #ifndef AC_DEPRECATED # define AC_DEPRECATED __declspec(deprecated) #endif #ifndef AC_DEPRECATED_EXPORT # define AC_DEPRECATED_EXPORT AC_EXPORT AC_DEPRECATED #endif #ifndef AC_DEPRECATED_NO_EXPORT # define AC_DEPRECATED_NO_EXPORT AC_NO_EXPORT AC_DEPRECATED #endif #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef AC_NO_DEPRECATED # define AC_NO_DEPRECATED # endif #endif #endif /* AC_EXPORT_H */ ================================================ FILE: edit_git_subst_cfg.cmd ================================================ notepad.exe "%LOCALAPPDATA%\git-subst.cfg" ================================================ FILE: ffmpeg-3.3.3-experimental-patch/common.before.h ================================================ /* * copyright (c) 2006 Michael Niedermayer * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * common internal and external API header */ #ifndef AVUTIL_COMMON_H #define AVUTIL_COMMON_H #if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) && !defined(UINT64_C) #error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS #endif #include #include #include #include #include #include #include #include #include "attributes.h" #include "macros.h" #include "version.h" #include "libavutil/avconfig.h" #if AV_HAVE_BIGENDIAN # define AV_NE(be, le) (be) #else # define AV_NE(be, le) (le) #endif //rounded division & shift #define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) /* assume b>0 */ #define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) /* Fast a/(1<=0 and b>=0 */ #define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \ : ((a) + (1<<(b)) - 1) >> (b)) /* Backwards compat. */ #define FF_CEIL_RSHIFT AV_CEIL_RSHIFT #define FFUDIV(a,b) (((a)>0 ?(a):(a)-(b)+1) / (b)) #define FFUMOD(a,b) ((a)-(b)*FFUDIV(a,b)) /** * Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they * are not representable as absolute values of their type. This is the same * as with *abs() * @see FFNABS() */ #define FFABS(a) ((a) >= 0 ? (a) : (-(a))) #define FFSIGN(a) ((a) > 0 ? 1 : -1) /** * Negative Absolute value. * this works for all integers of all types. * As with many macros, this evaluates its argument twice, it thus must not have * a sideeffect, that is FFNABS(x++) has undefined behavior. */ #define FFNABS(a) ((a) <= 0 ? (a) : (-(a))) /** * Comparator. * For two numerical expressions x and y, gives 1 if x > y, -1 if x < y, and 0 * if x == y. This is useful for instance in a qsort comparator callback. * Furthermore, compilers are able to optimize this to branchless code, and * there is no risk of overflow with signed types. * As with many macros, this evaluates its argument multiple times, it thus * must not have a side-effect. */ #define FFDIFFSIGN(x,y) (((x)>(y)) - ((x)<(y))) #define FFMAX(a,b) ((a) > (b) ? (a) : (b)) #define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c) #define FFMIN(a,b) ((a) > (b) ? (b) : (a)) #define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c) #define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) #define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) /* misc math functions */ #ifdef HAVE_AV_CONFIG_H # include "config.h" # include "intmath.h" #endif /* Pull in unguarded fallback defines at the end of this file. */ #include "common.h" #ifndef av_log2 av_const int av_log2(unsigned v); #endif #ifndef av_log2_16bit av_const int av_log2_16bit(unsigned v); #endif /** * Clip a signed integer value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const int av_clip_c(int a, int amin, int amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a signed 64bit integer value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a signed integer value into the 0-255 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const uint8_t av_clip_uint8_c(int a) { if (a&(~0xFF)) return (-a)>>31; else return a; } /** * Clip a signed integer value into the -128,127 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int8_t av_clip_int8_c(int a) { if ((a+0x80U) & ~0xFF) return (a>>31) ^ 0x7F; else return a; } /** * Clip a signed integer value into the 0-65535 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const uint16_t av_clip_uint16_c(int a) { if (a&(~0xFFFF)) return (-a)>>31; else return a; } /** * Clip a signed integer value into the -32768,32767 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int16_t av_clip_int16_c(int a) { if ((a+0x8000U) & ~0xFFFF) return (a>>31) ^ 0x7FFF; else return a; } /** * Clip a signed 64-bit integer value into the -2147483648,2147483647 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int32_t av_clipl_int32_c(int64_t a) { if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (int32_t)((a>>63) ^ 0x7FFFFFFF); else return (int32_t)a; } /** * Clip a signed integer into the -(2^p),(2^p-1) range. * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const int av_clip_intp2_c(int a, int p) { if (((unsigned)a + (1 << p)) & ~((2 << p) - 1)) return (a >> 31) ^ ((1 << p) - 1); else return a; } /** * Clip a signed integer to an unsigned power of two range. * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) { if (a & ~((1<> 31 & ((1<= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a double value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const double av_clipd_c(double a, double amin, double amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** Compute ceil(log2(x)). * @param x value used to compute ceil(log2(x)) * @return computed ceiling of log2(x) */ static av_always_inline av_const int av_ceil_log2_c(int x) { return av_log2((x - 1) << 1); } /** * Count number of bits set to one in x * @param x value to count bits of * @return the number of bits set to one in x */ static av_always_inline av_const int av_popcount_c(uint32_t x) { x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; x += x >> 8; return (x + (x >> 16)) & 0x3F; } /** * Count number of bits set to one in x * @param x value to count bits of * @return the number of bits set to one in x */ static av_always_inline av_const int av_popcount64_c(uint64_t x) { return av_popcount((uint32_t)x) + av_popcount((uint32_t)(x >> 32)); } static av_always_inline av_const int av_parity_c(uint32_t v) { return av_popcount(v) & 1; } #define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24)) #define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) /** * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_BYTE Expression reading one byte from the input. * Evaluated up to 7 times (4 for the currently * assigned Unicode range). With a memory buffer * input, this could be *ptr++. * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. * * @warning ERROR should not contain a loop control statement which * could interact with the internal while loop, and should force an * exit from the macro code (e.g. through a goto or a return) in order * to prevent undefined results. */ #define GET_UTF8(val, GET_BYTE, ERROR)\ val= (GET_BYTE);\ {\ uint32_t top = (val & 128) >> 1;\ if ((val & 0xc0) == 0x80 || val >= 0xFE)\ ERROR\ while (val & top) {\ int tmp= (GET_BYTE) - 128;\ if(tmp>>6)\ ERROR\ val= (val<<6) + tmp;\ top <<= 5;\ }\ val &= (top << 1) - 1;\ } /** * Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_16BIT Expression returning two bytes of UTF-16 data converted * to native byte order. Evaluated one or two times. * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. */ #define GET_UTF16(val, GET_16BIT, ERROR)\ val = GET_16BIT;\ {\ unsigned int hi = val - 0xD800;\ if (hi < 0x800) {\ val = GET_16BIT - 0xDC00;\ if (val > 0x3FFU || hi > 0x3FFU)\ ERROR\ val += (hi<<10) + 0x10000;\ }\ }\ /** * @def PUT_UTF8(val, tmp, PUT_BYTE) * Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long). * @param val is an input-only argument and should be of type uint32_t. It holds * a UCS-4 encoded Unicode character that is to be converted to UTF-8. If * val is given as a function it is executed only once. * @param tmp is a temporary variable and should be of type uint8_t. It * represents an intermediate value during conversion that is to be * output by PUT_BYTE. * @param PUT_BYTE writes the converted UTF-8 bytes to any proper destination. * It could be a function or a statement, and uses tmp as the input byte. * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be * executed up to 4 times for values in the valid UTF-8 range and up to * 7 times in the general case, depending on the length of the converted * Unicode character. */ #define PUT_UTF8(val, tmp, PUT_BYTE)\ {\ int bytes, shift;\ uint32_t in = val;\ if (in < 0x80) {\ tmp = in;\ PUT_BYTE\ } else {\ bytes = (av_log2(in) + 4) / 5;\ shift = (bytes - 1) * 6;\ tmp = (256 - (256 >> bytes)) | (in >> shift);\ PUT_BYTE\ while (shift >= 6) {\ shift -= 6;\ tmp = 0x80 | ((in >> shift) & 0x3f);\ PUT_BYTE\ }\ }\ } /** * @def PUT_UTF16(val, tmp, PUT_16BIT) * Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes). * @param val is an input-only argument and should be of type uint32_t. It holds * a UCS-4 encoded Unicode character that is to be converted to UTF-16. If * val is given as a function it is executed only once. * @param tmp is a temporary variable and should be of type uint16_t. It * represents an intermediate value during conversion that is to be * output by PUT_16BIT. * @param PUT_16BIT writes the converted UTF-16 data to any proper destination * in desired endianness. It could be a function or a statement, and uses tmp * as the input byte. For example, PUT_BYTE could be "*output++ = tmp;" * PUT_BYTE will be executed 1 or 2 times depending on input character. */ #define PUT_UTF16(val, tmp, PUT_16BIT)\ {\ uint32_t in = val;\ if (in < 0x10000) {\ tmp = in;\ PUT_16BIT\ } else {\ tmp = 0xD800 | ((in - 0x10000) >> 10);\ PUT_16BIT\ tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\ PUT_16BIT\ }\ }\ #include "mem.h" #ifdef HAVE_AV_CONFIG_H # include "internal.h" #endif /* HAVE_AV_CONFIG_H */ #endif /* AVUTIL_COMMON_H */ /* * The following definitions are outside the multiple inclusion guard * to ensure they are immediately available in intmath.h. */ #ifndef av_ceil_log2 # define av_ceil_log2 av_ceil_log2_c #endif #ifndef av_clip # define av_clip av_clip_c #endif #ifndef av_clip64 # define av_clip64 av_clip64_c #endif #ifndef av_clip_uint8 # define av_clip_uint8 av_clip_uint8_c #endif #ifndef av_clip_int8 # define av_clip_int8 av_clip_int8_c #endif #ifndef av_clip_uint16 # define av_clip_uint16 av_clip_uint16_c #endif #ifndef av_clip_int16 # define av_clip_int16 av_clip_int16_c #endif #ifndef av_clipl_int32 # define av_clipl_int32 av_clipl_int32_c #endif #ifndef av_clip_intp2 # define av_clip_intp2 av_clip_intp2_c #endif #ifndef av_clip_uintp2 # define av_clip_uintp2 av_clip_uintp2_c #endif #ifndef av_mod_uintp2 # define av_mod_uintp2 av_mod_uintp2_c #endif #ifndef av_sat_add32 # define av_sat_add32 av_sat_add32_c #endif #ifndef av_sat_dadd32 # define av_sat_dadd32 av_sat_dadd32_c #endif #ifndef av_clipf # define av_clipf av_clipf_c #endif #ifndef av_clipd # define av_clipd av_clipd_c #endif #ifndef av_popcount # define av_popcount av_popcount_c #endif #ifndef av_popcount64 # define av_popcount64 av_popcount64_c #endif #ifndef av_parity # define av_parity av_parity_c #endif ================================================ FILE: ffmpeg-3.3.3-experimental-patch/common.h ================================================ /* * copyright (c) 2006 Michael Niedermayer * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * common internal and external API header */ #ifndef AVUTIL_COMMON_H #define AVUTIL_COMMON_H #if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) && !defined(UINT64_C) #error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS #endif #include #include #include #include #include #include #include #include #include "attributes.h" #include "macros.h" #include "version.h" #include "libavutil/avconfig.h" #if AV_HAVE_BIGENDIAN # define AV_NE(be, le) (be) #else # define AV_NE(be, le) (le) #endif //rounded division & shift #define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) /* assume b>0 */ #define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) /* Fast a/(1<=0 and b>=0 */ #define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \ : ((a) + (1<<(b)) - 1) >> (b)) /* Backwards compat. */ #define FF_CEIL_RSHIFT AV_CEIL_RSHIFT #define FFUDIV(a,b) (((a)>0 ?(a):(a)-(b)+1) / (b)) #define FFUMOD(a,b) ((a)-(b)*FFUDIV(a,b)) /** * Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they * are not representable as absolute values of their type. This is the same * as with *abs() * @see FFNABS() */ #define FFABS(a) ((a) >= 0 ? (a) : (-(a))) #define FFSIGN(a) ((a) > 0 ? 1 : -1) /** * Negative Absolute value. * this works for all integers of all types. * As with many macros, this evaluates its argument twice, it thus must not have * a sideeffect, that is FFNABS(x++) has undefined behavior. */ #define FFNABS(a) ((a) <= 0 ? (a) : (-(a))) /** * Comparator. * For two numerical expressions x and y, gives 1 if x > y, -1 if x < y, and 0 * if x == y. This is useful for instance in a qsort comparator callback. * Furthermore, compilers are able to optimize this to branchless code, and * there is no risk of overflow with signed types. * As with many macros, this evaluates its argument multiple times, it thus * must not have a side-effect. */ #define FFDIFFSIGN(x,y) (((x)>(y)) - ((x)<(y))) #define FFMAX(a,b) ((a) > (b) ? (a) : (b)) #define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c) #define FFMIN(a,b) ((a) > (b) ? (b) : (a)) #define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c) #define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) #define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) /* misc math functions */ #ifdef HAVE_AV_CONFIG_H # include "config.h" # include "intmath.h" #endif /* Pull in unguarded fallback defines at the end of this file. */ #include "common.h" #ifndef av_log2 av_const int av_log2(unsigned v); #endif #ifndef av_log2_16bit av_const int av_log2_16bit(unsigned v); #endif /** * Clip a signed integer value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const int av_clip_c(int a, int amin, int amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a signed 64bit integer value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a signed integer value into the 0-255 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const uint8_t av_clip_uint8_c(int a) { if (a&(~0xFF)) return (-a)>>31; else return a; } /** * Clip a signed integer value into the -128,127 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int8_t av_clip_int8_c(int a) { if ((a+0x80U) & ~0xFF) return (a>>31) ^ 0x7F; else return a; } /** * Clip a signed integer value into the 0-65535 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const uint16_t av_clip_uint16_c(int a) { if (a&(~0xFFFF)) return (-a)>>31; else return a; } /** * Clip a signed integer value into the -32768,32767 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int16_t av_clip_int16_c(int a) { const int16_t noOverflowCandidate = a; return (noOverflowCandidate == a) ? noOverflowCandidate : ((noOverflowCandidate < a) ? INT16_MAX : INT16_MIN); } /** * Clip a signed 64-bit integer value into the -2147483648,2147483647 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int32_t av_clipl_int32_c(int64_t a) { if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (int32_t)((a>>63) ^ 0x7FFFFFFF); else return (int32_t)a; } /** * Clip a signed integer into the -(2^p),(2^p-1) range. * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const int av_clip_intp2_c(int a, int p) { if (((unsigned)a + (1 << p)) & ~((2 << p) - 1)) return (a >> 31) ^ ((1 << p) - 1); else return a; } /** * Clip a signed integer to an unsigned power of two range. * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) { const unsigned int bits = ((1 << p) - 1); return (((unsigned int)a) <= bits) ? a : ((a < 0) ? 0 : bits); } /** * Clear high bits from an unsigned integer starting with specific bit position * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const unsigned av_mod_uintp2_c(unsigned a, unsigned p) { return a & ((1 << p) - 1); } /** * Add two signed 32-bit values with saturation. * * @param a one value * @param b another value * @return sum with signed saturation */ static av_always_inline int av_sat_add32_c(int a, int b) { return av_clipl_int32((int64_t)a + b); } /** * Add a doubled value to another value with saturation at both stages. * * @param a first value * @param b value doubled and added to a * @return sum with signed saturation */ static av_always_inline int av_sat_dadd32_c(int a, int b) { return av_sat_add32(a, av_sat_add32(b, b)); } /** * Clip a float value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const float av_clipf_c(float a, float amin, float amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a double value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const double av_clipd_c(double a, double amin, double amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** Compute ceil(log2(x)). * @param x value used to compute ceil(log2(x)) * @return computed ceiling of log2(x) */ static av_always_inline av_const int av_ceil_log2_c(int x) { return av_log2((x - 1) << 1); } /** * Count number of bits set to one in x * @param x value to count bits of * @return the number of bits set to one in x */ static av_always_inline av_const int av_popcount_c(uint32_t x) { x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; x += x >> 8; return (x + (x >> 16)) & 0x3F; } /** * Count number of bits set to one in x * @param x value to count bits of * @return the number of bits set to one in x */ static av_always_inline av_const int av_popcount64_c(uint64_t x) { return av_popcount((uint32_t)x) + av_popcount((uint32_t)(x >> 32)); } static av_always_inline av_const int av_parity_c(uint32_t v) { return av_popcount(v) & 1; } #define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24)) #define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) /** * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_BYTE Expression reading one byte from the input. * Evaluated up to 7 times (4 for the currently * assigned Unicode range). With a memory buffer * input, this could be *ptr++. * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. * * @warning ERROR should not contain a loop control statement which * could interact with the internal while loop, and should force an * exit from the macro code (e.g. through a goto or a return) in order * to prevent undefined results. */ #define GET_UTF8(val, GET_BYTE, ERROR)\ val= (GET_BYTE);\ {\ uint32_t top = (val & 128) >> 1;\ if ((val & 0xc0) == 0x80 || val >= 0xFE)\ ERROR\ while (val & top) {\ int tmp= (GET_BYTE) - 128;\ if(tmp>>6)\ ERROR\ val= (val<<6) + tmp;\ top <<= 5;\ }\ val &= (top << 1) - 1;\ } /** * Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_16BIT Expression returning two bytes of UTF-16 data converted * to native byte order. Evaluated one or two times. * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. */ #define GET_UTF16(val, GET_16BIT, ERROR)\ val = GET_16BIT;\ {\ unsigned int hi = val - 0xD800;\ if (hi < 0x800) {\ val = GET_16BIT - 0xDC00;\ if (val > 0x3FFU || hi > 0x3FFU)\ ERROR\ val += (hi<<10) + 0x10000;\ }\ }\ /** * @def PUT_UTF8(val, tmp, PUT_BYTE) * Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long). * @param val is an input-only argument and should be of type uint32_t. It holds * a UCS-4 encoded Unicode character that is to be converted to UTF-8. If * val is given as a function it is executed only once. * @param tmp is a temporary variable and should be of type uint8_t. It * represents an intermediate value during conversion that is to be * output by PUT_BYTE. * @param PUT_BYTE writes the converted UTF-8 bytes to any proper destination. * It could be a function or a statement, and uses tmp as the input byte. * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be * executed up to 4 times for values in the valid UTF-8 range and up to * 7 times in the general case, depending on the length of the converted * Unicode character. */ #define PUT_UTF8(val, tmp, PUT_BYTE)\ {\ int bytes, shift;\ uint32_t in = val;\ if (in < 0x80) {\ tmp = in;\ PUT_BYTE\ } else {\ bytes = (av_log2(in) + 4) / 5;\ shift = (bytes - 1) * 6;\ tmp = (256 - (256 >> bytes)) | (in >> shift);\ PUT_BYTE\ while (shift >= 6) {\ shift -= 6;\ tmp = 0x80 | ((in >> shift) & 0x3f);\ PUT_BYTE\ }\ }\ } /** * @def PUT_UTF16(val, tmp, PUT_16BIT) * Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes). * @param val is an input-only argument and should be of type uint32_t. It holds * a UCS-4 encoded Unicode character that is to be converted to UTF-16. If * val is given as a function it is executed only once. * @param tmp is a temporary variable and should be of type uint16_t. It * represents an intermediate value during conversion that is to be * output by PUT_16BIT. * @param PUT_16BIT writes the converted UTF-16 data to any proper destination * in desired endianness. It could be a function or a statement, and uses tmp * as the input byte. For example, PUT_BYTE could be "*output++ = tmp;" * PUT_BYTE will be executed 1 or 2 times depending on input character. */ #define PUT_UTF16(val, tmp, PUT_16BIT)\ {\ uint32_t in = val;\ if (in < 0x10000) {\ tmp = in;\ PUT_16BIT\ } else {\ tmp = 0xD800 | ((in - 0x10000) >> 10);\ PUT_16BIT\ tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\ PUT_16BIT\ }\ }\ #include "mem.h" #ifdef HAVE_AV_CONFIG_H # include "internal.h" #endif /* HAVE_AV_CONFIG_H */ #endif /* AVUTIL_COMMON_H */ /* * The following definitions are outside the multiple inclusion guard * to ensure they are immediately available in intmath.h. */ #ifndef av_ceil_log2 # define av_ceil_log2 av_ceil_log2_c #endif #ifndef av_clip # define av_clip av_clip_c #endif #ifndef av_clip64 # define av_clip64 av_clip64_c #endif #ifndef av_clip_uint8 # define av_clip_uint8 av_clip_uint8_c #endif #ifndef av_clip_int8 # define av_clip_int8 av_clip_int8_c #endif #ifndef av_clip_uint16 # define av_clip_uint16 av_clip_uint16_c #endif #ifndef av_clip_int16 # define av_clip_int16 av_clip_int16_c #endif #ifndef av_clipl_int32 # define av_clipl_int32 av_clipl_int32_c #endif #ifndef av_clip_intp2 # define av_clip_intp2 av_clip_intp2_c #endif #ifndef av_clip_uintp2 # define av_clip_uintp2 av_clip_uintp2_c #endif #ifndef av_mod_uintp2 # define av_mod_uintp2 av_mod_uintp2_c #endif #ifndef av_sat_add32 # define av_sat_add32 av_sat_add32_c #endif #ifndef av_sat_dadd32 # define av_sat_dadd32 av_sat_dadd32_c #endif #ifndef av_clipf # define av_clipf av_clipf_c #endif #ifndef av_clipd # define av_clipd av_clipd_c #endif #ifndef av_popcount # define av_popcount av_popcount_c #endif #ifndef av_popcount64 # define av_popcount64 av_popcount64_c #endif #ifndef av_parity # define av_parity av_parity_c #endif ================================================ FILE: ffmpeg-3.3.3-experimental-patch/hevcdsp_template.before.c ================================================ /* * HEVC video decoder * * Copyright (C) 2012 - 2013 Guillaume Martres * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "get_bits.h" #include "hevcdec.h" #include "bit_depth_template.c" #include "hevcdsp.h" static void FUNC(put_pcm)(uint8_t *_dst, ptrdiff_t stride, int width, int height, GetBitContext *gb, int pcm_bit_depth) { int x, y; pixel *dst = (pixel *)_dst; stride /= sizeof(pixel); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = get_bits(gb, pcm_bit_depth) << (BIT_DEPTH - pcm_bit_depth); dst += stride; } } static av_always_inline void FUNC(add_residual)(uint8_t *_dst, int16_t *res, ptrdiff_t stride, int size) { int x, y; pixel *dst = (pixel *)_dst; stride /= sizeof(pixel); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { dst[x] = av_clip_pixel(dst[x] + *res); res++; } dst += stride; } } static void FUNC(add_residual4x4)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 4); } static void FUNC(add_residual8x8)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 8); } static void FUNC(add_residual16x16)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 16); } static void FUNC(add_residual32x32)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 32); } static void FUNC(transform_rdpcm)(int16_t *_coeffs, int16_t log2_size, int mode) { int16_t *coeffs = (int16_t *) _coeffs; int x, y; int size = 1 << log2_size; if (mode) { coeffs += size; for (y = 0; y < size - 1; y++) { for (x = 0; x < size; x++) coeffs[x] += coeffs[x - size]; coeffs += size; } } else { for (y = 0; y < size; y++) { for (x = 1; x < size; x++) coeffs[x] += coeffs[x - 1]; coeffs += size; } } } static void FUNC(dequant)(int16_t *coeffs, int16_t log2_size) { int shift = 15 - BIT_DEPTH - log2_size; int x, y; int size = 1 << log2_size; if (shift > 0) { int offset = 1 << (shift - 1); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { *coeffs = (*coeffs + offset) >> shift; coeffs++; } } } else { for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { *coeffs = *coeffs << -shift; coeffs++; } } } } #define SET(dst, x) (dst) = (x) #define SCALE(dst, x) (dst) = av_clip_int16(((x) + add) >> shift) #define TR_4x4_LUMA(dst, src, step, assign) \ do { \ int c0 = src[0 * step] + src[2 * step]; \ int c1 = src[2 * step] + src[3 * step]; \ int c2 = src[0 * step] - src[3 * step]; \ int c3 = 74 * src[1 * step]; \ \ assign(dst[2 * step], 74 * (src[0 * step] - \ src[2 * step] + \ src[3 * step])); \ assign(dst[0 * step], 29 * c0 + 55 * c1 + c3); \ assign(dst[1 * step], 55 * c2 - 29 * c1 + c3); \ assign(dst[3 * step], 55 * c0 + 29 * c2 - c3); \ } while (0) static void FUNC(transform_4x4_luma)(int16_t *coeffs) { int i; int shift = 7; int add = 1 << (shift - 1); int16_t *src = coeffs; for (i = 0; i < 4; i++) { TR_4x4_LUMA(src, src, 4, SCALE); src++; } shift = 20 - BIT_DEPTH; add = 1 << (shift - 1); for (i = 0; i < 4; i++) { TR_4x4_LUMA(coeffs, coeffs, 1, SCALE); coeffs += 4; } } #undef TR_4x4_LUMA #define TR_4(dst, src, dstep, sstep, assign, end) \ do { \ const int e0 = 64 * src[0 * sstep] + 64 * src[2 * sstep]; \ const int e1 = 64 * src[0 * sstep] - 64 * src[2 * sstep]; \ const int o0 = 83 * src[1 * sstep] + 36 * src[3 * sstep]; \ const int o1 = 36 * src[1 * sstep] - 83 * src[3 * sstep]; \ \ assign(dst[0 * dstep], e0 + o0); \ assign(dst[1 * dstep], e1 + o1); \ assign(dst[2 * dstep], e1 - o1); \ assign(dst[3 * dstep], e0 - o0); \ } while (0) #define TR_8(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_8[4]; \ int o_8[4] = { 0 }; \ for (i = 0; i < 4; i++) \ for (j = 1; j < end; j += 2) \ o_8[i] += transform[4 * j][i] * src[j * sstep]; \ TR_4(e_8, src, 1, 2 * sstep, SET, 4); \ \ for (i = 0; i < 4; i++) { \ assign(dst[i * dstep], e_8[i] + o_8[i]); \ assign(dst[(7 - i) * dstep], e_8[i] - o_8[i]); \ } \ } while (0) #define TR_16(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_16[8]; \ int o_16[8] = { 0 }; \ for (i = 0; i < 8; i++) \ for (j = 1; j < end; j += 2) \ o_16[i] += transform[2 * j][i] * src[j * sstep]; \ TR_8(e_16, src, 1, 2 * sstep, SET, 8); \ \ for (i = 0; i < 8; i++) { \ assign(dst[i * dstep], e_16[i] + o_16[i]); \ assign(dst[(15 - i) * dstep], e_16[i] - o_16[i]); \ } \ } while (0) #define TR_32(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_32[16]; \ int o_32[16] = { 0 }; \ for (i = 0; i < 16; i++) \ for (j = 1; j < end; j += 2) \ o_32[i] += transform[j][i] * src[j * sstep]; \ TR_16(e_32, src, 1, 2 * sstep, SET, end / 2); \ \ for (i = 0; i < 16; i++) { \ assign(dst[i * dstep], e_32[i] + o_32[i]); \ assign(dst[(31 - i) * dstep], e_32[i] - o_32[i]); \ } \ } while (0) #define IDCT_VAR4(H) \ int limit2 = FFMIN(col_limit + 4, H) #define IDCT_VAR8(H) \ int limit = FFMIN(col_limit, H); \ int limit2 = FFMIN(col_limit + 4, H) #define IDCT_VAR16(H) IDCT_VAR8(H) #define IDCT_VAR32(H) IDCT_VAR8(H) #define IDCT(H) \ static void FUNC(idct_ ## H ## x ## H )(int16_t *coeffs, \ int col_limit) \ { \ int i; \ int shift = 7; \ int add = 1 << (shift - 1); \ int16_t *src = coeffs; \ IDCT_VAR ## H(H); \ \ for (i = 0; i < H; i++) { \ TR_ ## H(src, src, H, H, SCALE, limit2); \ if (limit2 < H && i%4 == 0 && !!i) \ limit2 -= 4; \ src++; \ } \ \ shift = 20 - BIT_DEPTH; \ add = 1 << (shift - 1); \ for (i = 0; i < H; i++) { \ TR_ ## H(coeffs, coeffs, 1, 1, SCALE, limit); \ coeffs += H; \ } \ } #define IDCT_DC(H) \ static void FUNC(idct_ ## H ## x ## H ## _dc)(int16_t *coeffs) \ { \ int i, j; \ int shift = 14 - BIT_DEPTH; \ int add = 1 << (shift - 1); \ int coeff = (((coeffs[0] + 1) >> 1) + add) >> shift; \ \ for (j = 0; j < H; j++) { \ for (i = 0; i < H; i++) { \ coeffs[i + j * H] = coeff; \ } \ } \ } IDCT( 4) IDCT( 8) IDCT(16) IDCT(32) IDCT_DC( 4) IDCT_DC( 8) IDCT_DC(16) IDCT_DC(32) #undef TR_4 #undef TR_8 #undef TR_16 #undef TR_32 #undef SET #undef SCALE static void FUNC(sao_band_filter)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int16_t *sao_offset_val, int sao_left_class, int width, int height) { pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int offset_table[32] = { 0 }; int k, y, x; int shift = BIT_DEPTH - 5; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); for (k = 0; k < 4; k++) offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_table[src[x] >> shift]); dst += stride_dst; src += stride_src; } } #define CMP(a, b) (((a) > (b)) - ((a) < (b))) static void FUNC(sao_edge_filter)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, int16_t *sao_offset_val, int eo, int width, int height) { static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 }; static const int8_t pos[4][2][2] = { { { -1, 0 }, { 1, 0 } }, // horizontal { { 0, -1 }, { 0, 1 } }, // vertical { { -1, -1 }, { 1, 1 } }, // 45 degree { { 1, -1 }, { -1, 1 } }, // 135 degree }; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int a_stride, b_stride; int x, y; ptrdiff_t stride_src = (2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel); stride_dst /= sizeof(pixel); a_stride = pos[eo][0][0] + pos[eo][0][1] * stride_src; b_stride = pos[eo][1][0] + pos[eo][1][1] * stride_src; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { int diff0 = CMP(src[x], src[x + a_stride]); int diff1 = CMP(src[x], src[x + b_stride]); int offset_val = edge_idx[2 + diff0 + diff1]; dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]); } src += stride_src; dst += stride_dst; } } static void FUNC(sao_edge_restore_0)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, SAOParams *sao, int *borders, int _width, int _height, int c_idx, uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) { int x, y; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int16_t *sao_offset_val = sao->offset_val[c_idx]; int sao_eo_class = sao->eo_class[c_idx]; int init_x = 0, width = _width, height = _height; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); if (sao_eo_class != SAO_EO_VERT) { if (borders[0]) { int offset_val = sao_offset_val[0]; for (y = 0; y < height; y++) { dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); } init_x = 1; } if (borders[2]) { int offset_val = sao_offset_val[0]; int offset = width - 1; for (x = 0; x < height; x++) { dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); } width--; } } if (sao_eo_class != SAO_EO_HORIZ) { if (borders[1]) { int offset_val = sao_offset_val[0]; for (x = init_x; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_val); } if (borders[3]) { int offset_val = sao_offset_val[0]; ptrdiff_t y_stride_dst = stride_dst * (height - 1); ptrdiff_t y_stride_src = stride_src * (height - 1); for (x = init_x; x < width; x++) dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); height--; } } } static void FUNC(sao_edge_restore_1)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, SAOParams *sao, int *borders, int _width, int _height, int c_idx, uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) { int x, y; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int16_t *sao_offset_val = sao->offset_val[c_idx]; int sao_eo_class = sao->eo_class[c_idx]; int init_x = 0, init_y = 0, width = _width, height = _height; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); if (sao_eo_class != SAO_EO_VERT) { if (borders[0]) { int offset_val = sao_offset_val[0]; for (y = 0; y < height; y++) { dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); } init_x = 1; } if (borders[2]) { int offset_val = sao_offset_val[0]; int offset = width - 1; for (x = 0; x < height; x++) { dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); } width--; } } if (sao_eo_class != SAO_EO_HORIZ) { if (borders[1]) { int offset_val = sao_offset_val[0]; for (x = init_x; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_val); init_y = 1; } if (borders[3]) { int offset_val = sao_offset_val[0]; ptrdiff_t y_stride_dst = stride_dst * (height - 1); ptrdiff_t y_stride_src = stride_src * (height - 1); for (x = init_x; x < width; x++) dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); height--; } } { int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1]; int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2]; int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3]; int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3]; // Restore pixels that can't be modified if(vert_edge[0] && sao_eo_class != SAO_EO_VERT) { for(y = init_y+save_upper_left; y< height-save_lower_left; y++) dst[y*stride_dst] = src[y*stride_src]; } if(vert_edge[1] && sao_eo_class != SAO_EO_VERT) { for(y = init_y+save_upper_right; y< height-save_lower_right; y++) dst[y*stride_dst+width-1] = src[y*stride_src+width-1]; } if(horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) { for(x = init_x+save_upper_left; x < width-save_upper_right; x++) dst[x] = src[x]; } if(horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) { for(x = init_x+save_lower_left; x < width-save_lower_right; x++) dst[(height-1)*stride_dst+x] = src[(height-1)*stride_src+x]; } if(diag_edge[0] && sao_eo_class == SAO_EO_135D) dst[0] = src[0]; if(diag_edge[1] && sao_eo_class == SAO_EO_45D) dst[width-1] = src[width-1]; if(diag_edge[2] && sao_eo_class == SAO_EO_135D) dst[stride_dst*(height-1)+width-1] = src[stride_src*(height-1)+width-1]; if(diag_edge[3] && sao_eo_class == SAO_EO_45D) dst[stride_dst*(height-1)] = src[stride_src*(height-1)]; } } #undef CMP //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// static void FUNC(put_hevc_pel_pixels)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = src[x] << (14 - BIT_DEPTH); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_pel_uni_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); for (y = 0; y < height; y++) { memcpy(dst, src, width * sizeof(pixel)); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_pel_bi_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((src[x] << (14 - BIT_DEPTH)) + src2[x] + offset) >> shift); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_pel_uni_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((src[x] << (14 - BIT_DEPTH)) * wx + offset) >> shift) + ox); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_pel_bi_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); } src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// #define QPEL_FILTER(src, stride) \ (filter[0] * src[x - 3 * stride] + \ filter[1] * src[x - 2 * stride] + \ filter[2] * src[x - stride] + \ filter[3] * src[x ] + \ filter[4] * src[x + stride] + \ filter[5] * src[x + 2 * stride] + \ filter[6] * src[x + 3 * stride] + \ filter[7] * src[x + 4 * stride]) static void FUNC(put_hevc_qpel_h)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_v)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_hv)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; tmp += MAX_PB_SIZE; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// #define EPEL_FILTER(src, stride) \ (filter[0] * src[x - stride] + \ filter[1] * src[x] + \ filter[2] * src[x + stride] + \ filter[3] * src[x + 2 * stride]) static void FUNC(put_hevc_epel_h)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_v)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_hv)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; tmp += MAX_PB_SIZE; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_epel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); } dst += dststride; src += srcstride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_epel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); dst += dststride; src += srcstride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel((((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); } dst += dststride; src += srcstride; } } static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel((((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); } dst += dststride; src += srcstride; } } static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } // line zero #define P3 pix[-4 * xstride] #define P2 pix[-3 * xstride] #define P1 pix[-2 * xstride] #define P0 pix[-1 * xstride] #define Q0 pix[0 * xstride] #define Q1 pix[1 * xstride] #define Q2 pix[2 * xstride] #define Q3 pix[3 * xstride] // line three. used only for deblocking decision #define TP3 pix[-4 * xstride + 3 * ystride] #define TP2 pix[-3 * xstride + 3 * ystride] #define TP1 pix[-2 * xstride + 3 * ystride] #define TP0 pix[-1 * xstride + 3 * ystride] #define TQ0 pix[0 * xstride + 3 * ystride] #define TQ1 pix[1 * xstride + 3 * ystride] #define TQ2 pix[2 * xstride + 3 * ystride] #define TQ3 pix[3 * xstride + 3 * ystride] static void FUNC(hevc_loop_filter_luma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q) { int d, j; pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); beta <<= BIT_DEPTH - 8; for (j = 0; j < 2; j++) { const int dp0 = abs(P2 - 2 * P1 + P0); const int dq0 = abs(Q2 - 2 * Q1 + Q0); const int dp3 = abs(TP2 - 2 * TP1 + TP0); const int dq3 = abs(TQ2 - 2 * TQ1 + TQ0); const int d0 = dp0 + dq0; const int d3 = dp3 + dq3; const int tc = _tc[j] << (BIT_DEPTH - 8); const int no_p = _no_p[j]; const int no_q = _no_q[j]; if (d0 + d3 >= beta) { pix += 4 * ystride; continue; } else { const int beta_3 = beta >> 3; const int beta_2 = beta >> 2; const int tc25 = ((tc * 5 + 1) >> 1); if (abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 && abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 && (d0 << 1) < beta_2 && (d3 << 1) < beta_2) { // strong filtering const int tc2 = tc << 1; for (d = 0; d < 4; d++) { const int p3 = P3; const int p2 = P2; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; const int q2 = Q2; const int q3 = Q3; if (!no_p) { P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc2, tc2); P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc2, tc2); } if (!no_q) { Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc2, tc2); Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc2, tc2); } pix += ystride; } } else { // normal filtering int nd_p = 1; int nd_q = 1; const int tc_2 = tc >> 1; if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3)) nd_p = 2; if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3)) nd_q = 2; for (d = 0; d < 4; d++) { const int p2 = P2; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; const int q2 = Q2; int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; if (abs(delta0) < 10 * tc) { delta0 = av_clip(delta0, -tc, tc); if (!no_p) P0 = av_clip_pixel(p0 + delta0); if (!no_q) Q0 = av_clip_pixel(q0 - delta0); if (!no_p && nd_p > 1) { const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2); P1 = av_clip_pixel(p1 + deltap1); } if (!no_q && nd_q > 1) { const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); Q1 = av_clip_pixel(q1 + deltaq1); } } pix += ystride; } } } } } static void FUNC(hevc_loop_filter_chroma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int *_tc, uint8_t *_no_p, uint8_t *_no_q) { int d, j, no_p, no_q; pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); for (j = 0; j < 2; j++) { const int tc = _tc[j] << (BIT_DEPTH - 8); if (tc <= 0) { pix += 4 * ystride; continue; } no_p = _no_p[j]; no_q = _no_q[j]; for (d = 0; d < 4; d++) { int delta0; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc); if (!no_p) P0 = av_clip_pixel(p0 + delta0); if (!no_q) Q0 = av_clip_pixel(q0 - delta0); pix += ystride; } } } static void FUNC(hevc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_chroma)(pix, stride, sizeof(pixel), tc, no_p, no_q); } static void FUNC(hevc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_chroma)(pix, sizeof(pixel), stride, tc, no_p, no_q); } static void FUNC(hevc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_luma)(pix, stride, sizeof(pixel), beta, tc, no_p, no_q); } static void FUNC(hevc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_luma)(pix, sizeof(pixel), stride, beta, tc, no_p, no_q); } #undef P3 #undef P2 #undef P1 #undef P0 #undef Q0 #undef Q1 #undef Q2 #undef Q3 #undef TP3 #undef TP2 #undef TP1 #undef TP0 #undef TQ0 #undef TQ1 #undef TQ2 #undef TQ3 ================================================ FILE: ffmpeg-3.3.3-experimental-patch/hevcdsp_template.c ================================================ /* * HEVC video decoder * * Copyright (C) 2012 - 2013 Guillaume Martres * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef _MSC_VER #include #endif #include "get_bits.h" #include "hevcdec.h" #include "bit_depth_template.c" #include "hevcdsp.h" static void FUNC(put_pcm)(uint8_t *_dst, ptrdiff_t stride, int width, int height, GetBitContext *gb, int pcm_bit_depth) { int x, y; pixel *dst = (pixel *)_dst; stride /= sizeof(pixel); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = get_bits(gb, pcm_bit_depth) << (BIT_DEPTH - pcm_bit_depth); dst += stride; } } static av_always_inline void FUNC(add_residual)(uint8_t *_dst, int16_t *res, ptrdiff_t stride, int size) { int x, y; pixel *dst = (pixel *)_dst; stride /= sizeof(pixel); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { dst[x] = av_clip_pixel(dst[x] + *res); res++; } dst += stride; } } static void FUNC(add_residual4x4)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 4); } static void FUNC(add_residual8x8)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 8); } static void FUNC(add_residual16x16)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 16); } static void FUNC(add_residual32x32)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 32); } static void FUNC(transform_rdpcm)(int16_t *_coeffs, int16_t log2_size, int mode) { int16_t *coeffs = (int16_t *) _coeffs; int x, y; int size = 1 << log2_size; if (mode) { coeffs += size; for (y = 0; y < size - 1; y++) { for (x = 0; x < size; x++) coeffs[x] += coeffs[x - size]; coeffs += size; } } else { for (y = 0; y < size; y++) { for (x = 1; x < size; x++) coeffs[x] += coeffs[x - 1]; coeffs += size; } } } static void FUNC(dequant)(int16_t *coeffs, int16_t log2_size) { int shift = 15 - BIT_DEPTH - log2_size; int x, y; int size = 1 << log2_size; if (shift > 0) { int offset = 1 << (shift - 1); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { *coeffs = (*coeffs + offset) >> shift; coeffs++; } } } else { for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { *coeffs = *coeffs << -shift; coeffs++; } } } } #define SET(dst, x) (dst) = (x) #define SCALE(dst, x) (dst) = av_clip_int16(((x) + add) >> shift) #define TR_4x4_LUMA(dst, src, step, assign) \ do { \ int c0 = src[0 * step] + src[2 * step]; \ int c1 = src[2 * step] + src[3 * step]; \ int c2 = src[0 * step] - src[3 * step]; \ int c3 = 74 * src[1 * step]; \ \ assign(dst[2 * step], 74 * (src[0 * step] - \ src[2 * step] + \ src[3 * step])); \ assign(dst[0 * step], 29 * c0 + 55 * c1 + c3); \ assign(dst[1 * step], 55 * c2 - 29 * c1 + c3); \ assign(dst[3 * step], 55 * c0 + 29 * c2 - c3); \ } while (0) static void FUNC(transform_4x4_luma)(int16_t *coeffs) { int i; int shift = 7; int add = 1 << (shift - 1); int16_t *src = coeffs; for (i = 0; i < 4; i++) { TR_4x4_LUMA(src, src, 4, SCALE); src++; } shift = 20 - BIT_DEPTH; add = 1 << (shift - 1); for (i = 0; i < 4; i++) { TR_4x4_LUMA(coeffs, coeffs, 1, SCALE); coeffs += 4; } } #undef TR_4x4_LUMA #define TR_4(dst, src, dstep, sstep, assign, end) \ do { \ const int e0 = 64 * src[0 * sstep] + 64 * src[2 * sstep]; \ const int e1 = 64 * src[0 * sstep] - 64 * src[2 * sstep]; \ const int o0 = 83 * src[1 * sstep] + 36 * src[3 * sstep]; \ const int o1 = 36 * src[1 * sstep] - 83 * src[3 * sstep]; \ \ assign(dst[0 * dstep], e0 + o0); \ assign(dst[1 * dstep], e1 + o1); \ assign(dst[2 * dstep], e1 - o1); \ assign(dst[3 * dstep], e0 - o0); \ } while (0) #define TR_8(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_8[4]; \ int o_8[4] = { 0 }; \ for (j = 1; j < end; j += 2) \ for (i = 0; i < 4; i++) \ o_8[i] += transform[4 * j][i] * src[j * sstep]; \ TR_4(e_8, src, 1, 2 * sstep, SET, 4); \ \ for (i = 0; i < 4; i++) { \ assign(dst[i * dstep], e_8[i] + o_8[i]); \ assign(dst[(7 - i) * dstep], e_8[i] - o_8[i]); \ } \ } while (0) #ifdef _MSC_VER #define TR_16(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_16[8]; \ int o_16[8]; \ __m128i o_0, o_1; \ o_0 = o_1 = _mm_setzero_si128(); \ for (j = 1; j < end; j += 2) { \ __m128i vhi, vlo; \ const short multiplier = src[j * sstep]; \ __m128i coeffs = _mm_set1_epi16(multiplier); \ __m128i buf = _mm_castpd_si128(_mm_load_sd((const double*) transform[2 * j])); \ buf = _mm_srai_epi16(_mm_unpacklo_epi8(buf, buf), 8); \ vhi = _mm_mulhi_epi16(buf, coeffs); \ vlo = _mm_mullo_epi16(buf, coeffs); \ o_0 = _mm_add_epi32(o_0, _mm_unpacklo_epi16(vlo, vhi)); \ o_1 = _mm_add_epi32(o_1, _mm_unpackhi_epi16(vlo, vhi)); \ } \ ((__m128i*) o_16)[0] = o_0; \ ((__m128i*) o_16)[1] = o_1; \ TR_8(e_16, src, 1, 2 * sstep, SET, 8); \ \ for (i = 0; i < 8; i++) { \ assign(dst[i * dstep], e_16[i] + o_16[i]); \ assign(dst[(15 - i) * dstep], e_16[i] - o_16[i]); \ } \ } while (0) #define TR_32(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_32[16]; \ int o_32[16]; \ __m128i o_0, o_1, o_2, o_3; \ o_0 = o_1 = o_2 = o_3 = _mm_setzero_si128(); \ for (j = 1; j < end; j += 2) { \ __m128i vhi, vlo; \ const short multiplier = src[j * sstep]; \ __m128i coeffs = _mm_set1_epi16(multiplier); \ __m128i buf = _mm_castpd_si128(_mm_load_sd((const double*) transform[j])); \ buf = _mm_srai_epi16(_mm_unpacklo_epi8(buf, buf), 8); \ vhi = _mm_mulhi_epi16(buf, coeffs); \ vlo = _mm_mullo_epi16(buf, coeffs); \ o_0 = _mm_add_epi32(o_0, _mm_unpacklo_epi16(vlo, vhi)); \ o_1 = _mm_add_epi32(o_1, _mm_unpackhi_epi16(vlo, vhi)); \ buf = _mm_castpd_si128(_mm_load_sd((const double*) &transform[j][8])); \ buf = _mm_srai_epi16(_mm_unpacklo_epi8(buf, buf), 8); \ vhi = _mm_mulhi_epi16(buf, coeffs); \ vlo = _mm_mullo_epi16(buf, coeffs); \ o_2 = _mm_add_epi32(o_2, _mm_unpacklo_epi16(vlo, vhi)); \ o_3 = _mm_add_epi32(o_3, _mm_unpackhi_epi16(vlo, vhi)); \ } \ ((__m128i*) o_32)[0] = o_0; \ ((__m128i*) o_32)[1] = o_1; \ ((__m128i*) o_32)[2] = o_2; \ ((__m128i*) o_32)[3] = o_3; \ TR_16(e_32, src, 1, 2 * sstep, SET, end / 2); \ \ for (i = 0; i < 16; i++) { \ assign(dst[i * dstep], e_32[i] + o_32[i]); \ assign(dst[(31 - i) * dstep], e_32[i] - o_32[i]); \ } \ } while (0) #else #define TR_16(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_16[8]; \ int o_16[8] = { 0 }; \ for (j = 1; j < end; j += 2) \ for (i = 0; i < 8; i++) \ o_16[i] += transform[2 * j][i] * src[j * sstep]; \ TR_8(e_16, src, 1, 2 * sstep, SET, 8); \ \ for (i = 0; i < 8; i++) { \ assign(dst[i * dstep], e_16[i] + o_16[i]); \ assign(dst[(15 - i) * dstep], e_16[i] - o_16[i]); \ } \ } while (0) #define TR_32(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_32[16]; \ int o_32[16] = { 0 }; \ for (j = 1; j < end; j += 2) \ for (i = 0; i < 16; i++) \ o_32[i] += transform[j][i] * src[j * sstep]; \ TR_16(e_32, src, 1, 2 * sstep, SET, end / 2); \ \ for (i = 0; i < 16; i++) { \ assign(dst[i * dstep], e_32[i] + o_32[i]); \ assign(dst[(31 - i) * dstep], e_32[i] - o_32[i]); \ } \ } while (0) #endif #define IDCT_VAR4(H) \ int limit2 = FFMIN(col_limit + 4, H) #define IDCT_VAR8(H) \ int limit = FFMIN(col_limit, H); \ int limit2 = FFMIN(col_limit + 4, H) #define IDCT_VAR16(H) IDCT_VAR8(H) #define IDCT_VAR32(H) IDCT_VAR8(H) #define IDCT(H) \ static void FUNC(idct_ ## H ## x ## H )(int16_t *coeffs, \ int col_limit) \ { \ int i; \ int shift = 7; \ int add = 1 << (shift - 1); \ int16_t *src = coeffs; \ IDCT_VAR ## H(H); \ \ for (i = 0; i < H; i++) { \ TR_ ## H(src, src, H, H, SCALE, limit2); \ if (limit2 < H && i%4 == 0 && !!i) \ limit2 -= 4; \ src++; \ } \ \ shift = 20 - BIT_DEPTH; \ add = 1 << (shift - 1); \ for (i = 0; i < H; i++) { \ TR_ ## H(coeffs, coeffs, 1, 1, SCALE, limit); \ coeffs += H; \ } \ } #define IDCT_DC(H) \ static void FUNC(idct_ ## H ## x ## H ## _dc)(int16_t *coeffs) \ { \ int i, j; \ int shift = 14 - BIT_DEPTH; \ int add = 1 << (shift - 1); \ int coeff = (((coeffs[0] + 1) >> 1) + add) >> shift; \ \ for (j = 0; j < H; j++) { \ for (i = 0; i < H; i++) { \ coeffs[i + j * H] = coeff; \ } \ } \ } IDCT( 4) IDCT( 8) IDCT(16) IDCT(32) IDCT_DC( 4) IDCT_DC( 8) IDCT_DC(16) IDCT_DC(32) #undef TR_4 #undef TR_8 #undef TR_16 #undef TR_32 #undef SET #undef SCALE static void FUNC(sao_band_filter)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int16_t *sao_offset_val, int sao_left_class, int width, int height) { pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int offset_table[32] = { 0 }; int k, y, x; int shift = BIT_DEPTH - 5; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); for (k = 0; k < 4; k++) offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_table[src[x] >> shift]); dst += stride_dst; src += stride_src; } } #define CMP(a, b) (((a) > (b)) - ((a) < (b))) static void FUNC(sao_edge_filter)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, int16_t *sao_offset_val, int eo, int width, int height) { static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 }; static const int8_t pos[4][2][2] = { { { -1, 0 }, { 1, 0 } }, // horizontal { { 0, -1 }, { 0, 1 } }, // vertical { { -1, -1 }, { 1, 1 } }, // 45 degree { { 1, -1 }, { -1, 1 } }, // 135 degree }; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int a_stride, b_stride; int x, y; ptrdiff_t stride_src = (2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel); stride_dst /= sizeof(pixel); a_stride = pos[eo][0][0] + pos[eo][0][1] * stride_src; b_stride = pos[eo][1][0] + pos[eo][1][1] * stride_src; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { int diff0 = CMP(src[x], src[x + a_stride]); int diff1 = CMP(src[x], src[x + b_stride]); int offset_val = edge_idx[2 + diff0 + diff1]; dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]); } src += stride_src; dst += stride_dst; } } static void FUNC(sao_edge_restore_0)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, SAOParams *sao, int *borders, int _width, int _height, int c_idx, uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) { int x, y; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int16_t *sao_offset_val = sao->offset_val[c_idx]; int sao_eo_class = sao->eo_class[c_idx]; int init_x = 0, width = _width, height = _height; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); if (sao_eo_class != SAO_EO_VERT) { if (borders[0]) { int offset_val = sao_offset_val[0]; for (y = 0; y < height; y++) { dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); } init_x = 1; } if (borders[2]) { int offset_val = sao_offset_val[0]; int offset = width - 1; for (x = 0; x < height; x++) { dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); } width--; } } if (sao_eo_class != SAO_EO_HORIZ) { if (borders[1]) { int offset_val = sao_offset_val[0]; for (x = init_x; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_val); } if (borders[3]) { int offset_val = sao_offset_val[0]; ptrdiff_t y_stride_dst = stride_dst * (height - 1); ptrdiff_t y_stride_src = stride_src * (height - 1); for (x = init_x; x < width; x++) dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); height--; } } } static void FUNC(sao_edge_restore_1)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, SAOParams *sao, int *borders, int _width, int _height, int c_idx, uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) { int x, y; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int16_t *sao_offset_val = sao->offset_val[c_idx]; int sao_eo_class = sao->eo_class[c_idx]; int init_x = 0, init_y = 0, width = _width, height = _height; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); if (sao_eo_class != SAO_EO_VERT) { if (borders[0]) { int offset_val = sao_offset_val[0]; for (y = 0; y < height; y++) { dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); } init_x = 1; } if (borders[2]) { int offset_val = sao_offset_val[0]; int offset = width - 1; for (x = 0; x < height; x++) { dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); } width--; } } if (sao_eo_class != SAO_EO_HORIZ) { if (borders[1]) { int offset_val = sao_offset_val[0]; for (x = init_x; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_val); init_y = 1; } if (borders[3]) { int offset_val = sao_offset_val[0]; ptrdiff_t y_stride_dst = stride_dst * (height - 1); ptrdiff_t y_stride_src = stride_src * (height - 1); for (x = init_x; x < width; x++) dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); height--; } } { int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1]; int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2]; int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3]; int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3]; // Restore pixels that can't be modified if(vert_edge[0] && sao_eo_class != SAO_EO_VERT) { for(y = init_y+save_upper_left; y< height-save_lower_left; y++) dst[y*stride_dst] = src[y*stride_src]; } if(vert_edge[1] && sao_eo_class != SAO_EO_VERT) { for(y = init_y+save_upper_right; y< height-save_lower_right; y++) dst[y*stride_dst+width-1] = src[y*stride_src+width-1]; } if(horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) { for(x = init_x+save_upper_left; x < width-save_upper_right; x++) dst[x] = src[x]; } if(horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) { for(x = init_x+save_lower_left; x < width-save_lower_right; x++) dst[(height-1)*stride_dst+x] = src[(height-1)*stride_src+x]; } if(diag_edge[0] && sao_eo_class == SAO_EO_135D) dst[0] = src[0]; if(diag_edge[1] && sao_eo_class == SAO_EO_45D) dst[width-1] = src[width-1]; if(diag_edge[2] && sao_eo_class == SAO_EO_135D) dst[stride_dst*(height-1)+width-1] = src[stride_src*(height-1)+width-1]; if(diag_edge[3] && sao_eo_class == SAO_EO_45D) dst[stride_dst*(height-1)] = src[stride_src*(height-1)]; } } #undef CMP //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// static void FUNC(put_hevc_pel_pixels)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = src[x] << (14 - BIT_DEPTH); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_pel_uni_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); for (y = 0; y < height; y++) { memcpy(dst, src, width * sizeof(pixel)); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_pel_bi_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((src[x] << (14 - BIT_DEPTH)) + src2[x] + offset) >> shift); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_pel_uni_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((src[x] << (14 - BIT_DEPTH)) * wx + offset) >> shift) + ox); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_pel_bi_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift; int bias = ((ox0 + ox1) * (1 << (BIT_DEPTH - 8)) + 1) << (log2Wd - 1); wx1 <<= (14 - BIT_DEPTH); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel(( src[x] * wx1 + src2[x] * wx0 + bias) >> log2Wd); } src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// #define QPEL_FILTER_0(src, stride) \ (-1 * src[x - 3 * stride] + \ 4 * src[x - 2 * stride] + \ -10 * src[x - stride] + \ 58 * src[x] + \ 17 * src[x + stride] + \ -5 * src[x + 2 * stride] + \ 1 * src[x + 3 * stride]) #define QPEL_FILTER_1(src, stride) \ (-1 * src[x - 3 * stride] + \ 4 * src[x - 2 * stride] + \ -11 * src[x - stride] + \ 40 * src[x] + \ 40 * src[x + stride] + \ -11 * src[x + 2 * stride] + \ 4 * src[x + 3 * stride] + \ -1 * src[x + 4 * stride]) #define QPEL_FILTER_2(src, stride) \ (1 * src[x - 2 * stride] + \ -5 * src[x - stride] + \ 17 * src[x] + \ 58 * src[x + stride] + \ -10 * src[x + 2 * stride] + \ 4 * src[x + 3 * stride] + \ -1 * src[x + 4 * stride]) #define QPEL_MACRO_DISPATCH(idx, macro) \ if ((idx) < 1) { macro(QPEL_FILTER_0) } \ else if ((idx) == 1) { macro(QPEL_FILTER_1) } \ else { macro(QPEL_FILTER_2) } static void FUNC(put_hevc_qpel_h)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ dst += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_v)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); \ src += srcstride; \ dst += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_hv)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; \ tmp += MAX_PB_SIZE; \ dst += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); \ src += srcstride; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); \ src += srcstride; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); \ src += srcstride; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); \ src += srcstride; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); \ tmp += MAX_PB_SIZE; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift); \ tmp += MAX_PB_SIZE; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel((((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); \ src += srcstride; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + \ ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); \ src += srcstride; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel((((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); \ src += srcstride; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + \ ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); \ src += srcstride; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; ox = ox * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel((((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); \ tmp += MAX_PB_SIZE; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 + \ ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); \ tmp += MAX_PB_SIZE; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// #define EPEL_FILTER(src, stride) \ (filter[0] * src[x - stride] + \ filter[1] * src[x] + \ filter[2] * src[x + stride] + \ filter[3] * src[x + 2 * stride]) static void FUNC(put_hevc_epel_h)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_v)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_hv)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; tmp += MAX_PB_SIZE; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_epel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); } dst += dststride; src += srcstride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_epel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); dst += dststride; src += srcstride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel((((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); } dst += dststride; src += srcstride; } } static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel((((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); } dst += dststride; src += srcstride; } } static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) << log2Wd)) >> (log2Wd + 1)); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } // line zero #define P3 pix[-4 * xstride] #define P2 pix[-3 * xstride] #define P1 pix[-2 * xstride] #define P0 pix[-1 * xstride] #define Q0 pix[0 * xstride] #define Q1 pix[1 * xstride] #define Q2 pix[2 * xstride] #define Q3 pix[3 * xstride] // line three. used only for deblocking decision #define TP3 pix[-4 * xstride + 3 * ystride] #define TP2 pix[-3 * xstride + 3 * ystride] #define TP1 pix[-2 * xstride + 3 * ystride] #define TP0 pix[-1 * xstride + 3 * ystride] #define TQ0 pix[0 * xstride + 3 * ystride] #define TQ1 pix[1 * xstride + 3 * ystride] #define TQ2 pix[2 * xstride + 3 * ystride] #define TQ3 pix[3 * xstride + 3 * ystride] static void FUNC(hevc_loop_filter_luma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q) { int d, j; pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); beta <<= BIT_DEPTH - 8; for (j = 0; j < 2; j++) { const int dp0 = abs(P2 - 2 * P1 + P0); const int dq0 = abs(Q2 - 2 * Q1 + Q0); const int dp3 = abs(TP2 - 2 * TP1 + TP0); const int dq3 = abs(TQ2 - 2 * TQ1 + TQ0); const int d0 = dp0 + dq0; const int d3 = dp3 + dq3; const int tc = _tc[j] << (BIT_DEPTH - 8); const int no_p = _no_p[j]; const int no_q = _no_q[j]; if (d0 + d3 >= beta) { pix += 4 * ystride; continue; } else { const int beta_3 = beta >> 3; const int beta_2 = beta >> 2; const int tc25 = ((tc * 5 + 1) >> 1); if (abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 && abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 && (d0 << 1) < beta_2 && (d3 << 1) < beta_2) { // strong filtering const int tc2 = tc << 1; for (d = 0; d < 4; d++) { const int p3 = P3; const int p2 = P2; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; const int q2 = Q2; const int q3 = Q3; if (!no_p) { P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc2, tc2); P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc2, tc2); } if (!no_q) { Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc2, tc2); Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc2, tc2); } pix += ystride; } } else { // normal filtering int nd_p = 1; int nd_q = 1; const int tc_2 = tc >> 1; if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3)) nd_p = 2; if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3)) nd_q = 2; for (d = 0; d < 4; d++) { const int p2 = P2; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; const int q2 = Q2; int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; if (abs(delta0) < 10 * tc) { delta0 = av_clip(delta0, -tc, tc); if (!no_p) P0 = av_clip_pixel(p0 + delta0); if (!no_q) Q0 = av_clip_pixel(q0 - delta0); if (!no_p && nd_p > 1) { const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2); P1 = av_clip_pixel(p1 + deltap1); } if (!no_q && nd_q > 1) { const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); Q1 = av_clip_pixel(q1 + deltaq1); } } pix += ystride; } } } } } static void FUNC(hevc_loop_filter_chroma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int *_tc, uint8_t *_no_p, uint8_t *_no_q) { int d, j, no_p, no_q; pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); for (j = 0; j < 2; j++) { const int tc = _tc[j] << (BIT_DEPTH - 8); if (tc <= 0) { pix += 4 * ystride; continue; } no_p = _no_p[j]; no_q = _no_q[j]; for (d = 0; d < 4; d++) { int delta0; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc); if (!no_p) P0 = av_clip_pixel(p0 + delta0); if (!no_q) Q0 = av_clip_pixel(q0 - delta0); pix += ystride; } } } static void FUNC(hevc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_chroma)(pix, stride, sizeof(pixel), tc, no_p, no_q); } static void FUNC(hevc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_chroma)(pix, sizeof(pixel), stride, tc, no_p, no_q); } static void FUNC(hevc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_luma)(pix, stride, sizeof(pixel), beta, tc, no_p, no_q); } static void FUNC(hevc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_luma)(pix, sizeof(pixel), stride, beta, tc, no_p, no_q); } #undef P3 #undef P2 #undef P1 #undef P0 #undef Q0 #undef Q1 #undef Q2 #undef Q3 #undef TP3 #undef TP2 #undef TP1 #undef TP0 #undef TQ0 #undef TQ1 #undef TQ2 #undef TQ3 ================================================ FILE: ffmpeg-4.3.2-experimental-patch/common.before.h ================================================ /* * copyright (c) 2006 Michael Niedermayer * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * common internal and external API header */ #ifndef AVUTIL_COMMON_H #define AVUTIL_COMMON_H #if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) && !defined(UINT64_C) #error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS #endif #include #include #include #include #include #include #include #include #include "attributes.h" #include "macros.h" #include "version.h" #include "libavutil/avconfig.h" #if AV_HAVE_BIGENDIAN # define AV_NE(be, le) (be) #else # define AV_NE(be, le) (le) #endif //rounded division & shift #define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) /* assume b>0 */ #define ROUNDED_DIV(a,b) (((a)>=0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) /* Fast a/(1<=0 and b>=0 */ #define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \ : ((a) + (1<<(b)) - 1) >> (b)) /* Backwards compat. */ #define FF_CEIL_RSHIFT AV_CEIL_RSHIFT #define FFUDIV(a,b) (((a)>0 ?(a):(a)-(b)+1) / (b)) #define FFUMOD(a,b) ((a)-(b)*FFUDIV(a,b)) /** * Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they * are not representable as absolute values of their type. This is the same * as with *abs() * @see FFNABS() */ #define FFABS(a) ((a) >= 0 ? (a) : (-(a))) #define FFSIGN(a) ((a) > 0 ? 1 : -1) /** * Negative Absolute value. * this works for all integers of all types. * As with many macros, this evaluates its argument twice, it thus must not have * a sideeffect, that is FFNABS(x++) has undefined behavior. */ #define FFNABS(a) ((a) <= 0 ? (a) : (-(a))) /** * Comparator. * For two numerical expressions x and y, gives 1 if x > y, -1 if x < y, and 0 * if x == y. This is useful for instance in a qsort comparator callback. * Furthermore, compilers are able to optimize this to branchless code, and * there is no risk of overflow with signed types. * As with many macros, this evaluates its argument multiple times, it thus * must not have a side-effect. */ #define FFDIFFSIGN(x,y) (((x)>(y)) - ((x)<(y))) #define FFMAX(a,b) ((a) > (b) ? (a) : (b)) #define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c) #define FFMIN(a,b) ((a) > (b) ? (b) : (a)) #define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c) #define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) #define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) /* misc math functions */ #ifdef HAVE_AV_CONFIG_H # include "config.h" # include "intmath.h" #endif /* Pull in unguarded fallback defines at the end of this file. */ #include "common.h" #ifndef av_log2 av_const int av_log2(unsigned v); #endif #ifndef av_log2_16bit av_const int av_log2_16bit(unsigned v); #endif /** * Clip a signed integer value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const int av_clip_c(int a, int amin, int amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a signed 64bit integer value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a signed integer value into the 0-255 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const uint8_t av_clip_uint8_c(int a) { if (a&(~0xFF)) return (~a)>>31; else return a; } /** * Clip a signed integer value into the -128,127 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int8_t av_clip_int8_c(int a) { if ((a+0x80U) & ~0xFF) return (a>>31) ^ 0x7F; else return a; } /** * Clip a signed integer value into the 0-65535 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const uint16_t av_clip_uint16_c(int a) { if (a&(~0xFFFF)) return (~a)>>31; else return a; } /** * Clip a signed integer value into the -32768,32767 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int16_t av_clip_int16_c(int a) { if ((a+0x8000U) & ~0xFFFF) return (a>>31) ^ 0x7FFF; else return a; } /** * Clip a signed 64-bit integer value into the -2147483648,2147483647 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int32_t av_clipl_int32_c(int64_t a) { if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (int32_t)((a>>63) ^ 0x7FFFFFFF); else return (int32_t)a; } /** * Clip a signed integer into the -(2^p),(2^p-1) range. * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const int av_clip_intp2_c(int a, int p) { if (((unsigned)a + (1 << p)) & ~((2 << p) - 1)) return (a >> 31) ^ ((1 << p) - 1); else return a; } /** * Clip a signed integer to an unsigned power of two range. * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) { if (a & ~((1<> 31 & ((1<= 0 && a >= INT64_MAX - b) return INT64_MAX; if (b <= 0 && a <= INT64_MIN - b) return INT64_MIN; return a + b; #endif } /** * Subtract two signed 64-bit values with saturation. * * @param a one value * @param b another value * @return difference with signed saturation */ static av_always_inline int64_t av_sat_sub64_c(int64_t a, int64_t b) { #if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_sub_overflow) int64_t tmp; return !__builtin_sub_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN); #else if (b <= 0 && a >= INT64_MAX + b) return INT64_MAX; if (b >= 0 && a <= INT64_MIN + b) return INT64_MIN; return a - b; #endif } /** * Clip a float value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const float av_clipf_c(float a, float amin, float amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a double value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const double av_clipd_c(double a, double amin, double amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** Compute ceil(log2(x)). * @param x value used to compute ceil(log2(x)) * @return computed ceiling of log2(x) */ static av_always_inline av_const int av_ceil_log2_c(int x) { return av_log2((x - 1U) << 1); } /** * Count number of bits set to one in x * @param x value to count bits of * @return the number of bits set to one in x */ static av_always_inline av_const int av_popcount_c(uint32_t x) { x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; x += x >> 8; return (x + (x >> 16)) & 0x3F; } /** * Count number of bits set to one in x * @param x value to count bits of * @return the number of bits set to one in x */ static av_always_inline av_const int av_popcount64_c(uint64_t x) { return av_popcount((uint32_t)x) + av_popcount((uint32_t)(x >> 32)); } static av_always_inline av_const int av_parity_c(uint32_t v) { return av_popcount(v) & 1; } #define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24)) #define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) /** * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_BYTE Expression reading one byte from the input. * Evaluated up to 7 times (4 for the currently * assigned Unicode range). With a memory buffer * input, this could be *ptr++, or if you want to make sure * that *ptr stops at the end of a NULL terminated string then * *ptr ? *ptr++ : 0 * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. * * @warning ERROR should not contain a loop control statement which * could interact with the internal while loop, and should force an * exit from the macro code (e.g. through a goto or a return) in order * to prevent undefined results. */ #define GET_UTF8(val, GET_BYTE, ERROR)\ val= (GET_BYTE);\ {\ uint32_t top = (val & 128) >> 1;\ if ((val & 0xc0) == 0x80 || val >= 0xFE)\ {ERROR}\ while (val & top) {\ unsigned int tmp = (GET_BYTE) - 128;\ if(tmp>>6)\ {ERROR}\ val= (val<<6) + tmp;\ top <<= 5;\ }\ val &= (top << 1) - 1;\ } /** * Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_16BIT Expression returning two bytes of UTF-16 data converted * to native byte order. Evaluated one or two times. * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. */ #define GET_UTF16(val, GET_16BIT, ERROR)\ val = (GET_16BIT);\ {\ unsigned int hi = val - 0xD800;\ if (hi < 0x800) {\ val = (GET_16BIT) - 0xDC00;\ if (val > 0x3FFU || hi > 0x3FFU)\ {ERROR}\ val += (hi<<10) + 0x10000;\ }\ }\ /** * @def PUT_UTF8(val, tmp, PUT_BYTE) * Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long). * @param val is an input-only argument and should be of type uint32_t. It holds * a UCS-4 encoded Unicode character that is to be converted to UTF-8. If * val is given as a function it is executed only once. * @param tmp is a temporary variable and should be of type uint8_t. It * represents an intermediate value during conversion that is to be * output by PUT_BYTE. * @param PUT_BYTE writes the converted UTF-8 bytes to any proper destination. * It could be a function or a statement, and uses tmp as the input byte. * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be * executed up to 4 times for values in the valid UTF-8 range and up to * 7 times in the general case, depending on the length of the converted * Unicode character. */ #define PUT_UTF8(val, tmp, PUT_BYTE)\ {\ int bytes, shift;\ uint32_t in = val;\ if (in < 0x80) {\ tmp = in;\ PUT_BYTE\ } else {\ bytes = (av_log2(in) + 4) / 5;\ shift = (bytes - 1) * 6;\ tmp = (256 - (256 >> bytes)) | (in >> shift);\ PUT_BYTE\ while (shift >= 6) {\ shift -= 6;\ tmp = 0x80 | ((in >> shift) & 0x3f);\ PUT_BYTE\ }\ }\ } /** * @def PUT_UTF16(val, tmp, PUT_16BIT) * Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes). * @param val is an input-only argument and should be of type uint32_t. It holds * a UCS-4 encoded Unicode character that is to be converted to UTF-16. If * val is given as a function it is executed only once. * @param tmp is a temporary variable and should be of type uint16_t. It * represents an intermediate value during conversion that is to be * output by PUT_16BIT. * @param PUT_16BIT writes the converted UTF-16 data to any proper destination * in desired endianness. It could be a function or a statement, and uses tmp * as the input byte. For example, PUT_BYTE could be "*output++ = tmp;" * PUT_BYTE will be executed 1 or 2 times depending on input character. */ #define PUT_UTF16(val, tmp, PUT_16BIT)\ {\ uint32_t in = val;\ if (in < 0x10000) {\ tmp = in;\ PUT_16BIT\ } else {\ tmp = 0xD800 | ((in - 0x10000) >> 10);\ PUT_16BIT\ tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\ PUT_16BIT\ }\ }\ #include "mem.h" #ifdef HAVE_AV_CONFIG_H # include "internal.h" #endif /* HAVE_AV_CONFIG_H */ #endif /* AVUTIL_COMMON_H */ /* * The following definitions are outside the multiple inclusion guard * to ensure they are immediately available in intmath.h. */ #ifndef av_ceil_log2 # define av_ceil_log2 av_ceil_log2_c #endif #ifndef av_clip # define av_clip av_clip_c #endif #ifndef av_clip64 # define av_clip64 av_clip64_c #endif #ifndef av_clip_uint8 # define av_clip_uint8 av_clip_uint8_c #endif #ifndef av_clip_int8 # define av_clip_int8 av_clip_int8_c #endif #ifndef av_clip_uint16 # define av_clip_uint16 av_clip_uint16_c #endif #ifndef av_clip_int16 # define av_clip_int16 av_clip_int16_c #endif #ifndef av_clipl_int32 # define av_clipl_int32 av_clipl_int32_c #endif #ifndef av_clip_intp2 # define av_clip_intp2 av_clip_intp2_c #endif #ifndef av_clip_uintp2 # define av_clip_uintp2 av_clip_uintp2_c #endif #ifndef av_mod_uintp2 # define av_mod_uintp2 av_mod_uintp2_c #endif #ifndef av_sat_add32 # define av_sat_add32 av_sat_add32_c #endif #ifndef av_sat_dadd32 # define av_sat_dadd32 av_sat_dadd32_c #endif #ifndef av_sat_sub32 # define av_sat_sub32 av_sat_sub32_c #endif #ifndef av_sat_dsub32 # define av_sat_dsub32 av_sat_dsub32_c #endif #ifndef av_sat_add64 # define av_sat_add64 av_sat_add64_c #endif #ifndef av_sat_sub64 # define av_sat_sub64 av_sat_sub64_c #endif #ifndef av_clipf # define av_clipf av_clipf_c #endif #ifndef av_clipd # define av_clipd av_clipd_c #endif #ifndef av_popcount # define av_popcount av_popcount_c #endif #ifndef av_popcount64 # define av_popcount64 av_popcount64_c #endif #ifndef av_parity # define av_parity av_parity_c #endif ================================================ FILE: ffmpeg-4.3.2-experimental-patch/common.h ================================================ /* * copyright (c) 2006 Michael Niedermayer * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * common internal and external API header */ #ifndef AVUTIL_COMMON_H #define AVUTIL_COMMON_H #if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) && !defined(UINT64_C) #error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS #endif #include #include #include #include #include #include #include #include #include "attributes.h" #include "macros.h" #include "version.h" #include "libavutil/avconfig.h" #if AV_HAVE_BIGENDIAN # define AV_NE(be, le) (be) #else # define AV_NE(be, le) (le) #endif //rounded division & shift #define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) /* assume b>0 */ #define ROUNDED_DIV(a,b) (((a)>=0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) /* Fast a/(1<=0 and b>=0 */ #define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \ : ((a) + (1<<(b)) - 1) >> (b)) /* Backwards compat. */ #define FF_CEIL_RSHIFT AV_CEIL_RSHIFT #define FFUDIV(a,b) (((a)>0 ?(a):(a)-(b)+1) / (b)) #define FFUMOD(a,b) ((a)-(b)*FFUDIV(a,b)) /** * Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they * are not representable as absolute values of their type. This is the same * as with *abs() * @see FFNABS() */ #define FFABS(a) ((a) >= 0 ? (a) : (-(a))) #define FFSIGN(a) ((a) > 0 ? 1 : -1) /** * Negative Absolute value. * this works for all integers of all types. * As with many macros, this evaluates its argument twice, it thus must not have * a sideeffect, that is FFNABS(x++) has undefined behavior. */ #define FFNABS(a) ((a) <= 0 ? (a) : (-(a))) /** * Comparator. * For two numerical expressions x and y, gives 1 if x > y, -1 if x < y, and 0 * if x == y. This is useful for instance in a qsort comparator callback. * Furthermore, compilers are able to optimize this to branchless code, and * there is no risk of overflow with signed types. * As with many macros, this evaluates its argument multiple times, it thus * must not have a side-effect. */ #define FFDIFFSIGN(x,y) (((x)>(y)) - ((x)<(y))) #define FFMAX(a,b) ((a) > (b) ? (a) : (b)) #define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c) #define FFMIN(a,b) ((a) > (b) ? (b) : (a)) #define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c) #define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) #define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) /* misc math functions */ #ifdef HAVE_AV_CONFIG_H # include "config.h" # include "intmath.h" #endif /* Pull in unguarded fallback defines at the end of this file. */ #include "common.h" #ifndef av_log2 av_const int av_log2(unsigned v); #endif #ifndef av_log2_16bit av_const int av_log2_16bit(unsigned v); #endif /** * Clip a signed integer value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const int av_clip_c(int a, int amin, int amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a signed 64bit integer value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a signed integer value into the 0-255 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const uint8_t av_clip_uint8_c(int a) { if (a&(~0xFF)) return (~a)>>31; else return a; } /** * Clip a signed integer value into the -128,127 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int8_t av_clip_int8_c(int a) { if ((a+0x80U) & ~0xFF) return (a>>31) ^ 0x7F; else return a; } /** * Clip a signed integer value into the 0-65535 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const uint16_t av_clip_uint16_c(int a) { if (a&(~0xFFFF)) return (~a)>>31; else return a; } /** * Clip a signed integer value into the -32768,32767 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int16_t av_clip_int16_c(int a) { const int16_t noOverflowCandidate = a; return (noOverflowCandidate == a) ? noOverflowCandidate : ((noOverflowCandidate < a) ? INT16_MAX : INT16_MIN); } /** * Clip a signed 64-bit integer value into the -2147483648,2147483647 range. * @param a value to clip * @return clipped value */ static av_always_inline av_const int32_t av_clipl_int32_c(int64_t a) { if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (int32_t)((a>>63) ^ 0x7FFFFFFF); else return (int32_t)a; } /** * Clip a signed integer into the -(2^p),(2^p-1) range. * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const int av_clip_intp2_c(int a, int p) { if (((unsigned)a + (1 << p)) & ~((2 << p) - 1)) return (a >> 31) ^ ((1 << p) - 1); else return a; } /** * Clip a signed integer to an unsigned power of two range. * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) { const unsigned int bits = ((1 << p) - 1); return (((unsigned int)a) <= bits) ? a : ((a < 0) ? 0 : bits); } /** * Clear high bits from an unsigned integer starting with specific bit position * @param a value to clip * @param p bit position to clip at * @return clipped value */ static av_always_inline av_const unsigned av_mod_uintp2_c(unsigned a, unsigned p) { return a & ((1U << p) - 1); } /** * Add two signed 32-bit values with saturation. * * @param a one value * @param b another value * @return sum with signed saturation */ static av_always_inline int av_sat_add32_c(int a, int b) { return av_clipl_int32((int64_t)a + b); } /** * Add a doubled value to another value with saturation at both stages. * * @param a first value * @param b value doubled and added to a * @return sum sat(a + sat(2*b)) with signed saturation */ static av_always_inline int av_sat_dadd32_c(int a, int b) { return av_sat_add32(a, av_sat_add32(b, b)); } /** * Subtract two signed 32-bit values with saturation. * * @param a one value * @param b another value * @return difference with signed saturation */ static av_always_inline int av_sat_sub32_c(int a, int b) { return av_clipl_int32((int64_t)a - b); } /** * Subtract a doubled value from another value with saturation at both stages. * * @param a first value * @param b value doubled and subtracted from a * @return difference sat(a - sat(2*b)) with signed saturation */ static av_always_inline int av_sat_dsub32_c(int a, int b) { return av_sat_sub32(a, av_sat_add32(b, b)); } /** * Add two signed 64-bit values with saturation. * * @param a one value * @param b another value * @return sum with signed saturation */ static av_always_inline int64_t av_sat_add64_c(int64_t a, int64_t b) { #if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_add_overflow) int64_t tmp; return !__builtin_add_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN); #else if (b >= 0 && a >= INT64_MAX - b) return INT64_MAX; if (b <= 0 && a <= INT64_MIN - b) return INT64_MIN; return a + b; #endif } /** * Subtract two signed 64-bit values with saturation. * * @param a one value * @param b another value * @return difference with signed saturation */ static av_always_inline int64_t av_sat_sub64_c(int64_t a, int64_t b) { #if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_sub_overflow) int64_t tmp; return !__builtin_sub_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN); #else if (b <= 0 && a >= INT64_MAX + b) return INT64_MAX; if (b >= 0 && a <= INT64_MIN + b) return INT64_MIN; return a - b; #endif } /** * Clip a float value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const float av_clipf_c(float a, float amin, float amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** * Clip a double value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static av_always_inline av_const double av_clipd_c(double a, double amin, double amax) { #if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 if (amin > amax) abort(); #endif if (a < amin) return amin; else if (a > amax) return amax; else return a; } /** Compute ceil(log2(x)). * @param x value used to compute ceil(log2(x)) * @return computed ceiling of log2(x) */ static av_always_inline av_const int av_ceil_log2_c(int x) { return av_log2((x - 1U) << 1); } /** * Count number of bits set to one in x * @param x value to count bits of * @return the number of bits set to one in x */ static av_always_inline av_const int av_popcount_c(uint32_t x) { x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; x += x >> 8; return (x + (x >> 16)) & 0x3F; } /** * Count number of bits set to one in x * @param x value to count bits of * @return the number of bits set to one in x */ static av_always_inline av_const int av_popcount64_c(uint64_t x) { return av_popcount((uint32_t)x) + av_popcount((uint32_t)(x >> 32)); } static av_always_inline av_const int av_parity_c(uint32_t v) { return av_popcount(v) & 1; } #define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24)) #define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) /** * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_BYTE Expression reading one byte from the input. * Evaluated up to 7 times (4 for the currently * assigned Unicode range). With a memory buffer * input, this could be *ptr++, or if you want to make sure * that *ptr stops at the end of a NULL terminated string then * *ptr ? *ptr++ : 0 * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. * * @warning ERROR should not contain a loop control statement which * could interact with the internal while loop, and should force an * exit from the macro code (e.g. through a goto or a return) in order * to prevent undefined results. */ #define GET_UTF8(val, GET_BYTE, ERROR)\ val= (GET_BYTE);\ {\ uint32_t top = (val & 128) >> 1;\ if ((val & 0xc0) == 0x80 || val >= 0xFE)\ {ERROR}\ while (val & top) {\ unsigned int tmp = (GET_BYTE) - 128;\ if(tmp>>6)\ {ERROR}\ val= (val<<6) + tmp;\ top <<= 5;\ }\ val &= (top << 1) - 1;\ } /** * Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form. * * @param val Output value, must be an lvalue of type uint32_t. * @param GET_16BIT Expression returning two bytes of UTF-16 data converted * to native byte order. Evaluated one or two times. * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. */ #define GET_UTF16(val, GET_16BIT, ERROR)\ val = (GET_16BIT);\ {\ unsigned int hi = val - 0xD800;\ if (hi < 0x800) {\ val = (GET_16BIT) - 0xDC00;\ if (val > 0x3FFU || hi > 0x3FFU)\ {ERROR}\ val += (hi<<10) + 0x10000;\ }\ }\ /** * @def PUT_UTF8(val, tmp, PUT_BYTE) * Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long). * @param val is an input-only argument and should be of type uint32_t. It holds * a UCS-4 encoded Unicode character that is to be converted to UTF-8. If * val is given as a function it is executed only once. * @param tmp is a temporary variable and should be of type uint8_t. It * represents an intermediate value during conversion that is to be * output by PUT_BYTE. * @param PUT_BYTE writes the converted UTF-8 bytes to any proper destination. * It could be a function or a statement, and uses tmp as the input byte. * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be * executed up to 4 times for values in the valid UTF-8 range and up to * 7 times in the general case, depending on the length of the converted * Unicode character. */ #define PUT_UTF8(val, tmp, PUT_BYTE)\ {\ int bytes, shift;\ uint32_t in = val;\ if (in < 0x80) {\ tmp = in;\ PUT_BYTE\ } else {\ bytes = (av_log2(in) + 4) / 5;\ shift = (bytes - 1) * 6;\ tmp = (256 - (256 >> bytes)) | (in >> shift);\ PUT_BYTE\ while (shift >= 6) {\ shift -= 6;\ tmp = 0x80 | ((in >> shift) & 0x3f);\ PUT_BYTE\ }\ }\ } /** * @def PUT_UTF16(val, tmp, PUT_16BIT) * Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes). * @param val is an input-only argument and should be of type uint32_t. It holds * a UCS-4 encoded Unicode character that is to be converted to UTF-16. If * val is given as a function it is executed only once. * @param tmp is a temporary variable and should be of type uint16_t. It * represents an intermediate value during conversion that is to be * output by PUT_16BIT. * @param PUT_16BIT writes the converted UTF-16 data to any proper destination * in desired endianness. It could be a function or a statement, and uses tmp * as the input byte. For example, PUT_BYTE could be "*output++ = tmp;" * PUT_BYTE will be executed 1 or 2 times depending on input character. */ #define PUT_UTF16(val, tmp, PUT_16BIT)\ {\ uint32_t in = val;\ if (in < 0x10000) {\ tmp = in;\ PUT_16BIT\ } else {\ tmp = 0xD800 | ((in - 0x10000) >> 10);\ PUT_16BIT\ tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\ PUT_16BIT\ }\ }\ #include "mem.h" #ifdef HAVE_AV_CONFIG_H # include "internal.h" #endif /* HAVE_AV_CONFIG_H */ #endif /* AVUTIL_COMMON_H */ /* * The following definitions are outside the multiple inclusion guard * to ensure they are immediately available in intmath.h. */ #ifndef av_ceil_log2 # define av_ceil_log2 av_ceil_log2_c #endif #ifndef av_clip # define av_clip av_clip_c #endif #ifndef av_clip64 # define av_clip64 av_clip64_c #endif #ifndef av_clip_uint8 # define av_clip_uint8 av_clip_uint8_c #endif #ifndef av_clip_int8 # define av_clip_int8 av_clip_int8_c #endif #ifndef av_clip_uint16 # define av_clip_uint16 av_clip_uint16_c #endif #ifndef av_clip_int16 # define av_clip_int16 av_clip_int16_c #endif #ifndef av_clipl_int32 # define av_clipl_int32 av_clipl_int32_c #endif #ifndef av_clip_intp2 # define av_clip_intp2 av_clip_intp2_c #endif #ifndef av_clip_uintp2 # define av_clip_uintp2 av_clip_uintp2_c #endif #ifndef av_mod_uintp2 # define av_mod_uintp2 av_mod_uintp2_c #endif #ifndef av_sat_add32 # define av_sat_add32 av_sat_add32_c #endif #ifndef av_sat_dadd32 # define av_sat_dadd32 av_sat_dadd32_c #endif #ifndef av_sat_sub32 # define av_sat_sub32 av_sat_sub32_c #endif #ifndef av_sat_dsub32 # define av_sat_dsub32 av_sat_dsub32_c #endif #ifndef av_sat_add64 # define av_sat_add64 av_sat_add64_c #endif #ifndef av_sat_sub64 # define av_sat_sub64 av_sat_sub64_c #endif #ifndef av_clipf # define av_clipf av_clipf_c #endif #ifndef av_clipd # define av_clipd av_clipd_c #endif #ifndef av_popcount # define av_popcount av_popcount_c #endif #ifndef av_popcount64 # define av_popcount64 av_popcount64_c #endif #ifndef av_parity # define av_parity av_parity_c #endif ================================================ FILE: ffmpeg-4.3.2-experimental-patch/hevcdsp_template.before.c ================================================ /* * HEVC video decoder * * Copyright (C) 2012 - 2013 Guillaume Martres * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "get_bits.h" #include "hevcdec.h" #include "bit_depth_template.c" #include "hevcdsp.h" static void FUNC(put_pcm)(uint8_t *_dst, ptrdiff_t stride, int width, int height, GetBitContext *gb, int pcm_bit_depth) { int x, y; pixel *dst = (pixel *)_dst; stride /= sizeof(pixel); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = get_bits(gb, pcm_bit_depth) << (BIT_DEPTH - pcm_bit_depth); dst += stride; } } static av_always_inline void FUNC(add_residual)(uint8_t *_dst, int16_t *res, ptrdiff_t stride, int size) { int x, y; pixel *dst = (pixel *)_dst; stride /= sizeof(pixel); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { dst[x] = av_clip_pixel(dst[x] + *res); res++; } dst += stride; } } static void FUNC(add_residual4x4)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 4); } static void FUNC(add_residual8x8)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 8); } static void FUNC(add_residual16x16)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 16); } static void FUNC(add_residual32x32)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 32); } static void FUNC(transform_rdpcm)(int16_t *_coeffs, int16_t log2_size, int mode) { int16_t *coeffs = (int16_t *) _coeffs; int x, y; int size = 1 << log2_size; if (mode) { coeffs += size; for (y = 0; y < size - 1; y++) { for (x = 0; x < size; x++) coeffs[x] += coeffs[x - size]; coeffs += size; } } else { for (y = 0; y < size; y++) { for (x = 1; x < size; x++) coeffs[x] += coeffs[x - 1]; coeffs += size; } } } static void FUNC(dequant)(int16_t *coeffs, int16_t log2_size) { int shift = 15 - BIT_DEPTH - log2_size; int x, y; int size = 1 << log2_size; if (shift > 0) { int offset = 1 << (shift - 1); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { *coeffs = (*coeffs + offset) >> shift; coeffs++; } } } else { for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { *coeffs = *(uint16_t*)coeffs << -shift; coeffs++; } } } } #define SET(dst, x) (dst) = (x) #define SCALE(dst, x) (dst) = av_clip_int16(((x) + add) >> shift) #define TR_4x4_LUMA(dst, src, step, assign) \ do { \ int c0 = src[0 * step] + src[2 * step]; \ int c1 = src[2 * step] + src[3 * step]; \ int c2 = src[0 * step] - src[3 * step]; \ int c3 = 74 * src[1 * step]; \ \ assign(dst[2 * step], 74 * (src[0 * step] - \ src[2 * step] + \ src[3 * step])); \ assign(dst[0 * step], 29 * c0 + 55 * c1 + c3); \ assign(dst[1 * step], 55 * c2 - 29 * c1 + c3); \ assign(dst[3 * step], 55 * c0 + 29 * c2 - c3); \ } while (0) static void FUNC(transform_4x4_luma)(int16_t *coeffs) { int i; int shift = 7; int add = 1 << (shift - 1); int16_t *src = coeffs; for (i = 0; i < 4; i++) { TR_4x4_LUMA(src, src, 4, SCALE); src++; } shift = 20 - BIT_DEPTH; add = 1 << (shift - 1); for (i = 0; i < 4; i++) { TR_4x4_LUMA(coeffs, coeffs, 1, SCALE); coeffs += 4; } } #undef TR_4x4_LUMA #define TR_4(dst, src, dstep, sstep, assign, end) \ do { \ const int e0 = 64 * src[0 * sstep] + 64 * src[2 * sstep]; \ const int e1 = 64 * src[0 * sstep] - 64 * src[2 * sstep]; \ const int o0 = 83 * src[1 * sstep] + 36 * src[3 * sstep]; \ const int o1 = 36 * src[1 * sstep] - 83 * src[3 * sstep]; \ \ assign(dst[0 * dstep], e0 + o0); \ assign(dst[1 * dstep], e1 + o1); \ assign(dst[2 * dstep], e1 - o1); \ assign(dst[3 * dstep], e0 - o0); \ } while (0) #define TR_8(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_8[4]; \ int o_8[4] = { 0 }; \ for (i = 0; i < 4; i++) \ for (j = 1; j < end; j += 2) \ o_8[i] += transform[4 * j][i] * src[j * sstep]; \ TR_4(e_8, src, 1, 2 * sstep, SET, 4); \ \ for (i = 0; i < 4; i++) { \ assign(dst[i * dstep], e_8[i] + o_8[i]); \ assign(dst[(7 - i) * dstep], e_8[i] - o_8[i]); \ } \ } while (0) #define TR_16(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_16[8]; \ int o_16[8] = { 0 }; \ for (i = 0; i < 8; i++) \ for (j = 1; j < end; j += 2) \ o_16[i] += transform[2 * j][i] * src[j * sstep]; \ TR_8(e_16, src, 1, 2 * sstep, SET, 8); \ \ for (i = 0; i < 8; i++) { \ assign(dst[i * dstep], e_16[i] + o_16[i]); \ assign(dst[(15 - i) * dstep], e_16[i] - o_16[i]); \ } \ } while (0) #define TR_32(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_32[16]; \ int o_32[16] = { 0 }; \ for (i = 0; i < 16; i++) \ for (j = 1; j < end; j += 2) \ o_32[i] += transform[j][i] * src[j * sstep]; \ TR_16(e_32, src, 1, 2 * sstep, SET, end / 2); \ \ for (i = 0; i < 16; i++) { \ assign(dst[i * dstep], e_32[i] + o_32[i]); \ assign(dst[(31 - i) * dstep], e_32[i] - o_32[i]); \ } \ } while (0) #define IDCT_VAR4(H) \ int limit2 = FFMIN(col_limit + 4, H) #define IDCT_VAR8(H) \ int limit = FFMIN(col_limit, H); \ int limit2 = FFMIN(col_limit + 4, H) #define IDCT_VAR16(H) IDCT_VAR8(H) #define IDCT_VAR32(H) IDCT_VAR8(H) #define IDCT(H) \ static void FUNC(idct_ ## H ## x ## H )(int16_t *coeffs, \ int col_limit) \ { \ int i; \ int shift = 7; \ int add = 1 << (shift - 1); \ int16_t *src = coeffs; \ IDCT_VAR ## H(H); \ \ for (i = 0; i < H; i++) { \ TR_ ## H(src, src, H, H, SCALE, limit2); \ if (limit2 < H && i%4 == 0 && !!i) \ limit2 -= 4; \ src++; \ } \ \ shift = 20 - BIT_DEPTH; \ add = 1 << (shift - 1); \ for (i = 0; i < H; i++) { \ TR_ ## H(coeffs, coeffs, 1, 1, SCALE, limit); \ coeffs += H; \ } \ } #define IDCT_DC(H) \ static void FUNC(idct_ ## H ## x ## H ## _dc)(int16_t *coeffs) \ { \ int i, j; \ int shift = 14 - BIT_DEPTH; \ int add = 1 << (shift - 1); \ int coeff = (((coeffs[0] + 1) >> 1) + add) >> shift; \ \ for (j = 0; j < H; j++) { \ for (i = 0; i < H; i++) { \ coeffs[i + j * H] = coeff; \ } \ } \ } IDCT( 4) IDCT( 8) IDCT(16) IDCT(32) IDCT_DC( 4) IDCT_DC( 8) IDCT_DC(16) IDCT_DC(32) #undef TR_4 #undef TR_8 #undef TR_16 #undef TR_32 #undef SET #undef SCALE static void FUNC(sao_band_filter)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int16_t *sao_offset_val, int sao_left_class, int width, int height) { pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int offset_table[32] = { 0 }; int k, y, x; int shift = BIT_DEPTH - 5; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); for (k = 0; k < 4; k++) offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_table[src[x] >> shift]); dst += stride_dst; src += stride_src; } } #define CMP(a, b) (((a) > (b)) - ((a) < (b))) static void FUNC(sao_edge_filter)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, int16_t *sao_offset_val, int eo, int width, int height) { static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 }; static const int8_t pos[4][2][2] = { { { -1, 0 }, { 1, 0 } }, // horizontal { { 0, -1 }, { 0, 1 } }, // vertical { { -1, -1 }, { 1, 1 } }, // 45 degree { { 1, -1 }, { -1, 1 } }, // 135 degree }; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int a_stride, b_stride; int x, y; ptrdiff_t stride_src = (2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel); stride_dst /= sizeof(pixel); a_stride = pos[eo][0][0] + pos[eo][0][1] * stride_src; b_stride = pos[eo][1][0] + pos[eo][1][1] * stride_src; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { int diff0 = CMP(src[x], src[x + a_stride]); int diff1 = CMP(src[x], src[x + b_stride]); int offset_val = edge_idx[2 + diff0 + diff1]; dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]); } src += stride_src; dst += stride_dst; } } static void FUNC(sao_edge_restore_0)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, SAOParams *sao, int *borders, int _width, int _height, int c_idx, uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) { int x, y; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int16_t *sao_offset_val = sao->offset_val[c_idx]; int sao_eo_class = sao->eo_class[c_idx]; int init_x = 0, width = _width, height = _height; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); if (sao_eo_class != SAO_EO_VERT) { if (borders[0]) { int offset_val = sao_offset_val[0]; for (y = 0; y < height; y++) { dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); } init_x = 1; } if (borders[2]) { int offset_val = sao_offset_val[0]; int offset = width - 1; for (x = 0; x < height; x++) { dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); } width--; } } if (sao_eo_class != SAO_EO_HORIZ) { if (borders[1]) { int offset_val = sao_offset_val[0]; for (x = init_x; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_val); } if (borders[3]) { int offset_val = sao_offset_val[0]; ptrdiff_t y_stride_dst = stride_dst * (height - 1); ptrdiff_t y_stride_src = stride_src * (height - 1); for (x = init_x; x < width; x++) dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); height--; } } } static void FUNC(sao_edge_restore_1)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, SAOParams *sao, int *borders, int _width, int _height, int c_idx, uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) { int x, y; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int16_t *sao_offset_val = sao->offset_val[c_idx]; int sao_eo_class = sao->eo_class[c_idx]; int init_x = 0, init_y = 0, width = _width, height = _height; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); if (sao_eo_class != SAO_EO_VERT) { if (borders[0]) { int offset_val = sao_offset_val[0]; for (y = 0; y < height; y++) { dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); } init_x = 1; } if (borders[2]) { int offset_val = sao_offset_val[0]; int offset = width - 1; for (x = 0; x < height; x++) { dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); } width--; } } if (sao_eo_class != SAO_EO_HORIZ) { if (borders[1]) { int offset_val = sao_offset_val[0]; for (x = init_x; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_val); init_y = 1; } if (borders[3]) { int offset_val = sao_offset_val[0]; ptrdiff_t y_stride_dst = stride_dst * (height - 1); ptrdiff_t y_stride_src = stride_src * (height - 1); for (x = init_x; x < width; x++) dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); height--; } } { int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1]; int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2]; int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3]; int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3]; // Restore pixels that can't be modified if(vert_edge[0] && sao_eo_class != SAO_EO_VERT) { for(y = init_y+save_upper_left; y< height-save_lower_left; y++) dst[y*stride_dst] = src[y*stride_src]; } if(vert_edge[1] && sao_eo_class != SAO_EO_VERT) { for(y = init_y+save_upper_right; y< height-save_lower_right; y++) dst[y*stride_dst+width-1] = src[y*stride_src+width-1]; } if(horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) { for(x = init_x+save_upper_left; x < width-save_upper_right; x++) dst[x] = src[x]; } if(horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) { for(x = init_x+save_lower_left; x < width-save_lower_right; x++) dst[(height-1)*stride_dst+x] = src[(height-1)*stride_src+x]; } if(diag_edge[0] && sao_eo_class == SAO_EO_135D) dst[0] = src[0]; if(diag_edge[1] && sao_eo_class == SAO_EO_45D) dst[width-1] = src[width-1]; if(diag_edge[2] && sao_eo_class == SAO_EO_135D) dst[stride_dst*(height-1)+width-1] = src[stride_src*(height-1)+width-1]; if(diag_edge[3] && sao_eo_class == SAO_EO_45D) dst[stride_dst*(height-1)] = src[stride_src*(height-1)]; } } #undef CMP //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// static void FUNC(put_hevc_pel_pixels)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = src[x] << (14 - BIT_DEPTH); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_pel_uni_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); for (y = 0; y < height; y++) { memcpy(dst, src, width * sizeof(pixel)); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_pel_bi_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((src[x] << (14 - BIT_DEPTH)) + src2[x] + offset) >> shift); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_pel_uni_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((src[x] << (14 - BIT_DEPTH)) * wx + offset) >> shift) + ox); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_pel_bi_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src2[x] * wx0 + (ox0 + ox1 + 1) * (1 << log2Wd)) >> (log2Wd + 1)); } src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// #define QPEL_FILTER(src, stride) \ (filter[0] * src[x - 3 * stride] + \ filter[1] * src[x - 2 * stride] + \ filter[2] * src[x - stride] + \ filter[3] * src[x ] + \ filter[4] * src[x + stride] + \ filter[5] * src[x + 2 * stride] + \ filter[6] * src[x + 3 * stride] + \ filter[7] * src[x + 4 * stride]) static void FUNC(put_hevc_qpel_h)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_v)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_hv)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; tmp += MAX_PB_SIZE; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_qpel_filters[my - 1]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_qpel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; const int8_t *filter; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; src -= QPEL_EXTRA_BEFORE * srcstride; filter = ff_hevc_qpel_filters[mx - 1]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_qpel_filters[my - 1]; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// #define EPEL_FILTER(src, stride) \ (filter[0] * src[x - stride] + \ filter[1] * src[x] + \ filter[2] * src[x + stride] + \ filter[3] * src[x + 2 * stride]) static void FUNC(put_hevc_epel_h)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_v)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_hv)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; tmp += MAX_PB_SIZE; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_epel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); } dst += dststride; src += srcstride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_epel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); dst += dststride; src += srcstride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel((((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); } dst += dststride; src += srcstride; } } static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel((((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); } dst += dststride; src += srcstride; } } static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } // line zero #define P3 pix[-4 * xstride] #define P2 pix[-3 * xstride] #define P1 pix[-2 * xstride] #define P0 pix[-1 * xstride] #define Q0 pix[0 * xstride] #define Q1 pix[1 * xstride] #define Q2 pix[2 * xstride] #define Q3 pix[3 * xstride] // line three. used only for deblocking decision #define TP3 pix[-4 * xstride + 3 * ystride] #define TP2 pix[-3 * xstride + 3 * ystride] #define TP1 pix[-2 * xstride + 3 * ystride] #define TP0 pix[-1 * xstride + 3 * ystride] #define TQ0 pix[0 * xstride + 3 * ystride] #define TQ1 pix[1 * xstride + 3 * ystride] #define TQ2 pix[2 * xstride + 3 * ystride] #define TQ3 pix[3 * xstride + 3 * ystride] static void FUNC(hevc_loop_filter_luma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q) { int d, j; pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); beta <<= BIT_DEPTH - 8; for (j = 0; j < 2; j++) { const int dp0 = abs(P2 - 2 * P1 + P0); const int dq0 = abs(Q2 - 2 * Q1 + Q0); const int dp3 = abs(TP2 - 2 * TP1 + TP0); const int dq3 = abs(TQ2 - 2 * TQ1 + TQ0); const int d0 = dp0 + dq0; const int d3 = dp3 + dq3; const int tc = _tc[j] << (BIT_DEPTH - 8); const int no_p = _no_p[j]; const int no_q = _no_q[j]; if (d0 + d3 >= beta) { pix += 4 * ystride; continue; } else { const int beta_3 = beta >> 3; const int beta_2 = beta >> 2; const int tc25 = ((tc * 5 + 1) >> 1); if (abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 && abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 && (d0 << 1) < beta_2 && (d3 << 1) < beta_2) { // strong filtering const int tc2 = tc << 1; for (d = 0; d < 4; d++) { const int p3 = P3; const int p2 = P2; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; const int q2 = Q2; const int q3 = Q3; if (!no_p) { P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc2, tc2); P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc2, tc2); } if (!no_q) { Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc2, tc2); Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc2, tc2); } pix += ystride; } } else { // normal filtering int nd_p = 1; int nd_q = 1; const int tc_2 = tc >> 1; if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3)) nd_p = 2; if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3)) nd_q = 2; for (d = 0; d < 4; d++) { const int p2 = P2; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; const int q2 = Q2; int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; if (abs(delta0) < 10 * tc) { delta0 = av_clip(delta0, -tc, tc); if (!no_p) P0 = av_clip_pixel(p0 + delta0); if (!no_q) Q0 = av_clip_pixel(q0 - delta0); if (!no_p && nd_p > 1) { const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2); P1 = av_clip_pixel(p1 + deltap1); } if (!no_q && nd_q > 1) { const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); Q1 = av_clip_pixel(q1 + deltaq1); } } pix += ystride; } } } } } static void FUNC(hevc_loop_filter_chroma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int *_tc, uint8_t *_no_p, uint8_t *_no_q) { int d, j, no_p, no_q; pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); for (j = 0; j < 2; j++) { const int tc = _tc[j] << (BIT_DEPTH - 8); if (tc <= 0) { pix += 4 * ystride; continue; } no_p = _no_p[j]; no_q = _no_q[j]; for (d = 0; d < 4; d++) { int delta0; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc); if (!no_p) P0 = av_clip_pixel(p0 + delta0); if (!no_q) Q0 = av_clip_pixel(q0 - delta0); pix += ystride; } } } static void FUNC(hevc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_chroma)(pix, stride, sizeof(pixel), tc, no_p, no_q); } static void FUNC(hevc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_chroma)(pix, sizeof(pixel), stride, tc, no_p, no_q); } static void FUNC(hevc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_luma)(pix, stride, sizeof(pixel), beta, tc, no_p, no_q); } static void FUNC(hevc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_luma)(pix, sizeof(pixel), stride, beta, tc, no_p, no_q); } #undef P3 #undef P2 #undef P1 #undef P0 #undef Q0 #undef Q1 #undef Q2 #undef Q3 #undef TP3 #undef TP2 #undef TP1 #undef TP0 #undef TQ0 #undef TQ1 #undef TQ2 #undef TQ3 ================================================ FILE: ffmpeg-4.3.2-experimental-patch/hevcdsp_template.c ================================================ /* * HEVC video decoder * * Copyright (C) 2012 - 2013 Guillaume Martres * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef _MSC_VER #include #endif #include "get_bits.h" #include "hevcdec.h" #include "bit_depth_template.c" #include "hevcdsp.h" static void FUNC(put_pcm)(uint8_t *_dst, ptrdiff_t stride, int width, int height, GetBitContext *gb, int pcm_bit_depth) { int x, y; pixel *dst = (pixel *)_dst; stride /= sizeof(pixel); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = get_bits(gb, pcm_bit_depth) << (BIT_DEPTH - pcm_bit_depth); dst += stride; } } static av_always_inline void FUNC(add_residual)(uint8_t *_dst, int16_t *res, ptrdiff_t stride, int size) { int x, y; pixel *dst = (pixel *)_dst; stride /= sizeof(pixel); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { dst[x] = av_clip_pixel(dst[x] + *res); res++; } dst += stride; } } static void FUNC(add_residual4x4)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 4); } static void FUNC(add_residual8x8)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 8); } static void FUNC(add_residual16x16)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 16); } static void FUNC(add_residual32x32)(uint8_t *_dst, int16_t *res, ptrdiff_t stride) { FUNC(add_residual)(_dst, res, stride, 32); } static void FUNC(transform_rdpcm)(int16_t *_coeffs, int16_t log2_size, int mode) { int16_t *coeffs = (int16_t *) _coeffs; int x, y; int size = 1 << log2_size; if (mode) { coeffs += size; for (y = 0; y < size - 1; y++) { for (x = 0; x < size; x++) coeffs[x] += coeffs[x - size]; coeffs += size; } } else { for (y = 0; y < size; y++) { for (x = 1; x < size; x++) coeffs[x] += coeffs[x - 1]; coeffs += size; } } } static void FUNC(dequant)(int16_t *coeffs, int16_t log2_size) { int shift = 15 - BIT_DEPTH - log2_size; int x, y; int size = 1 << log2_size; if (shift > 0) { int offset = 1 << (shift - 1); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { *coeffs = (*coeffs + offset) >> shift; coeffs++; } } } else { for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { *coeffs = *(uint16_t*)coeffs << -shift; coeffs++; } } } } #define SET(dst, x) (dst) = (x) #define SCALE(dst, x) (dst) = av_clip_int16(((x) + add) >> shift) #define TR_4x4_LUMA(dst, src, step, assign) \ do { \ int c0 = src[0 * step] + src[2 * step]; \ int c1 = src[2 * step] + src[3 * step]; \ int c2 = src[0 * step] - src[3 * step]; \ int c3 = 74 * src[1 * step]; \ \ assign(dst[2 * step], 74 * (src[0 * step] - \ src[2 * step] + \ src[3 * step])); \ assign(dst[0 * step], 29 * c0 + 55 * c1 + c3); \ assign(dst[1 * step], 55 * c2 - 29 * c1 + c3); \ assign(dst[3 * step], 55 * c0 + 29 * c2 - c3); \ } while (0) static void FUNC(transform_4x4_luma)(int16_t *coeffs) { int i; int shift = 7; int add = 1 << (shift - 1); int16_t *src = coeffs; for (i = 0; i < 4; i++) { TR_4x4_LUMA(src, src, 4, SCALE); src++; } shift = 20 - BIT_DEPTH; add = 1 << (shift - 1); for (i = 0; i < 4; i++) { TR_4x4_LUMA(coeffs, coeffs, 1, SCALE); coeffs += 4; } } #undef TR_4x4_LUMA #define TR_4(dst, src, dstep, sstep, assign, end) \ do { \ const int e0 = 64 * src[0 * sstep] + 64 * src[2 * sstep]; \ const int e1 = 64 * src[0 * sstep] - 64 * src[2 * sstep]; \ const int o0 = 83 * src[1 * sstep] + 36 * src[3 * sstep]; \ const int o1 = 36 * src[1 * sstep] - 83 * src[3 * sstep]; \ \ assign(dst[0 * dstep], e0 + o0); \ assign(dst[1 * dstep], e1 + o1); \ assign(dst[2 * dstep], e1 - o1); \ assign(dst[3 * dstep], e0 - o0); \ } while (0) #define TR_8(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_8[4]; \ int o_8[4] = { 0 }; \ for (j = 1; j < end; j += 2) \ for (i = 0; i < 4; i++) \ o_8[i] += transform[4 * j][i] * src[j * sstep]; \ TR_4(e_8, src, 1, 2 * sstep, SET, 4); \ \ for (i = 0; i < 4; i++) { \ assign(dst[i * dstep], e_8[i] + o_8[i]); \ assign(dst[(7 - i) * dstep], e_8[i] - o_8[i]); \ } \ } while (0) #ifdef _MSC_VER #define TR_16(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_16[8]; \ int o_16[8]; \ __m128i o_0, o_1; \ o_0 = o_1 = _mm_setzero_si128(); \ for (j = 1; j < end; j += 2) { \ __m128i vhi, vlo; \ const short multiplier = src[j * sstep]; \ __m128i coeffs = _mm_set1_epi16(multiplier); \ __m128i buf = _mm_castpd_si128(_mm_load_sd((const double*) transform[2 * j])); \ buf = _mm_srai_epi16(_mm_unpacklo_epi8(buf, buf), 8); \ vhi = _mm_mulhi_epi16(buf, coeffs); \ vlo = _mm_mullo_epi16(buf, coeffs); \ o_0 = _mm_add_epi32(o_0, _mm_unpacklo_epi16(vlo, vhi)); \ o_1 = _mm_add_epi32(o_1, _mm_unpackhi_epi16(vlo, vhi)); \ } \ ((__m128i*) o_16)[0] = o_0; \ ((__m128i*) o_16)[1] = o_1; \ TR_8(e_16, src, 1, 2 * sstep, SET, 8); \ \ for (i = 0; i < 8; i++) { \ assign(dst[i * dstep], e_16[i] + o_16[i]); \ assign(dst[(15 - i) * dstep], e_16[i] - o_16[i]); \ } \ } while (0) #define TR_32(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_32[16]; \ int o_32[16]; \ __m128i o_0, o_1, o_2, o_3; \ o_0 = o_1 = o_2 = o_3 = _mm_setzero_si128(); \ for (j = 1; j < end; j += 2) { \ __m128i vhi, vlo; \ const short multiplier = src[j * sstep]; \ __m128i coeffs = _mm_set1_epi16(multiplier); \ __m128i buf = _mm_castpd_si128(_mm_load_sd((const double*) transform[j])); \ buf = _mm_srai_epi16(_mm_unpacklo_epi8(buf, buf), 8); \ vhi = _mm_mulhi_epi16(buf, coeffs); \ vlo = _mm_mullo_epi16(buf, coeffs); \ o_0 = _mm_add_epi32(o_0, _mm_unpacklo_epi16(vlo, vhi)); \ o_1 = _mm_add_epi32(o_1, _mm_unpackhi_epi16(vlo, vhi)); \ buf = _mm_castpd_si128(_mm_load_sd((const double*) &transform[j][8])); \ buf = _mm_srai_epi16(_mm_unpacklo_epi8(buf, buf), 8); \ vhi = _mm_mulhi_epi16(buf, coeffs); \ vlo = _mm_mullo_epi16(buf, coeffs); \ o_2 = _mm_add_epi32(o_2, _mm_unpacklo_epi16(vlo, vhi)); \ o_3 = _mm_add_epi32(o_3, _mm_unpackhi_epi16(vlo, vhi)); \ } \ ((__m128i*) o_32)[0] = o_0; \ ((__m128i*) o_32)[1] = o_1; \ ((__m128i*) o_32)[2] = o_2; \ ((__m128i*) o_32)[3] = o_3; \ TR_16(e_32, src, 1, 2 * sstep, SET, end / 2); \ \ for (i = 0; i < 16; i++) { \ assign(dst[i * dstep], e_32[i] + o_32[i]); \ assign(dst[(31 - i) * dstep], e_32[i] - o_32[i]); \ } \ } while (0) #else #define TR_16(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_16[8]; \ int o_16[8] = { 0 }; \ for (j = 1; j < end; j += 2) \ for (i = 0; i < 8; i++) \ o_16[i] += transform[2 * j][i] * src[j * sstep]; \ TR_8(e_16, src, 1, 2 * sstep, SET, 8); \ \ for (i = 0; i < 8; i++) { \ assign(dst[i * dstep], e_16[i] + o_16[i]); \ assign(dst[(15 - i) * dstep], e_16[i] - o_16[i]); \ } \ } while (0) #define TR_32(dst, src, dstep, sstep, assign, end) \ do { \ int i, j; \ int e_32[16]; \ int o_32[16] = { 0 }; \ for (j = 1; j < end; j += 2) \ for (i = 0; i < 16; i++) \ o_32[i] += transform[j][i] * src[j * sstep]; \ TR_16(e_32, src, 1, 2 * sstep, SET, end / 2); \ \ for (i = 0; i < 16; i++) { \ assign(dst[i * dstep], e_32[i] + o_32[i]); \ assign(dst[(31 - i) * dstep], e_32[i] - o_32[i]); \ } \ } while (0) #endif #define IDCT_VAR4(H) \ int limit2 = FFMIN(col_limit + 4, H) #define IDCT_VAR8(H) \ int limit = FFMIN(col_limit, H); \ int limit2 = FFMIN(col_limit + 4, H) #define IDCT_VAR16(H) IDCT_VAR8(H) #define IDCT_VAR32(H) IDCT_VAR8(H) #define IDCT(H) \ static void FUNC(idct_ ## H ## x ## H )(int16_t *coeffs, \ int col_limit) \ { \ int i; \ int shift = 7; \ int add = 1 << (shift - 1); \ int16_t *src = coeffs; \ IDCT_VAR ## H(H); \ \ for (i = 0; i < H; i++) { \ TR_ ## H(src, src, H, H, SCALE, limit2); \ if (limit2 < H && i%4 == 0 && !!i) \ limit2 -= 4; \ src++; \ } \ \ shift = 20 - BIT_DEPTH; \ add = 1 << (shift - 1); \ for (i = 0; i < H; i++) { \ TR_ ## H(coeffs, coeffs, 1, 1, SCALE, limit); \ coeffs += H; \ } \ } #define IDCT_DC(H) \ static void FUNC(idct_ ## H ## x ## H ## _dc)(int16_t *coeffs) \ { \ int i, j; \ int shift = 14 - BIT_DEPTH; \ int add = 1 << (shift - 1); \ int coeff = (((coeffs[0] + 1) >> 1) + add) >> shift; \ \ for (j = 0; j < H; j++) { \ for (i = 0; i < H; i++) { \ coeffs[i + j * H] = coeff; \ } \ } \ } IDCT( 4) IDCT( 8) IDCT(16) IDCT(32) IDCT_DC( 4) IDCT_DC( 8) IDCT_DC(16) IDCT_DC(32) #undef TR_4 #undef TR_8 #undef TR_16 #undef TR_32 #undef SET #undef SCALE static void FUNC(sao_band_filter)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, int16_t *sao_offset_val, int sao_left_class, int width, int height) { pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int offset_table[32] = { 0 }; int k, y, x; int shift = BIT_DEPTH - 5; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); for (k = 0; k < 4; k++) offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_table[src[x] >> shift]); dst += stride_dst; src += stride_src; } } #define CMP(a, b) (((a) > (b)) - ((a) < (b))) static void FUNC(sao_edge_filter)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, int16_t *sao_offset_val, int eo, int width, int height) { static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 }; static const int8_t pos[4][2][2] = { { { -1, 0 }, { 1, 0 } }, // horizontal { { 0, -1 }, { 0, 1 } }, // vertical { { -1, -1 }, { 1, 1 } }, // 45 degree { { 1, -1 }, { -1, 1 } }, // 135 degree }; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int a_stride, b_stride; int x, y; ptrdiff_t stride_src = (2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel); stride_dst /= sizeof(pixel); a_stride = pos[eo][0][0] + pos[eo][0][1] * stride_src; b_stride = pos[eo][1][0] + pos[eo][1][1] * stride_src; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { int diff0 = CMP(src[x], src[x + a_stride]); int diff1 = CMP(src[x], src[x + b_stride]); int offset_val = edge_idx[2 + diff0 + diff1]; dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]); } src += stride_src; dst += stride_dst; } } static void FUNC(sao_edge_restore_0)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, SAOParams *sao, int *borders, int _width, int _height, int c_idx, uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) { int x, y; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int16_t *sao_offset_val = sao->offset_val[c_idx]; int sao_eo_class = sao->eo_class[c_idx]; int init_x = 0, width = _width, height = _height; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); if (sao_eo_class != SAO_EO_VERT) { if (borders[0]) { int offset_val = sao_offset_val[0]; for (y = 0; y < height; y++) { dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); } init_x = 1; } if (borders[2]) { int offset_val = sao_offset_val[0]; int offset = width - 1; for (x = 0; x < height; x++) { dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); } width--; } } if (sao_eo_class != SAO_EO_HORIZ) { if (borders[1]) { int offset_val = sao_offset_val[0]; for (x = init_x; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_val); } if (borders[3]) { int offset_val = sao_offset_val[0]; ptrdiff_t y_stride_dst = stride_dst * (height - 1); ptrdiff_t y_stride_src = stride_src * (height - 1); for (x = init_x; x < width; x++) dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); height--; } } } static void FUNC(sao_edge_restore_1)(uint8_t *_dst, uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, SAOParams *sao, int *borders, int _width, int _height, int c_idx, uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge) { int x, y; pixel *dst = (pixel *)_dst; pixel *src = (pixel *)_src; int16_t *sao_offset_val = sao->offset_val[c_idx]; int sao_eo_class = sao->eo_class[c_idx]; int init_x = 0, init_y = 0, width = _width, height = _height; stride_dst /= sizeof(pixel); stride_src /= sizeof(pixel); if (sao_eo_class != SAO_EO_VERT) { if (borders[0]) { int offset_val = sao_offset_val[0]; for (y = 0; y < height; y++) { dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); } init_x = 1; } if (borders[2]) { int offset_val = sao_offset_val[0]; int offset = width - 1; for (x = 0; x < height; x++) { dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); } width--; } } if (sao_eo_class != SAO_EO_HORIZ) { if (borders[1]) { int offset_val = sao_offset_val[0]; for (x = init_x; x < width; x++) dst[x] = av_clip_pixel(src[x] + offset_val); init_y = 1; } if (borders[3]) { int offset_val = sao_offset_val[0]; ptrdiff_t y_stride_dst = stride_dst * (height - 1); ptrdiff_t y_stride_src = stride_src * (height - 1); for (x = init_x; x < width; x++) dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); height--; } } { int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1]; int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2]; int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3]; int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3]; // Restore pixels that can't be modified if(vert_edge[0] && sao_eo_class != SAO_EO_VERT) { for(y = init_y+save_upper_left; y< height-save_lower_left; y++) dst[y*stride_dst] = src[y*stride_src]; } if(vert_edge[1] && sao_eo_class != SAO_EO_VERT) { for(y = init_y+save_upper_right; y< height-save_lower_right; y++) dst[y*stride_dst+width-1] = src[y*stride_src+width-1]; } if(horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) { for(x = init_x+save_upper_left; x < width-save_upper_right; x++) dst[x] = src[x]; } if(horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) { for(x = init_x+save_lower_left; x < width-save_lower_right; x++) dst[(height-1)*stride_dst+x] = src[(height-1)*stride_src+x]; } if(diag_edge[0] && sao_eo_class == SAO_EO_135D) dst[0] = src[0]; if(diag_edge[1] && sao_eo_class == SAO_EO_45D) dst[width-1] = src[width-1]; if(diag_edge[2] && sao_eo_class == SAO_EO_135D) dst[stride_dst*(height-1)+width-1] = src[stride_src*(height-1)+width-1]; if(diag_edge[3] && sao_eo_class == SAO_EO_45D) dst[stride_dst*(height-1)] = src[stride_src*(height-1)]; } } #undef CMP //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// static void FUNC(put_hevc_pel_pixels)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = src[x] << (14 - BIT_DEPTH); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_pel_uni_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); for (y = 0; y < height; y++) { memcpy(dst, src, width * sizeof(pixel)); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_pel_bi_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((src[x] << (14 - BIT_DEPTH)) + src2[x] + offset) >> shift); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_pel_uni_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((src[x] << (14 - BIT_DEPTH)) * wx + offset) >> shift) + ox); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_pel_bi_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift; int bias = ((ox0 + ox1) * (1 << (BIT_DEPTH - 8)) + 1) << (log2Wd - 1); wx1 <<= (14 - BIT_DEPTH); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel(( src[x] * wx1 + src2[x] * wx0 + bias) >> log2Wd); } src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// #define QPEL_FILTER_0(src, stride) \ (-1 * src[x - 3 * stride] + \ 4 * src[x - 2 * stride] + \ -10 * src[x - stride] + \ 58 * src[x] + \ 17 * src[x + stride] + \ -5 * src[x + 2 * stride] + \ 1 * src[x + 3 * stride]) #define QPEL_FILTER_1(src, stride) \ (-1 * src[x - 3 * stride] + \ 4 * src[x - 2 * stride] + \ -11 * src[x - stride] + \ 40 * src[x] + \ 40 * src[x + stride] + \ -11 * src[x + 2 * stride] + \ 4 * src[x + 3 * stride] + \ -1 * src[x + 4 * stride]) #define QPEL_FILTER_2(src, stride) \ (1 * src[x - 2 * stride] + \ -5 * src[x - stride] + \ 17 * src[x] + \ 58 * src[x + stride] + \ -10 * src[x + 2 * stride] + \ 4 * src[x + 3 * stride] + \ -1 * src[x + 4 * stride]) #define QPEL_MACRO_DISPATCH(idx, macro) \ if ((idx) < 1) { macro(QPEL_FILTER_0) } \ else if ((idx) == 1) { macro(QPEL_FILTER_1) } \ else { macro(QPEL_FILTER_2) } static void FUNC(put_hevc_qpel_h)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ dst += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_v)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); \ src += srcstride; \ dst += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_hv)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; \ tmp += MAX_PB_SIZE; \ dst += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); \ src += srcstride; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); \ src += srcstride; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); \ src += srcstride; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); \ src += srcstride; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); \ tmp += MAX_PB_SIZE; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift); \ tmp += MAX_PB_SIZE; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel((((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); \ src += srcstride; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + \ ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); \ src += srcstride; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel((((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); \ src += srcstride; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + \ ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); \ src += srcstride; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; ox = ox * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel((((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); \ tmp += MAX_PB_SIZE; \ dst += dststride; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel*)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; src -= QPEL_EXTRA_BEFORE * srcstride; #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height + QPEL_EXTRA; y++) { \ for (x = 0; x < width; x++) \ tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); \ src += srcstride; \ tmp += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(mx - 1, QPEL_BODY) #undef QPEL_BODY tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); #define QPEL_BODY(QPEL_FILTER) \ for (y = 0; y < height; y++) { \ for (x = 0; x < width; x++) \ dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 + \ ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); \ tmp += MAX_PB_SIZE; \ dst += dststride; \ src2 += MAX_PB_SIZE; \ } QPEL_MACRO_DISPATCH(my - 1, QPEL_BODY) #undef QPEL_BODY } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// #define EPEL_FILTER(src, stride) \ (filter[0] * src[x - stride] + \ filter[1] * src[x] + \ filter[2] * src[x + stride] + \ filter[3] * src[x + 2 * stride]) static void FUNC(put_hevc_epel_h)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_v)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); src += srcstride; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_hv)(int16_t *dst, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; tmp += MAX_PB_SIZE; dst += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_epel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); } dst += dststride; src += srcstride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); src += srcstride; dst += dststride; } } static void FUNC(put_hevc_epel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift); dst += dststride; src += srcstride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel((((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); } dst += dststride; src += srcstride; } } static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { dst[x] = av_clip_pixel((((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); } dst += dststride; src += srcstride; } } static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[my - 1]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); src += srcstride; dst += dststride; src2 += MAX_PB_SIZE; } } static void FUNC(put_hevc_epel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = denom + 14 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); #else int offset = 0; #endif src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; ox = ox * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel((((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); tmp += MAX_PB_SIZE; dst += dststride; } } static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, uint8_t *_src, ptrdiff_t _srcstride, int16_t *src2, int height, int denom, int wx0, int wx1, int ox0, int ox1, intptr_t mx, intptr_t my, int width) { int x, y; pixel *src = (pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); const int8_t *filter = ff_hevc_epel_filters[mx - 1]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; src -= EPEL_EXTRA_BEFORE * srcstride; for (y = 0; y < height + EPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); src += srcstride; tmp += MAX_PB_SIZE; } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; filter = ff_hevc_epel_filters[my - 1]; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 + ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1)); tmp += MAX_PB_SIZE; dst += dststride; src2 += MAX_PB_SIZE; } } // line zero #define P3 pix[-4 * xstride] #define P2 pix[-3 * xstride] #define P1 pix[-2 * xstride] #define P0 pix[-1 * xstride] #define Q0 pix[0 * xstride] #define Q1 pix[1 * xstride] #define Q2 pix[2 * xstride] #define Q3 pix[3 * xstride] // line three. used only for deblocking decision #define TP3 pix[-4 * xstride + 3 * ystride] #define TP2 pix[-3 * xstride + 3 * ystride] #define TP1 pix[-2 * xstride + 3 * ystride] #define TP0 pix[-1 * xstride + 3 * ystride] #define TQ0 pix[0 * xstride + 3 * ystride] #define TQ1 pix[1 * xstride + 3 * ystride] #define TQ2 pix[2 * xstride + 3 * ystride] #define TQ3 pix[3 * xstride + 3 * ystride] static void FUNC(hevc_loop_filter_luma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int beta, int *_tc, uint8_t *_no_p, uint8_t *_no_q) { int d, j; pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); beta <<= BIT_DEPTH - 8; for (j = 0; j < 2; j++) { const int dp0 = abs(P2 - 2 * P1 + P0); const int dq0 = abs(Q2 - 2 * Q1 + Q0); const int dp3 = abs(TP2 - 2 * TP1 + TP0); const int dq3 = abs(TQ2 - 2 * TQ1 + TQ0); const int d0 = dp0 + dq0; const int d3 = dp3 + dq3; const int tc = _tc[j] << (BIT_DEPTH - 8); const int no_p = _no_p[j]; const int no_q = _no_q[j]; if (d0 + d3 >= beta) { pix += 4 * ystride; continue; } else { const int beta_3 = beta >> 3; const int beta_2 = beta >> 2; const int tc25 = ((tc * 5 + 1) >> 1); if (abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 && abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 && (d0 << 1) < beta_2 && (d3 << 1) < beta_2) { // strong filtering const int tc2 = tc << 1; for (d = 0; d < 4; d++) { const int p3 = P3; const int p2 = P2; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; const int q2 = Q2; const int q3 = Q3; if (!no_p) { P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc2, tc2); P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc2, tc2); } if (!no_q) { Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc2, tc2); Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc2, tc2); } pix += ystride; } } else { // normal filtering int nd_p = 1; int nd_q = 1; const int tc_2 = tc >> 1; if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3)) nd_p = 2; if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3)) nd_q = 2; for (d = 0; d < 4; d++) { const int p2 = P2; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; const int q2 = Q2; int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; if (abs(delta0) < 10 * tc) { delta0 = av_clip(delta0, -tc, tc); if (!no_p) P0 = av_clip_pixel(p0 + delta0); if (!no_q) Q0 = av_clip_pixel(q0 - delta0); if (!no_p && nd_p > 1) { const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2); P1 = av_clip_pixel(p1 + deltap1); } if (!no_q && nd_q > 1) { const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); Q1 = av_clip_pixel(q1 + deltaq1); } } pix += ystride; } } } } } static void FUNC(hevc_loop_filter_chroma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int *_tc, uint8_t *_no_p, uint8_t *_no_q) { int d, j, no_p, no_q; pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); for (j = 0; j < 2; j++) { const int tc = _tc[j] << (BIT_DEPTH - 8); if (tc <= 0) { pix += 4 * ystride; continue; } no_p = _no_p[j]; no_q = _no_q[j]; for (d = 0; d < 4; d++) { int delta0; const int p1 = P1; const int p0 = P0; const int q0 = Q0; const int q1 = Q1; delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc); if (!no_p) P0 = av_clip_pixel(p0 + delta0); if (!no_q) Q0 = av_clip_pixel(q0 - delta0); pix += ystride; } } } static void FUNC(hevc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_chroma)(pix, stride, sizeof(pixel), tc, no_p, no_q); } static void FUNC(hevc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_chroma)(pix, sizeof(pixel), stride, tc, no_p, no_q); } static void FUNC(hevc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_luma)(pix, stride, sizeof(pixel), beta, tc, no_p, no_q); } static void FUNC(hevc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t *tc, uint8_t *no_p, uint8_t *no_q) { FUNC(hevc_loop_filter_luma)(pix, sizeof(pixel), stride, beta, tc, no_p, no_q); } #undef P3 #undef P2 #undef P1 #undef P0 #undef Q0 #undef Q1 #undef Q2 #undef Q3 #undef TP3 #undef TP2 #undef TP1 #undef TP0 #undef TQ0 #undef TQ1 #undef TQ2 #undef TQ3 ================================================ FILE: getYoutubeCombined.py ================================================ import sys, platform, socket, re import os, json, requests, subprocess, importlib sys.stdout = LoggerStream() sys.stderr = LoggerStream() py_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" # 64bit / 32bit arch = platform.architecture()[0] STATE_FILE = f"YoutubeCombined_packages-{py_version}-{arch}.json" def _load_state(): return json.load(open(STATE_FILE)) if os.path.exists(STATE_FILE) else {} def _save_state(state): json.dump(state, open(STATE_FILE, "w")) def install_and_import(package, url=None): importlib.invalidate_caches() state = _load_state() if url is None: url = package prev = state.get(url, {}) headers = {} if prev.get("etag"): headers["If-None-Match"] = prev["etag"] if prev.get("last_modified"): headers["If-Modified-Since"] = prev["last_modified"] # HEAD try: r = requests.head(url, allow_redirects=True, timeout=10, headers=headers) except Exception as e: print(f"[WARN] HEAD failed for {url}: {e}") r = None need_install = False reason = "initial install" if r: if r.status_code == 304: need_install = False reason = "304 Not Modified" else: etag = r.headers.get("ETag") last_modified = r.headers.get("Last-Modified") size = r.headers.get("Content-Length") if etag and etag == prev.get("etag"): need_install = False reason = "ETag match" elif last_modified and last_modified == prev.get("last_modified"): need_install = False reason = "Last-Modified match" elif size and size == prev.get("size"): need_install = False reason = "Content-Length match" else: need_install = True reason = "metadata changed" try: importlib.import_module(package) if need_install: raise ImportError("force reinstall") print(f"[INFO] {package} already up-to-date ({reason})") except ImportError: print(f"[INFO] Installing {package} from {url} ({reason})...") library_dir = os.path.dirname(os.path.abspath(socket.__file__)) pip_path = os.path.join(library_dir, "../scripts/pip3") cmd = [pip_path, "install", "--force-reinstall", url] result = subprocess.run(cmd, capture_output=True, text=True, check=True) print("[INFO] pip output:\n", result.stdout) for line in result.stdout.splitlines(): if "Installing collected packages" in line or "Successfully installed" in line: print("[INFO] Dependencies updated:", line) if r: state[url] = { "etag": r.headers.get("ETag"), "last_modified": r.headers.get("Last-Modified"), "size": r.headers.get("Content-Length"), } _save_state(state) globals()[package] = importlib.import_module(package) # ---- yt-dlp based getYoutubeUrl ---- install_and_import("yt_dlp", "https://github.com/yt-dlp/yt-dlp/archive/refs/heads/master.zip") from urllib.parse import urlencode, urlparse, parse_qs def parsePlaylist(url: str, force: bool): # 1. Detect search query (no dots or slashes) if all(ch not in url for ch in "./"): parts = url.split() query = "+".join(urlencode({"q": p})[2:] for p in parts) search_url = f"ytsearch50:{query}" return _extract_flat_urls(search_url) # 2. URL case parsed = urlparse(url) qs = parse_qs(parsed.query) is_list = "list" in qs is_channel = _is_channel_url(url) # Playlist or channel -> treat as list if is_list or is_channel: return _extract_flat_urls(url) # 3. Single video page if force: return _extract_from_video_page(url) return [] def _is_channel_url(url: str): """Detects YouTube channel URLs but does NOT treat them as results.""" return any(part in url for part in [ "/channel/", "/user/", "/c/", "/@" ]) def _extract_flat_urls(yt_url: str): ydl_opts = { "quiet": True, "skip_download": True, "extract_flat": True, } with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(yt_url, download=False) entries = info.get("entries", []) urls = [] for e in entries: if "url" in e: # Filter out channel URLs if not _is_channel_url(e["url"]): urls.append(e["url"]) return urls def _extract_from_video_page(video_url: str): ydl_opts = { "quiet": True, "skip_download": True, "extract_flat": False, } with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(video_url, download=False) urls = [] # The video itself if "webpage_url" in info: urls.append(info["webpage_url"]) # Playlist URLs (if video is inside a playlist) for pl in info.get("playlists", []): if "id" in pl: urls.append(f"https://www.youtube.com/playlist?list={pl['id']}") # Try to approximate "related videos" using search title = info.get("title") if title: search_query = f"ytsearch10:{title}" with yt_dlp.YoutubeDL({"quiet": True, "extract_flat": True}) as ydl: s = ydl.extract_info(search_query, download=False) for e in s.get("entries", []): if "url" in e and not _is_channel_url(e["url"]): urls.append(e["url"]) # Deduplicate urls = list(dict.fromkeys(urls)) return urls def _iter_formats(info): if not info: return [] if isinstance(info, dict) and 'entries' in info and info['entries']: for e in info['entries']: if e: info = e break if isinstance(info, dict) and 'requested_formats' in info and info['requested_formats']: return info['requested_formats'] if isinstance(info, dict) and 'formats' in info and info['formats']: return info['formats'] if isinstance(info, dict) and 'url' in info: return [info] return [] def _is_usable_video_format(f): vcodec = f.get('vcodec') proto = f.get('protocol', '') if not vcodec or vcodec == 'none': return False if vcodec.startswith('av01'): return False if proto == 'm3u8_native' or proto == 'm3u8': return False return True def _is_usable_audio_format(f): vcodec = f.get('vcodec', 'none') proto = f.get('protocol', '') if vcodec != 'none': return False if proto == 'm3u8_native' or proto == 'm3u8': return False return 'audio_channels' in f and f.get('audio_channels') is not None or 'abr' in f def getYoutubeUrl(url, adaptive): socket.setdefaulttimeout(10) base_opts = { 'noplaylist': True, 'quiet': True, 'skip_download': True, } ydl_opts = base_opts.copy() ydl_opts['format'] = 'bestvideo+bestaudio' if adaptive else 'best' with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=False) formats = _iter_formats(info) if adaptive: video_candidates = [f for f in formats if _is_usable_video_format(f)] if video_candidates: best_video = max(video_candidates, key=lambda f: (f.get('height') or 0, f.get('tbr') or 0)) video_url = best_video.get('url') else: video_url = info.get('url') audio_candidates = [f for f in formats if _is_usable_audio_format(f)] if audio_candidates: best_audio = max(audio_candidates, key=lambda f: (f.get('audio_channels') or 0, f.get('abr') or 0)) audio_url = best_audio.get('url') else: any_audio = next((f for f in formats if f.get('vcodec') == 'none' and f.get('url')), None) audio_url = any_audio.get('url') if any_audio else None return (video_url, audio_url) combined_candidates = [f for f in formats if f.get('vcodec') != 'none' and f.get('audio_channels') is not None and not f.get('vcodec','').startswith('av01') and f.get('protocol') not in ('m3u8_native', 'm3u8')] if not combined_candidates: combined_candidates = [f for f in formats if 'url' in f] best_combined = max(combined_candidates, key=lambda f: (f.get('height') or 0, f.get('tbr') or 0, f.get('abr') or 0)) return best_combined.get('url') # ---- youtube_transcript_api based getYoutubeTranscript ---- install_and_import("youtube_transcript_api", "https://github.com/jdepoix/youtube-transcript-api/archive/master.zip") _YT_ID_RE = re.compile( r'(?:v=|\/v\/|youtu\.be\/|\/embed\/|\/shorts\/|watch\?.*v=)([A-Za-z0-9_-]{11})' ) def extract_youtube_id(url_or_id: str) -> str: if not isinstance(url_or_id, str): raise ValueError("video id or url must be a string") url_or_id = url_or_id.strip() if len(url_or_id) == 11 and re.fullmatch(r'[A-Za-z0-9_-]{11}', url_or_id): return url_or_id m = _YT_ID_RE.search(url_or_id) if m: return m.group(1) q = re.search(r'[?&]v=([A-Za-z0-9_-]{11})', url_or_id) if q: return q.group(1) raise ValueError("Could not extract YouTube video id from input") def getYoutubeTranscript(url_or_id: str): video_id = extract_youtube_id(url_or_id) return youtube_transcript_api.YouTubeTranscriptApi().fetch(video_id).to_raw_data() ================================================ FILE: networking/ReadMe.txt ================================================ ======================================================================== STATIC LIBRARY : unzip Project Overview ======================================================================== AppWizard has created this unzip library project for you. No source files were created as part of your project. unzip.vcxproj This is the main project file for VC++ projects generated using an Application Wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations, and project features selected with the Application Wizard. unzip.vcxproj.filters This is the filters file for VC++ projects generated using an Application Wizard. It contains information about the association between the files in your project and the filters. This association is used in the IDE to show grouping of files with similar extensions under a specific node (for e.g. ".cpp" files are associated with the "Source Files" filter). ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" comments to indicate parts of the source code you should add to or customize. ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: networking/crypt.h ================================================ /* crypt.h -- base code for crypt/uncrypt ZIPfile Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant This code is a modified version of crypting code in Infozip distribution The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). If you don't need crypting in your application, just define symbols NOCRYPT and NOUNCRYPT. This code support the "Traditional PKWARE Encryption". The new AES encryption added on Zip format by Winzip (see the page http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong Encryption is not supported. */ #define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) /*********************************************************************** * Return the next byte in the pseudo-random sequence */ static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab ) { //(void) pcrc_32_tab; /* avoid "unused parameter" warning */ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an * unpredictable manner on 16-bit systems; not a problem * with any known compiler so far, though */ temp = ((unsigned)(*(pkeys + 2)) & 0xffff) | 2; return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); } /*********************************************************************** * Update the encryption keys with the next byte of plain text */ static int update_keys(unsigned long* pkeys, const unsigned long* pcrc_32_tab, int c) { (*(pkeys + 0)) = CRC32((*(pkeys + 0)), c); (*(pkeys + 1)) += (*(pkeys + 0)) & 0xff; (*(pkeys + 1)) = (*(pkeys + 1)) * 134775813L + 1; { register int keyshift = (int)((*(pkeys + 1)) >> 24); (*(pkeys + 2)) = CRC32((*(pkeys + 2)), keyshift); } return c; } /*********************************************************************** * Initialize the encryption keys and the random header according to * the given password. */ static void init_keys(const char* passwd, unsigned long* pkeys, const unsigned long* pcrc_32_tab) { *(pkeys + 0) = 305419896L; *(pkeys + 1) = 591751049L; *(pkeys + 2) = 878082192L; while (*passwd != '\0') { update_keys(pkeys, pcrc_32_tab, (int)*passwd); passwd++; } } #define zdecode(pkeys,pcrc_32_tab,c) \ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) #define zencode(pkeys,pcrc_32_tab,c,t) \ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED #define RAND_HEAD_LEN 12 /* "last resort" source for second part of crypt seed pattern */ # ifndef ZCR_SEED2 # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ # endif static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) const char* passwd; /* password string */ unsigned char* buf; /* where to write header */ int bufSize; unsigned long* pkeys; const unsigned long* pcrc_32_tab; unsigned long crcForCrypting; { int n; /* index in random header */ int t; /* temporary */ int c; /* random byte */ unsigned char header[RAND_HEAD_LEN - 2]; /* random header */ static unsigned calls = 0; /* ensure different random header each time */ if (bufSize < RAND_HEAD_LEN) return 0; /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the * output of rand() to get less predictability, since rand() is * often poorly implemented. */ if (++calls == 1) { srand((unsigned)(time(NULL) ^ ZCR_SEED2)); } init_keys(passwd, pkeys, pcrc_32_tab); for (n = 0; n < RAND_HEAD_LEN - 2; n++) { c = (rand() >> 7) & 0xff; header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); } /* Encrypt random header (last two bytes is high word of crc) */ init_keys(passwd, pkeys, pcrc_32_tab); for (n = 0; n < RAND_HEAD_LEN - 2; n++) { buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); } buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); return n; } #endif ================================================ FILE: networking/http_download.cpp ================================================ #include "http_download.h" #include #include #include #include #include #include #include #include #include //#pragma comment(lib, "Winhttp") namespace { auto MakeGuard(HINTERNET h) { if (!h) { const DWORD errorCode = GetLastError(); throw std::system_error( errorCode, std::system_category(), "HINTERNET object creation failed."); } return std::unique_ptr, decltype(&WinHttpCloseHandle)> (h, WinHttpCloseHandle); } } // namespace bool HttpDownload(const TCHAR* url, const TCHAR* path) { std::wstring wszUrl(url, url + _tcslen(url)); URL_COMPONENTS urlComp{ sizeof(urlComp) }; // Set required component lengths to non-zero // so that they are cracked. urlComp.dwSchemeLength = (DWORD)-1; urlComp.dwHostNameLength = (DWORD)-1; urlComp.dwUrlPathLength = (DWORD)-1; urlComp.dwExtraInfoLength = (DWORD)-1; if (!WinHttpCrackUrl(wszUrl.c_str(), wszUrl.length(), 0, &urlComp)) { fprintf(stderr, "Error %u in WinHttpCrackUrl.\n", GetLastError()); return false; } // Use WinHttpOpen to obtain a session handle. auto hSession = MakeGuard(WinHttpOpen(L"HttpDownload/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0)); // Specify an HTTP server. std::wstring szHostName( urlComp.lpszHostName, urlComp.lpszHostName + urlComp.dwHostNameLength); auto hConnect = MakeGuard(WinHttpConnect(hSession.get(), szHostName.c_str(), urlComp.nPort, 0)); // Create an HTTP request handle. auto hRequest = MakeGuard(WinHttpOpenRequest(hConnect.get(), L"GET", urlComp.lpszUrlPath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, (urlComp.nScheme == INTERNET_SCHEME_HTTPS)? WINHTTP_FLAG_SECURE : 0)); // Send a request. BOOL bResults = WinHttpSendRequest(hRequest.get(), WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); // End the request. if (bResults) bResults = WinHttpReceiveResponse(hRequest.get(), NULL); // Keep checking for data until there is nothing left. if (bResults) { std::ofstream f(path, std::ofstream::binary); std::vector buf; for (;;) { DWORD dwSize = 0; // Check for available data. if (!WinHttpQueryDataAvailable(hRequest.get(), &dwSize)) fprintf(stderr, "Error %u in WinHttpQueryDataAvailable.\n", GetLastError()); if (dwSize == 0) break; // Allocate space for the buffer. if (buf.size() < dwSize) buf.resize(dwSize); DWORD dwDownloaded = 0; if (WinHttpReadData(hRequest.get(), buf.data(), dwSize, &dwDownloaded)) { f.write(buf.data(), dwDownloaded); } else { fprintf(stderr, "Error %u in WinHttpReadData.\n", GetLastError()); } } } // Report any errors. if (!bResults) fprintf(stderr, "Error %d has occurred.\n", GetLastError()); return !!bResults; } ================================================ FILE: networking/http_download.h ================================================ #pragma once #include bool HttpDownload(const TCHAR* url, const TCHAR* path); ================================================ FILE: networking/http_get.cpp ================================================ #include "http_get.h" #include "httprequest_h.h" #include #include namespace { const wchar_t USER_AGENT[] = L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.75 Safari/537.36"; class CComUsageScope { bool m_bInitialized; public: explicit CComUsageScope(DWORD dwCoInit = COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY) { m_bInitialized = SUCCEEDED(CoInitializeEx(NULL, dwCoInit)); } ~CComUsageScope() { if (m_bInitialized) CoUninitialize(); } }; std::string resolveHostnameToIP(const std::string& hostname) { addrinfo hints = { 0 }, * res = nullptr; hints.ai_family = AF_INET; // Allow IPv4 hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(hostname.c_str(), nullptr, &hints, &res) != 0) { return {}; } char ipStr[INET6_ADDRSTRLEN]; void* addr; if (res->ai_family == AF_INET) { // IPv4 sockaddr_in* sockaddr_ipv4 = reinterpret_cast(res->ai_addr); addr = &(sockaddr_ipv4->sin_addr); } else if (res->ai_family == AF_INET6) { // IPv6 sockaddr_in6* sockaddr_ipv6 = reinterpret_cast(res->ai_addr); addr = &(sockaddr_ipv6->sin6_addr); } else { freeaddrinfo(res); return {}; } inet_ntop(res->ai_family, addr, ipStr, sizeof(ipStr)); freeaddrinfo(res); return std::string(ipStr); } } // namespace long HttpGetStatus(std::string& url, bool useHHO) { if (useHHO) { auto matchEnd = std::mismatch(url.begin(), url.end(), "https://", [](char c1, char c2) { return tolower(c1) == tolower(c2); }); if (matchEnd.first == url.end() || *matchEnd.second != '\0') { useHHO = false; } } CComBSTR bstrUrl; CComBSTR bstrHostname; if (useHHO) { const auto pos = 8; // Move past "://" size_t endPos = url.find_first_of(":/", pos); if (endPos != std::string::npos) { std::string hostname = url.substr(pos, endPos - pos); std::string ip = resolveHostnameToIP(hostname); if (!ip.empty()) { bstrUrl = (url.substr(0, pos) + ip + url.substr(endPos)).c_str(); bstrHostname = hostname.c_str(); } else { useHHO = false; } } else { useHHO = false; } } if (!useHHO) { bstrUrl = url.c_str(); } VARIANT varFalse{ VT_BOOL }; VARIANT varEmpty{ VT_ERROR }; VARIANT varOption{ VT_I4 }; varOption.intVal = 0x1000; HRESULT result = 0; CComPtr pIWinHttpRequest; CComUsageScope scope; if (FAILED(result = pIWinHttpRequest.CoCreateInstance(L"WinHttp.WinHttpRequest.5.1", NULL, CLSCTX_INPROC_SERVER))) return result; if (FAILED(result = pIWinHttpRequest->Open(CComBSTR(L"HEAD"), bstrUrl, varFalse))) return result; if (useHHO && FAILED(result = pIWinHttpRequest->SetRequestHeader(CComBSTR(L"Host"), bstrHostname))) return result; if (FAILED(result = pIWinHttpRequest->SetRequestHeader(CComBSTR(L"User-Agent"), CComBSTR(USER_AGENT)))) return result; if (useHHO && FAILED(result = pIWinHttpRequest->put_Option(WinHttpRequestOption_SslErrorIgnoreFlags, varOption))) // Ignore SSL errors return result; if (FAILED(result = pIWinHttpRequest->put_Option(WinHttpRequestOption_EnableRedirects, varFalse))) return result; if (SUCCEEDED(result = pIWinHttpRequest->Send(varEmpty))) { pIWinHttpRequest->get_Status(&result); if (result == 302) { CComBSTR locationHeader; if (SUCCEEDED(pIWinHttpRequest->GetResponseHeader(CComBSTR(L"Location"), &locationHeader))) { url = CW2A(locationHeader); } } } return result; } CComVariant HttpGet(const char * url) { VARIANT varFalse{ VT_BOOL }; VARIANT varEmpty{ VT_ERROR }; CComVariant varBody; CComPtr pIWinHttpRequest; CComUsageScope scope; if (SUCCEEDED(pIWinHttpRequest.CoCreateInstance(L"WinHttp.WinHttpRequest.5.1", NULL, CLSCTX_INPROC_SERVER)) && SUCCEEDED(pIWinHttpRequest->Open(CComBSTR(L"GET"), CComBSTR(static_cast(url)), varFalse)) && SUCCEEDED(pIWinHttpRequest->SetRequestHeader(CComBSTR(L"User-Agent"), CComBSTR(USER_AGENT))) && SUCCEEDED(pIWinHttpRequest->Send(varEmpty))) { pIWinHttpRequest->get_ResponseBody(&varBody); } return varBody; } ================================================ FILE: networking/http_get.h ================================================ #pragma once #include #include // modifies url if it is a redirect long HttpGetStatus(std::string& url, bool useHHO = true); CComVariant HttpGet(const char* url); ================================================ FILE: networking/httpioapi.cpp ================================================ /* ioapi.c -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant Modified to integrate with WinHttpRequest. */ #include #include "zlib.h" #include "ioapi.h" #include "httprequest_h.h" #include voidpf ZCALLBACK qiodevice_open_file_func( voidpf opaque, voidpf filename, int mode) { VARIANT varFalse{ VT_BOOL }; VARIANT varEmpty{ VT_ERROR }; CComVariant varStream; CComPtr pIWinHttpRequest; if (SUCCEEDED(pIWinHttpRequest.CoCreateInstance(L"WinHttp.WinHttpRequest.5.1", nullptr, CLSCTX_INPROC_SERVER)) && SUCCEEDED(pIWinHttpRequest->Open(CComBSTR(L"GET"), CComBSTR(static_cast(filename)), varFalse)) && SUCCEEDED(pIWinHttpRequest->Send(varEmpty)) && SUCCEEDED(pIWinHttpRequest->get_ResponseStream(&varStream))) { if (CComQIPtr stream = V_UNKNOWN(&varStream)) { return stream.Detach(); } } return nullptr; } uLong ZCALLBACK qiodevice_read_file_func( voidpf, // opaque voidpf pf, void* buf, uLong size) { auto stream = static_cast(pf); uLong ret = 0; HRESULT hr = stream->Read(buf, size, &ret); return ret; } uLong ZCALLBACK qiodevice_write_file_func( voidpf, // opaque voidpf pf, const void* buf, uLong size) { auto stream = static_cast(pf); uLong ret = 0; stream->Write(buf, size, &ret); return ret; } uLong ZCALLBACK qiodevice_tell_file_func( voidpf, // opaque voidpf pf) { auto stream = static_cast(pf); const LARGE_INTEGER move{}; ULARGE_INTEGER libNewPosition{}; stream->Seek(move, STREAM_SEEK_CUR, &libNewPosition); return libNewPosition.LowPart; } int ZCALLBACK qiodevice_seek_file_func( voidpf, // opaque voidpf pf, uLong offset, int origin) { STREAM_SEEK dwOrigin; LARGE_INTEGER move; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR: dwOrigin = STREAM_SEEK_CUR; move.QuadPart = (long)offset; break; case ZLIB_FILEFUNC_SEEK_END: dwOrigin = STREAM_SEEK_END; move.QuadPart = (long)offset; break; case ZLIB_FILEFUNC_SEEK_SET: dwOrigin = STREAM_SEEK_SET; move.QuadPart = offset; break; default: return -1; } auto stream = static_cast(pf); ULARGE_INTEGER libNewPosition{}; int ret = FAILED(stream->Seek(move, dwOrigin, &libNewPosition)); return ret; } int ZCALLBACK qiodevice_close_file_func( voidpf, // opaque voidpf pf) { auto stream = static_cast(pf); auto cnt = stream->Release(); assert(cnt == 0); return 0; } int ZCALLBACK qiodevice_error_file_func( voidpf, // opaque voidpf //stream ) { return 0; } void fill_qiodevice_filefunc( zlib_filefunc_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen_file = qiodevice_open_file_func; pzlib_filefunc_def->zread_file = qiodevice_read_file_func; pzlib_filefunc_def->zwrite_file = qiodevice_write_file_func; pzlib_filefunc_def->ztell_file = qiodevice_tell_file_func; pzlib_filefunc_def->zseek_file = qiodevice_seek_file_func; pzlib_filefunc_def->zclose_file = qiodevice_close_file_func; pzlib_filefunc_def->zerror_file = qiodevice_error_file_func; pzlib_filefunc_def->opaque = nullptr; } ================================================ FILE: networking/httprequest_h.h ================================================ /* this ALWAYS GENERATED file contains the definitions for the interfaces */ /* File created by MIDL compiler version 8.00.0603 */ /* at Wed Jun 06 11:59:31 2018 */ /* Compiler settings for C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\httprequest.idl: Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.00.0603 protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: __declspec(uuid()), __declspec(selectany), __declspec(novtable) DECLSPEC_UUID(), MIDL_INTERFACE() */ /* @@MIDL_FILE_HEADING( ) */ #pragma warning( disable: 4049 ) /* more than 64k source lines */ /* verify that the version is high enough to compile this file*/ #ifndef __REQUIRED_RPCNDR_H_VERSION__ #define __REQUIRED_RPCNDR_H_VERSION__ 475 #endif #include "rpc.h" #include "rpcndr.h" #ifndef __RPCNDR_H_VERSION__ #error this stub requires an updated version of #endif // __RPCNDR_H_VERSION__ #ifndef __httprequest_h_h__ #define __httprequest_h_h__ #if defined(_MSC_VER) && (_MSC_VER >= 1020) #pragma once #endif /* Forward Declarations */ #ifndef __IWinHttpRequest_FWD_DEFINED__ #define __IWinHttpRequest_FWD_DEFINED__ typedef interface IWinHttpRequest IWinHttpRequest; #endif /* __IWinHttpRequest_FWD_DEFINED__ */ #ifndef __IWinHttpRequestEvents_FWD_DEFINED__ #define __IWinHttpRequestEvents_FWD_DEFINED__ typedef interface IWinHttpRequestEvents IWinHttpRequestEvents; #endif /* __IWinHttpRequestEvents_FWD_DEFINED__ */ #ifndef __WinHttpRequest_FWD_DEFINED__ #define __WinHttpRequest_FWD_DEFINED__ #ifdef __cplusplus typedef class WinHttpRequest WinHttpRequest; #else typedef struct WinHttpRequest WinHttpRequest; #endif /* __cplusplus */ #endif /* __WinHttpRequest_FWD_DEFINED__ */ #ifdef __cplusplus extern "C"{ #endif /* interface __MIDL_itf_httprequest_0000_0000 */ /* [local] */ //+------------------------------------------------------------------------- // // Microsoft Windows HTTP Services (WinHTTP) version 5.1 // Copyright (C) Microsoft Corporation. All rights reserved. // //-------------------------------------------------------------------------- extern RPC_IF_HANDLE __MIDL_itf_httprequest_0000_0000_v0_0_c_ifspec; extern RPC_IF_HANDLE __MIDL_itf_httprequest_0000_0000_v0_0_s_ifspec; #ifndef __WinHttp_LIBRARY_DEFINED__ #define __WinHttp_LIBRARY_DEFINED__ /* library WinHttp */ /* [version][lcid][helpstring][uuid] */ typedef /* [public] */ long HTTPREQUEST_PROXY_SETTING; #define HTTPREQUEST_PROXYSETTING_DEFAULT ( 0 ) #define HTTPREQUEST_PROXYSETTING_PRECONFIG ( 0 ) #define HTTPREQUEST_PROXYSETTING_DIRECT ( 0x1 ) #define HTTPREQUEST_PROXYSETTING_PROXY ( 0x2 ) typedef /* [public] */ long HTTPREQUEST_SETCREDENTIALS_FLAGS; #define HTTPREQUEST_SETCREDENTIALS_FOR_SERVER ( 0 ) #define HTTPREQUEST_SETCREDENTIALS_FOR_PROXY ( 0x1 ) typedef /* [helpstring][uuid] */ DECLSPEC_UUID("12782009-FE90-4877-9730-E5E183669B19") enum WinHttpRequestOption { WinHttpRequestOption_UserAgentString = 0, WinHttpRequestOption_URL = ( WinHttpRequestOption_UserAgentString + 1 ) , WinHttpRequestOption_URLCodePage = ( WinHttpRequestOption_URL + 1 ) , WinHttpRequestOption_EscapePercentInURL = ( WinHttpRequestOption_URLCodePage + 1 ) , WinHttpRequestOption_SslErrorIgnoreFlags = ( WinHttpRequestOption_EscapePercentInURL + 1 ) , WinHttpRequestOption_SelectCertificate = ( WinHttpRequestOption_SslErrorIgnoreFlags + 1 ) , WinHttpRequestOption_EnableRedirects = ( WinHttpRequestOption_SelectCertificate + 1 ) , WinHttpRequestOption_UrlEscapeDisable = ( WinHttpRequestOption_EnableRedirects + 1 ) , WinHttpRequestOption_UrlEscapeDisableQuery = ( WinHttpRequestOption_UrlEscapeDisable + 1 ) , WinHttpRequestOption_SecureProtocols = ( WinHttpRequestOption_UrlEscapeDisableQuery + 1 ) , WinHttpRequestOption_EnableTracing = ( WinHttpRequestOption_SecureProtocols + 1 ) , WinHttpRequestOption_RevertImpersonationOverSsl = ( WinHttpRequestOption_EnableTracing + 1 ) , WinHttpRequestOption_EnableHttpsToHttpRedirects = ( WinHttpRequestOption_RevertImpersonationOverSsl + 1 ) , WinHttpRequestOption_EnablePassportAuthentication = ( WinHttpRequestOption_EnableHttpsToHttpRedirects + 1 ) , WinHttpRequestOption_MaxAutomaticRedirects = ( WinHttpRequestOption_EnablePassportAuthentication + 1 ) , WinHttpRequestOption_MaxResponseHeaderSize = ( WinHttpRequestOption_MaxAutomaticRedirects + 1 ) , WinHttpRequestOption_MaxResponseDrainSize = ( WinHttpRequestOption_MaxResponseHeaderSize + 1 ) , WinHttpRequestOption_EnableHttp1_1 = ( WinHttpRequestOption_MaxResponseDrainSize + 1 ) , WinHttpRequestOption_EnableCertificateRevocationCheck = ( WinHttpRequestOption_EnableHttp1_1 + 1 ) , WinHttpRequestOption_RejectUserpwd = ( WinHttpRequestOption_EnableCertificateRevocationCheck + 1 ) } WinHttpRequestOption; typedef /* [uuid] */ DECLSPEC_UUID("9d8a6df8-13de-4b1f-a330-67c719d62514") enum WinHttpRequestAutoLogonPolicy { AutoLogonPolicy_Always = 0, AutoLogonPolicy_OnlyIfBypassProxy = ( AutoLogonPolicy_Always + 1 ) , AutoLogonPolicy_Never = ( AutoLogonPolicy_OnlyIfBypassProxy + 1 ) } WinHttpRequestAutoLogonPolicy; typedef /* [uuid] */ DECLSPEC_UUID("152a1ca2-55a9-43a3-b187-0605bb886349") enum WinHttpRequestSslErrorFlags { SslErrorFlag_UnknownCA = 0x100, SslErrorFlag_CertWrongUsage = 0x200, SslErrorFlag_CertCNInvalid = 0x1000, SslErrorFlag_CertDateInvalid = 0x2000, SslErrorFlag_Ignore_All = 0x3300 } WinHttpRequestSslErrorFlags; typedef /* [uuid] */ DECLSPEC_UUID("6b2c51c1-a8ea-46bd-b928-c9b76f9f14dd") enum WinHttpRequestSecureProtocols { SecureProtocol_SSL2 = 0x8, SecureProtocol_SSL3 = 0x20, SecureProtocol_TLS1 = 0x80, SecureProtocol_ALL = 0xa8 } WinHttpRequestSecureProtocols; EXTERN_C const IID LIBID_WinHttp; #ifndef __IWinHttpRequest_INTERFACE_DEFINED__ #define __IWinHttpRequest_INTERFACE_DEFINED__ /* interface IWinHttpRequest */ /* [unique][helpstring][nonextensible][oleautomation][dual][uuid][object] */ EXTERN_C const IID IID_IWinHttpRequest; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("016fe2ec-b2c8-45f8-b23b-39e53a75396b") IWinHttpRequest : public IDispatch { public: virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE SetProxy( /* [in] */ HTTPREQUEST_PROXY_SETTING ProxySetting, /* [optional][in] */ VARIANT ProxyServer, /* [optional][in] */ VARIANT BypassList) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE SetCredentials( /* [in] */ BSTR UserName, /* [in] */ BSTR Password, /* [in] */ HTTPREQUEST_SETCREDENTIALS_FLAGS Flags) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Open( /* [in] */ BSTR Method, /* [in] */ BSTR Url, /* [optional][in] */ VARIANT Async) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE SetRequestHeader( /* [in] */ BSTR Header, /* [in] */ BSTR Value) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE GetResponseHeader( /* [in] */ BSTR Header, /* [retval][out] */ BSTR *Value) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE GetAllResponseHeaders( /* [retval][out] */ BSTR *Headers) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Send( /* [optional][in] */ VARIANT Body) = 0; virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Status( /* [retval][out] */ long *Status) = 0; virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StatusText( /* [retval][out] */ BSTR *Status) = 0; virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_ResponseText( /* [retval][out] */ BSTR *Body) = 0; virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_ResponseBody( /* [retval][out] */ VARIANT *Body) = 0; virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_ResponseStream( /* [retval][out] */ VARIANT *Body) = 0; virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_Option( /* [in] */ WinHttpRequestOption Option, /* [retval][out] */ VARIANT *Value) = 0; virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_Option( /* [in] */ WinHttpRequestOption Option, /* [in] */ VARIANT Value) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE WaitForResponse( /* [optional][in] */ VARIANT Timeout, /* [retval][out] */ VARIANT_BOOL *Succeeded) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Abort( void) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE SetTimeouts( /* [in] */ long ResolveTimeout, /* [in] */ long ConnectTimeout, /* [in] */ long SendTimeout, /* [in] */ long ReceiveTimeout) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE SetClientCertificate( /* [in] */ BSTR ClientCertificate) = 0; virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE SetAutoLogonPolicy( /* [in] */ WinHttpRequestAutoLogonPolicy AutoLogonPolicy) = 0; }; #else /* C style interface */ typedef struct IWinHttpRequestVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IWinHttpRequest * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IWinHttpRequest * This); ULONG ( STDMETHODCALLTYPE *Release )( IWinHttpRequest * This); HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( IWinHttpRequest * This, /* [out] */ UINT *pctinfo); HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( IWinHttpRequest * This, /* [in] */ UINT iTInfo, /* [in] */ LCID lcid, /* [out] */ ITypeInfo **ppTInfo); HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( IWinHttpRequest * This, /* [in] */ REFIID riid, /* [size_is][in] */ LPOLESTR *rgszNames, /* [range][in] */ UINT cNames, /* [in] */ LCID lcid, /* [size_is][out] */ DISPID *rgDispId); /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( IWinHttpRequest * This, /* [annotation][in] */ _In_ DISPID dispIdMember, /* [annotation][in] */ _In_ REFIID riid, /* [annotation][in] */ _In_ LCID lcid, /* [annotation][in] */ _In_ WORD wFlags, /* [annotation][out][in] */ _In_ DISPPARAMS *pDispParams, /* [annotation][out] */ _Out_opt_ VARIANT *pVarResult, /* [annotation][out] */ _Out_opt_ EXCEPINFO *pExcepInfo, /* [annotation][out] */ _Out_opt_ UINT *puArgErr); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *SetProxy )( IWinHttpRequest * This, /* [in] */ HTTPREQUEST_PROXY_SETTING ProxySetting, /* [optional][in] */ VARIANT ProxyServer, /* [optional][in] */ VARIANT BypassList); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *SetCredentials )( IWinHttpRequest * This, /* [in] */ BSTR UserName, /* [in] */ BSTR Password, /* [in] */ HTTPREQUEST_SETCREDENTIALS_FLAGS Flags); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Open )( IWinHttpRequest * This, /* [in] */ BSTR Method, /* [in] */ BSTR Url, /* [optional][in] */ VARIANT Async); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *SetRequestHeader )( IWinHttpRequest * This, /* [in] */ BSTR Header, /* [in] */ BSTR Value); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetResponseHeader )( IWinHttpRequest * This, /* [in] */ BSTR Header, /* [retval][out] */ BSTR *Value); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetAllResponseHeaders )( IWinHttpRequest * This, /* [retval][out] */ BSTR *Headers); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Send )( IWinHttpRequest * This, /* [optional][in] */ VARIANT Body); /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Status )( IWinHttpRequest * This, /* [retval][out] */ long *Status); /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_StatusText )( IWinHttpRequest * This, /* [retval][out] */ BSTR *Status); /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_ResponseText )( IWinHttpRequest * This, /* [retval][out] */ BSTR *Body); /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_ResponseBody )( IWinHttpRequest * This, /* [retval][out] */ VARIANT *Body); /* [helpstring][id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_ResponseStream )( IWinHttpRequest * This, /* [retval][out] */ VARIANT *Body); /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Option )( IWinHttpRequest * This, /* [in] */ WinHttpRequestOption Option, /* [retval][out] */ VARIANT *Value); /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE *put_Option )( IWinHttpRequest * This, /* [in] */ WinHttpRequestOption Option, /* [in] */ VARIANT Value); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *WaitForResponse )( IWinHttpRequest * This, /* [optional][in] */ VARIANT Timeout, /* [retval][out] */ VARIANT_BOOL *Succeeded); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Abort )( IWinHttpRequest * This); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *SetTimeouts )( IWinHttpRequest * This, /* [in] */ long ResolveTimeout, /* [in] */ long ConnectTimeout, /* [in] */ long SendTimeout, /* [in] */ long ReceiveTimeout); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *SetClientCertificate )( IWinHttpRequest * This, /* [in] */ BSTR ClientCertificate); /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *SetAutoLogonPolicy )( IWinHttpRequest * This, /* [in] */ WinHttpRequestAutoLogonPolicy AutoLogonPolicy); END_INTERFACE } IWinHttpRequestVtbl; interface IWinHttpRequest { CONST_VTBL struct IWinHttpRequestVtbl *lpVtbl; }; #ifdef COBJMACROS #define IWinHttpRequest_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IWinHttpRequest_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IWinHttpRequest_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IWinHttpRequest_GetTypeInfoCount(This,pctinfo) \ ( (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) ) #define IWinHttpRequest_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ ( (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) ) #define IWinHttpRequest_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ ( (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) ) #define IWinHttpRequest_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ ( (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) ) #define IWinHttpRequest_SetProxy(This,ProxySetting,ProxyServer,BypassList) \ ( (This)->lpVtbl -> SetProxy(This,ProxySetting,ProxyServer,BypassList) ) #define IWinHttpRequest_SetCredentials(This,UserName,Password,Flags) \ ( (This)->lpVtbl -> SetCredentials(This,UserName,Password,Flags) ) #define IWinHttpRequest_Open(This,Method,Url,Async) \ ( (This)->lpVtbl -> Open(This,Method,Url,Async) ) #define IWinHttpRequest_SetRequestHeader(This,Header,Value) \ ( (This)->lpVtbl -> SetRequestHeader(This,Header,Value) ) #define IWinHttpRequest_GetResponseHeader(This,Header,Value) \ ( (This)->lpVtbl -> GetResponseHeader(This,Header,Value) ) #define IWinHttpRequest_GetAllResponseHeaders(This,Headers) \ ( (This)->lpVtbl -> GetAllResponseHeaders(This,Headers) ) #define IWinHttpRequest_Send(This,Body) \ ( (This)->lpVtbl -> Send(This,Body) ) #define IWinHttpRequest_get_Status(This,Status) \ ( (This)->lpVtbl -> get_Status(This,Status) ) #define IWinHttpRequest_get_StatusText(This,Status) \ ( (This)->lpVtbl -> get_StatusText(This,Status) ) #define IWinHttpRequest_get_ResponseText(This,Body) \ ( (This)->lpVtbl -> get_ResponseText(This,Body) ) #define IWinHttpRequest_get_ResponseBody(This,Body) \ ( (This)->lpVtbl -> get_ResponseBody(This,Body) ) #define IWinHttpRequest_get_ResponseStream(This,Body) \ ( (This)->lpVtbl -> get_ResponseStream(This,Body) ) #define IWinHttpRequest_get_Option(This,Option,Value) \ ( (This)->lpVtbl -> get_Option(This,Option,Value) ) #define IWinHttpRequest_put_Option(This,Option,Value) \ ( (This)->lpVtbl -> put_Option(This,Option,Value) ) #define IWinHttpRequest_WaitForResponse(This,Timeout,Succeeded) \ ( (This)->lpVtbl -> WaitForResponse(This,Timeout,Succeeded) ) #define IWinHttpRequest_Abort(This) \ ( (This)->lpVtbl -> Abort(This) ) #define IWinHttpRequest_SetTimeouts(This,ResolveTimeout,ConnectTimeout,SendTimeout,ReceiveTimeout) \ ( (This)->lpVtbl -> SetTimeouts(This,ResolveTimeout,ConnectTimeout,SendTimeout,ReceiveTimeout) ) #define IWinHttpRequest_SetClientCertificate(This,ClientCertificate) \ ( (This)->lpVtbl -> SetClientCertificate(This,ClientCertificate) ) #define IWinHttpRequest_SetAutoLogonPolicy(This,AutoLogonPolicy) \ ( (This)->lpVtbl -> SetAutoLogonPolicy(This,AutoLogonPolicy) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IWinHttpRequest_INTERFACE_DEFINED__ */ #ifndef __IWinHttpRequestEvents_INTERFACE_DEFINED__ #define __IWinHttpRequestEvents_INTERFACE_DEFINED__ /* interface IWinHttpRequestEvents */ /* [unique][helpstring][nonextensible][oleautomation][uuid][object] */ EXTERN_C const IID IID_IWinHttpRequestEvents; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("f97f4e15-b787-4212-80d1-d380cbbf982e") IWinHttpRequestEvents : public IUnknown { public: virtual void STDMETHODCALLTYPE OnResponseStart( /* [in] */ long Status, /* [in] */ BSTR ContentType) = 0; virtual void STDMETHODCALLTYPE OnResponseDataAvailable( /* [in] */ SAFEARRAY * *Data) = 0; virtual void STDMETHODCALLTYPE OnResponseFinished( void) = 0; virtual void STDMETHODCALLTYPE OnError( /* [in] */ long ErrorNumber, /* [in] */ BSTR ErrorDescription) = 0; }; #else /* C style interface */ typedef struct IWinHttpRequestEventsVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IWinHttpRequestEvents * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IWinHttpRequestEvents * This); ULONG ( STDMETHODCALLTYPE *Release )( IWinHttpRequestEvents * This); void ( STDMETHODCALLTYPE *OnResponseStart )( IWinHttpRequestEvents * This, /* [in] */ long Status, /* [in] */ BSTR ContentType); void ( STDMETHODCALLTYPE *OnResponseDataAvailable )( IWinHttpRequestEvents * This, /* [in] */ SAFEARRAY * *Data); void ( STDMETHODCALLTYPE *OnResponseFinished )( IWinHttpRequestEvents * This); void ( STDMETHODCALLTYPE *OnError )( IWinHttpRequestEvents * This, /* [in] */ long ErrorNumber, /* [in] */ BSTR ErrorDescription); END_INTERFACE } IWinHttpRequestEventsVtbl; interface IWinHttpRequestEvents { CONST_VTBL struct IWinHttpRequestEventsVtbl *lpVtbl; }; #ifdef COBJMACROS #define IWinHttpRequestEvents_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IWinHttpRequestEvents_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IWinHttpRequestEvents_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IWinHttpRequestEvents_OnResponseStart(This,Status,ContentType) \ ( (This)->lpVtbl -> OnResponseStart(This,Status,ContentType) ) #define IWinHttpRequestEvents_OnResponseDataAvailable(This,Data) \ ( (This)->lpVtbl -> OnResponseDataAvailable(This,Data) ) #define IWinHttpRequestEvents_OnResponseFinished(This) \ ( (This)->lpVtbl -> OnResponseFinished(This) ) #define IWinHttpRequestEvents_OnError(This,ErrorNumber,ErrorDescription) \ ( (This)->lpVtbl -> OnError(This,ErrorNumber,ErrorDescription) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IWinHttpRequestEvents_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_WinHttpRequest; #ifdef __cplusplus class DECLSPEC_UUID("2087c2f4-2cef-4953-a8ab-66779b670495") WinHttpRequest; #endif #endif /* __WinHttp_LIBRARY_DEFINED__ */ /* Additional Prototypes for ALL interfaces */ /* end of Additional Prototypes */ #ifdef __cplusplus } #endif #endif ================================================ FILE: networking/ioapi.h ================================================ /* ioapi.h -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant Modified to integrate with WinHttpRequest. */ #ifndef _ZLIBIOAPI_H #define _ZLIBIOAPI_H #define ZLIB_FILEFUNC_SEEK_CUR (1) #define ZLIB_FILEFUNC_SEEK_END (2) #define ZLIB_FILEFUNC_SEEK_SET (0) #define ZLIB_FILEFUNC_MODE_READ (1) #define ZLIB_FILEFUNC_MODE_WRITE (2) #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) #define ZLIB_FILEFUNC_MODE_EXISTING (4) #define ZLIB_FILEFUNC_MODE_CREATE (8) #ifndef ZCALLBACK #if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) #define ZCALLBACK CALLBACK #else #define ZCALLBACK #endif #endif #ifdef __cplusplus extern "C" { #endif typedef voidpf(ZCALLBACK* open_file_func) OF((voidpf opaque, voidpf file, int mode)); typedef uLong(ZCALLBACK* read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); typedef uLong(ZCALLBACK* write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); typedef uLong(ZCALLBACK* tell_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK* seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); typedef int (ZCALLBACK* close_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK* testerror_file_func) OF((voidpf opaque, voidpf stream)); typedef struct zlib_filefunc_def_s { open_file_func zopen_file; read_file_func zread_file; write_file_func zwrite_file; tell_file_func ztell_file; seek_file_func zseek_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc_def; void fill_qiodevice_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); #define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) #define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) #define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) #define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) #define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) #ifdef __cplusplus } #endif #endif ================================================ FILE: networking/networking.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {3DE6C2D2-FDFC-4745-8282-981DF7561405} Win32Proj unzip networking StaticLibrary true Unicode StaticLibrary true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode Level3 Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true Windows Level3 Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true Windows Level3 MaxSpeed true true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true Windows true true Level3 MaxSpeed true true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true Windows true true ================================================ FILE: networking/networking.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files ================================================ FILE: networking/unzip.c ================================================ /* unzip.c -- IO for uncompress .zip files using zlib Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant Read unzip.h for more info Modified to integrate with WinHttpRequest. */ /* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of compatibility with older software. The following is from the original crypt.c. Code woven in by Terry Thorsen 1/2003. */ /* Copyright (c) 1990-2000 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2000-Apr-09 or later (the contents of which are also included in zip.h) for terms of use. If, for some reason, all these files are missing, the Info-ZIP license also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html */ /* crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) */ /* This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). */ #include #include #include #include "zlib.h" #include "unzip.h" #ifdef STDC # include # include # include #endif #ifdef NO_ERRNO_H extern int errno; #else # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef CASESENSITIVITYDEFAULT_NO # if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) # define CASESENSITIVITYDEFAULT_NO # endif #endif #ifndef UNZ_BUFSIZE #define UNZ_BUFSIZE (16384) #endif #ifndef UNZ_MAXFILENAMEINZIP #define UNZ_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) free(p);} #endif #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; /* unz_file_info_interntal contain internal info about a file in zipfile*/ typedef struct unz_file_info_internal_s { uLong offset_curfile;/* relative offset of local header 4 bytes */ } unz_file_info_internal; /* file_in_zip_read_info_s contain internal information about a file in zipfile, when reading and decompress it */ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ uLong offset_local_extrafield;/* offset of the local extra field */ uInt size_local_extrafield;/* size of the local extra field */ uLong pos_local_extrafield; /* position in the local extra field in read*/ uLong crc32; /* crc32 of all data uncompressed */ uLong crc32_wait; /* crc32 we must obtain after decompress all */ uLong rest_read_compressed; /* number of byte to be decompressed */ uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ zlib_filefunc_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ uLong compression_method; /* compression method (0==store) */ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ int raw; } file_in_zip_read_info_s; /* unz_s contain internal information about the zipfile */ typedef struct { zlib_filefunc_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ unz_global_info gi; /* public global information */ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ uLong num_file; /* number of the current file in the zipfile*/ uLong pos_in_central_dir; /* pos of the current file in the central dir*/ uLong current_file_ok; /* flag about the usability of the current file*/ uLong central_pos; /* position of the beginning of the central dir*/ uLong size_central_dir; /* size of the central directory */ uLong offset_central_dir; /* offset of start of central directory with respect to the starting disk number */ unz_file_info cur_file_info; /* public info about the current file in zip*/ unz_file_info_internal cur_file_info_internal; /* private info about it*/ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current file if we are decompressing it */ int encrypted; # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const unsigned long* pcrc_32_tab; # endif } unz_s; #ifndef NOUNCRYPT #include "crypt.h" #endif /* =========================================================================== Read a byte from a gz_stream; update next_in and avail_in. Return EOF for end of file. IN assertion: the stream s has been sucessfully opened for reading. */ local int unzlocal_getByte OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, int *pi)); local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; int *pi; { unsigned char c; int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return UNZ_OK; } if (ZERROR(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; return UNZ_EOF; } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int unzlocal_getShort OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong *pX; { uLong x ; int i; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } local int unzlocal_getLong OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong *pX; { uLong x ; int i; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<16; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } /* My own strcmpi / strcasecmp */ local int strcmpcasenosensitive_internal (fileName1,fileName2) const char* fileName1; const char* fileName2; { for (;;) { char c1=*(fileName1++); char c2=*(fileName2++); if ((c1>='a') && (c1<='z')) c1 -= 0x20; if ((c2>='a') && (c2<='z')) c2 -= 0x20; if (c1=='\0') return ((c2=='\0') ? 0 : -1); if (c2=='\0') return 1; if (c1c2) return 1; } } #ifdef CASESENSITIVITYDEFAULT_NO #define CASESENSITIVITYDEFAULTVALUE 2 #else #define CASESENSITIVITYDEFAULTVALUE 1 #endif #ifndef STRCMPCASENOSENTIVEFUNCTION #define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal #endif /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) const char* fileName1; const char* fileName2; int iCaseSensitivity; { if (iCaseSensitivity==0) iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local uLong unzlocal_SearchCentralDir OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream)); local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; { unsigned char* buf; uLong uSizeFile; uLong uBackRead; uLong uMaxBack=0xffff; /* maximum size of global comment */ uLong uPosFound=0; if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } /* Open a Zip file. path contain the full pathname (by example, on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer "zlib/zlib114.zip". If the zipfile cannot be opened (file doesn't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ extern unzFile ZEXPORT unzOpen2 (file, pzlib_filefunc_def) voidpf file; zlib_filefunc_def* pzlib_filefunc_def; { unz_s us; unz_s *s; uLong central_pos,uL; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ uLong number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ int err=UNZ_OK; if (unz_copyright[0]!=' ') return NULL; if (pzlib_filefunc_def==NULL) fill_qiodevice_filefunc(&us.z_filefunc); else us.z_filefunc = *pzlib_filefunc_def; us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, file, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); if (us.filestream==NULL) return NULL; central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); if (central_pos==0) err=UNZ_ERRNO; if (ZSEEK(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir on this disk */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* zipfile comment length */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; if ((central_pospfile_in_zip_read!=NULL) unzCloseCurrentFile(file); ZCLOSE(s->z_filefunc, s->filestream); TRYFREE(s); return UNZ_OK; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) unzFile file; unz_global_info *pglobal_info; { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; *pglobal_info=s->gi; return UNZ_OK; } /* Translate date/time from Dos format to tm_unz (readable more easilty) */ local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) uLong ulDosDate; tm_unz* ptm; { uLong uDate; uDate = (uLong)(ulDosDate>>16); ptm->tm_mday = (uInt)(uDate&0x1f) ; ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, unz_file_info *pfile_info, unz_file_info_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); local int unzlocal_GetCurrentFileInfoInternal (file, pfile_info, pfile_info_internal, szFileName, fileNameBufferSize, extraField, extraFieldBufferSize, szComment, commentBufferSize) unzFile file; unz_file_info *pfile_info; unz_file_info_internal *pfile_info_internal; char *szFileName; uLong fileNameBufferSize; void *extraField; uLong extraFieldBufferSize; char *szComment; uLong commentBufferSize; { unz_s* s; unz_file_info file_info; unz_file_info_internal file_info_internal; int err=UNZ_OK; uLong uMagic; uLong uSeek=0; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (ZSEEK(s->z_filefunc, s->filestream, s->pos_in_central_dir+s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* we check the magic */ if (err==UNZ_OK) { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; uSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename0) && (fileNameBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; uSeek -= uSizeRead; } if ((err==UNZ_OK) && (extraField!=NULL)) { uLong uSizeRead ; if (file_info.size_file_extraz_filefunc, s->filestream,uSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) uSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; uSeek += file_info.size_file_extra - uSizeRead; } else uSeek+=file_info.size_file_extra; if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_commentz_filefunc, s->filestream,uSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) uSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; uSeek+=file_info.size_file_comment - uSizeRead; } else uSeek+=file_info.size_file_comment; if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) *pfile_info_internal=file_info_internal; return err; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetCurrentFileInfo (file, pfile_info, szFileName, fileNameBufferSize, extraField, extraFieldBufferSize, szComment, commentBufferSize) unzFile file; unz_file_info *pfile_info; char *szFileName; uLong fileNameBufferSize; void *extraField; uLong extraFieldBufferSize; char *szComment; uLong commentBufferSize; { return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToFirstFile (file) unzFile file; { int err=UNZ_OK; unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzGoToNextFile (file) unzFile file; { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ if (s->num_file+1==s->gi.number_entry) return UNZ_END_OF_LIST_OF_FILE; s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; s->num_file++; err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzipStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) unzFile file; const char *szFileName; int iCaseSensitivity; { unz_s* s; int err; /* We remember the 'current' position in the file so that we can jump * back there if we fail. */ unz_file_info cur_file_infoSaved; unz_file_info_internal cur_file_info_internalSaved; uLong num_fileSaved; uLong pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; /* Save the current state */ num_fileSaved = s->num_file; pos_in_central_dirSaved = s->pos_in_central_dir; cur_file_infoSaved = s->cur_file_info; cur_file_info_internalSaved = s->cur_file_info_internal; err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; err = unzGetCurrentFileInfo(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1, NULL,0,NULL,0); if (err == UNZ_OK) { if (unzStringFileNameCompare(szCurrentFileName, szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } } /* We failed, so restore the state of the 'current file' to where we * were. */ s->num_file = num_fileSaved ; s->pos_in_central_dir = pos_in_central_dirSaved ; s->cur_file_info = cur_file_infoSaved; s->cur_file_info_internal = cur_file_info_internalSaved; return err; } /* /////////////////////////////////////////// // Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) // I need random access // // Further optimization could be realized by adding an ability // to cache the directory in memory. The goal being a single // comprehensive file read to put the file I need in a memory. */ /* typedef struct unz_file_pos_s { uLong pos_in_zip_directory; // offset in file uLong num_of_file; // # of file } unz_file_pos; */ extern int ZEXPORT unzGetFilePos(file, file_pos) unzFile file; unz_file_pos* file_pos; { unz_s* s; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; file_pos->pos_in_zip_directory = s->pos_in_central_dir; file_pos->num_of_file = s->num_file; return UNZ_OK; } extern int ZEXPORT unzGoToFilePos(file, file_pos) unzFile file; unz_file_pos* file_pos; { unz_s* s; int err; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; /* jump to the right spot */ s->pos_in_central_dir = file_pos->pos_in_zip_directory; s->num_file = file_pos->num_of_file; /* set the current file */ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); /* return results */ s->current_file_ok = (err == UNZ_OK); return err; } /* // Unzip Helper Functions - should be here? /////////////////////////////////////////// */ /* Read the local header of the current zipfile Check the coherency of the local header and info in the end of central directory about this file store in *piSizeVar the size of extra info in local header (filename and size of extra field data) */ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, poffset_local_extrafield, psize_local_extrafield) unz_s* s; uInt* piSizeVar; uLong *poffset_local_extrafield; uInt *psize_local_extrafield; { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; int err=UNZ_OK; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (err==UNZ_OK) { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; /* else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) err=UNZ_BADZIPFILE; */ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) err=UNZ_BADZIPFILE; *piSizeVar += (uInt)size_filename; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) err=UNZ_ERRNO; *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; *psize_local_extrafield = (uInt)size_extra_field; *piSizeVar += (uInt)size_extra_field; return err; } /* Open for reading data the current file in the zipfile. If there is no error and the file is opened, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) unzFile file; int* method; int* level; int raw; const char* password; { int err=UNZ_OK; uInt iSizeVar; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uLong offset_local_extrafield; /* offset of the local extra field */ uInt size_local_extrafield; /* size of the local extra field */ # ifndef NOUNCRYPT char source[12]; # else if (password != NULL) return UNZ_PARAMERROR; # endif if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip_read_info_s*) ALLOC(sizeof(file_in_zip_read_info_s)); if (pfile_in_zip_read_info==NULL) return UNZ_INTERNALERROR; pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; pfile_in_zip_read_info->pos_local_extrafield=0; pfile_in_zip_read_info->raw=raw; if (pfile_in_zip_read_info->read_buffer==NULL) { TRYFREE(pfile_in_zip_read_info); return UNZ_INTERNALERROR; } pfile_in_zip_read_info->stream_initialised=0; if (method!=NULL) *method = (int)s->cur_file_info.compression_method; if (level!=NULL) { *level = 6; switch (s->cur_file_info.flag & 0x06) { case 6 : *level = 1; break; case 4 : *level = 2; break; case 2 : *level = 9; break; } } if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; pfile_in_zip_read_info->filestream=s->filestream; pfile_in_zip_read_info->z_filefunc=s->z_filefunc; pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; pfile_in_zip_read_info->stream.total_out = 0; if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) { pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = (voidpf)0; pfile_in_zip_read_info->stream.avail_in = 0; err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=1; else { TRYFREE(pfile_in_zip_read_info); return err; } /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. * In unzip, i don't wait absolutely Z_STREAM_END because I known the * size of both compressed and uncompressed data */ } pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + iSizeVar; pfile_in_zip_read_info->stream.avail_in = (uInt)0; s->pfile_in_zip_read = pfile_in_zip_read_info; # ifndef NOUNCRYPT if (password != NULL) { int i; s->pcrc_32_tab = get_crc_table(); init_keys(password,s->keys,s->pcrc_32_tab); if (ZSEEK(s->z_filefunc, s->filestream, s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, SEEK_SET)!=0) return UNZ_INTERNALERROR; if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) return UNZ_INTERNALERROR; for (i = 0; i<12; i++) zdecode(s->keys,s->pcrc_32_tab,source[i]); s->pfile_in_zip_read->pos_in_zipfile+=12; s->encrypted=1; } # endif return UNZ_OK; } extern int ZEXPORT unzOpenCurrentFile (file) unzFile file; { return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } extern int ZEXPORT unzOpenCurrentFilePassword (file, password) unzFile file; const char* password; { return unzOpenCurrentFile3(file, NULL, NULL, 0, password); } extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) unzFile file; int* method; int* level; int raw; { return unzOpenCurrentFile3(file, method, level, raw, NULL); } /* Read bytes from the current file. buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern int ZEXPORT unzReadCurrentFile (file, buf, len) unzFile file; voidp buf; unsigned len; { int err=UNZ_OK; uInt iRead = 0; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->read_buffer == NULL) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; pfile_in_zip_read_info->stream.avail_out = (uInt)len; if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && (!(pfile_in_zip_read_info->raw))) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; if ((len>pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in) && (pfile_in_zip_read_info->raw)) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in; while (pfile_in_zip_read_info->stream.avail_out>0) { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) { uInt uReadThis = UNZ_BUFSIZE; if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; if (uReadThis == 0) return UNZ_EOF; if (ZSEEK(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->read_buffer, uReadThis)!=uReadThis) return UNZ_ERRNO; # ifndef NOUNCRYPT if(s->encrypted) { uInt i; for(i=0;iread_buffer[i] = zdecode(s->keys,s->pcrc_32_tab, pfile_in_zip_read_info->read_buffer[i]); } # endif pfile_in_zip_read_info->pos_in_zipfile += uReadThis; pfile_in_zip_read_info->rest_read_compressed-=uReadThis; pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = uReadThis; } if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) { uInt uDoCopy,i ; if ((pfile_in_zip_read_info->stream.avail_in == 0) && (pfile_in_zip_read_info->rest_read_compressed == 0)) return (iRead==0) ? UNZ_EOF : iRead; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) uDoCopy = pfile_in_zip_read_info->stream.avail_out ; else uDoCopy = pfile_in_zip_read_info->stream.avail_in ; for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, pfile_in_zip_read_info->stream.next_out, uDoCopy); pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; pfile_in_zip_read_info->stream.next_in += uDoCopy; pfile_in_zip_read_info->stream.total_out += uDoCopy; iRead += uDoCopy; } else { uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; uLong uOutThis; int flush=Z_SYNC_FLUSH; uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; bufBefore = pfile_in_zip_read_info->stream.next_out; /* if ((pfile_in_zip_read_info->rest_read_uncompressed == pfile_in_zip_read_info->stream.avail_out) && (pfile_in_zip_read_info->rest_read_compressed == 0)) flush = Z_FINISH; */ err=inflate(&pfile_in_zip_read_info->stream,flush); if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) err = Z_DATA_ERROR; uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=Z_OK) break; } } if (err==Z_OK) return iRead; return err; } /* Give the current position in uncompressed data */ extern z_off_t ZEXPORT unztell (file) unzFile file; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; return (z_off_t)pfile_in_zip_read_info->stream.total_out; } /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzeof (file) unzFile file; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) return 1; return 0; } /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field that can be read if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) unzFile file; voidp buf; unsigned len; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uInt read_now; uLong size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; size_to_read = (pfile_in_zip_read_info->size_local_extrafield - pfile_in_zip_read_info->pos_local_extrafield); if (buf==NULL) return (int)size_to_read; if (len>size_to_read) read_now = (uInt)size_to_read; else read_now = (uInt)len ; if (read_now==0) return 0; if (ZSEEK(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, buf,read_now)!=read_now) return UNZ_ERRNO; return (int)read_now; } /* Close the file in zip opened with unzipOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzCloseCurrentFile (file) unzFile file; { int err=UNZ_OK; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && (!pfile_in_zip_read_info->raw)) { if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) err=UNZ_CRCERROR; } TRYFREE(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised) inflateEnd(&pfile_in_zip_read_info->stream); pfile_in_zip_read_info->stream_initialised = 0; TRYFREE(pfile_in_zip_read_info); s->pfile_in_zip_read=NULL; return err; } /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) unzFile file; char *szComment; uLong uSizeBuf; { unz_s* s; uLong uReadThis ; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; return (int)uReadThis; } /* Additions by RX '2004 */ extern uLong ZEXPORT unzGetOffset (file) unzFile file; { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return 0; if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) if (s->num_file==s->gi.number_entry) return 0; return s->pos_in_central_dir; } extern int ZEXPORT unzSetOffset (file, pos) unzFile file; uLong pos; { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir = pos; s->num_file = s->gi.number_entry; /* hack */ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } ================================================ FILE: networking/unzip.h ================================================ /* unzip.h -- IO for uncompress .zip files using zlib Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. Multi volume ZipFile (span) are not supported. Encryption compatible with pkzip 2.04g only supported Old compressions used by old PKZip 1.x are not supported I WAIT FEEDBACK at mail info@winimage.com Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Modified to integrate with WinHttpRequest. */ /* for more info about .ZIP format, see http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip http://www.info-zip.org/pub/infozip/doc/ PkWare has also a specification at : ftp://ftp.pkware.com/probdesc.zip */ #ifndef _unz_H #define _unz_H #ifdef __cplusplus extern "C" { #endif #ifndef _ZLIB_H #include "zlib.h" #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagunzFile__ { int unused; } unzFile__; typedef unzFile__* unzFile; #else typedef voidp unzFile; #endif #define UNZ_OK (0) #define UNZ_END_OF_LIST_OF_FILE (-100) #define UNZ_ERRNO (Z_ERRNO) #define UNZ_EOF (0) #define UNZ_PARAMERROR (-102) #define UNZ_BADZIPFILE (-103) #define UNZ_INTERNALERROR (-104) #define UNZ_CRCERROR (-105) /* tm_unz contain date/time info */ typedef struct tm_unz_s { uInt tm_sec; /* seconds after the minute - [0,59] */ uInt tm_min; /* minutes after the hour - [0,59] */ uInt tm_hour; /* hours since midnight - [0,23] */ uInt tm_mday; /* day of the month - [1,31] */ uInt tm_mon; /* months since January - [0,11] */ uInt tm_year; /* years - [1980..2044] */ } tm_unz; /* unz_global_info structure contain global data about the ZIPfile These data comes from the end of central dir */ typedef struct unz_global_info_s { uLong number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info; /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_info_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ uLong compressed_size; /* compressed size 4 bytes */ uLong uncompressed_size; /* uncompressed size 4 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info; extern int ZEXPORT unzStringFileNameCompare OF((const char* fileName1, const char* fileName2, int iCaseSensitivity)); /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern unzFile ZEXPORT unzOpen OF((voidpf file)); /* Open a Zip file. path contain whatever zopen_file from the IO API accepts. For Qt implementation it is a pointer to QIODevice, for fopen() implementation it's a file name. If the zipfile cannot be opened (file don't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ extern unzFile ZEXPORT unzOpen2 OF((voidpf file, zlib_filefunc_def* pzlib_filefunc_def)); /* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern int ZEXPORT unzClose OF((unzFile file)); /* Close a ZipFile opened with unzipOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzipCloseCurrentFile before call unzipClose. return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, unz_global_info* pglobal_info)); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalComment OF((unzFile file, char* szComment, uLong uSizeBuf)); /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ /***************************************************************************/ /* Unzip package allow you browse the directory of the zipfile */ extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToNextFile OF((unzFile file)); /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzLocateFile OF((unzFile file, const char* szFileName, int iCaseSensitivity)); /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ /* ****************************************** */ /* Ryan supplied functions */ /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_pos_s { uLong pos_in_zip_directory; /* offset in zip file directory */ uLong num_of_file; /* # of file */ } unz_file_pos; extern int ZEXPORT unzGetFilePos( unzFile file, unz_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos( unzFile file, unz_file_pos* file_pos); /* ****************************************** */ extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, unz_file_info* pfile_info, char* szFileName, uLong fileNameBufferSize, void* extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize)); /* Get Info about the current file if pfile_info!=NULL, the *pfile_info structure will contain somes info about the current file if szFileName!=NULL, the filemane string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). This is the Central-header version of the extra field if szComment!=NULL, the comment string of the file will be copied in szComment (commentBufferSize is the size of the buffer) */ /***************************************************************************/ /* for reading the content of the current zipfile, you can open it, read data from it, and close it (you can close it before reading all the file) */ extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); /* Open for reading data the current file in the zipfile. If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, const char* password)); /* Open for reading data the current file in the zipfile. password is a crypting password If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, int* method, int* level, int raw)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, int* method, int* level, int raw, const char* password)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzReadCurrentFile OF((unzFile file, voidp buf, unsigned len)); /* Read bytes from the current file (opened by unzOpenCurrentFile) buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern z_off_t ZEXPORT unztell OF((unzFile file)); /* Give the current position in uncompressed data */ extern int ZEXPORT unzeof OF((unzFile file)); /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, voidp buf, unsigned len)); /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ /***************************************************************************/ /* Get the current file offset */ extern uLong ZEXPORT unzGetOffset(unzFile file); /* Set the current file offset */ extern int ZEXPORT unzSetOffset(unzFile file, uLong pos); #ifdef __cplusplus } #endif #endif /* _unz_H */ ================================================ FILE: remove_pytube.cmd ================================================ RD "%LOCALAPPDATA%\pytube-master" /S /Q ================================================ FILE: remove_youtube_transcript_api.cmd ================================================ RD "%LOCALAPPDATA%\youtube-transcript-api-master" /S /Q ================================================ FILE: video/audioparserunnable.cpp ================================================ #include "ffmpegdecoder.h" #include "makeguard.h" #include "interlockedadd.h" #include #include #include #include #include namespace { uint8_t** getAudioData(AVFrame* audioFrame) { return audioFrame->extended_data != nullptr ? audioFrame->extended_data : &audioFrame->data[0]; } #if LIBAVUTIL_VERSION_MAJOR < 57 int64_t getChannelLayout(AVFrame* audioFrame) { const int audioFrameChannels = audioFrame->channels; return ((audioFrame->channel_layout != 0u) && audioFrameChannels == av_get_channel_layout_nb_channels(audioFrame->channel_layout)) ? audioFrame->channel_layout : av_get_default_channel_layout(audioFrameChannels); } #else auto getChannelLayout(AVFrame* audioFrame) { return audioFrame->ch_layout; } bool operator == (const AVChannelLayout& left, const AVChannelLayout& right) { return av_channel_layout_compare(&left, &right) == 0; } bool operator != (const AVChannelLayout& left, const AVChannelLayout& right) { return av_channel_layout_compare(&left, &right) != 0; } #endif } // namespace FFmpegDecoder::AudioParams::AudioParams(int freq, int chans, AVSampleFormat fmt) : frequency(freq), format(fmt) { #if LIBAVUTIL_VERSION_MAJOR < 57 channels = chans; channel_layout = av_get_default_channel_layout(chans); #else av_channel_layout_default(&channel_layout, chans); #endif } FFmpegDecoder::AudioParams::~AudioParams() { #if LIBAVUTIL_VERSION_MAJOR >= 57 av_channel_layout_uninit(&channel_layout); #endif } FFmpegDecoder::AudioParams& FFmpegDecoder::AudioParams::operator =(const FFmpegDecoder::AudioParams& other) { frequency = other.frequency; format = other.format; #if LIBAVUTIL_VERSION_MAJOR < 57 channels = other.channels; channel_layout = other.channel_layout; #else av_channel_layout_uninit(&channel_layout); av_channel_layout_copy(&channel_layout, &other.channel_layout); #endif return *this; } void FFmpegDecoder::audioParseRunnable() { CHANNEL_LOG(ffmpeg_threads) << "Audio thread started"; bool initialized = false; bool failed = false; m_audioPlayer->InitializeThread(); auto deinitializeThread = MakeGuard( m_audioPlayer.get(), std::mem_fn(&IAudioPlayer::DeinitializeThread)); std::vector resampleBuffer; double scheduledEndTime = 0; auto useHandleAudioResultLam = [this, &failed](bool result) { if (result) { if (failed) { failed = false; boost::lock_guard locker(m_isPausedMutex); if (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED) { m_audioPTS = (m_isPaused ? m_pauseTimer : GetHiResTime()) - m_videoStartClock; } } } else { failed = true; } }; while (!boost::this_thread::interruption_requested()) { AVPacket packet; if (!m_audioPacketsQueue.pop(packet)) { break; } auto packetGuard = MakeGuard(&packet, av_packet_unref); if (m_audioStreamNumber != packet.stream_index) { continue; } if (!initialized) { if (packet.pts == AV_NOPTS_VALUE) { if (packet.data == nullptr) continue; assert(false && "No audio pts found"); return; } const double pts = av_q2d(m_audioStream->time_base) * packet.pts; m_audioPTS = pts; scheduledEndTime = pts; // invoke changedFramePosition() if needed //AppendFrameClock(0); } else if (packet.pts != AV_NOPTS_VALUE) { const double diff = av_q2d(m_audioStream->time_base) * packet.pts - scheduledEndTime; if (diff > 0.01) { CHANNEL_LOG(ffmpeg_sync) << "Patching audio frame diff: " << diff; if (m_formatContexts.size() == 1 && m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED) { InterLockedAdd(m_videoStartClock, -diff); InterLockedAdd(m_audioPTS, diff); } else { const int size_multiplier = m_audioSettings.num_channels() * av_get_bytes_per_sample(m_audioSettings.format); const int numSteps = (diff + 0.1) / 0.1; const auto frame_clock = diff / numSteps; for (int i = 0; i < numSteps; ++i) { const auto speed = getSpeedRational(); const int nb_samples = frame_clock * m_audioSettings.frequency * speed.denominator / speed.numerator; const auto write_size = nb_samples * size_multiplier; std::vector buf(write_size, 0); useHandleAudioResultLam(handleAudioFrame(frame_clock, buf.data(), write_size, failed)); } } } scheduledEndTime += diff; } initialized = true; useHandleAudioResultLam(handleAudioPacket(packet, resampleBuffer, failed, scheduledEndTime)); } } bool FFmpegDecoder::handleAudioPacket( const AVPacket& packet, std::vector& resampleBuffer, bool failed, double& scheduledEndTime) { if (packet.stream_index != m_audioStream->index) { avcodec_free_context(&m_audioCodecContext); m_audioStream = m_formatContexts[m_audioContextIndex]->streams[packet.stream_index]; m_audioCodecContext = avcodec_alloc_context3(nullptr); if (m_audioCodecContext == nullptr) { return false; } if (!setupAudioCodec()) { return false; } } if (m_audioCodecContext == nullptr) { return false; } const int ret = avcodec_send_packet(m_audioCodecContext, &packet); if (ret < 0) { return ret == AVERROR(EAGAIN) || ret == AVERROR_EOF; } AVFramePtr audioFrame(av_frame_alloc()); bool result = true; while (avcodec_receive_frame(m_audioCodecContext, audioFrame.get()) == 0) { if (audioFrame->nb_samples <= 0) { continue; } const int original_buffer_size = av_samples_get_buffer_size( nullptr, #if LIBAVUTIL_VERSION_MAJOR < 57 audioFrame->channels, #else audioFrame->ch_layout.nb_channels, #endif audioFrame->nb_samples, static_cast(audioFrame->format), 1); // write buffer uint8_t* write_data = *getAudioData(audioFrame.get()); int64_t write_size = original_buffer_size; setupAudioSwrContext(audioFrame.get()); if (m_audioSwrContext != nullptr) { enum { EXTRA_SPACE = 256 }; const int out_count = static_cast(audioFrame->nb_samples) * m_audioSettings.frequency / m_audioCurrentPref.frequency + EXTRA_SPACE; const int size_multiplier = m_audioSettings.num_channels() * av_get_bytes_per_sample(m_audioSettings.format); const size_t buffer_size = out_count * size_multiplier; if (resampleBuffer.size() < buffer_size) { resampleBuffer.resize(buffer_size); } // Code for resampling uint8_t *out = resampleBuffer.data(); const int converted_size = swr_convert( m_audioSwrContext, &out, out_count, const_cast(getAudioData(audioFrame.get())), audioFrame->nb_samples); if (converted_size < 0) { BOOST_LOG_TRIVIAL(error) << "swr_convert() failed"; break; } if (converted_size == out_count) { BOOST_LOG_TRIVIAL(warning) << "audio buffer is probably too small"; swr_init(m_audioSwrContext); } write_data = out; write_size = converted_size * size_multiplier; assert(write_size < buffer_size); } const double frame_clock = audioFrame->sample_rate != 0? double(audioFrame->nb_samples) / audioFrame->sample_rate : 0; scheduledEndTime += frame_clock; if (!handleAudioFrame(frame_clock, write_data, write_size, failed)) { result = false; } } return result; } void FFmpegDecoder::setupAudioSwrContext(AVFrame* audioFrame) { const auto audioFrameFormat = static_cast(audioFrame->format); auto dec_channel_layout = getChannelLayout(audioFrame); const auto speed = getSpeedRational(); // Check if the new swr context required if (m_audioSwrContext == nullptr || audioFrameFormat != m_audioCurrentPref.format || dec_channel_layout != m_audioCurrentPref.channel_layout || (audioFrame->sample_rate * speed.numerator) / speed.denominator != m_audioCurrentPref.frequency) { swr_free(&m_audioSwrContext); #if LIBAVUTIL_VERSION_MAJOR < 57 m_audioSwrContext = swr_alloc_set_opts( nullptr, m_audioSettings.channel_layout, m_audioSettings.format, m_audioSettings.frequency * speed.denominator, dec_channel_layout, audioFrameFormat, audioFrame->sample_rate * speed.numerator, 0, nullptr); #else swr_alloc_set_opts2(&m_audioSwrContext, &m_audioSettings.channel_layout, m_audioSettings.format, m_audioSettings.frequency * speed.denominator, &dec_channel_layout, audioFrameFormat, audioFrame->sample_rate * speed.numerator, 0, nullptr); #endif if ((m_audioSwrContext == nullptr) || swr_init(m_audioSwrContext) < 0) { BOOST_LOG_TRIVIAL(error) << "unable to initialize swr convert context"; } m_audioCurrentPref.format = audioFrameFormat; #if LIBAVUTIL_VERSION_MAJOR < 57 m_audioCurrentPref.channels = audioFrame->channels; m_audioCurrentPref.channel_layout = dec_channel_layout; #else av_channel_layout_uninit(&m_audioCurrentPref.channel_layout); av_channel_layout_copy(&m_audioCurrentPref.channel_layout, &dec_channel_layout); #endif m_audioCurrentPref.frequency = (audioFrame->sample_rate * speed.numerator) / speed.denominator; } } bool FFmpegDecoder::handleAudioFrame( double frame_clock, uint8_t* write_data, int64_t write_size, bool failed) { bool skipAll = false; double delta = 0; bool isPaused = false; { boost::lock_guard locker(m_isPausedMutex); isPaused = m_isPaused; if (!isPaused) { delta = (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED) ? GetHiResTime() - m_videoStartClock - m_audioPTS : 0; } } if (isPaused) { if (!m_audioPaused) { m_audioPlayer->WaveOutPause(); m_audioPaused = true; } boost::unique_lock locker(m_isPausedMutex); while (delta = (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED) ? (m_isPaused ? m_pauseTimer : GetHiResTime()) - m_videoStartClock - m_audioPTS : 0 , m_isPaused && !(skipAll = delta >= frame_clock)) { m_isPausedCV.wait(locker); } } else if (delta > 1 && m_formatContexts.size() > 1 && delta > frame_clock) { CHANNEL_LOG(ffmpeg_sync) << "Skip audio frame"; skipAll = true; } if (!skipAll && m_mainVideoThread != nullptr && std::all_of(write_data, write_data + write_size, [](uint8_t data) { return data == 0; }) //&& m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED && m_videoPacketsQueue.empty() && (boost::lock_guard(m_videoFramesMutex), !m_videoFramesQueue.canPop())) { return true; // just ignore? } // Audio sync if (!failed && !skipAll && fabs(delta) > 0.1) { CHANNEL_LOG(ffmpeg_sync) << "Audio sync delta = " << delta; InterLockedAdd(m_videoStartClock, delta / 2); } if (m_audioPaused && !skipAll) { m_audioPlayer->WaveOutRestart(); m_audioPaused = false; } boost::this_thread::interruption_point(); if (skipAll) { InterLockedAdd(m_audioPTS, frame_clock); return true; } return m_audioPlayer->WriteAudio(write_data, write_size) || (m_audioPlayer->Close(), initAudioOutput()) && (swr_free(&m_audioSwrContext), m_audioPlayer->WriteAudio(write_data, write_size)); } ================================================ FILE: video/audioplayer.h ================================================ #pragma once #include struct IAudioPlayerCallback { virtual void AppendFrameClock(double frame_clock) = 0; }; struct IAudioPlayer { virtual ~IAudioPlayer() = default; virtual void SetCallback(IAudioPlayerCallback* callback) = 0; virtual void InitializeThread() = 0; virtual void DeinitializeThread() = 0; virtual void Close() = 0; virtual bool Open(int bytesPerSample, int channels, int* samplesPerSec) = 0; virtual void SetVolume(double volume) = 0; virtual double GetVolume() const = 0; // stops playback on the given output device and resets the current position virtual void WaveOutReset() = 0; // pauses playback on the given output device virtual void WaveOutPause() = 0; // resumes playback on a paused output device virtual void WaveOutRestart() = 0; virtual bool WriteAudio(uint8_t* write_data, int64_t write_size) = 0; }; ================================================ FILE: video/decoderinterface.h ================================================ #pragma once #include "ordered_scoped_token.h" #include #include #include #include #include #include #include #include struct IDirect3DDevice9; struct IDirect3DSurface9; struct IFrameDecoder; // Structure holding rendering data for a frame struct FrameRenderingData { uint8_t** image{}; // Pointer to frame image data const int* pitch{}; // Pointer to pitch (stride) values for each plane int width; // Frame width int height; // Frame height int aspectNum; // Numerator of aspect ratio int aspectDen; // Denominator of aspect ratio IDirect3DDevice9* d3d9device{}; // Direct3D device interface IDirect3DSurface9* surface{}; // Direct3D surface for rendering }; // Interface for listening to frame updates struct IFrameListener { virtual ~IFrameListener() = default; virtual void updateFrame(IFrameDecoder* decoder, unsigned int generation) = 0; // Called to update frame data virtual void drawFrame(IFrameDecoder* decoder, unsigned int generation) = 0; // Called to draw frame; decoder->finishedDisplayingFrame() must be called virtual void decoderClosing() = 0; // Called when decoder is closing }; // Interface for decoder event notifications struct FrameDecoderListener { virtual ~FrameDecoderListener() = default; virtual void changedFramePosition(long long /*start*/, long long /*frame*/, long long /*total*/) {} virtual void decoderClosed(bool /*fileReleased*/) {} // Notification of decoder closure virtual void fileLoaded(long long /*start*/, long long /*total*/) {} // Called when a file has been successfully loaded virtual void volumeChanged(double /*volume*/) {} // Called when volume level changes virtual void onEndOfStream(int /*idx*/, bool /*error*/) {} // Called when the end of the stream is reached virtual void onQueueFull(int /*idx*/) {} // Called when the frame queue is full virtual void playingFinished() {} // Called when playback finishes }; // Structure representing a rational number struct RationalNumber { int numerator; // Numerator of fraction int denominator; // Denominator of fraction }; // Equality operator for RationalNumber inline bool operator==(const RationalNumber& left, const RationalNumber& right) { return left.numerator == right.numerator && left.denominator == right.denominator; } // Interface for video frame decoding struct IFrameDecoder { // Supported pixel formats for frames enum FrameFormat { PIX_FMT_YUV420P, ///< Planar YUV 4:2:0 format, 12 bits per pixel PIX_FMT_YUYV422, ///< Packed YUV 4:2:2 format, 16 bits per pixel PIX_FMT_RGB24, ///< Packed RGB format (8 bits per channel) PIX_FMT_BGR24, ///< Packed BGR format (8 bits per channel) }; // Possible modes after displaying a frame. Controls how the decoder handles // frame memory and display state once a frame is no longer needed. enum FinishedDisplayingMode { RELEASE_FRAME, ///< Release memory associated with the frame only. ///< Use when frame data is not required anymore, ///< but no explicit "end of display" notification ///< is desired. FINALIZE_DISPLAY, ///< Mark display of this frame as complete without ///< releasing its memory. Typically used if another ///< subsystem (e.g. GPU renderer) still needs access ///< to the frame buffers until it decides to release. RELEASE_AND_FINALIZE ///< (Default) Both release frame memory and finalize ///< display. This is the safest option in most cases ///< and should be used unless special handling is ///< required. }; // Function type for performing image conversion; NV12 format typedef std::function& /*output*/, int& /*output width*/, int& /*output height*/ )> ImageConversionFunc; virtual ~IFrameDecoder() = default; // Set frame format and whether Direct3D data should be allowed virtual void SetFrameFormat(FrameFormat format, bool allowDirect3dData) = 0; // Open video streams or URLs virtual bool openUrls(std::initializer_list urls, const std::string& inputFormat = {}, bool useHHO = false) = 0; virtual bool openStream(std::unique_ptr stream) = 0; // Playback controls virtual void play(bool isPaused = false) = 0; virtual bool pauseResume() = 0; virtual bool nextFrame() = 0; virtual bool prevFrame() = 0; virtual void setVolume(double volume) = 0; virtual bool seekByPercent(double percent) = 0; virtual void videoReset() = 0; // Set event listeners virtual void setFrameListener(IFrameListener* listener) = 0; virtual void setDecoderListener(FrameDecoderListener* listener) = 0; // Retrieve rendering data virtual bool getFrameRenderingData(FrameRenderingData* data) = 0; virtual void doOnFinishedDisplayingFrame(unsigned int generation, FinishedDisplayingMode mode) = 0; // Notifies the decoder that a frame has finished displaying. // // Must be called exactly once for each frame that was passed to // IFrameListener::drawFrame(). This function may be called from // any thread, including UI/rendering threads, but the caller must // ensure correct synchronization. // // Parameters: // generation - Frame generation number received with drawFrame() // mode - Frame release mode (defaults to RELEASE_AND_FINALIZE) // // Calling this function incorrectly (e.g., missing calls or wrong // mode) may result in memory leaks, excessive buffering, or playback stalls. void finishedDisplayingFrame(unsigned int generation, FinishedDisplayingMode mode = RELEASE_AND_FINALIZE) { doOnFinishedDisplayingFrame(generation, mode); } virtual void close() = 0; // Playback status queries virtual bool isPlaying() const = 0; virtual bool isPaused() const = 0; virtual double volume() const = 0; virtual double getDurationSecs(int64_t duration) const = 0; // Audio track management virtual int getNumAudioTracks() const = 0; virtual int getAudioTrack() const = 0; virtual void setAudioTrack(int idx) = 0; // Speed control virtual RationalNumber getSpeedRational() const = 0; virtual void setSpeedRational(const RationalNumber& speed) = 0; // Hardware acceleration control virtual bool getHwAccelerated() const = 0; virtual void setHwAccelerated(bool hwAccelerated) = 0; // Retrieve properties of the content virtual std::vector getProperties() const = 0; // Retrieve video size virtual std::pair getVideoSize() const = 0; // Check compatibility of video and audio streams virtual std::pair isVideoAudioCompatible() const = 0; // Subtitle handling virtual std::vector listSubtitles() const = 0; virtual bool getSubtitles(int idx, std::function addIntervalCallback) = 0; // Set custom image conversion function virtual void setImageConversionFunc(ImageConversionFunc func) = 0; }; struct IAudioPlayer; // Function to retrieve a frame decoder with an associated audio player std::unique_ptr GetFrameDecoder(std::unique_ptr audioPlayer); ================================================ FILE: video/decoderiocontext.cpp ================================================ #include "decoderiocontext.h" extern "C" { #include } // static int DecoderIOContext::IOReadFunc(void *data, uint8_t *buf, int buf_size) { auto *hctx = static_cast(data); try { auto len = hctx->stream->sgetn((char*)buf, buf_size); if (len <= 0) { // Let FFmpeg know that we have reached EOF, or do something else return AVERROR_EOF; } return static_cast(len); } catch (const std::exception&) { // Handle any exceptions that may occur during sgetn return AVERROR(EIO); } } // whence: SEEK_SET, SEEK_CUR, SEEK_END (like fseek) and AVSEEK_SIZE // static int64_t DecoderIOContext::IOSeekFunc(void *data, int64_t pos, int whence) { auto *hctx = static_cast(data); if (whence == AVSEEK_SIZE) { // return the file size if you wish to auto current = hctx->stream->pubseekoff(0, std::ios_base::cur, std::ios_base::in); auto result = hctx->stream->pubseekoff(0, std::ios_base::end, std::ios_base::in); hctx->stream->pubseekoff(current, std::ios_base::beg, std::ios_base::in); return result; } std::ios_base::seekdir dir; switch (whence) { case SEEK_SET: dir = std::ios_base::beg; break; case SEEK_CUR: dir = std::ios_base::cur; break; case SEEK_END: dir = std::ios_base::end; break; default: return -1LL; } return hctx->stream->pubseekoff(pos, dir); } DecoderIOContext::DecoderIOContext(std::unique_ptr s) : stream(std::move(s)) { // allocate buffer bufferSize = 1024 * 64; // FIXME: not sure what size to use buffer = static_cast(av_malloc(bufferSize)); // see destructor for details // allocate the AVIOContext ioCtx = avio_alloc_context(buffer, bufferSize, // internal buffer and its size 0, // write flag (1=true,0=false) (void *)this, // user data, will be passed to our callback functions IOReadFunc, nullptr, // no writing IOSeekFunc); } DecoderIOContext::~DecoderIOContext() { //CHANNEL_LOG(ffmpeg_closing) << "In DecoderIOContext::~DecoderIOContext()"; // NOTE: ffmpeg messes up the buffer // so free the buffer first then free the context av_free(ioCtx->buffer); ioCtx->buffer = nullptr; av_free(ioCtx); } void DecoderIOContext::initAVFormatContext(AVFormatContext *pCtx) { pCtx->pb = ioCtx; pCtx->flags |= AVFMT_FLAG_CUSTOM_IO; // you can specify a format directly // pCtx->iformat = av_find_input_format("h264"); // or read some of the file and let ffmpeg do the guessing auto len = stream->sgetn((char *)buffer, bufferSize); if (len <= 0) { return; } // reset to beginning of file stream->pubseekoff(0, std::ios_base::beg, std::ios_base::in); AVProbeData probeData = {nullptr}; probeData.buf = buffer; probeData.buf_size = bufferSize - 1; probeData.filename = ""; pCtx->iformat = av_probe_input_format(&probeData, 1); } ================================================ FILE: video/decoderiocontext.h ================================================ #pragma once #include #include #include struct AVIOContext; struct AVFormatContext; class DecoderIOContext { private: AVIOContext *ioCtx; uint8_t *buffer; // internal buffer for ffmpeg int bufferSize; std::unique_ptr stream; public: DecoderIOContext(std::unique_ptr s); ~DecoderIOContext(); void initAVFormatContext(AVFormatContext * /*pCtx*/); static int IOReadFunc(void *data, uint8_t *buf, int buf_size); static int64_t IOSeekFunc(void *data, int64_t pos, int whence); }; ================================================ FILE: video/displayrunnable.cpp ================================================ #include "ffmpegdecoder.h" #include void FFmpegDecoder::displayRunnable() { CHANNEL_LOG(ffmpeg_threads) << "Displaying thread started"; #ifdef TRACE_DELAY_STATS double sumIndications = 0; double sqSumIndications = 0; enum { MAX_INDICATIONS = 200 }; int numIndications = 0; #endif while (!boost::this_thread::interruption_requested()) { { boost::unique_lock locker(m_videoFramesMutex); m_videoFramesCV.wait(locker, [this]() { return !m_frameDisplayingRequested && m_videoFramesQueue.canPop(); }); } VideoFrame& current_frame = m_videoFramesQueue.front(); m_frameDisplayingRequested = true; if (current_frame.m_convert.valid() && !current_frame.m_convert.get()) { finishedDisplayingFrame(m_generation); continue; } assert(m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED); // Frame skip if (!m_videoFramesQueue.canPush() && m_videoStartClock + current_frame.m_pts < GetHiResTime()) { CHANNEL_LOG(ffmpeg_threads) << __FUNCTION__ << " Framedrop"; finishedDisplayingFrame(m_generation); continue; } const auto pts = current_frame.m_pts; const auto duration = current_frame.m_duration; // Possibly give it time to render frame if (m_frameListener != nullptr) { m_frameListener->updateFrame(this, m_generation); } const auto speed = getSpeedRational(); for (;;) { const double delay = m_videoStartClock + pts - GetHiResTime(); if (delay < 0.005) { break; } if (delay > 0.1) { boost::this_thread::sleep_for( boost::chrono::milliseconds(100 * speed.denominator / speed.numerator)); continue; } boost::this_thread::sleep_for( boost::chrono::milliseconds(int(delay * 1000. * speed.denominator / speed.numerator))); break; } // It's time to display converted frame if (duration != AV_NOPTS_VALUE) { m_currentTime = duration; if ((m_decoderListener != nullptr) && m_seekDuration == AV_NOPTS_VALUE) { m_decoderListener->changedFramePosition( m_startTime, duration, m_duration + m_startTime); } } #ifdef TRACE_DELAY_STATS const double delay = m_videoStartClock + pts - GetHiResTime(); sumIndications += delay; sqSumIndications += delay * delay; if (++numIndications >= MAX_INDICATIONS) { const double avg = sumIndications / numIndications; CHANNEL_LOG(ffmpeg_threads) << "Average frame delay: " << avg << "; frame delay deviation: " << sqrt(sqSumIndications / numIndications - avg * avg); sumIndications = 0; sqSumIndications = 0; numIndications = 0; } #endif if (m_frameListener != nullptr) { m_frameListener->drawFrame(this, m_generation); } else { finishedDisplayingFrame(m_generation); } } } ================================================ FILE: video/ffmpeg_dxva2.cpp ================================================ /* * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef _WIN32 #include #include #include "ffmpeg_dxva2.h" extern "C" { #include "libavcodec/avcodec.h" #include "libavutil/pixfmt.h" #include "libavutil/rational.h" #include "libavcodec/dxva2.h" #include "libavutil/avassert.h" #include "libavutil/buffer.h" #include "libavutil/frame.h" #include "libavutil/imgutils.h" #include "libavutil/cpu.h" } #include #include #include // https://github.com/NVIDIA/gdrcopy/blob/master/memcpy_sse41.c // implementation of copy from BAR using MOVNTDQA // suggested by Nicholas Wilt // src is WC MMIO of GPU BAR // dest is host memory static int memcpy_uncached_load_sse41(void *dest, const void *src, size_t n_bytes) { int ret = 0; char *d = (char*)dest; uintptr_t d_int = (uintptr_t)d; const char *s = (const char *)src; uintptr_t s_int = (uintptr_t)s; size_t n = n_bytes; // align src to 128-bits if (s_int & 0xf) { size_t nh = (std::min)(0x10 - (s_int & 0x0f), n); memcpy(d, s, nh); d += nh; d_int += nh; s += nh; s_int += nh; n -= nh; } if (d_int & 0xf) { // dest is not aligned to 128-bits __m128i r0, r1, r2, r3, r4, r5, r6, r7; // unroll 8 while (n >= 8 * sizeof(__m128i)) { r0 = _mm_stream_load_si128((__m128i *)(s + 0 * sizeof(__m128i))); r1 = _mm_stream_load_si128((__m128i *)(s + 1 * sizeof(__m128i))); r2 = _mm_stream_load_si128((__m128i *)(s + 2 * sizeof(__m128i))); r3 = _mm_stream_load_si128((__m128i *)(s + 3 * sizeof(__m128i))); r4 = _mm_stream_load_si128((__m128i *)(s + 4 * sizeof(__m128i))); r5 = _mm_stream_load_si128((__m128i *)(s + 5 * sizeof(__m128i))); r6 = _mm_stream_load_si128((__m128i *)(s + 6 * sizeof(__m128i))); r7 = _mm_stream_load_si128((__m128i *)(s + 7 * sizeof(__m128i))); _mm_storeu_si128((__m128i *)(d + 0 * sizeof(__m128i)), r0); _mm_storeu_si128((__m128i *)(d + 1 * sizeof(__m128i)), r1); _mm_storeu_si128((__m128i *)(d + 2 * sizeof(__m128i)), r2); _mm_storeu_si128((__m128i *)(d + 3 * sizeof(__m128i)), r3); _mm_storeu_si128((__m128i *)(d + 4 * sizeof(__m128i)), r4); _mm_storeu_si128((__m128i *)(d + 5 * sizeof(__m128i)), r5); _mm_storeu_si128((__m128i *)(d + 6 * sizeof(__m128i)), r6); _mm_storeu_si128((__m128i *)(d + 7 * sizeof(__m128i)), r7); s += 8 * sizeof(__m128i); d += 8 * sizeof(__m128i); n -= 8 * sizeof(__m128i); } while (n >= sizeof(__m128i)) { r0 = _mm_stream_load_si128((__m128i *)(s + 0 * sizeof(__m128i))); _mm_storeu_si128((__m128i *)(d + 0 * sizeof(__m128i)), r0); s += sizeof(__m128i); d += sizeof(__m128i); n -= sizeof(__m128i); } } else { // or it IS aligned __m128i r0, r1, r2, r3, r4, r5, r6, r7; // unroll 8 while (n >= 8 * sizeof(__m128i)) { r0 = _mm_stream_load_si128((__m128i *)(s + 0 * sizeof(__m128i))); r1 = _mm_stream_load_si128((__m128i *)(s + 1 * sizeof(__m128i))); r2 = _mm_stream_load_si128((__m128i *)(s + 2 * sizeof(__m128i))); r3 = _mm_stream_load_si128((__m128i *)(s + 3 * sizeof(__m128i))); r4 = _mm_stream_load_si128((__m128i *)(s + 4 * sizeof(__m128i))); r5 = _mm_stream_load_si128((__m128i *)(s + 5 * sizeof(__m128i))); r6 = _mm_stream_load_si128((__m128i *)(s + 6 * sizeof(__m128i))); r7 = _mm_stream_load_si128((__m128i *)(s + 7 * sizeof(__m128i))); _mm_stream_si128((__m128i *)(d + 0 * sizeof(__m128i)), r0); _mm_stream_si128((__m128i *)(d + 1 * sizeof(__m128i)), r1); _mm_stream_si128((__m128i *)(d + 2 * sizeof(__m128i)), r2); _mm_stream_si128((__m128i *)(d + 3 * sizeof(__m128i)), r3); _mm_stream_si128((__m128i *)(d + 4 * sizeof(__m128i)), r4); _mm_stream_si128((__m128i *)(d + 5 * sizeof(__m128i)), r5); _mm_stream_si128((__m128i *)(d + 6 * sizeof(__m128i)), r6); _mm_stream_si128((__m128i *)(d + 7 * sizeof(__m128i)), r7); s += 8 * sizeof(__m128i); d += 8 * sizeof(__m128i); n -= 8 * sizeof(__m128i); } while (n >= sizeof(__m128i)) { r0 = _mm_stream_load_si128((__m128i *)(s + 0 * sizeof(__m128i))); _mm_stream_si128((__m128i *)(d + 0 * sizeof(__m128i)), r0); s += sizeof(__m128i); d += sizeof(__m128i); n -= sizeof(__m128i); } } if (n) memcpy(d, s, n); // fencing because of NT stores // potential optimization: issue only when NT stores are actually emitted _mm_sfence(); return ret; } static void CopyPlane(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize, int bytewidth, int height) { if (!dst || !src) return; for (;height > 0; height--) { memcpy_uncached_load_sse41(dst, src, bytewidth); dst += dst_linesize; src += src_linesize; } } // TODO use better criteria static intptr_t getHWAccelDevice(IDirect3D9* pDirect3D9) { intptr_t result = D3DADAPTER_DEFAULT; const auto count = pDirect3D9->GetAdapterCount(); if (count < 2) return result; UINT height{}, width{}; for (unsigned int i = 0; i < count; ++i) { D3DDISPLAYMODE d3ddm{}; if (SUCCEEDED(pDirect3D9->GetAdapterDisplayMode(i, &d3ddm)) && d3ddm.Height > height && d3ddm.Width > width) { result = i; height = d3ddm.Height; width = d3ddm.Width; } } return result; } /* define all the GUIDs used directly here, to avoid problems with inconsistent dxva2api.h versions in mingw-w64 and different MSVC version */ #include // https://docs.microsoft.com/windows-hardware/drivers/display/providing-capabilities-for-video-decoding // https://gix.github.io/media-types/ DEFINE_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551, 0xd5e7, 0x11d9, 0xaf, 0x55, 0x00, 0x05, 0x4e, 0x43, 0xff, 0x02); DEFINE_GUID(DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28, 0x4e65, 0xbe, 0xea, 0x1d, 0x26, 0xb5, 0x08, 0xad, 0xc9); DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD, 0x86695f12, 0x340e, 0x4f04, 0x9f, 0xd3, 0x92, 0x53, 0xdd, 0x32, 0x74, 0x60); DEFINE_GUID(DXVA2_ModeMPEG2_MoComp, 0xe6a9f44b, 0x61b0, 0x4563, 0x9e, 0xa4, 0x63, 0xd2, 0xa3, 0xc6, 0xfe, 0x66); DEFINE_GUID(DXVADDI_ModeMPEG2_IDCT, 0xbf22ad00, 0x03ea, 0x4690, 0x80, 0x77, 0x47, 0x33, 0x46, 0x20, 0x9b, 0x7e); DEFINE_GUID(DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951, 0x4C54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6); DEFINE_GUID(DXVADDI_ModeH264_A, 0x1b81be64, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeH264_B, 0x1b81be65, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeH264_C, 0x1b81be66, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeH264_D, 0x1b81be67, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main, 0x5b11d51b, 0x2f4c, 0x4452, 0xbc, 0xc3, 0x09, 0xf2, 0xa1, 0x16, 0x0c, 0xc0); DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main10,0x107af0e0, 0xef1a,0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13); DEFINE_GUID(DXVA2_ModeVP9_VLD_Profile0, 0x463707f8, 0xa1d0, 0x4585, 0x87, 0x6d, 0x83, 0xaa, 0x6d, 0x60, 0xb8, 0x9e); DEFINE_GUID(DXVA2_ModeVP9_VLD_10bit_Profile2, 0xa4c749ef, 0x6ecf, 0x48aa, 0x84, 0x48, 0x50, 0xa7, 0xa1, 0x16, 0x5f, 0xf7); DEFINE_GUID(DXVADDI_ModeVC1_A, 0x1b81beA0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeVC1_B, 0x1b81beA1, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeVC1_C, 0x1b81beA2, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeWMV8_A, 0x1b81be80, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeWMV8_B, 0x1b81be81, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeWMV9_A, 0x1b81be90, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeWMV9_B, 0x1b81be91, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVADDI_ModeWMV9_C, 0x1b81be94, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(DXVA2_NoEncrypt, 0x1b81beD0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); DEFINE_GUID(GUID_NULL, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); //DEFINE_GUID(DXVA2_Unknown, 0xA74CCAE2, 0xF466, 0x45AE, 0x86, 0xF5, 0xAB, 0x8B, 0xE8, 0xAF, 0x84, 0x83); typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT); typedef HRESULT WINAPI pCreateDeviceManager9(UINT *, IDirect3DDeviceManager9 **); typedef struct dxva2_mode { const GUID *guid; enum AVCodecID codec; } dxva2_mode; static const dxva2_mode dxva2_modes[] = { /* MPEG-2 */ { &DXVA2_ModeMPEG2_VLD, AV_CODEC_ID_MPEG2VIDEO }, { &DXVA2_ModeMPEG2and1_VLD, AV_CODEC_ID_MPEG2VIDEO }, { &DXVA2_ModeMPEG2_MoComp, AV_CODEC_ID_MPEG2VIDEO }, { &DXVADDI_ModeMPEG2_IDCT, AV_CODEC_ID_MPEG2VIDEO }, /* H.264 */ { &DXVA2_ModeH264_F, AV_CODEC_ID_H264 }, { &DXVA2_ModeH264_E, AV_CODEC_ID_H264 }, /* Intel specific H.264 mode */ { &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 }, { &DXVADDI_ModeH264_A, AV_CODEC_ID_H264 }, { &DXVADDI_ModeH264_B, AV_CODEC_ID_H264 }, { &DXVADDI_ModeH264_C, AV_CODEC_ID_H264 }, { &DXVADDI_ModeH264_D, AV_CODEC_ID_H264 }, /* VC-1 / WMV3 */ { &DXVA2_ModeVC1_D2010, AV_CODEC_ID_VC1 }, { &DXVA2_ModeVC1_D2010, AV_CODEC_ID_WMV3 }, { &DXVA2_ModeVC1_D, AV_CODEC_ID_VC1 }, { &DXVA2_ModeVC1_D, AV_CODEC_ID_WMV3 }, { &DXVADDI_ModeVC1_A, AV_CODEC_ID_VC1 }, { &DXVADDI_ModeVC1_A, AV_CODEC_ID_WMV3 }, { &DXVADDI_ModeVC1_B, AV_CODEC_ID_VC1 }, { &DXVADDI_ModeVC1_B, AV_CODEC_ID_WMV3 }, { &DXVADDI_ModeVC1_C, AV_CODEC_ID_VC1 }, { &DXVADDI_ModeVC1_C, AV_CODEC_ID_WMV3 }, { &DXVADDI_ModeWMV8_A, AV_CODEC_ID_WMV3 }, { &DXVADDI_ModeWMV8_B, AV_CODEC_ID_WMV3 }, { &DXVADDI_ModeWMV9_A, AV_CODEC_ID_WMV3 }, { &DXVADDI_ModeWMV9_B, AV_CODEC_ID_WMV3 }, { &DXVADDI_ModeWMV9_C, AV_CODEC_ID_WMV3 }, /* HEVC/H.265 */ { &DXVA2_ModeHEVC_VLD_Main10,AV_CODEC_ID_HEVC }, // comes first { &DXVA2_ModeHEVC_VLD_Main, AV_CODEC_ID_HEVC }, /* VP8/9 */ { &DXVA2_ModeVP9_VLD_10bit_Profile2, AV_CODEC_ID_VP9 }, { &DXVA2_ModeVP9_VLD_Profile0, AV_CODEC_ID_VP9 }, { NULL, AV_CODEC_ID_NONE }, }; typedef struct surface_info { int used; uint64_t age; } surface_info; typedef struct DXVA2Context { HMODULE d3dlib; HMODULE dxva2lib; HANDLE deviceHandle; IDirect3D9 *d3d9; IDirect3DDevice9 *d3d9device; IDirect3DDeviceManager9 *d3d9devmgr; IDirectXVideoDecoderService *decoder_service; IDirectXVideoDecoder *decoder; GUID decoder_guid; DXVA2_ConfigPictureDecode decoder_config; LPDIRECT3DSURFACE9 *surfaces; surface_info *surface_infos; uint32_t num_surfaces; uint64_t surface_age; AVFrame *tmp_frame; } DXVA2Context; typedef struct DXVA2SurfaceWrapper { DXVA2Context *ctx; LPDIRECT3DSURFACE9 surface; IDirectXVideoDecoder *decoder; } DXVA2SurfaceWrapper; enum HWAccelID { HWACCEL_NONE = 0, HWACCEL_AUTO, HWACCEL_VDPAU, HWACCEL_DXVA2, HWACCEL_VDA, HWACCEL_VIDEOTOOLBOX, HWACCEL_QSV, }; struct InputStream { AVCodecContext* dec_ctx; /* hwaccel options */ enum HWAccelID hwaccel_id; intptr_t hwaccel_device; /* hwaccel context */ enum HWAccelID active_hwaccel_id; void* hwaccel_ctx; void* hwaccel_context; // for DXVA2 enum AVPixelFormat hwaccel_pix_fmt; }; static void dxva2_destroy_decoder(InputStream* ist) { DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; if (ctx->surfaces) { for (int i = 0; i < ctx->num_surfaces; i++) { if (ctx->surfaces[i]) IDirect3DSurface9_Release(ctx->surfaces[i]); } } av_freep(&ctx->surfaces); av_freep(&ctx->surface_infos); ctx->num_surfaces = 0; ctx->surface_age = 0; if (ctx->decoder) { ctx->decoder->Release(); ctx->decoder = NULL; } } void dxva2_uninit(void* opaque) { InputStream* ist = static_cast(opaque); DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; if (ctx->decoder) dxva2_destroy_decoder(ist); if (ctx->decoder_service) ctx->decoder_service->Release(); if (ctx->d3d9devmgr && ctx->deviceHandle != INVALID_HANDLE_VALUE) ctx->d3d9devmgr->CloseDeviceHandle(ctx->deviceHandle); if (ctx->d3d9devmgr) ctx->d3d9devmgr->Release(); if (ctx->d3d9device) IDirect3DDevice9_Release(ctx->d3d9device); if (ctx->d3d9) IDirect3D9_Release(ctx->d3d9); if (ctx->d3dlib) FreeLibrary(ctx->d3dlib); if (ctx->dxva2lib) FreeLibrary(ctx->dxva2lib); av_frame_free(&ctx->tmp_frame); av_freep(&ist->hwaccel_ctx); av_freep(&ist->hwaccel_context); delete ist; } static void dxva2_release_buffer(void *opaque, uint8_t *data) { DXVA2SurfaceWrapper *w = (DXVA2SurfaceWrapper *)opaque; DXVA2Context *ctx = w->ctx; int i; for (i = 0; i < ctx->num_surfaces; i++) { if (ctx->surfaces[i] == w->surface) { ctx->surface_infos[i].used = 0; break; } } IDirect3DSurface9_Release(w->surface); w->decoder->Release(); av_free(w); } static int dxva2_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) { InputStream *ist = (InputStream *)s->opaque; DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; int i, old_unused = -1; LPDIRECT3DSURFACE9 surface; DXVA2SurfaceWrapper *w = NULL; av_assert0(frame->format == AV_PIX_FMT_DXVA2_VLD); for (i = 0; i < ctx->num_surfaces; i++) { surface_info *info = &ctx->surface_infos[i]; if (!info->used && (old_unused == -1 || info->age < ctx->surface_infos[old_unused].age)) old_unused = i; } if (old_unused == -1) { av_log(NULL, AV_LOG_ERROR, "No free DXVA2 surface!\n"); return AVERROR(ENOMEM); } i = old_unused; surface = ctx->surfaces[i]; w = (DXVA2SurfaceWrapper *)av_mallocz(sizeof(*w)); if (!w) return AVERROR(ENOMEM); frame->buf[0] = av_buffer_create((uint8_t*)surface, 0, dxva2_release_buffer, w, AV_BUFFER_FLAG_READONLY); if (!frame->buf[0]) { av_free(w); return AVERROR(ENOMEM); } w->ctx = ctx; w->surface = surface; IDirect3DSurface9_AddRef(w->surface); w->decoder = ctx->decoder; w->decoder->AddRef(); ctx->surface_infos[i].used = 1; ctx->surface_infos[i].age = ctx->surface_age++; frame->data[3] = (uint8_t *)surface; return 0; } static const auto CopyPlanePtr = (av_get_cpu_flags() & AV_CPU_FLAG_SSE4) ? CopyPlane : av_image_copy_plane; int dxva2_convert_data(IDirect3DSurface9* surface, AVFrame *tmp_frame, int width, int height) { D3DSURFACE_DESC surfaceDesc; D3DLOCKED_RECT LockedRect; HRESULT hr; int ret; IDirect3DSurface9_GetDesc(surface, &surfaceDesc); hr = IDirect3DSurface9_LockRect(surface, &LockedRect, NULL, D3DLOCK_READONLY); if (FAILED(hr)) { av_log(NULL, AV_LOG_ERROR, "Unable to lock DXVA2 surface\n"); return AVERROR_UNKNOWN; } tmp_frame->width = width; tmp_frame->height = height; switch (surfaceDesc.Format) { case MKTAG('N', 'V', '1', '2'): tmp_frame->format = AV_PIX_FMT_NV12; ret = av_frame_get_buffer(tmp_frame, 32); if (ret == 0) { CopyPlanePtr(tmp_frame->data[0], tmp_frame->linesize[0], (uint8_t*)LockedRect.pBits, LockedRect.Pitch, width, height); CopyPlanePtr(tmp_frame->data[1], tmp_frame->linesize[1], (uint8_t*)LockedRect.pBits + LockedRect.Pitch * surfaceDesc.Height, LockedRect.Pitch, width, height / 2); } break; case MKTAG('P', '0', '1', '0'): tmp_frame->format = AV_PIX_FMT_P010; ret = av_frame_get_buffer(tmp_frame, 32); if (ret == 0) { CopyPlanePtr(tmp_frame->data[0], tmp_frame->linesize[0], (uint8_t*)LockedRect.pBits, LockedRect.Pitch, width * 2, height); CopyPlanePtr(tmp_frame->data[1], tmp_frame->linesize[1], (uint8_t*)LockedRect.pBits + LockedRect.Pitch * surfaceDesc.Height, LockedRect.Pitch, width * 2, height / 2); } break; case D3DFMT_YUY2: tmp_frame->format = AV_PIX_FMT_YUYV422; ret = av_frame_get_buffer(tmp_frame, 32); if (ret == 0) { CopyPlanePtr(tmp_frame->data[0], tmp_frame->linesize[0], (uint8_t*)LockedRect.pBits, LockedRect.Pitch, width * 2, height); } break; default: // IMC3 tmp_frame->format = AV_PIX_FMT_YUV420P; ret = av_frame_get_buffer(tmp_frame, 32); if (ret == 0) { uint8_t* pU = (uint8_t*)LockedRect.pBits + (((height + 15) & ~15) * LockedRect.Pitch); uint8_t* pV = (uint8_t*)LockedRect.pBits + (((((height * 3) / 2) + 15) & ~15) * LockedRect.Pitch); CopyPlanePtr(tmp_frame->data[0], tmp_frame->linesize[0], (uint8_t*)LockedRect.pBits, LockedRect.Pitch, width, height); CopyPlanePtr(tmp_frame->data[1], tmp_frame->linesize[1], pU, LockedRect.Pitch, width / 2, height / 2); CopyPlanePtr(tmp_frame->data[2], tmp_frame->linesize[2], pV, LockedRect.Pitch, width / 2, height / 2); } } IDirect3DSurface9_UnlockRect(surface); return ret; } int dxva2_retrieve_data(AVCodecContext *s, AVFrame *frame) { LPDIRECT3DSURFACE9 surface = (LPDIRECT3DSURFACE9)frame->data[3]; InputStream *ist = (InputStream *)s->opaque; DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; int ret = dxva2_convert_data(surface, ctx->tmp_frame, frame->width, frame->height); if (ret < 0) return ret; ret = av_frame_copy_props(ctx->tmp_frame, frame); if (ret < 0) { av_frame_unref(ctx->tmp_frame); return ret; } av_frame_unref(frame); av_frame_move_ref(frame, ctx->tmp_frame); return 0; } static int dxva2_alloc(AVCodecContext *s) { InputStream *ist = (InputStream *)s->opaque; int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; DXVA2Context *ctx; pDirect3DCreate9 *createD3D = NULL; pCreateDeviceManager9 *createDeviceManager = NULL; HRESULT hr; D3DPRESENT_PARAMETERS d3dpp = { 0 }; unsigned resetToken = 0; UINT adapter = D3DADAPTER_DEFAULT; ctx = (DXVA2Context *)av_mallocz(sizeof(*ctx)); if (!ctx) { delete ist; return AVERROR(ENOMEM); } ctx->deviceHandle = INVALID_HANDLE_VALUE; ist->hwaccel_ctx = ctx; ctx->d3dlib = LoadLibraryW(L"d3d9.dll"); if (!ctx->d3dlib) { av_log(NULL, loglevel, "Failed to load D3D9 library\n"); goto fail; } ctx->dxva2lib = LoadLibraryW(L"dxva2.dll"); if (!ctx->dxva2lib) { av_log(NULL, loglevel, "Failed to load DXVA2 library\n"); goto fail; } createD3D = (pDirect3DCreate9 *)GetProcAddress(ctx->d3dlib, "Direct3DCreate9"); if (!createD3D) { av_log(NULL, loglevel, "Failed to locate Direct3DCreate9\n"); goto fail; } createDeviceManager = (pCreateDeviceManager9 *)GetProcAddress(ctx->dxva2lib, "DXVA2CreateDirect3DDeviceManager9"); if (!createDeviceManager) { av_log(NULL, loglevel, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n"); goto fail; } ctx->d3d9 = createD3D(D3D_SDK_VERSION); if (!ctx->d3d9) { av_log(NULL, loglevel, "Failed to create IDirect3D object\n"); goto fail; } if (ist->hwaccel_device) { adapter = ist->hwaccel_device; } else { adapter = getHWAccelDevice(ctx->d3d9); } av_log(NULL, AV_LOG_INFO, "Using HWAccel device %d\n", adapter); //IDirect3D9_GetAdapterDisplayMode(ctx->d3d9, adapter, &d3ddm); d3dpp.Windowed = TRUE; d3dpp.BackBufferWidth = GetSystemMetrics(SM_CXSCREEN); d3dpp.BackBufferHeight = GetSystemMetrics(SM_CYSCREEN); d3dpp.BackBufferCount = 1; d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;// d3ddm.Format; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.Flags = D3DPRESENTFLAG_VIDEO; d3dpp.Windowed = TRUE; d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; hr = IDirect3D9_CreateDevice(ctx->d3d9, adapter, D3DDEVTYPE_HAL, GetDesktopWindow(), D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &d3dpp, &ctx->d3d9device); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to create Direct3D device\n"); goto fail; } hr = createDeviceManager(&resetToken, &ctx->d3d9devmgr); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to create Direct3D device manager\n"); goto fail; } hr = ctx->d3d9devmgr->ResetDevice(ctx->d3d9device, resetToken); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to bind Direct3D device to device manager\n"); goto fail; } hr = ctx->d3d9devmgr->OpenDeviceHandle(&ctx->deviceHandle); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to open device handle\n"); goto fail; } hr = ctx->d3d9devmgr->GetVideoService(ctx->deviceHandle, IID_IDirectXVideoDecoderService, (void **)&ctx->decoder_service); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to create IDirectXVideoDecoderService\n"); goto fail; } ctx->tmp_frame = av_frame_alloc(); if (!ctx->tmp_frame) goto fail; s->hwaccel_context = av_mallocz(sizeof(struct dxva_context)); if (!s->hwaccel_context) goto fail; ist->hwaccel_context = s->hwaccel_context; return 0; fail: dxva2_uninit(ist); return AVERROR(EINVAL); } static int dxva2_get_decoder_configuration(AVCodecContext *s, const GUID *device_guid, const DXVA2_VideoDesc *desc, DXVA2_ConfigPictureDecode *config) { InputStream *ist = (InputStream *)s->opaque; int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; unsigned cfg_count = 0, best_score = 0; DXVA2_ConfigPictureDecode *cfg_list = NULL; DXVA2_ConfigPictureDecode best_cfg = { { 0 } }; HRESULT hr; int i; hr = ctx->decoder_service->GetDecoderConfigurations(*device_guid, desc, NULL, &cfg_count, &cfg_list); if (FAILED(hr)) { av_log(NULL, loglevel, "Unable to retrieve decoder configurations\n"); return AVERROR(EINVAL); } for (i = 0; i < cfg_count; i++) { DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; unsigned score; if (cfg->ConfigBitstreamRaw == 1) score = 1; else if (s->codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2) score = 2; else continue; if (IsEqualGUID(cfg->guidConfigBitstreamEncryption, DXVA2_NoEncrypt)) score += 16; if (score > best_score) { best_score = score; best_cfg = *cfg; } } CoTaskMemFree(cfg_list); if (!best_score) { av_log(NULL, loglevel, "No valid decoder configuration available\n"); return AVERROR(EINVAL); } *config = best_cfg; return 0; } static bool isMoreThan8BytesFmt(AVPixelFormat f) { if (auto d = av_pix_fmt_desc_get(f)) { for (int i = 0; i < d->nb_components; ++i) if (d->comp[i].depth > 8) return true; } return false; } static int dxva2_create_decoder(AVCodecContext *s) { InputStream *ist = (InputStream *)s->opaque; int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; struct dxva_context *dxva_ctx = (dxva_context *)s->hwaccel_context; GUID *guid_list = NULL; unsigned guid_count = 0, i, j; GUID device_guid = GUID_NULL; D3DFORMAT target_format = D3DFMT_UNKNOWN; DXVA2_VideoDesc desc = { 0 }; DXVA2_ConfigPictureDecode config; HRESULT hr; int surface_alignment; int ret; hr = ctx->decoder_service->GetDecoderDeviceGuids(&guid_count, &guid_list); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to retrieve decoder device GUIDs\n"); goto fail; } const bool is10format = isMoreThan8BytesFmt( (s->sw_pix_fmt == AV_PIX_FMT_NONE)? s->pix_fmt : s->sw_pix_fmt); for (i = 0; dxva2_modes[i].guid; i++) { D3DFORMAT *target_list = NULL; unsigned target_count = 0; const dxva2_mode *mode = &dxva2_modes[i]; if (mode->codec != s->codec_id) continue; for (j = 0; j < guid_count; j++) { if (IsEqualGUID(*mode->guid, guid_list[j])) break; } if (j == guid_count) continue; hr = ctx->decoder_service->GetDecoderRenderTargets(*mode->guid, &target_count, &target_list); if (FAILED(hr)) { continue; } for (j = 0; j < target_count; j++) { const D3DFORMAT format = target_list[j]; if (is10format? (format == MKTAG('P', '0', '1', '0')) : (format == MKTAG('N', 'V', '1', '2') || format == MKTAG('I', 'M', 'C', '3'))) { target_format = format; break; } } CoTaskMemFree(target_list); if (target_format) { device_guid = *mode->guid; break; } } CoTaskMemFree(guid_list); if (IsEqualGUID(device_guid, GUID_NULL)) { av_log(NULL, loglevel, "No decoder device for codec found\n"); goto fail; } desc.SampleWidth = s->coded_width; desc.SampleHeight = s->coded_height; desc.Format = target_format; ret = dxva2_get_decoder_configuration(s, &device_guid, &desc, &config); if (ret < 0) { goto fail; } /* decoding MPEG-2 requires additional alignment on some Intel GPUs, but it causes issues for H.264 on certain AMD GPUs..... */ if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO) surface_alignment = 32; /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure all coding features have enough room to work with */ else if (s->codec_id == AV_CODEC_ID_HEVC) surface_alignment = 128; else surface_alignment = 16; /* 4 base work surfaces */ //ctx->num_surfaces = 4; ctx->num_surfaces = 4 + 3; // two video queue elements and one cached by view /* add surfaces based on number of possible refs */ if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC) ctx->num_surfaces += 16; else if (s->codec_id == AV_CODEC_ID_VP9) ctx->num_surfaces += 8; else ctx->num_surfaces += 2; /* add extra surfaces for frame threading */ if (s->active_thread_type & FF_THREAD_FRAME) ctx->num_surfaces += s->thread_count; ctx->surfaces = (LPDIRECT3DSURFACE9 *)av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces)); ctx->surface_infos = (surface_info *)av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos)); if (!ctx->surfaces || !ctx->surface_infos) { av_log(NULL, loglevel, "Unable to allocate surface arrays\n"); goto fail; } hr = ctx->decoder_service->CreateSurface(FFALIGN(s->coded_width, surface_alignment), FFALIGN(s->coded_height, surface_alignment), ctx->num_surfaces - 1, target_format, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, ctx->surfaces, NULL); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to create %d video surfaces\n", ctx->num_surfaces); goto fail; } hr = ctx->decoder_service->CreateVideoDecoder(device_guid, &desc, &config, ctx->surfaces, ctx->num_surfaces, &ctx->decoder); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to create DXVA2 video decoder\n"); goto fail; } ctx->decoder_guid = device_guid; ctx->decoder_config = config; dxva_ctx->cfg = &ctx->decoder_config; dxva_ctx->decoder = ctx->decoder; dxva_ctx->surface = ctx->surfaces; dxva_ctx->surface_count = ctx->num_surfaces; #ifdef FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO if (IsEqualGUID(ctx->decoder_guid, DXVADDI_Intel_ModeH264_E)) dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; #endif return 0; fail: dxva2_destroy_decoder(ist); return AVERROR(EINVAL); } static AVPixelFormat GetHwFormat(AVCodecContext* s, const AVPixelFormat* pix_fmts) { auto* ist = static_cast(s->opaque); ist->active_hwaccel_id = HWACCEL_DXVA2; ist->hwaccel_pix_fmt = AV_PIX_FMT_DXVA2_VLD; return ist->hwaccel_pix_fmt; } int dxva2_init(AVCodecContext *s) { const int loglevel = AV_LOG_VERBOSE; bool ok = false; for (const dxva2_mode *mode = dxva2_modes; mode->guid; mode++) { if (mode->codec == s->codec_id) { ok = true; break; } } if (!ok) { av_log(NULL, loglevel, "Unsupported codec for DXVA2 HWAccel: %d\n", s->codec_id); return AVERROR(EINVAL); } if (s->codec_id == AV_CODEC_ID_H264 && (s->profile & ~AV_PROFILE_H264_CONSTRAINED) > AV_PROFILE_H264_HIGH) { av_log(NULL, loglevel, "Unsupported H.264 profile for DXVA2 HWAccel: %d\n", s->profile); return AVERROR(EINVAL); } if (s->codec_id == AV_CODEC_ID_HEVC && s->profile != AV_PROFILE_HEVC_MAIN && s->profile != AV_PROFILE_HEVC_MAIN_10) { av_log(NULL, loglevel, "Unsupported HEVC profile for DXVA2 HWAccel: %d\n", s->profile); return AVERROR(EINVAL); } auto ist = new InputStream(); ist->hwaccel_id = HWACCEL_AUTO; ist->dec_ctx = s; s->opaque = ist; int ret; if (!ist->hwaccel_ctx) { ret = dxva2_alloc(s); if (ret < 0) { s->opaque = nullptr; return ret; } } DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; if (ctx->decoder) dxva2_destroy_decoder(ist); ret = dxva2_create_decoder(s); if (ret < 0) { av_log(NULL, loglevel, "Error creating the DXVA2 decoder\n"); dxva2_uninit(ist); s->opaque = nullptr; return ret; } s->get_buffer2 = dxva2_get_buffer; s->get_format = GetHwFormat; return 0; } IDirect3DDevice9* get_device(AVCodecContext *s) { InputStream *ist = (InputStream *)s->opaque; DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx; return ctx->d3d9device; } #endif // _WIN32 ================================================ FILE: video/ffmpeg_dxva2.h ================================================ #pragma once /* * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef _WIN32 typedef struct AVCodecContext AVCodecContext; typedef struct AVFrame AVFrame; typedef struct IDirect3DDevice9 IDirect3DDevice9; typedef struct IDirect3DSurface9 IDirect3DSurface9; int dxva2_init(AVCodecContext *s); void dxva2_uninit(void* ist); int dxva2_convert_data(IDirect3DSurface9* surface, AVFrame *tmp_frame, int width, int height); int dxva2_retrieve_data(AVCodecContext *s, AVFrame *frame); IDirect3DDevice9* get_device(AVCodecContext *s); #endif // _WIN32 ================================================ FILE: video/ffmpegdecoder.cpp ================================================ #include "ffmpegdecoder.h" #include #include #include "makeguard.h" #include "interlockedadd.h" #include "subtitles.h" #include "decoderiocontext.h" #include #include #include #include #include #include #include #include extern "C" { #include "libavutil/pixdesc.h" #include "libavdevice/avdevice.h" } #ifdef _WIN32 #include #define USE_HWACCEL #else #include #include #endif // http://stackoverflow.com/questions/34602561 #ifdef USE_HWACCEL #include "ffmpeg_dxva2.h" #endif namespace { static std::vector resolveHostnameToIPs(const std::string& hostname) { std::vector result; addrinfo hints = {}; hints.ai_family = AF_INET; // return IPv4 only hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; addrinfo* res = nullptr; if (getaddrinfo(hostname.c_str(), nullptr, &hints, &res) != 0) return result; char ipStr[INET6_ADDRSTRLEN] = {0}; for (addrinfo* it = res; it != nullptr; it = it->ai_next) { void* addr = nullptr; if (it->ai_family == AF_INET) { sockaddr_in* sa = reinterpret_cast(it->ai_addr); addr = &sa->sin_addr; } //else if (it->ai_family == AF_INET6) //{ // sockaddr_in6* sa6 = reinterpret_cast(it->ai_addr); // addr = &sa6->sin6_addr; //} else { continue; } if (inet_ntop(it->ai_family, addr, ipStr, sizeof(ipStr))) { result.emplace_back(ipStr); } } freeaddrinfo(res); return result; } void FreeVideoCodecContext(AVCodecContext*& videoCodecContext) { #ifdef USE_HWACCEL if (videoCodecContext != nullptr) { if (auto stream = videoCodecContext->opaque) { videoCodecContext->opaque = nullptr; avcodec_free_context(&videoCodecContext); dxva2_uninit(stream); return; } } #endif // Close the codec avcodec_free_context(&videoCodecContext); } inline void Shutdown(const std::unique_ptr& th) { if (th) { th->interrupt(); th->join(); } } int ThisThreadInterruptionRequested(void* ptr) { boost::atomic_bool* flag = static_cast(ptr); return static_cast(*flag || boost::this_thread::interruption_requested()); } int g_lastHttpCode = 0; std::string g_lastLocationHttpHeader; boost::atomic_bool* g_interruptionRequestedFlag = nullptr; void log_callback(void *ptr, int level, const char *fmt, va_list vargs) { if (level <= AV_LOG_ERROR) { char buffer[4096]; int length = vsnprintf(buffer, sizeof(buffer), fmt, vargs); if (length > 0) { for (; length > 0 && (isspace(static_cast(buffer[length - 1])) != 0); --length) { ; } buffer[length] = '\0'; CHANNEL_LOG(ffmpeg_internal) << buffer; } } else if (const auto interruptionRequestedFlag = g_interruptionRequestedFlag) { if (strcmp(fmt, "http_code=%d\n") == 0) { g_lastHttpCode = va_arg(vargs, int); } else if (g_lastHttpCode == 302 && g_lastLocationHttpHeader.empty() && strcmp(fmt, "header='%s'\n") == 0) { auto header = va_arg(vargs, const char*); if (header != nullptr && strncmp("Location: ", header, 10) == 0) { g_lastLocationHttpHeader = header + 10; *interruptionRequestedFlag = true; } } } } } // namespace namespace channel_logger { using boost::log::keywords::channel; ChannelLogger ffmpeg_closing(channel = "ffmpeg_closing"), ffmpeg_opening(channel = "ffmpeg_opening"), ffmpeg_pause(channel = "ffmpeg_pause"), ffmpeg_seek(channel = "ffmpeg_seek"), ffmpeg_sync(channel = "ffmpeg_sync"), ffmpeg_threads(channel = "ffmpeg_threads"), ffmpeg_volume(channel = "ffmpeg_volume"), ffmpeg_internal(channel = "ffmpeg_internal"); } // namespace channel_logger std::unique_ptr GetFrameDecoder(std::unique_ptr audioPlayer) { return std::unique_ptr(new FFmpegDecoder(std::move(audioPlayer))); } ////////////////////////////////////////////////////////////////////////////// FFmpegDecoder::FFmpegDecoder(std::unique_ptr audioPlayer) : m_frameListener(nullptr), m_decoderListener(nullptr), m_audioSettings(48000, 2, AV_SAMPLE_FMT_S16), m_pixelFormat(AV_PIX_FMT_YUV420P), m_allowDirect3dData(false), m_audioPlayer(std::move(audioPlayer)), m_hwAccelerated(true) { av_log_set_level(AV_LOG_ERROR); av_log_set_callback(log_callback); m_audioPlayer->SetCallback(this); resetVariables(); // init codecs #if ( LIBAVFORMAT_VERSION_INT <= AV_VERSION_INT(58,9,100) ) avcodec_register_all(); av_register_all(); #endif avdevice_register_all(); avformat_network_init(); } FFmpegDecoder::~FFmpegDecoder() { close(); } void FFmpegDecoder::resetVariables() { m_videoCodec = nullptr; m_formatContexts.clear(); m_formatContextInterrupts.clear(); m_videoCodecContext = nullptr; m_audioCodecContext = nullptr; m_audioSwrContext = nullptr; m_videoStream = nullptr; m_audioStream = nullptr; m_startTime = 0; m_currentTime = 0; m_duration = 0; m_prevTime = AV_NOPTS_VALUE; m_imageCovertContext = nullptr; m_audioPTS = 0; m_frameDisplayingRequested = false; ++m_generation; m_isPaused = false; m_seekDuration = AV_NOPTS_VALUE; m_videoResetDuration = AV_NOPTS_VALUE; m_seekRendezVous.count = 0; m_videoResetRendezVous.count = 0; m_videoResetting = false; m_videoStartClock = VIDEO_START_CLOCK_NOT_INITIALIZED; m_isVideoSeekingWhilePaused = false; m_isPlaying = false; m_audioPaused = false; m_audioIndices.clear(); { boost::lock_guard locker(m_addIntervalMutex); m_subtitleItems.clear(); } m_subtitleIdx = -1; m_addIntervalCallback = {}; m_subtitlesCodecContext = nullptr; m_speedRational = { 1, 1 }; CHANNEL_LOG(ffmpeg_closing) << "Variables reset"; } void FFmpegDecoder::close() { CHANNEL_LOG(ffmpeg_closing) << "Start file closing"; CHANNEL_LOG(ffmpeg_closing) << "Aborting threads"; // controls other threads, hence stop first for (auto& mainParseThread : m_mainParseThreads) { Shutdown(mainParseThread); } Shutdown(m_mainVideoThread); Shutdown(m_mainAudioThread); Shutdown(m_mainDisplayThread); m_audioPlayer->Close(); if (m_frameListener != nullptr) { m_frameListener->decoderClosing(); } closeProcessing(); if (m_decoderListener != nullptr) { m_decoderListener->playingFinished(); } } void FFmpegDecoder::closeProcessing() { m_audioPacketsQueue.clear(); m_videoPacketsQueue.clear(); CHANNEL_LOG(ffmpeg_closing) << "Closing old vars"; m_mainVideoThread.reset(); m_mainAudioThread.reset(); m_mainParseThreads.clear(); m_mainDisplayThread.reset(); // Free videoFrames { boost::lock_guard locker(m_videoFramesMutex); m_videoFramesQueue.clear(); } sws_freeContext(m_imageCovertContext); if (m_audioSwrContext != nullptr) { swr_free(&m_audioSwrContext); } FreeVideoCodecContext(m_videoCodecContext); // Close the audio codec avcodec_free_context(&m_audioCodecContext); avcodec_free_context(&m_subtitlesCodecContext); bool isFileReallyClosed = false; // Close video file for (auto& formatContext : m_formatContexts) if (formatContext != nullptr) { avformat_close_input(&formatContext); isFileReallyClosed = true; } m_ioCtx.reset(); CHANNEL_LOG(ffmpeg_closing) << "Old file closed"; resetVariables(); if (m_decoderListener != nullptr) { m_decoderListener->decoderClosed(isFileReallyClosed); } } const char szUserAgent[] = "User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"; bool FFmpegDecoder::openUrls(std::initializer_list urls, const std::string& inputFormat, bool useHHO) { close(); m_formatContextInterrupts = std::vector(urls.size()); for (auto& elem : m_formatContextInterrupts) { elem.store(false); } for (auto url : urls) { const auto interruptionRequestedFlag = &m_formatContextInterrupts.at(m_formatContexts.size()); auto iformat = inputFormat.empty() ? nullptr : av_find_input_format(inputFormat.c_str()); int redirectsLeft = 10; for (;;) { std::vector finalUrls{ url }; std::string hostname; const bool isHttps = boost::starts_with(url, "https://"); if (isHttps) { if (useHHO) { const auto pos = 8; // Move past "://" size_t endPos = url.find_first_of(":/", pos); if (endPos != std::string::npos) { hostname = url.substr(pos, endPos - pos); auto ips = resolveHostnameToIPs(hostname); if (!ips.empty()) { finalUrls.clear(); for (const auto& ip : ips) { std::string urlWithIp = url.substr(0, pos) + ip + url.substr(endPos); finalUrls.push_back(urlWithIp); } } } } } // Open video file g_lastLocationHttpHeader.clear(); g_lastHttpCode = 0; g_interruptionRequestedFlag = interruptionRequestedFlag; AVFormatContext* formatContext = nullptr; int error = -1; for (const auto& finalUrl : finalUrls) { AVDictionary* streamOpts = nullptr; auto avOptionsGuard = MakeGuard(&streamOpts, av_dict_free); av_dict_set(&streamOpts, "rw_timeout", "5000000", 0); // 5 seconds I/O timeout. if (isHttps || boost::starts_with(url, "http://")) // seems to be a bug { av_dict_set(&streamOpts, "timeout", "5000000", 0); // 5 seconds tcp timeout. } if (useHHO && !hostname.empty()) { CHANNEL_LOG(ffmpeg_opening) << "Opening using a HHO Host: " << hostname << " URL: " << url; av_dict_set(&streamOpts, "headers", ("Host: " + hostname + "\r\n" + szUserAgent).c_str(), 0); } else { av_dict_set(&streamOpts, "headers", szUserAgent, 0); } if (iformat) { av_dict_set(&streamOpts, "rtbufsize", "15000000", 0); // https://superuser.com/questions/1158820/ffmpeg-real-time-buffer-issue-rtbufsize-parameter } if (iformat ? (iformat->name != nullptr && strcmp(iformat->name, "sdp") == 0) : boost::iends_with(url, ".sdp")) { av_dict_set(&streamOpts, "protocol_whitelist", "file,http,https,tls,rtp,tcp,udp,crypto,httpproxy,data", 0); } av_dict_set(&streamOpts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE); formatContext = avformat_alloc_context(); formatContext->interrupt_callback.opaque = interruptionRequestedFlag; formatContext->interrupt_callback.callback = ThisThreadInterruptionRequested; error = avformat_open_input(&formatContext, finalUrl.c_str(), iformat, &streamOpts); if (error == 0) { break; } } auto formatContextGuard = MakeGuard(&formatContext, avformat_close_input); g_interruptionRequestedFlag = nullptr; *interruptionRequestedFlag = false; if (error == 0) { CHANNEL_LOG(ffmpeg_opening) << "Opening video/audio file..."; // Retrieve stream information if (avformat_find_stream_info(formatContext, nullptr) < 0) { CHANNEL_LOG(ffmpeg_opening) << "Couldn't find stream information"; return false; } m_formatContexts.push_back(formatContext); formatContextGuard.release(); break; } if (!isHttps || !useHHO || g_lastLocationHttpHeader.empty() || --redirectsLeft < 0) { char err_buf[AV_ERROR_MAX_STRING_SIZE + 2] = ": "; BOOST_LOG_TRIVIAL(error) << "Couldn't open video/audio file error " << error << (av_strerror(error, err_buf + 2, sizeof(err_buf) - 2) == 0 ? err_buf : "") << " url = " << url; return false; } url = g_lastLocationHttpHeader; CHANNEL_LOG(ffmpeg_opening) << "Redirecting to URL: " << url; } } return doOpen(urls); } bool FFmpegDecoder::openStream(std::unique_ptr stream) { close(); auto ioCtx = std::make_unique(std::move(stream)); auto formatContext = avformat_alloc_context(); auto formatContextGuard = MakeGuard(&formatContext, avformat_close_input); ioCtx->initAVFormatContext(formatContext); formatContext->interrupt_callback.callback = ThisThreadInterruptionRequested; AVDictionary *streamOpts = nullptr; auto avOptionsGuard = MakeGuard(&streamOpts, av_dict_free); const int error = avformat_open_input(&formatContext, nullptr, nullptr, &streamOpts); if (error != 0) { char err_buf[AV_ERROR_MAX_STRING_SIZE + 2] = ": "; BOOST_LOG_TRIVIAL(error) << "Couldn't open video/audio stream error " << error << (av_strerror(error, err_buf + 2, sizeof(err_buf) - 2) == 0 ? err_buf : ""); return false; } CHANNEL_LOG(ffmpeg_opening) << "Opening video/audio file..."; // Retrieve stream information if (avformat_find_stream_info(formatContext, nullptr) < 0) { CHANNEL_LOG(ffmpeg_opening) << "Couldn't find stream information"; return false; } m_formatContexts.push_back(formatContext); formatContextGuard.release(); m_ioCtx = std::move(ioCtx); return doOpen(); } bool FFmpegDecoder::doOpen(const std::initializer_list& urls) { m_referenceTime = boost::chrono::high_resolution_clock::now().time_since_epoch(); // Find the first video stream m_videoContextIndex = -1; m_videoStreamNumber = -1; m_audioContextIndex = -1; m_audioStreamNumber = -1; for (int contextIdx = m_formatContexts.size(); --contextIdx >= 0;) { const auto formatContext = m_formatContexts[contextIdx]; for (int i = formatContext->nb_streams; --i >= 0;) { auto stream = formatContext->streams[i]; switch (stream->codecpar->codec_type) { case AVMEDIA_TYPE_VIDEO: stream->discard = AVDISCARD_ALL; m_videoContextIndex = contextIdx; m_videoStreamNumber = i; break; case AVMEDIA_TYPE_AUDIO: if (m_audioContextIndex == -1 || m_audioContextIndex == contextIdx) { m_audioIndices.push_back(i); m_audioStream = stream; m_audioContextIndex = contextIdx; m_audioStreamNumber = i; } break; } } } const auto firstUnused = (std::max)(m_videoContextIndex, m_audioContextIndex) + 1; if (firstUnused == 0) return false; for (int contextIdx = m_formatContexts.size(); --contextIdx >= firstUnused;) { avformat_close_input(&m_formatContexts[contextIdx]); } m_formatContexts.resize(firstUnused); if (m_videoStreamNumber != -1) { m_videoStreamNumber = av_find_best_stream( m_formatContexts[m_videoContextIndex], AVMEDIA_TYPE_VIDEO, m_videoStreamNumber, -1, nullptr, 0); m_videoStream = m_formatContexts[m_videoContextIndex]->streams[m_videoStreamNumber]; m_videoStream->discard = AVDISCARD_DEFAULT; } std::reverse(m_audioIndices.begin(), m_audioIndices.end()); LoadSubtitleItems(urls); AVStream* timeStream = nullptr; if (m_videoStreamNumber == -1) { CHANNEL_LOG(ffmpeg_opening) << "Can't find video stream"; } else { timeStream = m_videoStream; } int contextIdx = m_videoContextIndex; if (m_audioStreamNumber == -1) { CHANNEL_LOG(ffmpeg_opening) << "No audio stream"; if (m_videoStreamNumber == -1) { return false; // no multimedia } } else if (!basedOnVideoStream()) { // Changing video -> audio duration timeStream = m_audioStream; contextIdx = m_audioContextIndex; } m_startTime = (timeStream->start_time > 0) ? timeStream->start_time : ((m_formatContexts[contextIdx]->start_time == AV_NOPTS_VALUE)? 0 : int64_t((m_formatContexts[contextIdx]->start_time / av_q2d(timeStream->time_base)) / AV_TIME_BASE)); m_duration = (timeStream->duration > 0) ? timeStream->duration : ((m_formatContexts[contextIdx]->duration == AV_NOPTS_VALUE)? 0 : int64_t((m_formatContexts[contextIdx]->duration / av_q2d(timeStream->time_base)) / AV_TIME_BASE)); if (!resetVideoProcessing()) { return false; } if (!setupAudioProcessing()) { return false; } return true; } void FFmpegDecoder::LoadSubtitleItems(const std::initializer_list& urls) { int lastSubtitleNr = 0; for (int contextIdx = 0; contextIdx < m_formatContexts.size(); ++contextIdx) { const auto formatContext = m_formatContexts[contextIdx]; for (int i = 0; i < formatContext->nb_streams; ++i) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { std::string description; if (auto title = av_dict_get(formatContext->streams[i]->metadata, "title", nullptr, 0)) { description = title->value; } else { description = "Track " + std::to_string(++lastSubtitleNr); if (auto lang = av_dict_get(formatContext->streams[i]->metadata, "language", nullptr, 0)) { description += " ("; description += lang->value; description += ')'; } } boost::lock_guard locker(m_addIntervalMutex); m_subtitleItems.push_back({ contextIdx, i, std::move(description), (urls.size() == 0) ? std::string() : *(urls.begin() + contextIdx) }); } } } } bool FFmpegDecoder::resetVideoProcessing() { FreeVideoCodecContext(m_videoCodecContext); // Find the decoder for the video stream if (m_videoStreamNumber >= 0) { CHANNEL_LOG(ffmpeg_opening) << "Video stream number: " << m_videoStreamNumber; m_videoCodecContext = avcodec_alloc_context3(nullptr); if (m_videoCodecContext == nullptr) { return false; } auto videoCodecContextGuard = MakeGuard(&m_videoCodecContext, avcodec_free_context); if (avcodec_parameters_to_context(m_videoCodecContext, m_videoStream->codecpar) < 0) { return false; } m_videoCodecContext->pkt_timebase = m_videoStream->time_base; m_videoCodec = avcodec_find_decoder(m_videoCodecContext->codec_id); if (m_videoCodec == nullptr) { assert(false && "No such codec found"); return false; // Codec not found } m_videoCodecContext->codec_id = m_videoCodec->id; #ifdef USE_HWACCEL if (m_hwAccelerated) { m_videoCodecContext->coded_width = m_videoCodecContext->width; m_videoCodecContext->coded_height = m_videoCodecContext->height; m_videoCodecContext->thread_count = 1; // Multithreading is apparently not compatible with hardware decoding if (dxva2_init(m_videoCodecContext) >= 0) { #if LIBAVCODEC_VERSION_MAJOR < 59 m_videoCodecContext->thread_safe_callbacks = 1; #endif } else { m_videoCodecContext->thread_count = 2; m_videoCodecContext->flags2 |= AV_CODEC_FLAG2_FAST; } } else #endif { m_videoCodecContext->thread_count = 2; m_videoCodecContext->flags2 |= AV_CODEC_FLAG2_FAST; } // Open codec if (avcodec_open2(m_videoCodecContext, m_videoCodec, nullptr) < 0) { assert(false && "Error on codec opening"); return false; // Could not open codec } // this may be a valid case //if (m_videoCodecContext->width <= 0 || m_videoCodecContext->height <= 0) //{ // assert(false && "This file lacks resolution"); // return false; // Could not open codec //} videoCodecContextGuard.release(); } return true; } bool FFmpegDecoder::setupAudioProcessing() { m_audioCurrentPref = m_audioSettings; const auto audioStreamNumber = m_audioStreamNumber.load(); if (audioStreamNumber >= 0) { CHANNEL_LOG(ffmpeg_opening) << "Audio stream number: " << audioStreamNumber; m_audioCodecContext = avcodec_alloc_context3(nullptr); if (m_audioCodecContext == nullptr) { return false; } auto audioCodecContextGuard = MakeGuard(&m_audioCodecContext, avcodec_free_context); if (!setupAudioCodec()) { return false; } if (!initAudioOutput()) { return false; } audioCodecContextGuard.release(); } return true; } bool FFmpegDecoder::setupAudioCodec() { if (avcodec_parameters_to_context(m_audioCodecContext, m_audioStream->codecpar) < 0) { return false; } // Find audio codec m_audioCodec = avcodec_find_decoder(m_audioCodecContext->codec_id); if (m_audioCodec == nullptr) { assert(false && "No such codec found"); return false; // Codec not found } // Open audio codec if (avcodec_open2(m_audioCodecContext, m_audioCodec, nullptr) < 0) { assert(false && "Error on codec opening"); return false; // Could not open codec } return true; } bool FFmpegDecoder::initAudioOutput() { return m_audioPlayer->Open(av_get_bytes_per_sample(m_audioSettings.format), m_audioSettings.num_channels(), &m_audioSettings.frequency); } void FFmpegDecoder::play(bool isPaused) { CHANNEL_LOG(ffmpeg_opening) << "Starting playing"; m_isPaused = isPaused; if (isPaused) { boost::lock_guard locker(m_isPausedMutex); m_pauseTimer = GetHiResTime(); } if (m_mainParseThreads.empty()) { m_isPlaying = true; for (int i = 0; i < m_formatContexts.size(); ++i) { m_mainParseThreads.push_back(std::make_unique(&FFmpegDecoder::parseRunnable, this, i)); } m_mainDisplayThread = std::make_unique(&FFmpegDecoder::displayRunnable, this); CHANNEL_LOG(ffmpeg_opening) << "Playing"; } } void FFmpegDecoder::AppendFrameClock(double frame_clock) { if (!basedOnVideoStream() && m_decoderListener != nullptr && m_seekDuration == AV_NOPTS_VALUE) { m_decoderListener->changedFramePosition( m_startTime, int64_t((m_audioPTS + frame_clock) / av_q2d(m_audioStream->time_base)), m_duration + m_startTime); } const auto speed = getSpeedRational(); InterLockedAdd(m_audioPTS, frame_clock * speed.numerator / speed.denominator); } void FFmpegDecoder::setVolume(double volume) { if (volume < 0 || volume > 1.) { return; } CHANNEL_LOG(ffmpeg_volume) << "Volume: " << volume; m_audioPlayer->SetVolume(volume); if (m_decoderListener != nullptr) { m_decoderListener->volumeChanged(volume); } } double FFmpegDecoder::volume() const { return m_audioPlayer->GetVolume(); } void FFmpegDecoder::SetFrameFormat(FrameFormat format, bool allowDirect3dData) { static_assert(PIX_FMT_YUV420P == AV_PIX_FMT_YUV420P, "FrameFormat and AVPixelFormat values must coincide."); static_assert(PIX_FMT_YUYV422 == AV_PIX_FMT_YUYV422, "FrameFormat and AVPixelFormat values must coincide."); static_assert(PIX_FMT_RGB24 == AV_PIX_FMT_RGB24, "FrameFormat and AVPixelFormat values must coincide."); static_assert(PIX_FMT_BGR24 == AV_PIX_FMT_BGR24, "FrameFormat and AVPixelFormat values must coincide."); m_pixelFormat = static_cast(format); m_allowDirect3dData = allowDirect3dData; } void FFmpegDecoder::doOnFinishedDisplayingFrame(unsigned int generation, FinishedDisplayingMode mode) { { boost::lock_guard locker(m_videoFramesMutex); if (!m_frameDisplayingRequested || generation != m_generation) { return; } if (mode != FINALIZE_DISPLAY && m_videoFramesQueue.canPop()) { VideoFrame ¤t_frame = m_videoFramesQueue.front(); if (current_frame.m_image->format == AV_PIX_FMT_DXVA2_VLD) { av_frame_unref(current_frame.m_image.get()); } m_videoFramesQueue.popFront(); } if (mode != RELEASE_FRAME) { m_frameDisplayingRequested = false; } } m_videoFramesCV.notify_all(); } bool FFmpegDecoder::seekDuration(int64_t duration) { if (m_isPaused && duration == m_currentTime) { return true; // previous frame special case } if (!m_mainParseThreads.empty() && m_seekDuration.exchange(duration) == AV_NOPTS_VALUE) { m_videoPacketsQueue.notify(); m_audioPacketsQueue.notify(); } return true; } void FFmpegDecoder::videoReset() { m_videoResetting = true; if (!m_mainParseThreads.empty() && m_videoResetDuration.exchange(m_currentTime) == AV_NOPTS_VALUE) { m_videoPacketsQueue.notify(); m_audioPacketsQueue.notify(); } } void FFmpegDecoder::seekWhilePaused() { boost::lock_guard locker(m_isPausedMutex); const bool paused = m_isPaused; if (paused) { if (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED) { InterLockedAdd(m_videoStartClock, GetHiResTime() - m_pauseTimer); } m_pauseTimer = GetHiResTime(); } m_isVideoSeekingWhilePaused = paused; } bool FFmpegDecoder::seekByPercent(double percent) { return seekDuration(m_startTime + int64_t(m_duration * percent)); } bool FFmpegDecoder::getFrameRenderingData(FrameRenderingData *data) { if (!m_frameDisplayingRequested || m_mainParseThreads.empty() || m_videoResetting) { return false; } VideoFrame ¤t_frame = m_videoFramesQueue.front(); if (current_frame.m_image == nullptr) { return false; } #ifdef USE_HWACCEL if (current_frame.m_image->format == AV_PIX_FMT_DXVA2_VLD) { data->d3d9device = get_device(m_videoCodecContext); auto surface = static_cast(static_cast(current_frame.m_image->data[3])); if (surface == nullptr) { return false; } data->surface = surface; } #endif data->image = current_frame.m_image->data; data->pitch = current_frame.m_image->linesize; data->width = current_frame.m_image->width; data->height = current_frame.m_image->height; if (current_frame.m_image->sample_aspect_ratio.num != 0 && current_frame.m_image->sample_aspect_ratio.den != 0) { data->aspectNum = current_frame.m_image->sample_aspect_ratio.num; data->aspectDen = current_frame.m_image->sample_aspect_ratio.den; } else if (m_videoStream && m_videoStream->sample_aspect_ratio.num != 0 && m_videoStream->sample_aspect_ratio.den != 0) { data->aspectNum = m_videoStream->sample_aspect_ratio.num; data->aspectDen = m_videoStream->sample_aspect_ratio.den; } else { data->aspectNum = 1; data->aspectDen = 1; } return true; } double FFmpegDecoder::getDurationSecs(int64_t duration) const { return av_q2d(basedOnVideoStream() ? m_videoStream->time_base : m_audioStream->time_base) * duration; } bool FFmpegDecoder::pauseResume() { if (m_mainParseThreads.empty()) { return false; } if (!m_isPaused) { CHANNEL_LOG(ffmpeg_pause) << "Pause"; { boost::lock_guard locker(m_isPausedMutex); m_isPaused = true; m_pauseTimer = GetHiResTime(); } m_videoFramesCV.notify_all(); m_videoPacketsQueue.notify(); m_audioPacketsQueue.notify(); return true; } CHANNEL_LOG(ffmpeg_pause) << "Resume"; { boost::lock_guard locker(m_isPausedMutex); if (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED) { InterLockedAdd(m_videoStartClock, GetHiResTime() - m_pauseTimer); } m_isVideoSeekingWhilePaused = false; m_isPaused = false; m_prevTime = AV_NOPTS_VALUE; } m_isPausedCV.notify_all(); return true; } bool FFmpegDecoder::nextFrame() { if (m_mainParseThreads.empty()) { return false; } if (m_videoPacketsQueue.empty()) { return false; } if (m_prevTime != AV_NOPTS_VALUE) { return false; } CHANNEL_LOG(ffmpeg_pause) << "Next frame"; { boost::lock_guard locker(m_isPausedMutex); if (!m_isPaused || m_isVideoSeekingWhilePaused) { return false; } const auto currentTime = GetHiResTime(); if (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED) { InterLockedAdd(m_videoStartClock, currentTime - m_pauseTimer); } m_pauseTimer = currentTime; m_isVideoSeekingWhilePaused = true; } m_isPausedCV.notify_all(); m_videoPacketsQueue.notify(); return true; } bool FFmpegDecoder::prevFrame() { if (m_mainParseThreads.empty()) { return false; } if (!m_videoStream || m_videoStream->r_frame_rate.den <= 0 || m_videoStream->time_base.den <= 0 || m_videoStream->r_frame_rate.num <= 0 || m_videoStream->time_base.num <= 0) { return false; } if (m_prevTime != AV_NOPTS_VALUE) { return false; } CHANNEL_LOG(ffmpeg_pause) << "Previous frame"; { boost::lock_guard locker(m_isPausedMutex); if (!m_isPaused || m_isVideoSeekingWhilePaused) { return false; } int64_t expected = AV_NOPTS_VALUE; if (!m_seekDuration.compare_exchange_strong(expected, m_currentTime)) { return false; } } m_videoPacketsQueue.notify(); m_audioPacketsQueue.notify(); return true; } int FFmpegDecoder::getNumAudioTracks() const { return m_audioIndices.size(); } int FFmpegDecoder::getAudioTrack() const { const auto audioStreamNumber = m_audioStreamNumber.load(); return (audioStreamNumber < 0) ? -1 : std::find(m_audioIndices.begin(), m_audioIndices.end(), audioStreamNumber) - m_audioIndices.begin(); } void FFmpegDecoder::setAudioTrack(int idx) { if (idx >= 0 && idx < m_audioIndices.size()) { m_audioStreamNumber = m_audioIndices[idx]; } } RationalNumber FFmpegDecoder::getSpeedRational() const { return m_speedRational; } void FFmpegDecoder::setSpeedRational(const RationalNumber& speed) { boost::lock_guard locker(m_isPausedMutex); const auto time = GetHiResTime(); m_speedRational = speed; m_referenceTime = (boost::chrono::high_resolution_clock::now() - boost::chrono::microseconds(int64_t(time * speed.denominator / speed.numerator * 1000000.))) .time_since_epoch(); } std::vector FFmpegDecoder::getProperties() const { std::vector result; for (auto formatContext : m_formatContexts) { if (formatContext && formatContext->iformat && formatContext->iformat->long_name) result.emplace_back(formatContext->iformat->long_name); } if (m_videoCodec && m_videoCodec->long_name) result.emplace_back(m_videoCodec->long_name); if (m_videoStream && m_videoCodecContext) { const auto eps_zero = 0.000025; double fps = 0; if (m_videoStream->r_frame_rate.den) fps = av_q2d(m_videoStream->r_frame_rate); if (fps < eps_zero && m_videoStream->avg_frame_rate.den) fps = av_q2d(m_videoStream->avg_frame_rate); if (fps < eps_zero && m_videoStream->time_base.num && m_videoStream->time_base.den) fps = 1.0 / av_q2d(m_videoStream->time_base); int bpp = 0; int depth = 0; if (auto av_pix_fmt_desc = av_pix_fmt_desc_get( (m_videoCodecContext->sw_pix_fmt == AV_PIX_FMT_NONE) ? m_videoCodecContext->pix_fmt : m_videoCodecContext->sw_pix_fmt)) { bpp = av_get_bits_per_pixel(av_pix_fmt_desc); for (int i = 0; i < av_pix_fmt_desc->nb_components; ++i) if (av_pix_fmt_desc->comp[i].depth > depth) depth = av_pix_fmt_desc->comp[i].depth; } char buffer[1000]; snprintf(buffer, sizeof(buffer) / sizeof(buffer[0]), "%d / %d @ %.2f FPS %d BPP %d bits", m_videoCodecContext->width, m_videoCodecContext->height, fps, bpp, depth); result.emplace_back(buffer); } if (m_audioCodec && m_audioCodec->long_name) result.emplace_back(m_audioCodec->long_name); return result; } std::pair FFmpegDecoder::getVideoSize() const { return m_videoCodecContext ? std::pair(m_videoCodecContext->width, m_videoCodecContext->height) : std::pair(); } std::pair FFmpegDecoder::isVideoAudioCompatible() const { auto videoLam = [this] { if (!m_videoCodec || !m_videoCodecContext) { return false; } if (m_videoCodecContext->color_trc == AVCOL_TRC_BT709) { return false; } if (m_videoCodec->id != AV_CODEC_ID_H264 && m_videoCodec->id != AV_CODEC_ID_MPEG2VIDEO && m_videoCodec->id != AV_CODEC_ID_MPEG4) { return false; } if (auto d = av_pix_fmt_desc_get((m_videoCodecContext->sw_pix_fmt == AV_PIX_FMT_NONE) ? m_videoCodecContext->pix_fmt : m_videoCodecContext->sw_pix_fmt)) { for (int i = 0; i < d->nb_components; ++i) if (d->comp[i].depth > 8) return false; } else { return false; } return true; }; return {videoLam(), m_audioCodec && m_audioCodecContext && (m_audioCodec->id == AV_CODEC_ID_MP3 || m_audioCodec->id == AV_CODEC_ID_AAC && (m_audioCodecContext->profile == AV_PROFILE_AAC_LOW || m_audioCodecContext->profile == AV_PROFILE_AAC_HE)) && m_audioCurrentPref.num_channels() == 2}; } void FFmpegDecoder::handleDirect3dData(AVFrame* videoFrame, bool forceConversion) { #ifdef USE_HWACCEL if ((forceConversion || !m_allowDirect3dData) && videoFrame->format == AV_PIX_FMT_DXVA2_VLD) { dxva2_retrieve_data(m_videoCodecContext, videoFrame); assert(videoFrame->format != AV_PIX_FMT_DXVA2_VLD); } #endif } double FFmpegDecoder::GetHiResTime() const { const auto speed = getSpeedRational(); return boost::chrono::duration_cast( boost::chrono::high_resolution_clock::now() - boost::chrono::high_resolution_clock::time_point(m_referenceTime)).count() / 1000000. * speed.numerator / speed.denominator; } bool FFmpegDecoder::basedOnVideoStream() const { return m_videoStream != nullptr && (m_audioStream == nullptr || (m_videoStream->disposition & AV_DISPOSITION_ATTACHED_PIC) == 0); } bool FFmpegDecoder::getHwAccelerated() const { return m_hwAccelerated; } void FFmpegDecoder::setHwAccelerated(bool hwAccelerated) { m_hwAccelerated = hwAccelerated; } std::vector FFmpegDecoder::listSubtitles() const { std::vector result; boost::lock_guard locker(m_addIntervalMutex); for (const auto& item : m_subtitleItems) { result.push_back(item.description); } return result; } bool FFmpegDecoder::getSubtitles(int idx, std::function addIntervalCallback) { int streamNumber; std::string url; { boost::lock_guard locker(m_addIntervalMutex); if (idx < 0 || idx >= m_subtitleItems.size()) return false; m_subtitleIdx = idx; m_addIntervalCallback = addIntervalCallback; avcodec_free_context(&m_subtitlesCodecContext); const auto& subtitleItem = m_subtitleItems.at(idx); if (subtitleItem.url.empty()) { return true; } streamNumber = subtitleItem.streamIdx; url = subtitleItem.url; } auto formatContext = avformat_alloc_context(); if (formatContext == nullptr) { return false; } auto formatContextGuard = MakeGuard(&formatContext, avformat_close_input); int error = avformat_open_input(&formatContext, url.c_str(), nullptr, nullptr); if (error != 0) { return false; } auto codecContext = MakeSubtitlesCodecContext(formatContext->streams[streamNumber]->codecpar); if (codecContext == nullptr) { return false; } auto codecContextGuard = MakeGuard(&codecContext, avcodec_free_context); bool ok = false; AVPacket packet; while (av_read_frame(formatContext, &packet) >= 0) { if (packet.stream_index == streamNumber) { std::string text = GetSubtitle(codecContext, packet); if (!text.empty()) { if (!addIntervalCallback(packet.pts / 1000., (packet.pts + packet.duration) / 1000., text)) { av_packet_unref(&packet); return false; } ok = true; } } av_packet_unref(&packet); } return ok; } void FFmpegDecoder::setImageConversionFunc(ImageConversionFunc func) { m_imageConversionFunc = boost::make_shared(std::move(func)); } ================================================ FILE: video/ffmpegdecoder.h ================================================ #pragma once #include "decoderinterface.h" #include "audioplayer.h" extern "C" { #include #include #include #include //#include } #include #include #include #include #include #include #include #include #include namespace channel_logger { // Weird x64 debug build crash fixed #if defined(_MSC_VER) && _MSC_VER < 1910 class ChannelLogger : public boost::log::sources::channel_logger_mt<> { template auto open_record_unlocked(basic_logger*) { return basic_logger::open_record_unlocked(); } public: using channel_logger_mt<>::channel_logger_mt; boost::log::record open_record() { // Perform a quick check first if (this->core()->get_logging_enabled()) { open_record_lock lock(this->get_threading_model()); return open_record_unlocked(this); } return boost::log::record(); } }; #else using ChannelLogger = boost::log::sources::channel_logger_mt<>; #endif extern ChannelLogger ffmpeg_closing, ffmpeg_opening, ffmpeg_pause, ffmpeg_seek, ffmpeg_sync, ffmpeg_threads, ffmpeg_volume, ffmpeg_internal; } // namespace channel_logger #define CHANNEL_LOG(channel) BOOST_LOG(::channel_logger::channel) #include "fqueue.h" #include "videoframe.h" #include "vqueue.h" struct RendezVousData { boost::mutex mtx; unsigned int generation{}; unsigned int count{}; boost::condition_variable cond; }; class DecoderIOContext; // Inspired by http://dranger.com/ffmpeg/ffmpeg.html class FFmpegDecoder final : public IFrameDecoder, public IAudioPlayerCallback { public: FFmpegDecoder(std::unique_ptr audioPlayer); ~FFmpegDecoder() override; FFmpegDecoder(const FFmpegDecoder&) = delete; FFmpegDecoder& operator=(const FFmpegDecoder&) = delete; void SetFrameFormat(FrameFormat format, bool allowDirect3dData) override; bool openUrls(std::initializer_list urls, const std::string& inputFormat = {}, bool useHHO = false) override; bool openStream(std::unique_ptr stream) override; bool seekDuration(int64_t duration); bool seekByPercent(double percent) override; void videoReset() override; double volume() const override; bool isPlaying() const override { return m_isPlaying; } bool isPaused() const override { return m_isPaused; } void setFrameListener(IFrameListener* listener) override { m_frameListener = listener; } void setDecoderListener(FrameDecoderListener* listener) override { m_decoderListener = listener; } bool getFrameRenderingData(FrameRenderingData* data) override; double getDurationSecs(int64_t duration) const override; void doOnFinishedDisplayingFrame(unsigned int generation, FinishedDisplayingMode mode) override; void close() override; void play(bool isPaused = false) override; bool pauseResume() override; bool nextFrame() override; bool prevFrame() override; void setVolume(double volume) override; int getNumAudioTracks() const override; int getAudioTrack() const override; void setAudioTrack(int idx) override; RationalNumber getSpeedRational() const override; void setSpeedRational(const RationalNumber& speed) override; bool getHwAccelerated() const override; void setHwAccelerated(bool hwAccelerated) override; std::vector getProperties() const override; std::pair getVideoSize() const override; std::pair isVideoAudioCompatible() const override; std::vector listSubtitles() const override; bool getSubtitles(int idx, std::function addIntervalCallback) override; void setImageConversionFunc(ImageConversionFunc func) override; private: struct VideoParseContext; // Threads void parseRunnable(int idx); void audioParseRunnable(); void videoParseRunnable(); void displayRunnable(); bool doOpen(const std::initializer_list& urls = {}); void LoadSubtitleItems(const std::initializer_list& urls); bool dispatchPacket(int idx, AVPacket& packet); void handleSubtitlePacket(int idx, const AVPacket& packet); void flush(int idx); void startAudioThread(); void startVideoThread(); bool resetDecoding(int64_t seekDuration, bool resetVideo); bool doSeekFrame(int idx, int64_t seekDuration, AVPacket* packet); bool respawn(int64_t seekDuration, bool resetVideo); void fixDuration(); bool handleAudioPacket( const AVPacket& packet, std::vector& resampleBuffer, bool failed, double& scheduledEndTime); void setupAudioSwrContext(AVFrame* audioFrame); bool handleAudioFrame( double frame_clock, uint8_t* write_data, int64_t write_size, bool failed); bool handleVideoPacket( const AVPacket& packet, VideoParseContext& context); bool handleVideoFrame( AVFramePtr& frame, VideoParseContext& context, int64_t next_timestamp); // IAudioPlayerCallback void AppendFrameClock(double frame_clock) override; void resetVariables(); void closeProcessing(); bool resetVideoProcessing(); bool setupAudioProcessing(); bool setupAudioCodec(); bool initAudioOutput(); void seekWhilePaused(); void handleDirect3dData(AVFrame* videoFrame, bool forceConversion); double GetHiResTime() const; bool basedOnVideoStream() const; // Frame display listener IFrameListener* m_frameListener; FrameDecoderListener* m_decoderListener; // Indicators bool m_isPlaying; std::unique_ptr m_mainVideoThread; std::unique_ptr m_mainAudioThread; std::vector> m_mainParseThreads; std::unique_ptr m_mainDisplayThread; // Synchronization boost::atomic m_audioPTS; // Real duration from video stream int64_t m_startTime; boost::atomic_int64_t m_currentTime; int64_t m_duration; boost::atomic_int64_t m_prevTime; // Basic stuff std::vector m_formatContexts; std::vector m_formatContextInterrupts; boost::atomic_int64_t m_seekDuration; boost::atomic_int64_t m_videoResetDuration; RendezVousData m_seekRendezVous; RendezVousData m_videoResetRendezVous; boost::atomic_bool m_videoResetting; // Video Stuff enum { VIDEO_START_CLOCK_NOT_INITIALIZED = -1000000000 }; boost::atomic m_videoStartClock; const AVCodec* m_videoCodec; AVCodecContext* m_videoCodecContext; AVStream* m_videoStream; int m_videoContextIndex; int m_videoStreamNumber; // Audio Stuff const AVCodec* m_audioCodec; AVCodecContext* m_audioCodecContext; AVStream* m_audioStream; int m_audioContextIndex; boost::atomic m_audioStreamNumber; SwrContext* m_audioSwrContext; struct AudioParams { int frequency; #if LIBAVUTIL_VERSION_MAJOR < 57 int channels; int64_t channel_layout; auto num_channels() const { return channels; } #else AVChannelLayout channel_layout; auto num_channels() const { return channel_layout.nb_channels; } #endif AVSampleFormat format; AudioParams() = default; AudioParams(int freq, int chans, AVSampleFormat fmt); ~AudioParams(); AudioParams& operator=(const AudioParams& other); }; AudioParams m_audioSettings; AudioParams m_audioCurrentPref; // Stuff for converting image SwsContext* m_imageCovertContext; AVPixelFormat m_pixelFormat; bool m_allowDirect3dData; // Video and audio queues enum { MAX_VIDEO_QUEUE_SIZE = 100 * 1024 * 1024, MAX_AUDIO_QUEUE_SIZE = 15 * 1024 * 1024, MAX_VIDEO_FRAMES = 500, MAX_AUDIO_FRAMES = 500, }; FQueue m_videoPacketsQueue; FQueue m_audioPacketsQueue; VQueue m_videoFramesQueue; bool m_frameDisplayingRequested; unsigned int m_generation = 0; boost::mutex m_videoFramesMutex; boost::condition_variable m_videoFramesCV; boost::atomic_bool m_isPaused; boost::mutex m_isPausedMutex; boost::condition_variable m_isPausedCV; double m_pauseTimer; boost::atomic_bool m_isVideoSeekingWhilePaused; // Audio std::unique_ptr m_audioPlayer; bool m_audioPaused; std::vector m_audioIndices; std::unique_ptr m_ioCtx; boost::atomic m_referenceTime; boost::atomic m_speedRational; // Numerator, Denominator bool m_hwAccelerated; struct SubtitleItem { int contextIdx; int streamIdx; std::string description; std::string url; }; std::vector m_subtitleItems; mutable boost::mutex m_addIntervalMutex; int m_subtitleIdx; std::function m_addIntervalCallback; AVCodecContext* m_subtitlesCodecContext; boost::atomic_shared_ptr m_imageConversionFunc; }; ================================================ FILE: video/fqueue.h ================================================ #pragma once #include #include #include #include template class FQueue { public: FQueue() {} FQueue(const FQueue&) = delete; FQueue& operator=(const FQueue&) = delete; template bool push(const AVPacket& packet, T abortFunc) { const auto pos = packet.pos; const auto dts = packet.dts; auto& prev = m_positions[packet.stream_index]; if ((dts != AV_NOPTS_VALUE) ? dts < prev.m_dts : (pos != -1 && pos < prev.m_pos)) { return false; } bool wasEmpty; { boost::unique_lock locker(m_mutex); while (isPacketsQueueFull()) { if (abortFunc()) { return false; } m_condVar.wait(locker); } wasEmpty = m_queue.empty(); enqueue(packet); if (pos != -1) prev.m_pos = pos; if (dts != AV_NOPTS_VALUE) prev.m_dts = dts; } if (wasEmpty) { m_condVar.notify_all(); } return true; } template bool pop(AVPacket& packet, T abortFunc = T()) { bool wasFull; { boost::unique_lock locker(m_mutex); while (m_queue.empty()) { if (abortFunc()) { return false; } m_condVar.wait(locker); } wasFull = isPacketsQueueFull(); packet = dequeue(); } if (wasFull) { m_condVar.notify_all(); } return true; } void clear() { for (AVPacket& packet : m_queue) { av_packet_unref(&packet); } m_packetsSize = 0; m_positions.clear(); std::deque().swap(m_queue); } bool empty() { boost::lock_guard locker(m_mutex); return m_queue.empty(); } void notify() { m_condVar.notify_all(); } private: AVPacket dequeue() { assert(!m_queue.empty()); AVPacket packet = m_queue.front(); m_queue.pop_front(); m_packetsSize -= packet.size; assert(m_packetsSize >= 0); return packet; } void enqueue(const AVPacket& packet) { m_packetsSize += packet.size; assert(m_packetsSize >= 0); m_queue.push_back(packet); } bool isPacketsQueueFull() const { return m_packetsSize > MAX_QUEUE_SIZE || m_queue.size() > MAX_FRAMES; } private: struct PositionData { int64_t m_pos = -1; int64_t m_dts = AV_NOPTS_VALUE; }; int64_t m_packetsSize = 0; std::deque m_queue; boost::mutex m_mutex; boost::condition_variable m_condVar; std::map m_positions; }; ================================================ FILE: video/interlockedadd.h ================================================ #pragma once #include inline void InterLockedAdd(boost::atomic& clock, double correction) { for (double v = clock; !clock.compare_exchange_weak(v, v + correction);) { } } ================================================ FILE: video/makeguard.h ================================================ #pragma once #include #include template inline auto MakeGuard(T* t, D d) { return std::unique_ptr(t, std::move(d)); } ================================================ FILE: video/ordered_scoped_token.h ================================================ // ordered_scoped_token.h #pragma once #include #include #include #include #include #include #include class OrderedScopedTokenGenerator { public: OrderedScopedTokenGenerator() : ctrl_(std::make_shared()) {} struct LockScope; private: struct Control { std::mutex mtx; std::condition_variable cv; uint64_t next_index{ 0 }; // next index allowed to enter critical section uint64_t alloc_index{ 0 }; // next index to assign // indices that have been consumed (either released or discarded) but are > next_index std::unordered_set consumed; }; std::shared_ptr ctrl_; // helper: mark index consumed and advance next_index while possible static void consume_and_advance(const std::shared_ptr& ctrl, uint64_t index) { std::lock_guard lk(ctrl->mtx); if (index < ctrl->next_index) { // already passed return; } if (index == ctrl->next_index) { ++ctrl->next_index; // advance while contiguous indices are present in consumed set while (true) { auto it = ctrl->consumed.find(ctrl->next_index); if (it == ctrl->consumed.end()) break; ctrl->consumed.erase(it); ++ctrl->next_index; } } else { // index > next_index: remember it for later coalescing ctrl->consumed.insert(index); } ctrl->cv.notify_all(); } public: // Movable-only token representing a one-time ordered gate struct Token { Token() = default; // non-copyable Token(const Token&) = delete; Token& operator=(const Token&) = delete; // movable Token(Token&& other) noexcept = default; Token& operator=(Token&& other) noexcept = default; ~Token() { // If token was never claimed (locked or consumed), consume it now so it doesn't block later tokens. // This destructor does NOT block; it marks the index consumed immediately so later tokens can proceed. if (!state_) return; bool expected = false; if (!state_->claimed.compare_exchange_strong(expected, true)) { // already claimed/locked; nothing to do return; } // mark consumed and advance generator (non-blocking) consume_and_advance(state_->ctrl, state_->index); } // Acquire the token and enter the critical section. // Returns a movable LockScope that holds the critical section until destroyed. // Throws std::runtime_error if token is invalid or already claimed. LockScope lock() { if (!state_) throw std::runtime_error("Invalid token"); bool expected = false; if (!state_->claimed.compare_exchange_strong(expected, true)) { throw std::runtime_error("Token already claimed"); } std::unique_lock lk(state_->ctrl->mtx); state_->ctrl->cv.wait(lk, [&] { return state_->ctrl->next_index == state_->index; }); // Do NOT advance next_index here; LockScope destructor will advance when scope ends. return LockScope(state_); } // Try to acquire immediately. If successful returns LockScope; otherwise returns empty optional. // If token already claimed, returns empty. LockScope try_lock() { if (!state_) return {}; bool expected = false; if (!state_->claimed.compare_exchange_strong(expected, true)) { return {}; // already claimed } // check if it's our turn now { std::lock_guard lk(state_->ctrl->mtx); if (state_->ctrl->next_index != state_->index) { // Not our turn; we keep claimed=true to respect one-time semantics, // but try_lock fails to enter the critical section immediately. return {}; } } // It's our turn; return LockScope (do not advance here) return LockScope(state_); } // Query whether token has been claimed (locked or consumed) bool is_claimed() const { return state_ && state_->claimed.load(); } explicit operator bool() const noexcept { return static_cast(state_); } private: friend class OrderedScopedTokenGenerator; struct State { std::shared_ptr ctrl; uint64_t index; std::atomic claimed{ false }; // ensures token is used only once }; explicit Token(std::shared_ptr s) : state_(std::move(s)) {} std::shared_ptr state_; }; // RAII scope returned by Token::lock(); movable-only. struct LockScope { LockScope() = default; // non-copyable LockScope(const LockScope&) = delete; LockScope& operator=(const LockScope&) = delete; // movable LockScope(LockScope&& other) noexcept : state_(std::exchange(other.state_, {})), owns_(other.owns_) { other.owns_ = false; } LockScope& operator=(LockScope&& other) noexcept { if (this != &other) { release_if_needed(); state_ = std::exchange(other.state_, {}); owns_ = other.owns_; other.owns_ = false; } return *this; } ~LockScope() { release_if_needed(); } // explicit release before destruction (optional) void release() { release_if_needed(); } bool valid() const noexcept { return state_ && owns_; } private: friend struct Token; explicit LockScope(std::shared_ptr s) : state_(std::move(s)), owns_(true) {} void release_if_needed() { if (!state_ || !owns_) return; // mark consumed and advance generator consume_and_advance(state_->ctrl, state_->index); owns_ = false; } std::shared_ptr state_; bool owns_{ false }; }; // generate a new movable-only token Token generate() { uint64_t idx; { // allocate index under mutex to avoid ABA with consumed set (not strictly necessary but simpler) std::lock_guard lk(ctrl_->mtx); idx = ctrl_->alloc_index++; } auto s = std::make_shared(); s->ctrl = ctrl_; s->index = idx; s->claimed.store(false); return Token(s); } }; ================================================ FILE: video/parserunnable.cpp ================================================ #include "ffmpegdecoder.h" #include "makeguard.h" #include "subtitles.h" #include #include #include #ifdef _WIN32 #include #else #include #endif const int AVERROR_ECONNRESET = #ifdef _WIN32 AVERROR(WSAECONNRESET); #else AVERROR(ECONNRESET); #endif namespace { bool isSeekable(AVFormatContext* formatContext) { #ifdef AVFMTCTX_UNSEEKABLE return ((formatContext->ctx_flags & AVFMTCTX_UNSEEKABLE) == 0); #else return (formatContext->pb == nullptr || (formatContext->pb->seekable & AVIO_SEEKABLE_NORMAL) != 0) && (formatContext->iformat->name == nullptr || strcmp(formatContext->iformat->name, "sdp") != 0 || formatContext->iformat->read_packet == nullptr || formatContext->iformat->read_seek != nullptr || formatContext->iformat->read_seek2 != nullptr); #endif } template bool RendezVous( boost::atomic_int64_t& duration, RendezVousData& data, unsigned int threshold, bool& encountered, T func) { if (threshold == 1) { const auto prevDuration = duration.exchange(AV_NOPTS_VALUE); if (prevDuration != AV_NOPTS_VALUE) { if (!func(prevDuration)) return false; encountered = true; } return true; } if (duration == AV_NOPTS_VALUE) return true; boost::mutex::scoped_lock lock(data.mtx); if (data.count == threshold - 1) { ++data.generation; data.count = 0; const auto prevDuration = duration.exchange(AV_NOPTS_VALUE); const bool result = func(prevDuration); lock.unlock(); data.cond.notify_all(); encountered = result; return result; } encountered = true; ++data.count; const unsigned int gen = data.generation; while (gen == data.generation) data.cond.wait(lock); return true; } } // namespace void FFmpegDecoder::parseRunnable(int idx) { CHANNEL_LOG(ffmpeg_threads) << "Parse thread started"; AVPacket packet; enum { UNSET, SET_EOF, SET_INVALID, REPORTED } eof = UNSET; if (idx == 0) { // detect real framesize fixDuration(); if (m_decoderListener != nullptr) { m_decoderListener->fileLoaded(m_startTime, m_duration + m_startTime); m_decoderListener->changedFramePosition(m_startTime, m_startTime, m_duration + m_startTime); } startAudioThread(); startVideoThread(); } int64_t lastTime = m_currentTime; int64_t timeLeft = 0; // effective frame time left from packet (adjusted by start_time) enum { TO_RECOVER, RECOVERING, RECOVERED } recovering = RECOVERED; const bool seekable = isSeekable(m_formatContexts[idx]); for (;;) { if (boost::this_thread::interruption_requested()) { return; } bool restarted = false; RendezVous(m_seekDuration, m_seekRendezVous, m_formatContexts.size(), restarted, std::bind(&FFmpegDecoder::resetDecoding, this, std::placeholders::_1, false)); if (!RendezVous(m_videoResetDuration, m_videoResetRendezVous, m_formatContexts.size(), restarted, std::bind(&FFmpegDecoder::resetDecoding, this, std::placeholders::_1, true))) { return; } if (restarted) { eof = UNSET; recovering = RECOVERED; } const int readStatus = av_read_frame(m_formatContexts[idx], &packet); if (readStatus >= 0) { const bool dispatched = dispatchPacket(idx, packet); eof = UNSET; // Compute effective frame timestamp from packet using its pts (work in stream PTS units). if (seekable && packet.pts != AV_NOPTS_VALUE) { const AVFormatContext* fmt = m_formatContexts[idx]; if (fmt && packet.stream_index >= 0 && packet.stream_index < fmt->nb_streams) { const AVStream* stream = fmt->streams[packet.stream_index]; const int64_t pts = packet.pts; const int64_t dur = (packet.duration != AV_NOPTS_VALUE) ? packet.duration : 0; const int64_t start_time = (stream && stream->start_time != AV_NOPTS_VALUE) ? stream->start_time : 0; const int64_t effectiveStreamTs = pts + dur - start_time; if (effectiveStreamTs > 0) { // Prefer stream duration (stream PTS units) if (stream && stream->duration != AV_NOPTS_VALUE) { timeLeft = stream->duration - effectiveStreamTs; } // Fallback: use format context duration if available. Convert fmt->duration (AV_TIME_BASE units) // to the stream's time_base units before computing timeLeft. else if (fmt->duration != AV_NOPTS_VALUE && stream && stream->time_base.den != 0) { // Convert fmt->duration (AV_TIME_BASE units) to the stream's time_base units using integer rescaling. // Create an AVRational representing AV_TIME_BASE units: {1, AV_TIME_BASE} AVRational avTimeBase = { 1, AV_TIME_BASE }; const int64_t fmtDurationInStreamUnits = av_rescale_q(fmt->duration, avTimeBase, stream->time_base); if (fmtDurationInStreamUnits > effectiveStreamTs) { timeLeft = fmtDurationInStreamUnits - effectiveStreamTs; } else { timeLeft = 0; } } } } } if (recovering == TO_RECOVER && m_currentTime > lastTime) { recovering = RECOVERING; } else if (dispatched && recovering == RECOVERING) { recovering = RECOVERED; } } else if ((readStatus == AVERROR_ECONNRESET || readStatus == AVERROR_INVALIDDATA || timeLeft > 1) && recovering == RECOVERED) { recovering = TO_RECOVER; lastTime = m_currentTime; timeLeft = 0; if (doSeekFrame(idx, lastTime, nullptr)) { CHANNEL_LOG(ffmpeg_seek) << __FUNCTION__ << " Trying to recover from " << readStatus << "; index: " << idx; } else { CHANNEL_LOG(ffmpeg_seek) << __FUNCTION__ << " Can't recover from " << readStatus << "; index: " << idx; } } else { using namespace boost; if (eof == SET_EOF || eof == SET_INVALID) { if ((m_decoderListener != nullptr) && m_videoPacketsQueue.empty() && m_audioPacketsQueue.empty() && (lock_guard(m_videoFramesMutex), !m_videoFramesQueue.canPop())) { if (eof == SET_EOF) { flush(idx); } if (readStatus != AVERROR_EOF && readStatus != AVERROR_ECONNRESET) { char err_buf[AV_ERROR_MAX_STRING_SIZE + 2] = ": "; CHANNEL_LOG(ffmpeg_seek) << __FUNCTION__ << " End of stream caused by " << readStatus << (av_strerror(readStatus, err_buf + 2, sizeof(err_buf) - 2) == 0 ? err_buf : "") << "; index: " << idx; } m_decoderListener->onEndOfStream(idx, eof == SET_INVALID); eof = REPORTED; } } if (eof == UNSET) { eof = (readStatus == AVERROR_EOF) ? SET_EOF : SET_INVALID; } this_thread::sleep_for(chrono::milliseconds(1)); } // Continue packet reading loop... } CHANNEL_LOG(ffmpeg_threads) << "Decoding ended"; } bool FFmpegDecoder::dispatchPacket(int idx, AVPacket& packet) { auto guard = MakeGuard(&packet, av_packet_unref); if (m_seekDuration != AV_NOPTS_VALUE || m_videoResetDuration != AV_NOPTS_VALUE) { return false; } auto seekLambda = [this, idx] { if (m_seekDuration != AV_NOPTS_VALUE || m_videoResetDuration != AV_NOPTS_VALUE) return true; if (m_decoderListener != nullptr) m_decoderListener->onQueueFull(idx); return false; }; if (idx == m_videoContextIndex && packet.stream_index == m_videoStreamNumber) { if (m_videoPacketsQueue.push(packet, seekLambda)) { guard.release(); return true; } } else if (idx == m_audioContextIndex && std::find(m_audioIndices.begin(), m_audioIndices.end(), packet.stream_index) != m_audioIndices.end()) { if (m_audioPacketsQueue.push(packet, seekLambda)) { guard.release(); return true; } } else if (m_formatContexts[idx]->streams[packet.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { handleSubtitlePacket(idx, packet); } return false; } void FFmpegDecoder::handleSubtitlePacket(int idx, const AVPacket& packet) { std::function addIntervalCallback; { boost::lock_guard locker(m_addIntervalMutex); if (m_subtitleIdx == -1 || !m_addIntervalCallback) return; const auto& subtitleItem = m_subtitleItems.at(m_subtitleIdx); if (subtitleItem.contextIdx != idx || subtitleItem.streamIdx != packet.stream_index) return; addIntervalCallback = m_addIntervalCallback; if (m_subtitlesCodecContext == nullptr) { m_subtitlesCodecContext = MakeSubtitlesCodecContext( m_formatContexts[idx]->streams[packet.stream_index]->codecpar); if (m_subtitlesCodecContext == nullptr) { return; } } } std::string text = GetSubtitle(m_subtitlesCodecContext, packet); if (!text.empty()) { // Convert subtitle pts/duration to seconds using the subtitle stream time_base. const AVFormatContext* fmt = m_formatContexts[idx]; if (fmt && packet.stream_index >= 0 && packet.stream_index < fmt->nb_streams) { const AVStream* s = fmt->streams[packet.stream_index]; if (s && packet.pts != AV_NOPTS_VALUE) { double startSec = av_q2d(s->time_base) * static_cast(packet.pts); double endSec = (packet.duration != AV_NOPTS_VALUE) ? av_q2d(s->time_base) * static_cast(packet.pts + packet.duration) : startSec; if (!addIntervalCallback(startSec, endSec, text)) { boost::lock_guard locker(m_addIntervalMutex); m_subtitleIdx = -1; m_addIntervalCallback = {}; avcodec_free_context(&m_subtitlesCodecContext); } } } } } void FFmpegDecoder::flush(int idx) { if (m_videoStreamNumber >= 0 && m_videoContextIndex == idx) { AVPacket packet{}; packet.stream_index = m_videoStreamNumber; packet.pts = AV_NOPTS_VALUE; packet.dts = AV_NOPTS_VALUE; dispatchPacket(m_videoContextIndex, packet); } if (m_audioStreamNumber >= 0 && m_audioContextIndex == idx) { AVPacket packet{}; packet.stream_index = m_audioStreamNumber; packet.pts = AV_NOPTS_VALUE; packet.dts = AV_NOPTS_VALUE; dispatchPacket(m_audioContextIndex, packet); } } void FFmpegDecoder::startAudioThread() { if (m_audioStreamNumber >= 0) { m_mainAudioThread = std::make_unique(&FFmpegDecoder::audioParseRunnable, this); } } void FFmpegDecoder::startVideoThread() { if (m_videoStreamNumber >= 0) { m_mainVideoThread = std::make_unique(&FFmpegDecoder::videoParseRunnable, this); } } bool FFmpegDecoder::resetDecoding(int64_t seekDuration, bool resetVideo) { CHANNEL_LOG(ffmpeg_seek) << __FUNCTION__ << " resetVideo=" << resetVideo; AVPacket packet{}; auto guard = MakeGuard(&packet, av_packet_unref); for (int i = 0; i < m_formatContexts.size(); ++i) { if (!doSeekFrame(i, seekDuration, resetVideo ? nullptr : &packet)) return false; } if (!respawn(seekDuration, resetVideo)) return false; if (packet.data != nullptr) { guard.release(); dispatchPacket(m_videoContextIndex, packet); } return true; } bool FFmpegDecoder::doSeekFrame(int idx, int64_t seekDuration, AVPacket* packet) { if (idx != m_audioContextIndex && !basedOnVideoStream()) return true; // continue; auto formatContext = m_formatContexts[idx]; if (!isSeekable(formatContext)) return true;//continue; const int64_t currentTime = m_currentTime; const bool handlingPrevFrame = m_isPaused && seekDuration == currentTime && packet != nullptr && m_videoContextIndex != -1; if (handlingPrevFrame) { const auto threshold = m_videoStream->avg_frame_rate.den * m_videoStream->time_base.den / (2LL * m_videoStream->avg_frame_rate.num * m_videoStream->time_base.num); seekDuration -= threshold; } const bool backward = seekDuration < currentTime; const int streamNumber = (idx == m_videoContextIndex && basedOnVideoStream()) ? m_videoStreamNumber : m_audioStreamNumber.load(); auto convertedSeekDuration = seekDuration; if (handlingPrevFrame) { convertedSeekDuration = (std::max)(m_startTime, convertedSeekDuration - m_videoStream->time_base.den / m_videoStream->time_base.num); } if (idx != m_videoContextIndex && m_videoContextIndex != -1) { convertedSeekDuration = av_rescale_q(convertedSeekDuration, m_videoStream->time_base, m_audioStream->time_base); } if (handlingPrevFrame) { if (av_seek_frame(formatContext, streamNumber, convertedSeekDuration, AVSEEK_FLAG_BACKWARD) < 0) { CHANNEL_LOG(ffmpeg_seek) << "Seek failed"; return false; } } else { if (av_seek_frame(formatContext, streamNumber, convertedSeekDuration, 0) < 0 && ((convertedSeekDuration >= 0 ? av_seek_frame(formatContext, streamNumber, convertedSeekDuration, AVSEEK_FLAG_BACKWARD) : av_seek_frame(formatContext, streamNumber, 0, 0)) < 0)) { CHANNEL_LOG(ffmpeg_seek) << "Seek failed"; return false; } } if (packet && backward && idx == m_videoContextIndex && convertedSeekDuration > m_startTime) { const int readStatus = av_read_frame(formatContext, packet); if (readStatus >= 0) { auto pts = packet->pts; if (pts != AV_NOPTS_VALUE) { if (packet->stream_index != m_videoStreamNumber) { const auto& time_base = formatContext->streams[packet->stream_index]->time_base; pts = (time_base.den && time_base.num) ? av_rescale_q(pts, time_base, m_videoStream->time_base) : 0; if (handlingPrevFrame) pts += m_videoStream->avg_frame_rate.den * m_videoStream->time_base.den / (2LL * m_videoStream->avg_frame_rate.num * m_videoStream->time_base.num); } if (pts >= currentTime) { const int flags = handlingPrevFrame ? AVSEEK_FLAG_BACKWARD : 0; av_packet_unref(packet); if (av_seek_frame(formatContext, streamNumber, m_startTime, flags) < 0 || av_seek_frame(formatContext, streamNumber, convertedSeekDuration, flags) < 0) { CHANNEL_LOG(ffmpeg_seek) << "Seek correction failed"; return false; } } } } } if (handlingPrevFrame) { m_prevTime = seekDuration; m_isVideoSeekingWhilePaused = true; } return true; } bool FFmpegDecoder::respawn(int64_t seekDuration, bool resetVideo) { const bool hasVideo = m_mainVideoThread != nullptr; const bool hasAudio = m_mainAudioThread != nullptr; if (hasVideo) { m_mainVideoThread->interrupt(); } if (hasAudio) { m_mainAudioThread->interrupt(); } if (hasVideo) { m_mainVideoThread->join(); } if (hasAudio) { m_mainAudioThread->join(); } // Reset stuff m_videoPacketsQueue.clear(); m_audioPacketsQueue.clear(); m_mainDisplayThread->interrupt(); m_mainDisplayThread->join(); // Free videoFrames { boost::lock_guard locker(m_videoFramesMutex); m_videoFramesQueue.clear(); m_frameDisplayingRequested = false; ++m_generation; } m_videoResetting = false; m_videoStartClock = VIDEO_START_CLOCK_NOT_INITIALIZED; if (resetVideo && !resetVideoProcessing()) { return false; } m_mainDisplayThread = std::make_unique(&FFmpegDecoder::displayRunnable, this); if (hasVideo) { if (m_videoCodecContext != nullptr) { avcodec_flush_buffers(m_videoCodecContext); } } if (hasAudio) { if (m_audioCodecContext != nullptr) { avcodec_flush_buffers(m_audioCodecContext); } m_audioPlayer->WaveOutReset(); } if (m_decoderListener != nullptr) { m_decoderListener->changedFramePosition(m_startTime, seekDuration, m_duration + m_startTime); } seekWhilePaused(); // Restart startAudioThread(); startVideoThread(); return true; } void FFmpegDecoder::fixDuration() { if (m_duration <= 0) { const int streamNumber = (m_videoContextIndex == 0) ? m_videoStreamNumber : m_audioStreamNumber.load(); m_duration = 0; if (!isSeekable(m_formatContexts[0])) { return; } AVPacket packet; while (av_read_frame(m_formatContexts[0], &packet) >= 0) { if (packet.stream_index == streamNumber) { if (packet.pts != AV_NOPTS_VALUE) { m_duration = packet.pts; } else if (packet.dts != AV_NOPTS_VALUE) { m_duration = packet.dts; } } av_packet_unref(&packet); if (boost::this_thread::interruption_requested()) { CHANNEL_LOG(ffmpeg_threads) << "Parse thread broken"; return; } } if (avformat_seek_file(m_formatContexts[0], streamNumber, 0, 0, 0, AVSEEK_FLAG_FRAME) < 0) { CHANNEL_LOG(ffmpeg_seek) << "Seek failed"; return; } } } ================================================ FILE: video/subtitles.cpp ================================================ #include "subtitles.h" #include "makeguard.h" #include #include namespace { /* * from mpv/sub/sd_ass.c * ass_to_plaintext() was written by wm4 and he says it can be under LGPL */ std::string ass_to_plaintext(const char *in) { std::string result; bool in_tag = false; const char *open_tag_pos = nullptr; bool in_drawing = false; while (*in) { if (in_tag) { if (in[0] == '}') { in += 1; in_tag = false; } else if (in[0] == '\\' && in[1] == 'p') { in += 2; // Skip text between \pN and \p0 tags. A \p without a number // is the same as \p0, and leading 0s are also allowed. in_drawing = false; while (in[0] >= '0' && in[0] <= '9') { if (in[0] != '0') in_drawing = true; in += 1; } } else { in += 1; } } else { if (in[0] == '\\' && (in[1] == 'N' || in[1] == 'n')) { in += 2; result += '\n'; } else if (in[0] == '\\' && in[1] == 'h') { in += 2; result += ' '; } else if (in[0] == '{') { open_tag_pos = in; in += 1; in_tag = true; } else { if (!in_drawing) result += in[0]; in += 1; } } } // A '{' without a closing '}' is always visible. if (in_tag) { result += open_tag_pos; } return result; } std::string fromAss(const char* ass) { auto b = ass_to_plaintext(ass); int hour1, min1, sec1, hunsec1,hour2, min2, sec2, hunsec2; char line[1024]; // fixme: "\0" maybe not allowed if (sscanf(b.c_str(), "Dialogue: Marked=%*d,%d:%d:%d.%d,%d:%d:%d.%d%1023[^\r\n]", //¬hing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line) < 9) if (sscanf(b.c_str(), "Dialogue: %*d,%d:%d:%d.%d,%d:%d:%d.%d%1023[^\r\n]", //¬hing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line) < 9) if (sscanf(b.c_str(), "%d,%d%1023[^\r\n]", //¬hing, &sec1, &sec2, line) < 3) return b; // libass ASS_Event.Text has no Dialogue auto ret = strchr(line, ','); if (!ret) return line; static const char kDefaultStyle[] = "Default,"; for (int comma = 0; comma < 6; comma++) { if (!(ret = strchr(++ret, ','))) { // workaround for ffmpeg decoded srt in ass format: "Dialogue: 0,0:42:29.20,0:42:31.08,Default,Chinese\NEnglish. if (!(ret = strstr(line, kDefaultStyle))) { if (line[0] == ',') //work around for libav-9- return line + 1; return line; } ret += sizeof(kDefaultStyle) - 1 - 1; // tail \0 } } ret++; const auto p = strcspn(b.c_str(), "\r\n"); if (p == b.size()) //not found return ret; std::string line2 = b.substr(p + 1); boost::algorithm::trim(line2); if (line2.empty()) return ret; return ret + ("\n" + line2); } } // namespace AVCodecContext* MakeSubtitlesCodecContext(AVCodecParameters* codecpar) { auto codecContext = avcodec_alloc_context3(nullptr); if (codecContext == nullptr) { return nullptr; } auto codecContextGuard = MakeGuard(&codecContext, avcodec_free_context); if (avcodec_parameters_to_context(codecContext, codecpar) < 0) { return nullptr; } auto codec = avcodec_find_decoder(codecContext->codec_id); if (codec == nullptr) { return nullptr; // Codec not found } // Open codec if (avcodec_open2(codecContext, codec, nullptr) < 0) { assert(false && "Error on codec opening"); return nullptr; // Could not open codec } codecContextGuard.release(); return codecContext; } std::string GetSubtitle(AVCodecContext* ctx, const AVPacket& packet) { AVSubtitle sub{}; auto subtitleGuard = MakeGuard(&sub, avsubtitle_free); int got_subtitle = 0; int ret = avcodec_decode_subtitle2(ctx, &sub, &got_subtitle, const_cast(&packet)); if (ret >= 0 && got_subtitle) { std::string text; for (unsigned i = 0; i < sub.num_rects; i++) { switch (sub.rects[i]->type) { case SUBTITLE_ASS: text += fromAss(sub.rects[i]->ass); text += '\n'; break; case SUBTITLE_TEXT: text += sub.rects[i]->text; text += '\n'; break; default: break; } } return text; } return {}; } ================================================ FILE: video/subtitles.h ================================================ #pragma once extern "C" { #include } #include AVCodecContext* MakeSubtitlesCodecContext(AVCodecParameters* codecpar); std::string GetSubtitle(AVCodecContext* ctx, const AVPacket& packet); ================================================ FILE: video/video.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {3013C140-DDFC-4BF4-9091-0C4131A0D2A6} Win32Proj video video StaticLibrary true Unicode StaticLibrary true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode Level3 Disabled NOMINMAX;WIN32;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0602;_DEBUG;_LIB;%(PreprocessorDefinitions) true %(AdditionalIncludeDirectories) Windows true Level3 Disabled NOMINMAX;WIN32;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0602;_DEBUG;_LIB;%(PreprocessorDefinitions) true %(AdditionalIncludeDirectories) Windows true Level3 MaxSpeed true true NOMINMAX;WIN32;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0602;NDEBUG;_LIB;%(PreprocessorDefinitions) true %(AdditionalIncludeDirectories) Windows true true true Level3 MaxSpeed true true NOMINMAX;WIN32;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0602;NDEBUG;_LIB;%(PreprocessorDefinitions) true %(AdditionalIncludeDirectories) Windows true true true ================================================ FILE: video/video.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files ================================================ FILE: video/videoframe.h ================================================ #pragma once #include #include struct AVFrameDeleter { void operator()(AVFrame *frame) const { av_frame_free(&frame); }; }; typedef std::unique_ptr AVFramePtr; struct VideoFrame { double m_pts{0}; int64_t m_duration{0}; AVFramePtr m_image; std::future m_convert; VideoFrame() : m_image(av_frame_alloc()) {} VideoFrame(const VideoFrame&) = delete; VideoFrame& operator=(const VideoFrame&) = delete; void free() { m_convert = {}; // Finish first av_frame_unref(m_image.get()); } void realloc(AVPixelFormat pix_fmt, int width, int height) { if (pix_fmt != m_image->format || width != m_image->width || height != m_image->height || !av_frame_is_writable(m_image.get())) { av_frame_unref(m_image.get()); m_image->format = pix_fmt; m_image->width = width; m_image->height = height; av_frame_get_buffer(m_image.get(), 16); } } }; ================================================ FILE: video/videoparserunnable.cpp ================================================ #include "ffmpegdecoder.h" #include "makeguard.h" #include "interlockedadd.h" extern "C" { #include "libavutil/imgutils.h" } #include #include namespace { #ifdef _MSC_VER #define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s) inline uint8_t clamp255(uint32_t v) { const uint8_t noOverflowCandidate = v; return (noOverflowCandidate == v) ? noOverflowCandidate : 255; } #define C16TO8(v, scale) clamp255(((v) * (scale)) >> 16) /* asm volatile ( "movd %3,%%xmm2 \n" "punpcklwd %%xmm2,%%xmm2 \n" "pshufd $0x0,%%xmm2,%%xmm2 \n" // 32 pixels per loop. LABELALIGN "1: \n" "movdqu (%0),%%xmm0 \n" "movdqu 0x10(%0),%%xmm1 \n" "add $0x20,%0 \n" "pmulhuw %%xmm2,%%xmm0 \n" "pmulhuw %%xmm2,%%xmm1 \n" "packuswb %%xmm1,%%xmm0 \n" "movdqu %%xmm0,(%1) \n" "add $0x10,%1 \n" "sub $0x10,%2 \n" "jg 1b \n" : "+r"(src_y), // %0 "+r"(dst_y), // %1 "+r"(width) // %2 : "r"(scale) // %3 : "memory", "cc", "xmm0", "xmm1", "xmm2"); */ const auto DITHER_SCALE = 16352; // Use scale to convert lsb formats to msb, depending how many bits there are: // 32768 = 9 bits // 16384 = 10 bits (dither - 16352) // 4096 = 12 bits // 256 = 16 bits void Convert16To8Row_SSSE3(const __m128i* src_y, __m128i* dst_y, int scale, int width, int32_t odd_even_add /*either 0x00000002 or 0x00030001*/) { // Load scale factor into SIMD register __m128i s = _mm_cvtsi32_si128(scale); s = _mm_unpacklo_epi16(s, s); s = _mm_shuffle_epi32(s, 0); // Broadcast 32-bit odd/even offset across the 128-bit register (four times) __m128i odd_even_offset = _mm_set1_epi32(odd_even_add); for (; width > 0; width -= 16) { // Load 16-bit grayscale values from the source buffer __m128i a0 = *src_y++; __m128i a1 = *src_y++; // Apply alternating Bayer mask values using the fully populated 128-bit register a0 = _mm_add_epi16(a0, odd_even_offset); a1 = _mm_add_epi16(a1, odd_even_offset); // Scale down from 16-bit to 8-bit using multiply-high a0 = _mm_mulhi_epu16(a0, s); a1 = _mm_mulhi_epu16(a1, s); // Pack two 16-bit sets into one 8-bit register a0 = _mm_packus_epi16(a0, a1); // Store the final 8-bit pixels into the destination buffer *dst_y++ = a0; } } void ScaleRowDown2_16To8_C(const uint16_t* src_ptr, ptrdiff_t src_stride, uint8_t* dst, int dst_width, int scale) { int x; (void)src_stride; assert(scale >= 256); assert(scale <= 32768); for (x = 0; x < dst_width - 1; x += 2) { dst[0] = C16TO8(src_ptr[1], scale); dst[1] = C16TO8(src_ptr[3], scale); dst += 2; src_ptr += 4; } if (dst_width & 1) { dst[0] = C16TO8(src_ptr[1], scale); } } void Convert16To8Row_Any_SSSE3(const uint16_t* src_ptr, uint8_t* dst_ptr, int scale, int width, int y) { enum { SBPP = 2, BPP = 1, MASK = 15, }; const int r = width & MASK; const int n = width & ~MASK; const int32_t add = (scale != DITHER_SCALE) ? 0 : ((y & 1) ? 0x00000002 : 0x00030001); if (n > 0) { Convert16To8Row_SSSE3((const __m128i*)src_ptr, (__m128i*)dst_ptr, scale, n, add); } if (r > 0) { __declspec(align(16)) uint16_t temp[32]; __declspec(align(16)) uint8_t out[32]; memset(temp, 0, 32 * SBPP); /* for msan */ memcpy(temp, src_ptr + n, r * SBPP); Convert16To8Row_SSSE3((const __m128i*)temp, (__m128i*)out, scale, MASK + 1, add); memcpy(dst_ptr + n, out, r * BPP); } } void Convert16To8Plane(const uint16_t* src_y, int src_stride_y, uint8_t* dst_y, int dst_stride_y, int scale, // 16384 for 10 bits int width, int height) { // Negative height means invert the image. if (height < 0) { height = -height; dst_y = dst_y + (height - 1) * dst_stride_y; dst_stride_y = -dst_stride_y; } // Coalesce rows. if (src_stride_y == width && dst_stride_y == width) { width *= height; height = 1; src_stride_y = dst_stride_y = 0; } // Convert plane for (int y = 0; y < height; ++y) { Convert16To8Row_Any_SSSE3(src_y, dst_y, scale, width, y); src_y += src_stride_y; dst_y += dst_stride_y; } } void ScalePlaneDown2_16To8(int dst_width, int dst_height, int src_stride, int dst_stride, const uint16_t* src_ptr, uint8_t* dst_ptr, int scale) { int row_stride = src_stride * 2; //if (!filtering) { src_ptr += src_stride; // Point to odd rows. src_stride = 0; //} for (int y = 0; y < dst_height; ++y) { ScaleRowDown2_16To8_C(src_ptr, src_stride, dst_ptr, dst_width, scale); src_ptr += row_stride; dst_ptr += dst_stride; } } int I010ToI420(const uint16_t* src_y, int src_stride_y, const uint16_t* src_u, int src_stride_u, const uint16_t* src_v, int src_stride_v, uint8_t* dst_y, int dst_stride_y, uint8_t* dst_u, int dst_stride_u, uint8_t* dst_v, int dst_stride_v, int width, int height) { int halfwidth = (width + 1) >> 1; int halfheight = (height + 1) >> 1; if (!src_u || !src_v || !dst_u || !dst_v || width <= 0 || height == 0) { return -1; } // Negative height means invert the image. if (height < 0) { height = -height; halfheight = (height + 1) >> 1; src_y = src_y + (height - 1) * src_stride_y; src_u = src_u + (halfheight - 1) * src_stride_u; src_v = src_v + (halfheight - 1) * src_stride_v; src_stride_y = -src_stride_y; src_stride_u = -src_stride_u; src_stride_v = -src_stride_v; } // Convert Y plane. Convert16To8Plane(src_y, src_stride_y, dst_y, dst_stride_y, DITHER_SCALE, width, height); // Convert UV planes. Convert16To8Plane(src_u, src_stride_u, dst_u, dst_stride_u, 16384, halfwidth, halfheight); Convert16To8Plane(src_v, src_stride_v, dst_v, dst_stride_v, 16384, halfwidth, halfheight); return 0; } int I410ToI420(const uint16_t* src_y, int src_stride_y, const uint16_t* src_u, int src_stride_u, const uint16_t* src_v, int src_stride_v, uint8_t* dst_y, int dst_stride_y, uint8_t* dst_u, int dst_stride_u, uint8_t* dst_v, int dst_stride_v, int width, int height) { const int depth = 10; const int scale = 1 << (24 - depth); if (width <= 0 || height == 0) { return -1; } // Negative height means invert the image. if (height < 0) { height = -height; src_y = src_y + (height - 1) * src_stride_y; src_u = src_u + (height - 1) * src_stride_u; src_v = src_v + (height - 1) * src_stride_v; src_stride_y = -src_stride_y; src_stride_u = -src_stride_u; src_stride_v = -src_stride_v; } { const int uv_width = SUBSAMPLE(width, 1, 1); const int uv_height = SUBSAMPLE(height, 1, 1); Convert16To8Plane(src_y, src_stride_y, dst_y, dst_stride_y, DITHER_SCALE, width, height); ScalePlaneDown2_16To8(uv_width, uv_height, src_stride_u, dst_stride_u, src_u, dst_u, scale); ScalePlaneDown2_16To8(uv_width, uv_height, src_stride_v, dst_stride_v, src_v, dst_v, scale); } return 0; } #endif bool frameToImage( VideoFrame& videoFrameData, AVFramePtr& videoFrame, SwsContext*& imageCovertContext, AVPixelFormat pixelFormat) { if (videoFrame->format == AV_PIX_FMT_NONE) { return false; } if (videoFrame->format == pixelFormat || videoFrame->format == AV_PIX_FMT_DXVA2_VLD) { std::swap(videoFrame, videoFrameData.m_image); } else { const int width = videoFrame->width; const int height = videoFrame->height; videoFrameData.realloc(pixelFormat, width, height); #ifdef _MSC_VER if ((videoFrame->format == AV_PIX_FMT_YUV420P10LE || videoFrame->format == AV_PIX_FMT_YUV444P10LE) && pixelFormat == AV_PIX_FMT_YUV420P && !((intptr_t(videoFrame->data[0]) & 15) || (videoFrame->linesize[0] & 15) || (intptr_t(videoFrame->data[1]) & 15) || (videoFrame->linesize[1] & 15) || (intptr_t(videoFrame->data[2]) & 15) || (videoFrame->linesize[2] & 15))) { if (videoFrame->format == AV_PIX_FMT_YUV420P10LE) { I010ToI420( (const uint16_t*)videoFrame->data[0], videoFrame->linesize[0] / 2, (const uint16_t*)videoFrame->data[1], videoFrame->linesize[1] / 2, (const uint16_t*)videoFrame->data[2], videoFrame->linesize[2] / 2, videoFrameData.m_image->data[0], videoFrameData.m_image->linesize[0], videoFrameData.m_image->data[1], videoFrameData.m_image->linesize[1], videoFrameData.m_image->data[2], videoFrameData.m_image->linesize[2], width, height ); } else { I410ToI420( (const uint16_t*)videoFrame->data[0], videoFrame->linesize[0] / 2, (const uint16_t*)videoFrame->data[1], videoFrame->linesize[1] / 2, (const uint16_t*)videoFrame->data[2], videoFrame->linesize[2] / 2, videoFrameData.m_image->data[0], videoFrameData.m_image->linesize[0], videoFrameData.m_image->data[1], videoFrameData.m_image->linesize[1], videoFrameData.m_image->data[2], videoFrameData.m_image->linesize[2], width, height ); } } else #endif { // Prepare image conversion imageCovertContext = sws_getCachedContext(imageCovertContext, videoFrame->width, videoFrame->height, static_cast(videoFrame->format), width, height, pixelFormat, 0, nullptr, nullptr, nullptr); assert(imageCovertContext != nullptr); if (imageCovertContext == nullptr) { return false; } // Doing conversion if (sws_scale(imageCovertContext, videoFrame->data, videoFrame->linesize, 0, videoFrame->height, videoFrameData.m_image->data, videoFrameData.m_image->linesize) <= 0) { assert(false && "sws_scale failed"); BOOST_LOG_TRIVIAL(error) << "sws_scale failed"; return false; } } videoFrameData.m_image->sample_aspect_ratio = videoFrame->sample_aspect_ratio; } return true; } auto GetAsyncConversionFunction(OrderedScopedTokenGenerator::Token t, AVFramePtr input, std::promise &videoFramePromise, boost::shared_ptr imageConversionFunc, AVPixelFormat pixelFormat) { return [t = std::move(t), input = std::move(input), outputFut = videoFramePromise.get_future(), imageConversionFunc = std::move(imageConversionFunc), pixelFormat]() mutable { try { if (input->format == AV_PIX_FMT_NONE) return false; const int stride = (input->width + 1) & ~1; std::vector img(stride * (input->height + (input->height + 1) / 2)); const auto data = img.data(); if (input->format == AV_PIX_FMT_NV12) { av_image_copy_plane(data, stride, input->data[0], input->linesize[0], input->width, input->height); av_image_copy_plane(data + stride * input->height, stride, input->data[1], input->linesize[1], input->width, input->height / 2); } else { auto img_convert_ctx = sws_getContext( input->width, input->height, (AVPixelFormat)input->format, input->width, input->height, AV_PIX_FMT_NV12, SWS_FAST_BILINEAR, NULL, NULL, NULL); uint8_t* const dst[] = { data, data + stride * input->height }; const int dstStride[] = { stride, stride }; sws_scale(img_convert_ctx, input->data, input->linesize, 0, input->height, dst, dstStride); sws_freeContext(img_convert_ctx); } const auto width = input->width; const auto height = input->height; const auto pts = input->pts; input.reset(); // Free input frame as soon as possible since it's not needed anymore std::vector outputImg; int outputHeight{}; int outputWidth{}; if (!(*imageConversionFunc)(std::move(t), data, stride, width, height, pts, outputImg, outputWidth, outputHeight)) { return false; } const int outputStride = outputWidth; auto output = outputFut.get(); if (!output) return false; output->realloc(pixelFormat, outputWidth, outputHeight); auto img_convert_ctx = sws_getContext( outputWidth, outputHeight, AV_PIX_FMT_NV12, outputWidth, outputHeight, pixelFormat, SWS_FAST_BILINEAR, NULL, NULL, NULL); const auto outputData = outputImg.data(); uint8_t* const src[] = { outputData, outputData + outputStride * outputHeight }; const int srcStride[] = { outputStride, outputStride }; sws_scale(img_convert_ctx, src, srcStride, 0, outputHeight, output->m_image->data, output->m_image->linesize); sws_freeContext(img_convert_ctx); } catch (const std::exception& ex) { CHANNEL_LOG(ffmpeg_sync) << "Exception in async converter: " << typeid(ex).name() << ": " << ex.what(); return false; } return true; }; } } // namespace struct FFmpegDecoder::VideoParseContext { bool initialized = false; int numSkipped = 0; OrderedScopedTokenGenerator tokenGenerator; AVFramePtr prevVideoFrame; }; void FFmpegDecoder::videoParseRunnable() { CHANNEL_LOG(ffmpeg_threads) << "Video thread started"; m_videoStartClock = VIDEO_START_CLOCK_NOT_INITIALIZED; VideoParseContext context{}; while (!boost::this_thread::interruption_requested()) { AVPacket packet; if (!m_videoPacketsQueue.pop(packet)) { break; } auto packetGuard = MakeGuard(&packet, av_packet_unref); handleVideoPacket(packet, context); } } bool FFmpegDecoder::handleVideoPacket( const AVPacket& packet, VideoParseContext& context) { const int ret = avcodec_send_packet(m_videoCodecContext, &packet); if (ret < 0) { return false; } AVFramePtr videoFrame(av_frame_alloc()); while (avcodec_receive_frame(m_videoCodecContext, videoFrame.get()) == 0) { if (context.prevVideoFrame) { handleVideoFrame(context.prevVideoFrame, context, videoFrame->best_effort_timestamp); context.prevVideoFrame.reset(); } if (!handleVideoFrame(videoFrame, context, AV_NOPTS_VALUE)) { context.prevVideoFrame = std::move(videoFrame); videoFrame.reset(av_frame_alloc()); } } return true; } bool FFmpegDecoder::handleVideoFrame( AVFramePtr& videoFrame, VideoParseContext& context, int64_t next_timestamp) { enum { MAX_SKIPPED_TILL_REDRAW = 5 }; enum { SKIP_LOOP_FILTER_THRESHOLD = 50 }; const double MAX_DELAY = 0.2; const auto best_effort_timestamp = videoFrame->best_effort_timestamp; const double pts = best_effort_timestamp * av_q2d(m_videoStream->time_base); restart: boost::posix_time::time_duration td(boost::posix_time::pos_infin); bool inNextFrame = false; bool continueHandlingPrevTime = false; for (;;) { boost::unique_lock locker(m_isPausedMutex); while (m_isPaused && !m_isVideoSeekingWhilePaused) { m_isPausedCV.wait(locker); } const bool isPaused = m_isPaused; inNextFrame = isPaused && m_isVideoSeekingWhilePaused; if (!context.initialized || inNextFrame) { const auto val = (isPaused ? m_pauseTimer : GetHiResTime()) - pts; m_videoStartClock = val; CHANNEL_LOG(ffmpeg_sync) << "isPaused = " << isPaused << " m_videoStartClock = " << val << " pts = " << pts; } if (inNextFrame && m_prevTime != AV_NOPTS_VALUE) { if (next_timestamp == AV_NOPTS_VALUE) { return false; } else if (next_timestamp < m_prevTime) { continueHandlingPrevTime = true; } else { m_prevTime = AV_NOPTS_VALUE; } } // Skipping frames if (context.initialized && !inNextFrame && !m_videoPacketsQueue.empty()) { const double deltaTime = m_videoStartClock + pts - GetHiResTime(); if (deltaTime <= 0) { if (deltaTime < -MAX_DELAY) { InterLockedAdd(m_videoStartClock, MAX_DELAY); } ++context.numSkipped; if (context.numSkipped == SKIP_LOOP_FILTER_THRESHOLD) { if (m_videoCodecContext->skip_loop_filter != AVDISCARD_ALL) { // https://trac.kodi.tv/ticket/4943 CHANNEL_LOG(ffmpeg_sync) << "skip_loop_filter = AVDISCARD_ALL"; m_videoCodecContext->skip_loop_filter = AVDISCARD_ALL; } } if ((context.numSkipped % MAX_SKIPPED_TILL_REDRAW) != 0) { CHANNEL_LOG(ffmpeg_sync) << "Hard skip frame"; return true; } } else { if (deltaTime > 0.3 && m_formatContexts.size() == 1) { locker.unlock(); boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); continue; } const auto speed = getSpeedRational(); context.numSkipped = 0; td = boost::posix_time::milliseconds( int(deltaTime * 1000. * speed.denominator / speed.numerator) + 1); } } break; } context.initialized = true; if (continueHandlingPrevTime) { m_isPausedCV.notify_all(); return true; } boost::shared_ptr imageConversionFunc = m_imageConversionFunc; const bool useAsyncConversion = imageConversionFunc != nullptr && (*imageConversionFunc); handleDirect3dData(videoFrame.get(), useAsyncConversion); std::future convert; std::promise videoFramePromise; auto promGuard = MakeGuard(&videoFramePromise, [](auto promise) { promise->set_value(nullptr); }); if (useAsyncConversion) { AVFramePtr input(av_frame_alloc()); //std::swap(input, videoFrame); const auto res = av_frame_ref(input.get(), videoFrame.get()); assert(res == 0); if (res != 0) { CHANNEL_LOG(ffmpeg_sync) << "av_frame_ref failed with error code: " << res; return true; } auto asyncConversion = GetAsyncConversionFunction( context.tokenGenerator.generate(), std::move(input), videoFramePromise, std::move(imageConversionFunc), m_pixelFormat); convert = std::async(std::launch::async, std::move(asyncConversion)); } { boost::unique_lock locker(m_videoFramesMutex); if (!m_videoFramesCV.timed_wait(locker, td, [this] { return m_isPaused && !m_isVideoSeekingWhilePaused || m_videoFramesQueue.canPush(); })) { CHANNEL_LOG(ffmpeg_sync) << "Frame wait abandoned"; return true; } } { boost::lock_guard locker(m_isPausedMutex); if (m_isPaused && !m_isVideoSeekingWhilePaused) { goto restart; } m_isVideoSeekingWhilePaused = false; } if (inNextFrame) { m_isPausedCV.notify_all(); } VideoFrame& current_frame = m_videoFramesQueue.back(); if (!useAsyncConversion && !frameToImage(current_frame, videoFrame, m_imageCovertContext, m_pixelFormat)) { return true; } current_frame.m_pts = pts; current_frame.m_duration = best_effort_timestamp; if (useAsyncConversion) { promGuard.release(); videoFramePromise.set_value(¤t_frame); current_frame.m_convert = std::move(convert); } { boost::lock_guard locker(m_videoFramesMutex); m_videoFramesQueue.pushBack(); } m_videoFramesCV.notify_all(); return true; } ================================================ FILE: video/vqueue.h ================================================ #pragma once #include // Video frame struct for RGB24 frame (used by displays) class VQueue { public: VQueue() = default; VQueue(const VQueue&) = delete; VQueue& operator=(const VQueue&) = delete; void clear() { for (auto& frame : m_frames) { frame.free(); } // Reset readers m_write_counter = 0; m_read_counter = 0; m_busy = 0; } bool canPush() const { return m_busy < QUEUE_SIZE; } VideoFrame& back() { return m_frames[m_write_counter]; } void pushBack() { m_write_counter = (m_write_counter + 1) % QUEUE_SIZE; ++m_busy; assert(m_busy <= VQueue::QUEUE_SIZE); } bool canPop() const { return m_busy > 0; } VideoFrame& front() { return m_frames[m_read_counter]; } void popFront() { --m_busy; assert(m_busy >= 0); m_read_counter = (m_read_counter + 1) % QUEUE_SIZE; } private: enum { QUEUE_SIZE = 2 }; // enough for displaying one frame. VideoFrame m_frames[QUEUE_SIZE]; int m_write_counter = 0; int m_read_counter = 0; int m_busy = 0; };