Skip to content

Commit

Permalink
arm64 support
Browse files Browse the repository at this point in the history
  • Loading branch information
mmozeiko committed Oct 18, 2024
1 parent 3059b4b commit dd3eed1
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 59 deletions.
21 changes: 15 additions & 6 deletions .github/workflows/wcap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,44 @@ name: wcap
on:
push:
branches: main
paths-ignore:
- '**/README.md'
pull_request:
branches: main
paths-ignore:
- '**/README.md'

jobs:
build:
runs-on: windows-2022

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: repo

- name: Build
- name: Build (x64)
shell: cmd
run: cd repo && build.cmd
run: cd repo && build.cmd x64

- name: Build (arm64)
shell: cmd
run: cd repo && build.cmd arm64

- name: Checkout wiki
uses: actions/checkout@v3
uses: actions/checkout@v4
if: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
with:
repository: ${{github.repository}}.wiki
path: wiki

- name: Upload binary
- name: Upload binaries
if: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
shell: cmd
run: |
copy repo\wcap.exe wiki
copy repo\wcap-x64.exe wiki
copy repo\wcap-arm64.exe wiki
cd wiki
git config --local user.email "[email protected]"
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ wcap

Simple and efficient screen recording utility for Windows.

Get latest binary here: [wcap.exe][]
Get latest binary here: [wcap-x64.exe][] or [wcap-arm64.exe][]

**WARNING**: Windows Defender or other AV software might report false positive detection

Expand Down Expand Up @@ -36,12 +36,12 @@ little CPU and memory.
You can choose in settings to capture only client area or full size of window - client area will not include title bar and
borders for standard windows style. Recorded video size is determined by initial window size.

Make sure your GPU drivers are updated if something is not working with hardware video encoding - by default hardware encoder
is enabled, you can disable it in settings. Then video will be encoded using [Microsoft Media Foundation H264][MSMFH264]
By default hardware encoder is enabled, you can disable it in settings Make sure your GPU drivers are updated if something is
not working with hardware video encoding. Then video will be encoded using [Microsoft Media Foundation H264][MSMFH264]
software encoder. You might want to explicitly use software encoder on older GPU's as their hardware encoder quality is not great.

Audio is captured using [WASAPI loopback recording][] and encoded using [Microsoft Media Foundation AAC][MSMFAAC] encoder, or
undocumented Media Foundation FLAC encoder (it seems it always is present in Windows 10).
undocumented Media Foundation FLAC encoder (it seems it always is present in Windows 10 and 11).

Recorded mp4 file can be set to use fragmented mp4 format in settings (only for H264 codec). Fragmented mp4 file does not
require "finalizing" it. Which means that in case application or GPU driver crashes or if you run out of disk space then
Expand All @@ -62,7 +62,7 @@ HEVC Software Encoding
======================

HEVC encoding in software (on CPU) will require installing HEVC Video Extensions from Windows Store. It will support only
8-bit encoding. You can get direct download to installer package without using Windows Store with following steps:
8-bit encoding. You can get direct download to installer package without using Windows Store application with following steps:

1) open https://store.rg-adguard.net/
2) search `https://www.microsoft.com/store/productId/9n4wgh0z6vhq` for `Retail` channel
Expand Down Expand Up @@ -96,7 +96,8 @@ This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as
a compiled binary, for any purpose, commercial or non-commercial, and by any means.

[wcap.exe]: https://raw.githubusercontent.com/wiki/mmozeiko/wcap/wcap.exe
[wcap-x64.exe]: https://raw.githubusercontent.com/wiki/mmozeiko/wcap/wcap-x64.exe
[wcap-arm64.exe]: https://raw.githubusercontent.com/wiki/mmozeiko/wcap/wcap-arm64.exe
[wgc]: https://blogs.windows.com/windowsdeveloper/2019/09/16/new-ways-to-do-screen-capture/
[MSMFH264]: https://docs.microsoft.com/en-us/windows/win32/medfound/h-264-video-encoder
[VS]: https://visualstudio.microsoft.com/vs/
Expand Down
36 changes: 26 additions & 10 deletions build.cmd
Original file line number Diff line number Diff line change
@@ -1,31 +1,47 @@
@echo off
setlocal enabledelayedexpansion

if "%PROCESSOR_ARCHITECTURE%" equ "AMD64" (
set HOST_ARCH=x64
) else if "%PROCESSOR_ARCHITECTURE%" equ "ARM64" (
set HOST_ARCH=arm64
)

set ARGS=%*
if "%ARGS%" equ "" set ARGS=%HOST_ARCH%

if "%ARGS:x64=%" neq "!ARGS!" (
set TARGET_ARCH=x64
) else if "%ARGS:arm64=%" neq "!ARGS!" (
set TARGET_ARCH=arm64
) else (
set TARGET_ARCH=%HOST_ARCH%
)

