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