diff --git a/contrib/src/ffmpeg/package.json b/contrib/src/ffmpeg/package.json index 8aec16c575aea4ba1cd588767b2349e0940598fe..c042b93c31d7a6ba35e7a021b4bf17ee8bf2e82e 100644 --- a/contrib/src/ffmpeg/package.json +++ b/contrib/src/ffmpeg/package.json @@ -16,7 +16,8 @@ "libopusdec-enable-FEC.patch", "windows-configure.patch", "windows-configure-ffnvcodec.patch", - "windows-configure-libmfx.patch" + "windows-configure-libmfx.patch", + "windows-dxgi-support.patch" ], "win_patches": [ ], diff --git a/contrib/src/ffmpeg/windows-configure-make.sh b/contrib/src/ffmpeg/windows-configure-make.sh index 04c0688a7334b80823d309386cda6c7e0c74ffa8..272f4d27ad81de8b67c68d5cf6fd867ff4e7eaca 100644 --- a/contrib/src/ffmpeg/windows-configure-make.sh +++ b/contrib/src/ffmpeg/windows-configure-make.sh @@ -145,7 +145,7 @@ if [ "$1" == "uwp" ]; then OUTDIR=Output/Windows10/x86 fi elif [ "$1" == "win32" ]; then - EXTRACFLAGS='-MD -D_WINDLL -I../../../../../msvc/include -I../../../../../msvc/include/opus -I../../../../../msvc/include/vpx -I../../../../../msvc/include/ffnvcodec -I../../../../../msvc/include/mfx' + EXTRACFLAGS='-MD -D_WINDLL -I../../../../../msvc/include -I../../../../../msvc/include/opus -I../../../../../msvc/include/vpx -I../../../../../msvc/include/ffnvcodec -I../../../../../msvc/include/mfx -D_WIN32_WINNT=0x0A00' FFMPEGCONF+=' --enable-libvpx --enable-encoder=libvpx_vp8 @@ -154,7 +154,8 @@ elif [ "$1" == "win32" ]; then FFMPEGCONF+=' --enable-indev=dshow --enable-indev=gdigrab - --enable-dxva2' + --enable-dxva2 + --enable-indev=dxgigrab' FFMPEGCONF+=' --enable-ffnvcodec --enable-cuvid @@ -199,6 +200,6 @@ pwd FFMPEGCONF=$(echo $FFMPEGCONF | sed -e "s/[[:space:]]\+/ /g") set -x set -e -../../../configure $FFMPEGCONF --extra-cflags="${EXTRACFLAGS}" --extra-ldflags="${EXTRALDFLAGS}" --prefix="${PREFIX}" +../../../configure $FFMPEGCONF --extra-cflags="${EXTRACFLAGS}" --extra-ldflags="${EXTRALDFLAGS}" --prefix="${PREFIX}" --extra-cxxflags="-std:c++17" make -j8 install cd ../../.. diff --git a/contrib/src/ffmpeg/windows-dxgi-support.patch b/contrib/src/ffmpeg/windows-dxgi-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..4458c0315604ecb9daafc2e08e8ef633b1054f0f --- /dev/null +++ b/contrib/src/ffmpeg/windows-dxgi-support.patch @@ -0,0 +1,957 @@ +From 9055aa8b78fcd8e913642ffe21759579283ae1bb Mon Sep 17 00:00:00 2001 +From: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> +Date: Thu, 29 Dec 2022 12:59:19 -0300 +Subject: [PATCH] Add dxgi support + +--- + configure | 1 + + libavdevice/Makefile | 4 + + libavdevice/alldevices.c | 1 + + libavdevice/d3dHelpers.h | 59 +++++++++ + libavdevice/direct3d11.interop.h | 51 ++++++++ + libavdevice/dxgigrab.cpp | 218 +++++++++++++++++++++++++++++++ + libavdevice/dxgigrab.h | 83 ++++++++++++ + libavdevice/dxgigrab_c.c | 59 +++++++++ + libavdevice/dxgigrab_c.h | 98 ++++++++++++++ + libavdevice/windows_capture.cpp | 184 ++++++++++++++++++++++++++ + libavdevice/windows_capture.h | 82 ++++++++++++ + 11 files changed, 840 insertions(+) + create mode 100644 libavdevice/d3dHelpers.h + create mode 100644 libavdevice/direct3d11.interop.h + create mode 100644 libavdevice/dxgigrab.cpp + create mode 100644 libavdevice/dxgigrab.h + create mode 100644 libavdevice/dxgigrab_c.c + create mode 100644 libavdevice/dxgigrab_c.h + create mode 100644 libavdevice/windows_capture.cpp + create mode 100644 libavdevice/windows_capture.h + +diff --git a/configure b/configure +index 6b5ef6332e..3606ce5bc3 100755 +--- a/configure ++++ b/configure +@@ -3509,6 +3509,7 @@ fbdev_outdev_deps="linux_fb_h" + gdigrab_indev_deps="CreateDIBSection" + gdigrab_indev_extralibs="-lgdi32" + gdigrab_indev_select="bmp_decoder" ++dxgigrab_indev_extralibs="-ldxgi -ld3d11" + iec61883_indev_deps="libiec61883" + jack_indev_deps="libjack" + jack_indev_deps_any="sem_timedwait dispatch_dispatch_h" +diff --git a/libavdevice/Makefile b/libavdevice/Makefile +index 53efda0514..a6fb30cdea 100644 +--- a/libavdevice/Makefile ++++ b/libavdevice/Makefile +@@ -28,6 +28,7 @@ OBJS-$(CONFIG_FBDEV_INDEV) += fbdev_dec.o \ + OBJS-$(CONFIG_FBDEV_OUTDEV) += fbdev_enc.o \ + fbdev_common.o + OBJS-$(CONFIG_GDIGRAB_INDEV) += gdigrab.o ++OBJS-$(CONFIG_DXGIGRAB_INDEV) += windows_capture.o dxgigrab.o dxgigrab_c.o + OBJS-$(CONFIG_IEC61883_INDEV) += iec61883.o + OBJS-$(CONFIG_JACK_INDEV) += jack.o timefilter.o + OBJS-$(CONFIG_KMSGRAB_INDEV) += kmsgrab.o +@@ -70,5 +71,8 @@ SKIPHEADERS-$(CONFIG_V4L2_INDEV) += v4l2-common.h + SKIPHEADERS-$(CONFIG_V4L2_OUTDEV) += v4l2-common.h + SKIPHEADERS-$(CONFIG_ALSA) += alsa.h + SKIPHEADERS-$(CONFIG_SNDIO) += sndio.h ++SKIPHEADERS-$(CONFIG_DXGIGRAB_INDEV) += dxgigrab.h \ ++ windows_capture.h \ ++ dxgigrab_c.h + + TESTPROGS-$(CONFIG_JACK_INDEV) += timefilter +diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c +index 22323a0a44..fb0a37513b 100644 +--- a/libavdevice/alldevices.c ++++ b/libavdevice/alldevices.c +@@ -35,6 +35,7 @@ extern const AVInputFormat ff_dshow_demuxer; + extern const AVInputFormat ff_fbdev_demuxer; + extern const AVOutputFormat ff_fbdev_muxer; + extern const AVInputFormat ff_gdigrab_demuxer; ++extern const AVInputFormat ff_dxgigrab_demuxer; + extern const AVInputFormat ff_iec61883_demuxer; + extern const AVInputFormat ff_jack_demuxer; + extern const AVInputFormat ff_kmsgrab_demuxer; +diff --git a/libavdevice/d3dHelpers.h b/libavdevice/d3dHelpers.h +new file mode 100644 +index 0000000000..d8d2c003ec +--- /dev/null ++++ b/libavdevice/d3dHelpers.h +@@ -0,0 +1,59 @@ ++/* ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2.1 ++ * of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#pragma once ++ ++#include <winrt/Windows.UI.Composition.h> ++#include <windows.ui.composition.interop.h> ++#include <d2d1_1.h> ++ ++inline auto ++CreateD3DDevice( ++ D3D_DRIVER_TYPE const type, ++ winrt::com_ptr<ID3D11Device>& device) ++{ ++ WINRT_ASSERT(!device); ++ ++ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; ++ ++ return D3D11CreateDevice( ++ nullptr, ++ type, ++ nullptr, ++ flags, ++ nullptr, 0, ++ D3D11_SDK_VERSION, ++ device.put(), ++ nullptr, ++ nullptr); ++} ++ ++inline auto ++CreateD3DDevice() ++{ ++ winrt::com_ptr<ID3D11Device> device; ++ HRESULT hr = CreateD3DDevice(D3D_DRIVER_TYPE_HARDWARE, device); ++ ++ if (DXGI_ERROR_UNSUPPORTED == hr) ++ { ++ hr = CreateD3DDevice(D3D_DRIVER_TYPE_WARP, device); ++ } ++ ++ winrt::check_hresult(hr); ++ return device; ++} +diff --git a/libavdevice/direct3d11.interop.h b/libavdevice/direct3d11.interop.h +new file mode 100644 +index 0000000000..62c9b0843e +--- /dev/null ++++ b/libavdevice/direct3d11.interop.h +@@ -0,0 +1,51 @@ ++/* ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2.1 ++ * of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#pragma once ++#include <winrt/windows.graphics.directx.direct3d11.h> ++ ++extern "C" ++{ ++ HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(::IDXGIDevice* dxgiDevice, ++ ::IInspectable** graphicsDevice); ++ ++ HRESULT __stdcall CreateDirect3D11SurfaceFromDXGISurface(::IDXGISurface* dgxiSurface, ++ ::IInspectable** graphicsSurface); ++} ++ ++struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) ++ IDirect3DDxgiInterfaceAccess : ::IUnknown ++{ ++ virtual HRESULT __stdcall GetInterface(GUID const& id, void** object) = 0; ++}; ++ ++inline auto CreateDirect3DDevice(IDXGIDevice* dxgi_device) ++{ ++ winrt::com_ptr<::IInspectable> d3d_device; ++ winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgi_device, d3d_device.put())); ++ return d3d_device.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>(); ++} ++ ++template <typename T> ++auto GetDXGIInterfaceFromObject(winrt::Windows::Foundation::IInspectable const& object) ++{ ++ auto access = object.as<IDirect3DDxgiInterfaceAccess>(); ++ winrt::com_ptr<T> result; ++ winrt::check_hresult(access->GetInterface(winrt::guid_of<T>(), result.put_void())); ++ return result; ++} +diff --git a/libavdevice/dxgigrab.cpp b/libavdevice/dxgigrab.cpp +new file mode 100644 +index 0000000000..365ba4f9aa +--- /dev/null ++++ b/libavdevice/dxgigrab.cpp +@@ -0,0 +1,218 @@ ++/* ++ * DXGI video grab interface ++ * ++ * This file is part of FFmpeg. ++ * ++ * Copyright (C) 2022 Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2.1 ++ * of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * DXGI frame device demuxer ++ * @author Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ */ ++ ++#include "dxgigrab.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++#include <libavutil/parseutils.h> ++#ifdef __cplusplus ++} ++#endif ++ ++static BOOL CALLBACK enumMonitor(HMONITOR handle, HDC hdc, LPRECT rect, ++ LPARAM lParam) ++{ ++ MonitorData* monitorData = reinterpret_cast<MonitorData*>(lParam); ++ ++ if (monitorData->curId == 0 || monitorData->desiredId == monitorData->curId) { ++ monitorData->rect = *rect; ++ monitorData->id = monitorData->curId; ++ ++ monitorData->hmnt = handle; ++ } ++ ++ return (monitorData->desiredId > monitorData->curId++); ++} ++ ++int dxgigrab_read_header(AVFormatContext *s1) ++{ ++ struct dxgigrab *s = static_cast<dxgigrab*>(s1->priv_data); ++ if (!s->internal_struct) ++ s->internal_struct = new dxgigrab_internal(); ++ struct dxgigrab_internal* x_internal = static_cast<dxgigrab_internal*>(s->internal_struct); ++ MonitorData monitorData; ++ monitorData.rect.top = 0; ++ monitorData.rect.left = 0; ++ monitorData.rect.bottom = 0; ++ monitorData.rect.right = 0; ++ const char *name = NULL; ++ AVStream *st = NULL; ++ ++ if (!strncmp(s1->url, "title=", 6)) { ++ name = s1->url + 6; ++ s->hwnd = FindWindow(NULL, name); ++ if (!s->hwnd) { ++ av_log(s1, AV_LOG_ERROR, ++ "Can't find window '%s', aborting.\n", name); ++ return AVERROR_EXTERNAL; ++ } ++ } else { ++ s->hwnd = NULL; ++ char *display_number = av_strdup(s1->url); ++ if (!sscanf(s1->url, "%[^+]+%d,%d ", display_number, &s->offset_x, &s->offset_y)) { ++ av_log(s1, AV_LOG_ERROR, ++ "Please use \"<screenNumber>+<X,Y> <WidthxHeight>\" or \"title=<windowname>\" to specify your target.\n"); ++ return AVERROR_EXTERNAL; ++ } ++ if (s->offset_x || s->offset_y) { ++ av_log(s1, ++ AV_LOG_ERROR, ++ "This device does not support partial screen sharing.\n"); ++ return AVERROR_PATCHWELCOME; ++ } ++ monitorData.desiredId = std::stoi(display_number); ++ av_freep(&display_number); ++ try { ++ LPARAM lParam = reinterpret_cast<LPARAM>(&monitorData); ++ EnumDisplayMonitors(NULL, NULL, enumMonitor, lParam); ++ } catch (...) { ++ } ++ } ++ ++ s->hmnt = monitorData.hmnt; ++ s->rect = monitorData.rect; ++ ++ if (!s->hmnt && !s->hwnd) { ++ av_log(s1, AV_LOG_ERROR, ++ "Please use \"<screenNumber>+<X,Y> <WidthxHeight>\" or \"title=<windowname>\" to " ++ "specify your target.\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ bool openWindow = true; ++ if (!s1->nb_streams) { ++ st = avformat_new_stream(s1, NULL); ++ } else { ++ openWindow = false; ++ st = s1->streams[0]; ++ } ++ if (!st) { ++ return AVERROR(ENOMEM); ++ } ++ ++ std::lock_guard lk(x_internal->mtx); ++ if (openWindow) { ++ auto d3dDevice = CreateD3DDevice(); ++ auto dxgiDevice = d3dDevice.as<IDXGIDevice>(); ++ auto m_device = CreateDirect3DDevice(dxgiDevice.get()); ++ ++ auto activation_factory = winrt::get_activation_factory<winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); ++ auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>(); ++ winrt::Windows::Graphics::Capture::GraphicsCaptureItem windowItem = { nullptr }; ++ ++ try { ++ if (s->hwnd) { ++ winrt::check_hresult(interop_factory ++ ->CreateForWindow(s->hwnd, // we should have create for display too ++ winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), ++ reinterpret_cast<void**>(winrt::put_abi(windowItem)))); ++ } else if (s->hmnt) { ++ winrt::check_hresult(interop_factory ++ ->CreateForMonitor(s->hmnt, // we should have create for display too ++ winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), ++ reinterpret_cast<void**>(winrt::put_abi(windowItem)))); ++ } ++ } catch (winrt::hresult_error &err) { ++ av_log(s, AV_LOG_ERROR, "CreateForWindow failed: %s\n", err.message().c_str()); ++ return AVERROR_EXTERNAL; ++ } catch (...) { ++ av_log(s, AV_LOG_ERROR, "CreateForWindow failed\n"); ++ return AVERROR_EXTERNAL; ++ } ++ av_log(s, AV_LOG_ERROR, "CreateForWindow success\n"); ++ ++ x_internal->m_capture.reset(new WindowsCapture(m_device, windowItem, monitorData)); ++ x_internal->m_capture->StartCapture(); ++ x_internal->m_capture->checkNewFrameArrived(); ++ x_internal->m_capture->window = name ? name : ""; ++ x_internal->windowName = name ? name : ""; ++ } ++ ++ s->time_base = av_inv_q(s->framerate); ++ ++ avpriv_set_pts_info(st, 64, 1, 1000000); ++ ++ s->width = x_internal->m_capture->m_DeviceSize.Width; ++ s->height = x_internal->m_capture->m_DeviceSize.Height; ++ ++ x_internal->m_capture->screen.first = monitorData.hmnt; ++ x_internal->m_capture->screen.second = monitorData.rect; ++ ++ s->time_frame = av_gettime_relative(); ++ ++ st->avg_frame_rate = av_inv_q(s->time_base); ++ ++ auto frame_size_bits = (int64_t)s->width * s->height * 4 * 8; ++ if (frame_size_bits / 8 + AV_INPUT_BUFFER_PADDING_SIZE > INT_MAX) { ++ av_log(s, AV_LOG_ERROR, "Captured area is too large\n"); ++ return AVERROR_PATCHWELCOME; ++ } ++ s->frame_size = frame_size_bits / 8; ++ ++ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; ++ st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; ++ st->codecpar->width = s->width; ++ st->codecpar->height = s->height; ++ st->codecpar->format = AV_PIX_FMT_RGBA; ++ st->codecpar->bit_rate = av_rescale(frame_size_bits, st->avg_frame_rate.num, st->avg_frame_rate.den); ++ ++ return 0; ++} ++ ++int dxgigrab_read_packet(AVFormatContext *s1, AVPacket *pkt) ++{ ++ struct dxgigrab *s = static_cast<dxgigrab*>(s1->priv_data); ++ struct dxgigrab_internal *x_internal = static_cast<dxgigrab_internal*>(s->internal_struct); ++ std::lock_guard lk(x_internal->mtx); ++ int ret = 0; ++ int64_t pts = av_gettime(); ++ ++ if (!x_internal->m_capture) ++ return 0; // If m_capture not available, return empty frame ++ ret = x_internal->m_capture->GetPkt(pkt); ++ if (ret < 0) ++ return ret; ++ pkt->dts = pkt->pts = pts; ++ ++ return ret; ++} ++ ++int dxgigrab_read_close(AVFormatContext *s1) ++{ ++ struct dxgigrab* s = static_cast<dxgigrab*>(s1->priv_data); ++ struct dxgigrab_internal* x_internal = static_cast<dxgigrab_internal*>(s->internal_struct); ++ std::lock_guard lk(x_internal->mtx); ++ ++ if (!x_internal->m_capture) ++ return 0; // If m_capture not available, no need to close it ++ x_internal->m_capture->Close(); ++ ++ return 0; ++} +diff --git a/libavdevice/dxgigrab.h b/libavdevice/dxgigrab.h +new file mode 100644 +index 0000000000..a247077e7d +--- /dev/null ++++ b/libavdevice/dxgigrab.h +@@ -0,0 +1,83 @@ ++/* ++ * DXGI video grab interface ++ * ++ * This file is part of FFmpeg. ++ * ++ * Copyright (C) 2022 Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2.1 ++ * of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * DXGI frame device demuxer ++ * @author Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ */ ++ ++#ifndef AVDEVICE_DXGI_H ++#define AVDEVICE_DXGI_H ++ ++#include <stdlib.h> ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++#include "dxgigrab_c.h" ++#ifdef __cplusplus ++} ++#endif ++ ++#include <Unknwn.h> ++#include <inspectable.h> ++ ++// WinRT ++#include <winrt/Windows.Foundation.h> ++#include <winrt/Windows.System.h> ++#include <winrt/Windows.UI.h> ++#include <winrt/Windows.UI.Composition.h> ++#include <winrt/Windows.UI.Composition.Desktop.h> ++#include <winrt/Windows.UI.Popups.h> ++#include <winrt/Windows.Graphics.Capture.h> ++#include <winrt/Windows.Graphics.DirectX.h> ++#include <winrt/Windows.Graphics.DirectX.Direct3d11.h> ++#include <windows.graphics.capture.interop.h> ++#include <windows.graphics.capture.h> ++ ++#include <windows.ui.composition.interop.h> ++#include <DispatcherQueue.h> ++ ++// STL ++#include <atomic> ++#include <memory> ++#include <mutex> ++ ++// D3D ++#include <d3d11_4.h> ++#include <dxgi1_6.h> ++#include <d2d1_3.h> ++#include <wincodec.h> ++ ++// Internal ++#include "d3dHelpers.h" ++#include "direct3d11.interop.h" ++#include "windows_capture.h" ++ ++struct dxgigrab_internal { ++ std::unique_ptr<WindowsCapture> m_capture{ nullptr }; ++ std::string windowName; ++ std::mutex mtx; ++}; ++ ++#endif /* AVDEVICE_DXGI_H */ +diff --git a/libavdevice/dxgigrab_c.c b/libavdevice/dxgigrab_c.c +new file mode 100644 +index 0000000000..c53d757abd +--- /dev/null ++++ b/libavdevice/dxgigrab_c.c +@@ -0,0 +1,59 @@ ++/* ++ * DXGI video grab interface ++ * ++ * This file is part of FFmpeg. ++ * ++ * Copyright (C) 2022 Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2.1 ++ * of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * DXGI frame device demuxer ++ * @author Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ */ ++ ++#include "dxgigrab_c.h" ++ ++#define OFFSET(x) offsetof(struct dxgigrab, x) ++#define DEC AV_OPT_FLAG_DECODING_PARAM ++static const AVOption options[] = { ++ { "framerate", "set video frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "ntsc"}, 0, INT_MAX, DEC }, ++ { "video_size", "set video frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, ++ { "offset_x", "capture area x offset", OFFSET(offset_x), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC }, ++ { "offset_y", "capture area y offset", OFFSET(offset_y), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC }, ++ { NULL }, ++}; ++ ++static const AVClass dxgigrab_class = { ++ .class_name = "DXGIgrab indev", ++ .item_name = av_default_item_name, ++ .option = options, ++ .version = LIBAVUTIL_VERSION_INT, ++ .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, ++}; ++ ++/** dxgi grabber device demuxer declaration */ ++AVInputFormat ff_dxgigrab_demuxer = { ++ .name = "dxgigrab", ++ .long_name = NULL_IF_CONFIG_SMALL("DXGI API Windows frame grabber"), ++ .priv_data_size = sizeof(struct dxgigrab), ++ .read_header = dxgigrab_read_header, ++ .read_packet = dxgigrab_read_packet, ++ .read_close = dxgigrab_read_close, ++ .flags = AVFMT_NOFILE, ++ .priv_class = &dxgigrab_class, ++}; +diff --git a/libavdevice/dxgigrab_c.h b/libavdevice/dxgigrab_c.h +new file mode 100644 +index 0000000000..d624ca0683 +--- /dev/null ++++ b/libavdevice/dxgigrab_c.h +@@ -0,0 +1,98 @@ ++/* ++ * DXGI video grab interface ++ * ++ * This file is part of FFmpeg. ++ * ++ * Copyright (C) 2022 Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2.1 ++ * of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * DXGI frame device demuxer ++ * @author Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ */ ++ ++#ifndef AVDEVICE_DXGI_C_H ++#define AVDEVICE_DXGI_C_H ++ ++#include <stdlib.h> ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "libavformat/internal.h" ++#include "config.h" ++#include "libavutil/opt.h" ++#include "libavutil/time.h" ++ ++#include <windows.h> ++ ++/** ++ * DXGI Device Demuxer context ++ */ ++struct dxgigrab { ++ const AVClass *avclass; /**< Class for private options */ ++ ++ int frame_size; /**< Size in bytes of the frame pixel data */ ++ AVRational time_base; /**< Time base */ ++ int64_t time_frame; /**< Current time */ ++ ++ AVRational framerate; /**< Capture framerate (private option) */ ++ int width; /**< Width of the grab frame (private option) */ ++ int height; /**< Height of the grab frame (private option) */ ++ int offset_x; /**< Capture x offset (private option) */ ++ int offset_y; /**< Capture y offset (private option) */ ++ ++ HWND hwnd; /**< Handle of the Window for the grab */ ++ HMONITOR hmnt; /**< Handle of the Screen for the grab */ ++ RECT rect; /**< Rect of the Screen for the grab */ ++ ++ void* internal_struct; ++}; ++ ++/** ++ * Initializes the dxgi grab device demuxer (public device demuxer API). ++ * ++ * @param s1 Context from avformat core ++ * @return AVERROR_IO error, 0 success ++ */ ++int dxgigrab_read_header(AVFormatContext *s1); ++ ++/** ++ * Grabs a frame from dxgi (public device demuxer API). ++ * ++ * @param s1 Context from avformat core ++ * @param pkt Packet holding the grabbed frame ++ * @return frame size in bytes ++ */ ++int dxgigrab_read_packet(AVFormatContext *s1, AVPacket *pkt); ++ ++ ++/** ++ * Closes dxgi frame grabber (public device demuxer API). ++ * ++ * @param s1 Context from avformat core ++ * @return 0 success, !0 failure ++ */ ++int dxgigrab_read_close(AVFormatContext *s1); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* AVDEVICE_DXGI_C_H */ +diff --git a/libavdevice/windows_capture.cpp b/libavdevice/windows_capture.cpp +new file mode 100644 +index 0000000000..9eaf8cf222 +--- /dev/null ++++ b/libavdevice/windows_capture.cpp +@@ -0,0 +1,184 @@ ++/* ++ * This file is part of FFmpeg. ++ * ++ * Copyright (C) 2022 Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2.1 ++ * of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @author Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ */ ++ ++#include "dxgigrab.h" ++ ++#include <d3d11.h> ++ ++using namespace winrt; ++using namespace Windows; ++using namespace Windows::Foundation; ++using namespace Windows::System; ++using namespace Windows::Graphics; ++using namespace Windows::Graphics::Capture; ++using namespace Windows::Graphics::DirectX; ++using namespace Windows::Graphics::DirectX::Direct3D11; ++using namespace Windows::Foundation::Numerics; ++using namespace Windows::UI; ++using namespace Windows::UI::Composition; ++ ++WindowsCapture::WindowsCapture( ++ IDirect3DDevice const& device, ++ GraphicsCaptureItem const& item, ++ MonitorData& monitorData) ++{ ++ m_item = item; ++ m_device = device; ++ ++ m_d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device); ++ m_d3dDevice->GetImmediateContext(m_d3dContext.put()); ++ screen.first = monitorData.hmnt; ++ screen.second = monitorData.rect; ++ ++ m_DeviceSize = m_item.Size(); ++ ++ // Create framepool, define pixel format (DXGI_FORMAT_B8G8R8A8_UNORM), and frame size. ++ m_framePool = Direct3D11CaptureFramePool::Create( ++ m_device, ++ DirectXPixelFormat::R8G8B8A8UIntNormalized, ++ 2, ++ m_DeviceSize); ++ m_session = m_framePool.CreateCaptureSession(m_item); ++ auto sup = m_session.IsSupported(); ++} ++ ++// Start sending capture frames ++void ++WindowsCapture::StartCapture() ++{ ++ std::lock_guard<std::mutex> lk(mtx_); ++ running_ = true; ++ m_session.StartCapture(); ++} ++ ++// Process captured frames ++void ++WindowsCapture::Close() ++{ ++ std::lock_guard<std::mutex> lk(mtx_); ++ running_ = false; ++ if (texture) { ++ texture->Release(); ++ } ++ m_framePool.Close(); ++ m_session.Close(); ++ m_d3dContext->ClearState(); ++ m_d3dContext->Flush(); ++ m_d3dDevice->Release(); ++ ++ texture = nullptr; ++ m_framePool = nullptr; ++ m_session = nullptr; ++ m_item = nullptr; ++} ++ ++bool ++WindowsCapture::checkNewFrameArrived() ++{ ++ std::lock_guard<std::mutex> lk(mtx_); ++ if (!running_) ++ return 0; ++ auto shouldResize = false; ++ ++ auto frame = m_framePool.TryGetNextFrame(); ++ if (!frame) ++ return 0; ++ ++ auto frameSurface = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface()); ++ ++ D3D11_TEXTURE2D_DESC desc; ++ frameSurface->GetDesc(&desc); ++ auto frameContentSize = frame.ContentSize(); ++ if (desc.Width <= 0 || desc.Height <= 0) ++ return 0; ++ ++ shouldResize = frameContentSize.Width != m_DeviceSize.Width || frameContentSize.Height != m_DeviceSize.Height; ++ ++ if (shouldResize) { ++ m_DeviceSize.Width = frameContentSize.Width; ++ m_DeviceSize.Height = frameContentSize.Height; ++ m_framePool.Recreate(m_device, DirectXPixelFormat::R8G8B8A8UIntNormalized, 2, m_DeviceSize); ++ return false; ++ } ++ ++ texDesc.Width = desc.Width; ++ texDesc.Height = desc.Height; ++ texDesc.MipLevels = desc.MipLevels; ++ texDesc.ArraySize = desc.ArraySize; ++ texDesc.Format = desc.Format; ++ texDesc.SampleDesc = desc.SampleDesc; ++ texDesc.Usage = D3D11_USAGE_STAGING; ++ texDesc.BindFlags = 0; ++ texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; ++ texDesc.MiscFlags = 0; ++ ++ m_d3dDevice->CreateTexture2D(&texDesc, nullptr, &texture); ++ ++ // copy the texture to a staging resource ++ m_d3dContext->CopyResource(texture, frameSurface.get()); ++ ++ return 1; ++} ++ ++int ++WindowsCapture::GetPkt(AVPacket *pkt) ++{ ++ if (!checkNewFrameArrived() || !running_ || !texture) ++ return 0; ++ std::lock_guard<std::mutex> lk(mtx_); ++ ++ // now, map the staging resource TO CPU ++ D3D11_MAPPED_SUBRESOURCE mapInfo; ++ m_d3dContext->Map( ++ texture, ++ 0, ++ D3D11_MAP_READ, ++ 0, ++ &mapInfo); ++ ++ m_d3dContext->Unmap(texture, 0); ++ ++ auto ret = av_new_packet(pkt, mapInfo.DepthPitch); ++ if (ret < 0) { ++ if (mapInfo.DepthPitch) { ++ av_packet_unref(pkt); ++ } ++ texture->Release(); ++ texture = nullptr; ++ return ret; ++ } ++ ++ auto idx = 0; ++ for (auto y = 0; y < m_DeviceSize.Height; y++) { ++ for (auto x = 0; x < m_DeviceSize.Width * 4; x++) { ++ pkt->data[idx] = static_cast<uint8_t*>(mapInfo.pData)[y * mapInfo.RowPitch + x]; ++ idx++; ++ } ++ } ++ ++ texture->Release(); ++ texture = nullptr; ++ ++ return 0; ++} +diff --git a/libavdevice/windows_capture.h b/libavdevice/windows_capture.h +new file mode 100644 +index 0000000000..3e9269ed58 +--- /dev/null ++++ b/libavdevice/windows_capture.h +@@ -0,0 +1,82 @@ ++/* ++ * This file is part of FFmpeg. ++ * ++ * Copyright (C) 2022 Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2.1 ++ * of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @author Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> ++ */ ++ ++#pragma once ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++#include <libavutil/hwcontext_d3d11va.h> ++#ifdef __cplusplus ++} ++#endif ++ ++#include <mutex> ++#include <memory> ++ ++#include <windows.h> ++ ++struct MonitorData { ++ HMONITOR hmnt = NULL; ++ RECT rect; ++ int id = 0; ++ int curId = 0; ++ int desiredId = 0; ++}; ++ ++class WindowsCapture ++{ ++public: ++ WindowsCapture( ++ winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice const& device, ++ winrt::Windows::Graphics::Capture::GraphicsCaptureItem const& item, ++ MonitorData& monitorData); ++ ~WindowsCapture() { Close(); } ++ ++ void StartCapture(); ++ ++ int GetPkt(AVPacket *pkt); ++ ++ void Close(); ++ std::string window; ++ ++ bool checkNewFrameArrived(); ++ winrt::Windows::Graphics::SizeInt32 m_DeviceSize; ++ std::pair<bool, RECT> screen; ++ ++private: ++ winrt::Windows::Graphics::Capture::GraphicsCaptureItem m_item{ nullptr }; ++ winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool m_framePool{ nullptr }; ++ winrt::Windows::Graphics::Capture::GraphicsCaptureSession m_session{ nullptr }; ++ ++ winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device {nullptr}; ++ winrt::com_ptr<ID3D11Device> m_d3dDevice{nullptr}; ++ winrt::com_ptr<ID3D11DeviceContext> m_d3dContext {nullptr}; ++ ++ D3D11_TEXTURE2D_DESC texDesc; ++ ID3D11Texture2D *texture {nullptr}; ++ ++ std::mutex mtx_; ++ bool running_; ++}; +-- +2.30.2.windows.1 + diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp index cc8ad30f4f4f927f7c098fb86e6a0013090bc5fc..f9e5d04a2850ac240454bd8824cbcf4741659fc2 100644 --- a/src/media/media_decoder.cpp +++ b/src/media/media_decoder.cpp @@ -350,7 +350,7 @@ MediaDemuxer::decode() av_packet_free( &p); }); - if (inputParams_.format == "x11grab") { + if (inputParams_.format == "x11grab" || inputParams_.format == "dxgigrab") { auto ret = inputCtx_->iformat->read_header(inputCtx_); if (ret == AVERROR_EXTERNAL) { JAMI_ERR("Couldn't read frame: %s\n", libav_utils::getError(ret).c_str()); diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp index aacf8a3f5f0101b774ca1c5d9ad383779ad1f219..d56c291b58fcf7dc71a1a6a229b2855e77856bf8 100644 --- a/src/media/video/video_input.cpp +++ b/src/media/video/video_input.cpp @@ -285,7 +285,7 @@ VideoInput::createDecoder() [](void* data) -> int { return not static_cast<VideoInput*>(data)->isCapturing(); }, this); bool ready = false, restartSink = false; - if (decOpts_.format == "x11grab" && !decOpts_.is_area) { + if ((decOpts_.format == "x11grab" || decOpts_.format == "dxgigrab") && !decOpts_.is_area) { decOpts_.width = 0; decOpts_.height = 0; } @@ -417,7 +417,6 @@ VideoInput::initX11(const std::string& display) p.input = display.substr(1, space); if (p.window_id.empty()) { p.input = display.substr(0, space); - JAMI_INFO() << "p.window_id.empty()"; auto splits = jami::split_string_to_unsigned(display.substr(space + 1), 'x'); // round to 8 pixel block p.width = round2pow(splits[0], 3); @@ -469,31 +468,61 @@ VideoInput::initAVFoundation(const std::string& display) return true; } +#ifdef WIN32 bool -VideoInput::initGdiGrab(const std::string& params) +VideoInput::initWindowsGrab(const std::string& display) { - size_t space = params.find(' '); - clearOptions(); - decOpts_ = jami::getVideoDeviceMonitor().getDeviceParams(DEVICE_DESKTOP); - - if (space != std::string::npos) { - std::istringstream iss(params.substr(space + 1)); - char sep; - unsigned w, h; - iss >> w >> sep >> h; - decOpts_.width = round2pow(w, 3); - decOpts_.height = round2pow(h, 3); + // Patterns + // full screen sharing : :1+0,0 2560x1440 - SCREEN 1, POSITION 0X0, RESOLUTION 2560X1440 + // area sharing : :1+882,211 1532x779 - SCREEN 1, POSITION 882x211, RESOLUTION 1532x779 + // window sharing : :+1,0 0x0 window-id:TITLE - POSITION 0X0 + size_t space = display.find(' '); + std::string windowIdStr = "window-id:"; + size_t winIdPos = display.find(windowIdStr); - size_t plus = params.find('+'); - std::istringstream dss(params.substr(plus + 1, space - plus)); - dss >> decOpts_.offset_x >> sep >> decOpts_.offset_y; + DeviceParams p = jami::getVideoDeviceMonitor().getDeviceParams(DEVICE_DESKTOP); + if (winIdPos != std::string::npos) { + p.input = display.substr(winIdPos + windowIdStr.size()); // "TITLE"; + p.name = display.substr(winIdPos + windowIdStr.size()); // "TITLE"; + p.is_area = 0; } else { - decOpts_.width = default_grab_width; - decOpts_.height = default_grab_height; + p.input = display.substr(1); + p.name = display.substr(1); + p.is_area = 1; + if (space != std::string::npos) { + auto splits = jami::split_string_to_unsigned(display.substr(space + 1), 'x'); + if (splits.size() != 2) + return false; + + // round to 8 pixel block + p.width = splits[0]; + p.height = splits[1]; + + size_t plus = display.find('+'); + auto position = display.substr(plus + 1, space - plus - 1); + splits = jami::split_string_to_unsigned(position, ','); + if (splits.size() != 2) + return false; + p.offset_x = splits[0]; + p.offset_y = splits[1]; + } else { + p.width = default_grab_width; + p.height = default_grab_height; + } } + auto dec = std::make_unique<MediaDecoder>(); + if (dec->openInput(p) < 0 || dec->setupVideo() < 0) + return initCamera(jami::getVideoDeviceMonitor().getDefaultDevice()); + + clearOptions(); + decOpts_ = p; + decOpts_.width = dec->getStream().width; + decOpts_.height = dec->getStream().height; + return true; } +#endif bool VideoInput::initFile(std::string path) @@ -588,8 +617,8 @@ VideoInput::switchInput(const std::string& resource) /* X11 display name */ #ifdef __APPLE__ ready = initAVFoundation(suffix); -#elif defined(_WIN32) - ready = initGdiGrab(suffix); +#elif defined(WIN32) + ready = initWindowsGrab(suffix); #else ready = initX11(suffix); #endif diff --git a/src/media/video/video_input.h b/src/media/video/video_input.h index f8a27eb37e02b08ffb7facee755e2e5ef4f989aa..570427ec9d275d06fa68e3a6a6d69013a863b0d3 100644 --- a/src/media/video/video_input.h +++ b/src/media/video/video_input.h @@ -135,7 +135,9 @@ private: bool initX11(const std::string& display); bool initAVFoundation(const std::string& display); bool initFile(std::string path); - bool initGdiGrab(const std::string& params); +#ifdef WIN32 + bool initWindowsGrab(const std::string& display); +#endif bool isCapturing() const noexcept; void startLoop(); diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp index c3a0d46cd2f7d97c933abbea290797db3be89f0e..8dbe3114efaea879774764b04773ef28cbd5c67f 100644 --- a/src/media/video/video_rtp_session.cpp +++ b/src/media/video/video_rtp_session.cpp @@ -166,7 +166,7 @@ VideoRtpSession::startSender() // Current implementation does not handle resolution change // (needed by window sharing feature) with HW codecs, so HW // codecs will be disabled for now. - bool allowHwAccel = (localVideoParams_.format != "x11grab"); + bool allowHwAccel = (localVideoParams_.format != "x11grab" && localVideoParams_.format != "dxgigrab"); if (socketPair_) initSeqVal_ = socketPair_->lastSeqValOut(); diff --git a/src/media/video/winvideo/video_device_impl.cpp b/src/media/video/winvideo/video_device_impl.cpp index adb752423ba8d925a6e8e158308d0df312941eaf..11d8e9a57842d47b36a4660d60a70433396b0f84 100644 --- a/src/media/video/winvideo/video_device_impl.cpp +++ b/src/media/video/winvideo/video_device_impl.cpp @@ -281,7 +281,7 @@ VideoDeviceImpl::getDeviceParams() const params.unique_id = id; params.input = id; if (id == DEVICE_DESKTOP) { - params.format = "gdigrab"; + params.format = "dxgigrab"; params.framerate = desktopFrameRate_; return params; }