where /Q cl.exe || (
set __VSCMD_ARG_NO_LOGO=1
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
if "!VS!" equ "" (
echo ERROR: Visual Studio installation not found
exit /b 1
)
call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
)

if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
exit /b 1
)
call "!VS!\Common7\Tools\VsDevCmd.bat" -arch=%TARGET_ARCH% -host_arch=%HOST_ARCH% -startdir=none -no_logo || exit /b 1
)

if "%1" equ "debug" (
set CL=/MTd /Od /Zi /D_DEBUG /RTC1 /Fdwcap.pdb /fsanitize=address
if "%ARGS:debug=%" neq "%ARGS%" (
set CL=/MTd /Od /Z7 /D_DEBUG /RTC1
set LINK=/DEBUG
set FXC=/Od /Zi
if "%TARGET_ARCH%" equ "x64" set CL=!CL! /fsanitize=address
) else (
set CL=/GL /O1 /Oi /DNDEBUG /GS-
set LINK=/LTCG /OPT:REF /OPT:ICF ucrt.lib libvcruntime.lib
set FXC=/O3 /Qstrip_reflect /Qstrip_debug /Qstrip_priv
)

if "%TARGET_ARCH%" equ "arm64" set CL=%CL% /arch:armv8.1
if "%TARGET_ARCH%" equ "x64" set LINK=%LINK% /FIXED /merge:_RDATA=.rdata

call :fxc ResizePassH || exit /b 1
call :fxc ResizePassV || exit /b 1
call :fxc ResizeLinearPassH || exit /b 1
Expand All @@ -37,7 +53,7 @@ call :fxc ConvertPass2 || exit /b 1
for /f %%i in ('call git describe --always --dirty') do set CL=%CL% -DWCAP_GIT_INFO=\"%%i\"

rc.exe /nologo wcap.rc || exit /b 1
cl.exe /nologo /W3 /WX wcap.c wcap.res /link /INCREMENTAL:NO /MANIFEST:EMBED /MANIFESTINPUT:wcap.manifest /SUBSYSTEM:WINDOWS /FIXED /merge:_RDATA=.rdata || exit /b 1
cl.exe /nologo /std:c11 /experimental:c11atomics /W3 /WX wcap.c wcap.res /Fewcap-%TARGET_ARCH%.exe /link /INCREMENTAL:NO /MANIFEST:EMBED /MANIFESTINPUT:wcap.manifest /SUBSYSTEM:WINDOWS || exit /b 1
del *.obj *.res >nul

goto :eof
Expand Down
2 changes: 2 additions & 0 deletions wcap.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
#pragma comment (lib, "OneCore")
#pragma comment (lib, "CoreMessaging")

#if defined(_M_AMD64)
// this is needed to be able to use Nvidia Media Foundation encoders on Optimus systems
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
#endif

#define WM_WCAP_ALREADY_RUNNING (WM_USER+1)
#define WM_WCAP_STOP_CAPTURE (WM_USER+2)
Expand Down
1 change: 1 addition & 0 deletions wcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <intrin.h>
#include <stdatomic.h>

#define WCAP_TITLE L"wcap"
#define WCAP_URL L"https://github.com/mmozeiko/wcap"
Expand Down
34 changes: 19 additions & 15 deletions wcap_audio_capture.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ typedef struct

uint8_t* Buffer;
uint32_t BufferSize;
uint32_t BufferRead;
uint32_t BufferWrite;
_Atomic(uint32_t) BufferRead;
_Atomic(uint32_t) BufferWrite;
}
AudioCapture;

