diff --git a/CHANGELOG.md b/CHANGELOG.md index 312b77a..fc9d989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog ========= +##### 2024.10.18 + * add native arm64 Windows support + * add AV1 codec for encoding - 8-bit and 10-bit main profile + ##### 2024.09.15 * record application local audio for window capture (Windows 10 "20H1" and up) * allow to disable yellow capture border indicator (Windows 11) diff --git a/README.md b/README.md index 07d4e43..b997561 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Features * press Ctrl + Shift + PrintScreen to select & record fixed region on current monitor * press any of previous combinations to stop recording * right or double-click on tray icon to change settings - * video encoded using [H264/AVC][] or [H265/HEVC][], with 10-bit support for HEVC + * video encoded using [H264/AVC][], [H265/HEVC][] or [AV1][], with 10-bit support for HEVC and AV1 * audio encoded using [AAC][] or [FLAC][] * for window capture record full window area (including title bar/borders) or just the client area * window capture can record **application local audio**, no other system/process audio included @@ -107,5 +107,6 @@ a compiled binary, for any purpose, commercial or non-commercial, and by any mea [paletteuse]: https://ffmpeg.org/ffmpeg-filters.html#paletteuse [H264/AVC]: https://en.wikipedia.org/wiki/Advanced_Video_Coding [H265/HEVC]: https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding +[AV1]: https://en.wikipedia.org/wiki/AV1 [AAC]: https://en.wikipedia.org/wiki/Advanced_Audio_Coding [FLAC]: https://en.wikipedia.org/wiki/FLAC diff --git a/wcap_config.h b/wcap_config.h index 9d33c84..902f848 100644 --- a/wcap_config.h +++ b/wcap_config.h @@ -8,6 +8,7 @@ #define CONFIG_VIDEO_H264 0 #define CONFIG_VIDEO_H265 1 +#define CONFIG_VIDEO_AV1 2 #define CONFIG_VIDEO_BASE 0 #define CONFIG_VIDEO_MAIN 1 @@ -151,7 +152,7 @@ static BOOL Config_ShowDialog(Config* C); static const DWORD gAudioBitrates[] = { 96, 128, 160, 192, 0 }; static const DWORD gAudioSamplerates[] = { 44100, 48000, 0 }; -static const LPCWSTR gVideoCodecs[] = { L"H264", L"H265", NULL}; +static const LPCWSTR gVideoCodecs[] = { L"H264", L"H265", L"AV1", NULL}; static const LPCWSTR gVideoProfiles[] = { L"Base", L"Main", L"High", L"Main10", NULL }; static const LPCWSTR gAudioCodecs[] = { L"AAC", L"FLAC", NULL }; @@ -159,6 +160,7 @@ static const int gValidVideoProfiles[][4] = { { CONFIG_VIDEO_BASE, CONFIG_VIDEO_MAIN, CONFIG_VIDEO_HIGH, -1 }, { CONFIG_VIDEO_MAIN, CONFIG_VIDEO_MAIN_10, -1 }, + { CONFIG_VIDEO_MAIN, CONFIG_VIDEO_MAIN_10, -1 }, }; // currently open dialog window @@ -194,6 +196,15 @@ static void Config__UpdateVideoProfiles(HWND Window, DWORD Codec) ComboBox_AddString(Control, L"Main (8-bit)"); ComboBox_AddString(Control, L"Main10 (10-bit)"); + ComboBox_SetItemData(Control, 0, CONFIG_VIDEO_MAIN); + ComboBox_SetItemData(Control, 1, CONFIG_VIDEO_MAIN_10); + ComboBox_SetCurSel(Control, 1); + } + else if (Codec == CONFIG_VIDEO_AV1) + { + ComboBox_AddString(Control, L"Main (8-bit)"); + ComboBox_AddString(Control, L"Main (10-bit)"); + ComboBox_SetItemData(Control, 0, CONFIG_VIDEO_MAIN); ComboBox_SetItemData(Control, 1, CONFIG_VIDEO_MAIN_10); ComboBox_SetCurSel(Control, 1); @@ -444,6 +455,7 @@ static LRESULT CALLBACK Config__DialogProc(HWND Window, UINT Message, WPARAM WPa SendDlgItemMessageW(Window, ID_VIDEO_CODEC, CB_ADDSTRING, 0, (LPARAM)L"H264 / AVC"); SendDlgItemMessageW(Window, ID_VIDEO_CODEC, CB_ADDSTRING, 0, (LPARAM)L"H265 / HEVC"); + SendDlgItemMessageW(Window, ID_VIDEO_CODEC, CB_ADDSTRING, 0, (LPARAM)L"AV1"); SendDlgItemMessageW(Window, ID_AUDIO_CODEC, CB_ADDSTRING, 0, (LPARAM)L"AAC"); SendDlgItemMessageW(Window, ID_AUDIO_CODEC, CB_ADDSTRING, 0, (LPARAM)L"FLAC"); diff --git a/wcap_encoder.h b/wcap_encoder.h index 9b81a68..6c9eeee 100644 --- a/wcap_encoder.h +++ b/wcap_encoder.h @@ -331,43 +331,52 @@ BOOL Encoder_Start(Encoder* Encoder, ID3D11Device* Device, LPWSTR FileName, cons const GUID* Container; const GUID* Codec; UINT32 Profile; - const GUID* MediaFormatYUV; - DXGI_FORMAT ConvertFormat; + const GUID* VideoInputFormat; if (Config->Config->VideoCodec == CONFIG_VIDEO_H264) { - MediaFormatYUV = &MFVideoFormat_NV12; - ConvertFormat = DXGI_FORMAT_NV12; - + VideoInputFormat = &MFVideoFormat_NV12; Container = Config->Config->FragmentedOutput ? &MFTranscodeContainerType_FMPEG4 : &MFTranscodeContainerType_MPEG4; Codec = &MFVideoFormat_H264; Profile = ((UINT32[]) { eAVEncH264VProfile_Base, eAVEncH264VProfile_Main, eAVEncH264VProfile_High })[Config->Config->VideoProfile]; - } else if (Config->Config->VideoCodec == CONFIG_VIDEO_H265 && Config->Config->VideoProfile == CONFIG_VIDEO_MAIN) { - MediaFormatYUV = &MFVideoFormat_NV12; - ConvertFormat = DXGI_FORMAT_NV12; - + VideoInputFormat = &MFVideoFormat_NV12; Container = &MFTranscodeContainerType_MPEG4; Codec = &MFVideoFormat_HEVC; Profile = eAVEncH265VProfile_Main_420_8; - } - else // Config->Config->VideoCodec == CONFIG_VIDEO_H265 && Config->Config->VideoProfile == CONFIG_VIDEO_MAIN_10 + else if (Config->Config->VideoCodec == CONFIG_VIDEO_H265 && Config->Config->VideoProfile == CONFIG_VIDEO_MAIN_10) { - MediaFormatYUV = &MFVideoFormat_P010; - ConvertFormat = DXGI_FORMAT_P010; - + VideoInputFormat = &MFVideoFormat_P010; Container = &MFTranscodeContainerType_MPEG4; Codec = &MFVideoFormat_HEVC; Profile = eAVEncH265VProfile_Main_420_10; } + else if (Config->Config->VideoCodec == CONFIG_VIDEO_AV1 && Config->Config->VideoProfile == CONFIG_VIDEO_MAIN) + { + VideoInputFormat = &MFVideoFormat_NV12; + Container = &MFTranscodeContainerType_MPEG4; + Codec = &MFVideoFormat_AV1; + Profile = eAVEncAV1VProfile_Main_420_8; + } + else if (Config->Config->VideoCodec == CONFIG_VIDEO_AV1 && Config->Config->VideoProfile == CONFIG_VIDEO_MAIN_10) + { + VideoInputFormat = &MFVideoFormat_P010; + Container = &MFTranscodeContainerType_MPEG4; + Codec = &MFVideoFormat_AV1; + Profile = eAVEncAV1VProfile_Main_420_10; + } + else + { + Assert(0); + } // make sure MFT video encoder exists, some vendors wrongly allow SinkWriter to be created for invalid configuration { bool Ok = false; - MFT_REGISTER_TYPE_INFO InputType = { MFMediaType_Video, *MediaFormatYUV }; + MFT_REGISTER_TYPE_INFO InputType = { MFMediaType_Video, *VideoInputFormat }; MFT_REGISTER_TYPE_INFO OutputType = { MFMediaType_Video, *Codec }; IMFAttributes* EnumAttributes; @@ -422,7 +431,9 @@ BOOL Encoder_Start(Encoder* Encoder, ID3D11Device* Device, LPWSTR FileName, cons } // https://github.com/mpv-player/mpv/blob/release/0.38/video/csputils.c#L150-L153 - bool IsHD = (OutputWidth >= 1280) || (OutputHeight > 576) || Config->Config->VideoCodec == CONFIG_VIDEO_H265; + bool IsHD = (OutputWidth >= 1280) || (OutputHeight > 576) + || Config->Config->VideoCodec == CONFIG_VIDEO_H265 + || Config->Config->VideoCodec == CONFIG_VIDEO_AV1; // output file { @@ -486,7 +497,7 @@ BOOL Encoder_Start(Encoder* Encoder, ID3D11Device* Device, LPWSTR FileName, cons IMFMediaType* Type; HR(MFCreateMediaType(&Type)); HR(IMFMediaType_SetGUID(Type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video)); - HR(IMFMediaType_SetGUID(Type, &MF_MT_SUBTYPE, MediaFormatYUV)); + HR(IMFMediaType_SetGUID(Type, &MF_MT_SUBTYPE, VideoInputFormat)); HR(IMFMediaType_SetUINT32(Type, &MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive)); HR(IMFMediaType_SetUINT64(Type, &MF_MT_FRAME_RATE, MFT64(Config->FramerateNum, Config->FramerateDen))); HR(IMFMediaType_SetUINT64(Type, &MF_MT_FRAME_SIZE, MFT64(OutputWidth, OutputHeight))); @@ -637,6 +648,8 @@ BOOL Encoder_Start(Encoder* Encoder, ID3D11Device* Device, LPWSTR FileName, cons YuvColorSpace ColorSpace = IsHD ? YuvColorSpace_BT709 : YuvColorSpace_BT601; YuvConvert_Create(&Encoder->Convert, Device, Encoder->Resize.OutputTexture, OutputWidth, OutputHeight, ColorSpace, Config->Config->ImprovedColorConversion); + DXGI_FORMAT ConvertFormat = IsEqualGUID(VideoInputFormat, &MFVideoFormat_NV12) ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010; + for (size_t OutputIndex = 0; OutputIndex < ENCODER_VIDEO_BUFFER_COUNT; OutputIndex++) { YuvConvertOutput_Create(&Encoder->ConvertOutput[OutputIndex], Device, OutputWidth, OutputHeight, ConvertFormat);