Expand Down Expand Up @@ -81,7 +81,7 @@ DEFINE_GUID(IID_IActivateAudioInterfaceCompletionHandler, 0x41d949ab, 0x9862, 0x
typedef struct
{
IActivateAudioInterfaceCompletionHandler Handler;
uint32_t ReadyFlag;
_Atomic(uint32_t) ReadyFlag;
}
AudioCaptureActivate;

Expand Down Expand Up @@ -115,8 +115,8 @@ static HRESULT STDMETHODCALLTYPE AudioCaptureActivate__ActivateCompleted(IActiva
{
AudioCaptureActivate* Activate = CONTAINING_RECORD(This, AudioCaptureActivate, Handler);

InterlockedIncrement(&Activate->ReadyFlag);
WakeByAddressSingle(&Activate->ReadyFlag);
atomic_store_explicit(&Activate->ReadyFlag, 1, memory_order_release);
WakeByAddressSingle((PVOID)&Activate->ReadyFlag);

return S_OK;
}
Expand Down Expand Up @@ -157,7 +157,7 @@ static DWORD CALLBACK AudioCapture__Thread(LPVOID Arg)
UINT64 Timestamp = 0; // in QPC unuts
while (SUCCEEDED(IAudioCaptureClient_GetBuffer(CaptureClient, &Buffer, &Frames, &Flags, &Position, &Timestamp)) && Frames != 0)
{
uint32_t BufferAvailable = BufferSize - (BufferWrite - Capture->BufferRead);
uint32_t BufferAvailable = BufferSize - (BufferWrite - atomic_load_explicit(&Capture->BufferRead, memory_order_relaxed));

uint32_t WriteSize = sizeof(Frames) + sizeof(Position) + sizeof(Timestamp) + Frames * BytesPerFrame;
if (WriteSize <= BufferAvailable)
Expand All @@ -174,7 +174,9 @@ static DWORD CALLBACK AudioCapture__Thread(LPVOID Arg)
{
CopyMemory(BufferPtr, Buffer, Frames * BytesPerFrame);
}
BufferWrite = InterlockedAdd(&Capture->BufferWrite, WriteSize);

BufferWrite += WriteSize;
atomic_store_explicit(&Capture->BufferWrite, BufferWrite, memory_order_release);
}
else
{
Expand Down Expand Up @@ -225,17 +227,18 @@ bool AudioCapture_Start(AudioCapture* Capture, HWND ApplicationWindow)
{
.Handler.lpVtbl = &AudioCaptureActivateVtbl,
};
atomic_init(&ActivateCompletion.ReadyFlag, 0);

IActivateAudioInterfaceAsyncOperation* AsyncOperation;
if (FAILED(ActivateAudioInterfaceAsync(VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, &IID_IAudioClient, &Params, &ActivateCompletion.Handler, &AsyncOperation)))
{
return false;
}

while (!ActivateCompletion.ReadyFlag)
while (!atomic_load_explicit(&ActivateCompletion.ReadyFlag, memory_order_acquire))
{
uint32_t ReadyFlag = 0;
WaitOnAddress(&ActivateCompletion.ReadyFlag, &ReadyFlag, sizeof(ReadyFlag), INFINITE);
WaitOnAddress((PVOID)&ActivateCompletion.ReadyFlag, &ReadyFlag, sizeof(ReadyFlag), INFINITE);
}

HRESULT ActivateResult;
Expand Down Expand Up @@ -375,8 +378,8 @@ bool AudioCapture_Start(AudioCapture* Capture, HWND ApplicationWindow)

Capture->Buffer = View1;
Capture->BufferSize = BufferSize;
Capture->BufferRead = 0;
Capture->BufferWrite = 0;
atomic_init(&Capture->BufferRead, 0);
atomic_init(&Capture->BufferWrite, 0);

Capture->Stop = false;

Expand Down Expand Up @@ -438,13 +441,14 @@ bool AudioCapture_GetData(AudioCapture* Capture, AudioCaptureData* Data, uint64_
uint64_t Position;
uint64_t Timestamp;

uint32_t AvailableSize = Capture->BufferWrite - Capture->BufferRead;
uint32_t BufferRead = atomic_load_explicit(&Capture->BufferRead, memory_order_relaxed);
uint32_t AvailableSize = atomic_load_explicit(&Capture->BufferWrite, memory_order_acquire) - BufferRead;
if (AvailableSize < sizeof(Frames) + sizeof(Position) + sizeof(Timestamp))
{
return false;
}

uint8_t* BufferPtr = Capture->Buffer + (Capture->BufferRead & (Capture->BufferSize - 1));
uint8_t* BufferPtr = Capture->Buffer + (BufferRead & (Capture->BufferSize - 1));
CopyMemory(&Frames, BufferPtr, sizeof(Frames)); BufferPtr += sizeof(Frames);
CopyMemory(&Position, BufferPtr, sizeof(Position)); BufferPtr += sizeof(Position);
CopyMemory(&Timestamp, BufferPtr, sizeof(Timestamp)); BufferPtr += sizeof(Timestamp);
Expand Down Expand Up @@ -490,6 +494,6 @@ bool AudioCapture_GetData(AudioCapture* Capture, AudioCaptureData* Data, uint64_
void AudioCapture_ReleaseData(AudioCapture* Capture, AudioCaptureData* Data)
{
uint32_t ReadSize = (uint32_t)(sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint64_t) + Data->Count * Capture->Format->nBlockAlign);
Assert(ReadSize <= Capture->BufferWrite - Capture->BufferRead);
InterlockedAdd(&Capture->BufferRead, ReadSize);
Assert(ReadSize <= atomic_load_explicit(&Capture->BufferWrite, memory_order_relaxed) - atomic_load_explicit(&Capture->BufferRead, memory_order_relaxed));
atomic_fetch_add_explicit(&Capture->BufferRead, ReadSize, memory_order_release);
}
Loading

0 comments on commit dd3eed1

Please sign in to comment.