aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-07-23 17:33:37 +0200
committerdec05eba <dec05eba@protonmail.com>2021-07-23 17:33:37 +0200
commit4617e2c45aee13d1558d9d1aa7f964f550705d63 (patch)
treee24a8b0ceaf478ffe7fa63be051f668d131a22de
parent7b39bd81c0933e94e0b9f662a035bb76b3439373 (diff)
Add option to record a display or all displays. This requires nvfbc
-rw-r--r--README.md3
-rw-r--r--TODO7
-rw-r--r--include/NvFBC.h2006
-rw-r--r--include/NvFBCLibrary.hpp245
-rw-r--r--src/main.cpp509
5 files changed, 2582 insertions, 188 deletions
diff --git a/README.md b/README.md
index 55fb67f..135367d 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,8 @@ When recording a 4k game, fps drops from 30 to 7 when using OBS Studio, however
the fps remains at 30.
# Installation
-gpu screen recorder can be built using [sibs](https://git.dec05eba.com/sibs) or if you are running Arch Linux, then you can find it on aur under the name gpu-screen-recorder-git (`yay -S gpu-screen-recorder-git`).
+gpu screen recorder can be built using [sibs](https://git.dec05eba.com/sibs) or if you are running Arch Linux, then you can find it on aur under the name gpu-screen-recorder-git (`yay -S gpu-screen-recorder-git`).\
+Recording displays requires a gpu with NvFBC support. Normally only tesla and quadro gpus support this, but by using https://github.com/keylase/nvidia-patch you can do this on all gpus that support nvenc as well (gpus as old as the nvidia 600 series), provided you are not using outdated gpu drivers.
# How to use
Run `interactive.sh` or run gpu-screen-recorder directly, for example: `gpu-screen-recorder -w 0x1c00001 -c mp4 -f 60 -a bluez_sink.00_18_09_8A_07_93.a2dp_sink.monitor > test_video.mp4`\
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..c2af783
--- /dev/null
+++ b/TODO
@@ -0,0 +1,7 @@
+Check for reparent.
+Only add window to list if its the window is a topmost window.
+Use nvEncoder api directly? maybe with this we could copy the window opengl texture directly to the gpu which doesn't work right now for some reason.
+ Right now we are required to copy the opengl texture to another opengl texture first.
+Load cuda at runtime with dlopen.
+Track window damages and only update then. That is better for output file size.
+Remove cuda to cuda copy when using nvFBC if possible. ffmpeg is getting in the way. \ No newline at end of file
diff --git a/include/NvFBC.h b/include/NvFBC.h
new file mode 100644
index 0000000..8990eea
--- /dev/null
+++ b/include/NvFBC.h
@@ -0,0 +1,2006 @@
+/*!
+ * \file
+ *
+ * This file contains the interface constants, structure definitions and
+ * function prototypes defining the NvFBC API for Linux.
+ *
+ * Copyright (c) 2013-2020, NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef _NVFBC_H_
+#define _NVFBC_H_
+
+#include <stdint.h>
+
+/*!
+ * \mainpage NVIDIA Framebuffer Capture (NvFBC) for Linux.
+ *
+ * NvFBC is a high performance, low latency API to capture the framebuffer of
+ * an X server screen.
+ *
+ * The output from NvFBC captures everything that would be visible if we were
+ * directly looking at the monitor. This includes window manager decoration,
+ * mouse cursor, overlay, etc.
+ *
+ * It is ideally suited to desktop or fullscreen application capture and
+ * remoting.
+ */
+
+/*!
+ * \defgroup FBC_REQ Requirements
+ *
+ * The following requirements are provided by the regular NVIDIA Display Driver
+ * package:
+ *
+ * - OpenGL core >= 4.2:
+ * Required. NvFBC relies on OpenGL to perform frame capture and
+ * post-processing.
+ *
+ * - Vulkan 1.1:
+ * Required.
+ *
+ * - libcuda.so.1 >= 5.5:
+ * Optional. Used for capture to video memory with CUDA interop.
+ *
+ * The following requirements must be installed separately depending on the
+ * Linux distribution being used:
+ *
+ * - XRandR extension >= 1.2:
+ * Optional. Used for RandR output tracking.
+ *
+ * - libX11-xcb.so.1 >= 1.2:
+ * Required. NvFBC uses a mix of Xlib and XCB. Xlib is needed to use GLX,
+ * XCB is needed to make NvFBC more resilient against X server terminations
+ * while a capture session is active.
+ *
+ * - libxcb.so.1 >= 1.3:
+ * Required. See above.
+ *
+ * - xorg-server >= 1.3:
+ * Optional. Required for push model to work properly.
+ *
+ * Note that all optional dependencies are dlopen()'d at runtime. Failure to
+ * load an optional library is not fatal.
+ */
+
+/*!
+ * \defgroup FBC_CHANGES ChangeLog
+ *
+ * NvFBC Linux API version 0.1
+ * - Initial BETA release.
+ *
+ * NvFBC Linux API version 0.2
+ * - Added 'bEnableMSE' field to NVFBC_H264_HW_ENC_CONFIG.
+ * - Added 'dwMSE' field to NVFBC_TOH264_GRAB_FRAME_PARAMS.
+ * - Added 'bEnableAQ' field to NVFBC_H264_HW_ENC_CONFIG.
+ * - Added 'NVFBC_H264_PRESET_LOSSLESS_HP' enum to NVFBC_H264_PRESET.
+ * - Added 'NVFBC_BUFFER_FORMAT_YUV444P' enum to NVFBC_BUFFER_FORMAT.
+ * - Added 'eInputBufferFormat' field to NVFBC_H264_HW_ENC_CONFIG.
+ * - Added '0' and '244' values for NVFBC_H264_HW_ENC_CONFIG::dwProfile.
+ *
+ * NvFBC Linux API version 0.3
+ * - Improved multi-threaded support by implementing an API locking mechanism.
+ * - Added 'nvFBCBindContext' API entry point.
+ * - Added 'nvFBCReleaseContext' API entry point.
+ *
+ * NvFBC Linux API version 1.0
+ * - Added codec agnostic interface for HW encoding.
+ * - Deprecated H.264 interface.
+ * - Added support for H.265/HEVC HW encoding.
+ *
+ * NvFBC Linux API version 1.1
+ * - Added 'nvFBCToHwGetCaps' API entry point.
+ * - Added 'dwDiffMapScalingFactor' field to NVFBC_TOSYS_SETUP_PARAMS.
+ *
+ * NvFBC Linux API version 1.2
+ * - Deprecated ToHwEnc interface.
+ * - Added ToGL interface that captures frames to an OpenGL texture in video
+ * memory.
+ * - Added 'bDisableAutoModesetRecovery' field to
+ * NVFBC_CREATE_CAPTURE_SESSION_PARAMS.
+ * - Added 'bExternallyManagedContext' field to NVFBC_CREATE_HANDLE_PARAMS.
+ *
+ * NvFBC Linux API version 1.3
+ * - Added NVFBC_BUFFER_FORMAT_RGBA
+ * - Added 'dwTimeoutMs' field to NVFBC_TOSYS_GRAB_FRAME_PARAMS,
+ * NVFBC_TOCUDA_GRAB_FRAME_PARAMS, and NVFBC_TOGL_GRAB_FRAME_PARAMS.
+ *
+ * NvFBC Linux API version 1.4
+ * - Clarified that NVFBC_BUFFER_FORMAT_{ARGB,RGB,RGBA} are byte-order formats.
+ * - Renamed NVFBC_BUFFER_FORMAT_YUV420P to NVFBC_BUFFER_FORMAT_NV12.
+ * - Added new requirements.
+ * - Made NvFBC more resilient against the X server terminating during an active
+ * capture session. See new comments for ::NVFBC_ERR_X.
+ * - Relaxed requirement that 'frameSize' must have a width being a multiple of
+ * 4 and a height being a multiple of 2.
+ * - Added 'bRoundFrameSize' field to NVFBC_CREATE_CAPTURE_SESSION_PARAMS.
+ * - Relaxed requirement that the scaling factor for differential maps must be
+ * a multiple of the size of the frame.
+ * - Added 'diffMapSize' field to NVFBC_TOSYS_SETUP_PARAMS and
+ * NVFBC_TOGL_SETUP_PARAMS.
+ *
+ * NvFBC Linux API version 1.5
+ * - Added NVFBC_BUFFER_FORMAT_BGRA
+ *
+ * NvFBC Linux API version 1.6
+ * - Added the 'NVFBC_TOSYS_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY',
+ * 'NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY', and
+ * 'NVFBC_TOGL_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY' capture flags.
+ * - Exposed debug and performance logs through the NVFBC_LOG_LEVEL environment
+ * variable. Setting it to "1" enables performance logs, setting it to "2"
+ * enables debugging logs, setting it to "3" enables both.
+ * - Logs are printed to stdout or to the file pointed by the NVFBC_LOG_FILE
+ * environment variable.
+ * - Added 'ulTimestampUs' to NVFBC_FRAME_GRAB_INFO.
+ * - Added 'dwSamplingRateMs' to NVFBC_CREATE_CAPTURE_SESSION_PARAMS.
+ * - Added 'bPushModel' to NVFBC_CREATE_CAPTURE_SESSION_PARAMS.
+ *
+ * NvFBC Linux API version 1.7
+ * - Retired the NVFBC_CAPTURE_TO_HW_ENCODER interface.
+ * This interface has been deprecated since NvFBC 1.2 and has received no
+ * updates or new features since. We recommend using the NVIDIA Video Codec
+ * SDK to encode NvFBC frames.
+ * See: https://developer.nvidia.com/nvidia-video-codec-sdk
+ * - Added a 'Capture Modes' section to those headers.
+ * - Added a 'Post Processing' section to those headers.
+ * - Added an 'Environment Variables' section to those headers.
+ * - Added 'bInModeset' to NVFBC_GET_STATUS_PARAMS.
+ * - Added 'bAllowDirectCapture' to NVFBC_CREATE_CAPTURE_SESSION_PARAMS.
+ * - Added 'bDirectCaptured' to NVFBC_FRAME_GRAB_INFO.
+ * - Added 'bRequiredPostProcessing' to NVFBC_FRAME_GRAB_INFO.
+ */
+
+/*!
+ * \defgroup FBC_MODES Capture Modes
+ *
+ * When creating a capture session, NvFBC instantiates a capture subsystem
+ * living in the NVIDIA X driver.
+ *
+ * This subsystem listens for damage events coming from applications then
+ * generates (composites) frames for NvFBC when new content is available.
+ *
+ * This capture server can operate on a timer where it periodically checks if
+ * there are any pending damage events, or it can generate frames as soon as it
+ * receives a new damage event.
+ * See NVFBC_CREATE_CAPTURE_SESSION_PARAMS::dwSamplingRateMs,
+ * and NVFBC_CREATE_CAPTURE_SESSION_PARAMS::bPushModel.
+ *
+ * NvFBC can also attach itself to a fullscreen unoccluded application and have
+ * it copy its frames directly into a buffer owned by NvFBC upon present. This
+ * mode bypasses the X server.
+ * See NVFBC_CREATE_CAPTURE_SESSION_PARAMS::bAllowDirectCapture.
+ *
+ * NvFBC is designed to capture frames with as few copies as possible. The
+ * NVIDIA X driver composites frames directly into the NvFBC buffers, and
+ * direct capture copies frames directly into these buffers as well.
+ *
+ * Depending on the configuration of a capture session, an extra copy (rendering
+ * pass) may be needed. See the 'Post Processing' section.
+ */
+
+/*!
+ * \defgroup FBC_PP Post Processing
+ *
+ * Depending on the configuration of a capture session, NvFBC might require to
+ * do post processing on frames.
+ *
+ * Post processing is required for the following reasons:
+ * - NvFBC needs to do a pixel format conversion.
+ * - Diffmaps are requested.
+ * - Capture to system memory is requested.
+ *
+ * NvFBC needs to do a conversion if the requested pixel format does not match
+ * the native format. The native format is NVFBC_BUFFER_FORMAT_BGRA.
+ *
+ * Note: post processing is *not* required for frame scaling and frame cropping.
+ *
+ * Skipping post processing can reduce capture latency. An application can know
+ * whether post processing was required by checking
+ * NVFBC_FRAME_GRAB_INFO::bRequiredPostProcessing.
+ */
+
+/*!
+ * \defgroup FBC_ENVVAR Environment Variables
+ *
+ * Below are the environment variables supported by NvFBC:
+ *
+ * - NVFBC_LOG_LEVEL
+ * Bitfield where the first bit enables debug logs and the second bit enables
+ * performance logs. Both can be enabled by setting this envvar to 3.
+ *
+ * - NVFBC_LOG_FILE
+ * Write all NvFBC logs to the given file.
+ *
+ * - NVFBC_FORCE_ALLOW_DIRECT_CAPTURE
+ * Used to override NVFBC_CREATE_CAPTURE_SESSION_PARAMS::bAllowDirectCapture.
+ *
+ * - NVFBC_FORCE_POST_PROCESSING
+ * Used to force the post processing step, even if it could be skipped.
+ * See the 'Post Processing' section.
+ */
+
+/*!
+ * \defgroup FBC_STRUCT Structure Definition
+ *
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * Calling convention.
+ */
+#define NVFBCAPI
+
+/*!
+ * NvFBC API major version.
+ */
+#define NVFBC_VERSION_MAJOR 1
+
+/*!
+ * NvFBC API minor version.
+ */
+#define NVFBC_VERSION_MINOR 7
+
+/*!
+ * NvFBC API version.
+ */
+#define NVFBC_VERSION (uint32_t) (NVFBC_VERSION_MINOR | (NVFBC_VERSION_MAJOR << 8))
+
+/*!
+ * Creates a version number for structure parameters.
+ */
+#define NVFBC_STRUCT_VERSION(typeName, ver) \
+ (uint32_t) (sizeof(typeName) | ((ver) << 16) | (NVFBC_VERSION << 24))
+
+/*!
+ * Defines error codes.
+ *
+ * \see NvFBCGetLastErrorStr
+ */
+typedef enum _NVFBCSTATUS
+{
+ /*!
+ * This indicates that the API call returned with no errors.
+ */
+ NVFBC_SUCCESS = 0,
+ /*!
+ * This indicates that the API version between the client and the library
+ * is not compatible.
+ */
+ NVFBC_ERR_API_VERSION = 1,
+ /*!
+ * An internal error occurred.
+ */
+ NVFBC_ERR_INTERNAL = 2,
+ /*!
+ * This indicates that one or more of the parameter passed to the API call
+ * is invalid.
+ */
+ NVFBC_ERR_INVALID_PARAM = 3,
+ /*!
+ * This indicates that one or more of the pointers passed to the API call
+ * is invalid.
+ */
+ NVFBC_ERR_INVALID_PTR = 4,
+ /*!
+ * This indicates that the handle passed to the API call to identify the
+ * client is invalid.
+ */
+ NVFBC_ERR_INVALID_HANDLE = 5,
+ /*!
+ * This indicates that the maximum number of threaded clients of the same
+ * process has been reached. The limit is 10 threads per process.
+ * There is no limit on the number of process.
+ */
+ NVFBC_ERR_MAX_CLIENTS = 6,
+ /*!
+ * This indicates that the requested feature is not currently supported
+ * by the library.
+ */
+ NVFBC_ERR_UNSUPPORTED = 7,
+ /*!
+ * This indicates that the API call failed because it was unable to allocate
+ * enough memory to perform the requested operation.
+ */
+ NVFBC_ERR_OUT_OF_MEMORY = 8,
+ /*!
+ * This indicates that the API call was not expected. This happens when
+ * API calls are performed in a wrong order, such as trying to capture
+ * a frame prior to creating a new capture session; or trying to set up
+ * a capture to video memory although a capture session to system memory
+ * was created.
+ */
+ NVFBC_ERR_BAD_REQUEST = 9,
+ /*!
+ * This indicates an X error, most likely meaning that the X server has
+ * been terminated. When this error is returned, the only resort is to
+ * create another FBC handle using NvFBCCreateHandle().
+ *
+ * The previous handle should still be freed with NvFBCDestroyHandle(), but
+ * it might leak resources, in particular X, GLX, and GL resources since
+ * it is no longer possible to communicate with an X server to free them
+ * through the driver.
+ *
+ * The best course of action to eliminate this potential leak is to close
+ * the OpenGL driver, close the forked process running the capture, or
+ * restart the application.
+ */
+ NVFBC_ERR_X = 10,
+ /*!
+ * This indicates a GLX error.
+ */
+ NVFBC_ERR_GLX = 11,
+ /*!
+ * This indicates an OpenGL error.
+ */
+ NVFBC_ERR_GL = 12,
+ /*!
+ * This indicates a CUDA error.
+ */
+ NVFBC_ERR_CUDA = 13,
+ /*!
+ * This indicates a HW encoder error.
+ */
+ NVFBC_ERR_ENCODER = 14,
+ /*!
+ * This indicates an NvFBC context error.
+ */
+ NVFBC_ERR_CONTEXT = 15,
+ /*!
+ * This indicates that the application must recreate the capture session.
+ *
+ * This error can be returned if a modeset event occurred while capturing
+ * frames, and NVFBC_CREATE_HANDLE_PARAMS::bDisableAutoModesetRecovery
+ * was set to NVFBC_TRUE.
+ */
+ NVFBC_ERR_MUST_RECREATE = 16,
+ /*!
+ * This indicates a Vulkan error.
+ */
+ NVFBC_ERR_VULKAN = 17,
+} NVFBCSTATUS;
+
+/*!
+ * Defines boolean values.
+ */
+typedef enum _NVFBC_BOOL
+{
+ /*!
+ * False value.
+ */
+ NVFBC_FALSE = 0,
+ /*!
+ * True value.
+ */
+ NVFBC_TRUE,
+} NVFBC_BOOL;
+
+/*!
+ * Maximum size in bytes of an error string.
+ */
+#define NVFBC_ERR_STR_LEN 512
+
+/*!
+ * Capture type.
+ */
+typedef enum _NVFBC_CAPTURE_TYPE
+{
+ /*!
+ * Capture frames to a buffer in system memory.
+ */
+ NVFBC_CAPTURE_TO_SYS = 0,
+ /*!
+ * Capture frames to a CUDA device in video memory.
+ *
+ * Specifying this will dlopen() libcuda.so.1 and fail if not available.
+ */
+ NVFBC_CAPTURE_SHARED_CUDA,
+ /*!
+ * Retired. Do not use.
+ */
+ /* NVFBC_CAPTURE_TO_HW_ENCODER, */
+ /*!
+ * Capture frames to an OpenGL buffer in video memory.
+ */
+ NVFBC_CAPTURE_TO_GL = 3,
+} NVFBC_CAPTURE_TYPE;
+
+/*!
+ * Tracking type.
+ *
+ * NvFBC can track a specific region of the framebuffer to capture.
+ *
+ * An X screen corresponds to the entire framebuffer.
+ *
+ * An RandR CRTC is a component of the GPU that reads pixels from a region of
+ * the X screen and sends them through a pipeline to an RandR output.
+ * A physical monitor can be connected to an RandR output. Tracking an RandR
+ * output captures the region of the X screen that the RandR CRTC is sending to
+ * the RandR output.
+ */
+typedef enum
+{
+ /*!
+ * By default, NvFBC tries to track a connected primary output. If none is
+ * found, then it tries to track the first connected output. If none is
+ * found then it tracks the entire X screen.
+ *
+ * If the XRandR extension is not available, this option has the same effect
+ * as ::NVFBC_TRACKING_SCREEN.
+ *
+ * This default behavior might be subject to changes in the future.
+ */
+ NVFBC_TRACKING_DEFAULT = 0,
+ /*!
+ * Track an RandR output specified by its ID in the appropriate field.
+ *
+ * The list of connected outputs can be queried via NvFBCGetStatus().
+ * This list can also be obtained using e.g., xrandr(1).
+ *
+ * If the XRandR extension is not available, setting this option returns an
+ * error.
+ */
+ NVFBC_TRACKING_OUTPUT,
+ /*!
+ * Track the entire X screen.
+ */
+ NVFBC_TRACKING_SCREEN,
+} NVFBC_TRACKING_TYPE;
+
+/*!
+ * Buffer format.
+ */
+typedef enum _NVFBC_BUFFER_FORMAT
+{
+ /*!
+ * Data will be converted to ARGB8888 byte-order format. 32 bpp.
+ */
+ NVFBC_BUFFER_FORMAT_ARGB = 0,
+ /*!
+ * Data will be converted to RGB888 byte-order format. 24 bpp.
+ */
+ NVFBC_BUFFER_FORMAT_RGB,
+ /*!
+ * Data will be converted to NV12 format using HDTV weights
+ * according to ITU-R BT.709. 12 bpp.
+ */
+ NVFBC_BUFFER_FORMAT_NV12,
+ /*!
+ * Data will be converted to YUV 444 planar format using HDTV weights
+ * according to ITU-R BT.709. 24 bpp
+ */
+ NVFBC_BUFFER_FORMAT_YUV444P,
+ /*!
+ * Data will be converted to RGBA8888 byte-order format. 32 bpp.
+ */
+ NVFBC_BUFFER_FORMAT_RGBA,
+ /*!
+ * Native format. No pixel conversion needed.
+ * BGRA8888 byte-order format. 32 bpp.
+ */
+ NVFBC_BUFFER_FORMAT_BGRA,
+} NVFBC_BUFFER_FORMAT;
+
+#define NVFBC_BUFFER_FORMAT_YUV420P NVFBC_BUFFER_FORMAT_NV12
+
+/*!
+ * Handle used to identify an NvFBC session.
+ */
+typedef uint64_t NVFBC_SESSION_HANDLE;
+
+/*!
+ * Box used to describe an area of the tracked region to capture.
+ *
+ * The coordinates are relative to the tracked region.
+ *
+ * E.g., if the size of the X screen is 3520x1200 and the tracked RandR output
+ * scans a region of 1600x1200+1920+0, then setting a capture box of
+ * 800x600+100+50 effectively captures a region of 800x600+2020+50 relative to
+ * the X screen.
+ */
+typedef struct _NVFBC_BOX
+{
+ /*!
+ * [in] X offset of the box.
+ */
+ uint32_t x;
+ /*!
+ * [in] Y offset of the box.
+ */
+ uint32_t y;
+ /*!
+ * [in] Width of the box.
+ */
+ uint32_t w;
+ /*!
+ * [in] Height of the box.
+ */
+ uint32_t h;
+} NVFBC_BOX;
+
+/*!
+ * Size used to describe the size of a frame.
+ */
+typedef struct _NVFBC_SIZE
+{
+ /*!
+ * [in] Width.
+ */
+ uint32_t w;
+ /*!
+ * [in] Height.
+ */
+ uint32_t h;
+} NVFBC_SIZE;
+
+/*!
+ * Describes information about a captured frame.
+ */
+typedef struct _NVFBC_FRAME_GRAB_INFO
+{
+ /*!
+ * [out] Width of the captured frame.
+ */
+ uint32_t dwWidth;
+ /*!
+ * [out] Height of the captured frame.
+ */
+ uint32_t dwHeight;
+ /*!
+ * [out] Size of the frame in bytes.
+ */
+ uint32_t dwByteSize;
+ /*!
+ * [out] Incremental ID of the current frame.
+ *
+ * This can be used to identify a frame.
+ */
+ uint32_t dwCurrentFrame;
+ /*!
+ * [out] Whether the captured frame is a new frame.
+ *
+ * When using non blocking calls it is possible to capture a frame
+ * that was already captured before if the display server did not
+ * render a new frame in the meantime. In that case, this flag
+ * will be set to NVFBC_FALSE.
+ *
+ * When using blocking calls each captured frame will have
+ * this flag set to NVFBC_TRUE since the blocking mechanism waits for
+ * the display server to render a new frame.
+ *
+ * Note that this flag does not guarantee that the content of
+ * the frame will be different compared to the previous captured frame.
+ *
+ * In particular, some compositing managers report the entire
+ * framebuffer as damaged when an application refreshes its content.
+ *
+ * Consider a single X screen spanned across physical displays A and B
+ * and an NvFBC application tracking display A. Depending on the
+ * compositing manager, it is possible that an application refreshing
+ * itself on display B will trigger a frame capture on display A.
+ *
+ * Workarounds include:
+ * - Using separate X screens
+ * - Disabling the composite extension
+ * - Using a compositing manager that properly reports what regions
+ * are damaged
+ * - Using NvFBC's diffmaps to find out if the frame changed
+ */
+ NVFBC_BOOL bIsNewFrame;
+ /*!
+ * [out] Frame timestamp
+ *
+ * Time in micro seconds when the display server started rendering the
+ * frame.
+ *
+ * This does not account for when the frame was captured. If capturing an
+ * old frame (e.g., bIsNewFrame is NVFBC_FALSE) the reported timestamp
+ * will reflect the time when the old frame was rendered by the display
+ * server.
+ */
+ uint64_t ulTimestampUs;
+ /*
+ * [out] Number of frames generated since the last capture.
+ *
+ * This can help applications tell whether they missed frames or there
+ * were no frames generated by the server since the last capture.
+ */
+ uint32_t dwMissedFrames;
+ /*
+ * [out] Whether the captured frame required post processing.
+ *
+ * See the 'Post Processing' section.
+ */
+ NVFBC_BOOL bRequiredPostProcessing;
+ /*
+ * [out] Whether this frame was obtained via direct capture.
+ *
+ * See NVFBC_CREATE_CAPTURE_SESSION_PARAMS::bAllowDirectCapture.
+ */
+ NVFBC_BOOL bDirectCapture;
+} NVFBC_FRAME_GRAB_INFO;
+
+/*!
+ * Defines parameters for the CreateHandle() API call.
+ */
+typedef struct _NVFBC_CREATE_HANDLE_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_CREATE_HANDLE_PARAMS_VER
+ */
+ uint32_t dwVersion;
+ /*!
+ * [in] Application specific private information passed to the NvFBC
+ * session.
+ */
+ const void *privateData;
+ /*!
+ * [in] Size of the application specific private information passed to the
+ * NvFBC session.
+ */
+ uint32_t privateDataSize;
+ /*!
+ * [in] Whether NvFBC should not create and manage its own graphics context
+ *
+ * NvFBC internally uses OpenGL to perfom graphics operations on the
+ * captured frames. By default, NvFBC will create and manage (e.g., make
+ * current, detect new threads, etc.) its own OpenGL context.
+ *
+ * If set to NVFBC_TRUE, NvFBC will use the application's context. It will
+ * be the application's responsibility to make sure that a context is
+ * current on the thread calling into the NvFBC API.
+ */
+ NVFBC_BOOL bExternallyManagedContext;
+ /*!
+ * [in] GLX context
+ *
+ * GLX context that NvFBC should use internally to create pixmaps and
+ * make them current when creating a new capture session.
+ *
+ * Note: NvFBC expects a context created against a GLX_RGBA_TYPE render
+ * type.
+ */
+ void *glxCtx;
+ /*!
+ * [in] GLX framebuffer configuration
+ *
+ * Framebuffer configuration that was used to create the GLX context, and
+ * that will be used to create pixmaps internally.
+ *
+ * Note: NvFBC expects a configuration having at least the following
+ * attributes:
+ * GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT
+ * GLX_BIND_TO_TEXTURE_RGBA_EXT, 1
+ * GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT
+ */
+ void *glxFBConfig;
+} NVFBC_CREATE_HANDLE_PARAMS;
+
+/*!
+ * NVFBC_CREATE_HANDLE_PARAMS structure version.
+ */
+#define NVFBC_CREATE_HANDLE_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_CREATE_HANDLE_PARAMS, 2)
+
+/*!
+ * Defines parameters for the ::NvFBCDestroyHandle() API call.
+ */
+typedef struct _NVFBC_DESTROY_HANDLE_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_DESTROY_HANDLE_PARAMS_VER
+ */
+ uint32_t dwVersion;
+} NVFBC_DESTROY_HANDLE_PARAMS;
+
+/*!
+ * NVFBC_DESTROY_HANDLE_PARAMS structure version.
+ */
+#define NVFBC_DESTROY_HANDLE_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_DESTROY_HANDLE_PARAMS, 1)
+
+/*!
+ * Maximum number of connected RandR outputs to an X screen.
+ */
+#define NVFBC_OUTPUT_MAX 5
+
+/*!
+ * Maximum size in bytes of an RandR output name.
+ */
+#define NVFBC_OUTPUT_NAME_LEN 128
+
+/*!
+ * Describes an RandR output.
+ *
+ * Filling this structure relies on the XRandR extension. This feature cannot
+ * be used if the extension is missing or its version is below the requirements.
+ *
+ * \see Requirements
+ */
+typedef struct _NVFBC_OUTPUT
+{
+ /*!
+ * Identifier of the RandR output.
+ */
+ uint32_t dwId;
+ /*!
+ * Name of the RandR output, as reported by tools such as xrandr(1).
+ *
+ * Example: "DVI-I-0"
+ */
+ char name[NVFBC_OUTPUT_NAME_LEN];
+ /*!
+ * Region of the X screen tracked by the RandR CRTC driving this RandR
+ * output.
+ */
+ NVFBC_BOX trackedBox;
+} NVFBC_RANDR_OUTPUT_INFO;
+
+/*!
+ * Defines parameters for the ::NvFBCGetStatus() API call.
+ */
+typedef struct _NVFBC_GET_STATUS_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_GET_STATUS_PARAMS_VER
+ */
+ uint32_t dwVersion;
+ /*!
+ * [out] Whether or not framebuffer capture is supported by the graphics
+ * driver.
+ */
+ NVFBC_BOOL bIsCapturePossible;
+ /*!
+ * [out] Whether or not there is already a capture session on this system.
+ */
+ NVFBC_BOOL bCurrentlyCapturing;
+ /*!
+ * [out] Whether or not it is possible to create a capture session on this
+ * system.
+ */
+ NVFBC_BOOL bCanCreateNow;
+ /*!
+ * [out] Size of the X screen (framebuffer).
+ */
+ NVFBC_SIZE screenSize;
+ /*!
+ * [out] Whether the XRandR extension is available.
+ *
+ * If this extension is not available then it is not possible to have
+ * information about RandR outputs.
+ */
+ NVFBC_BOOL bXRandRAvailable;
+ /*!
+ * [out] Array of outputs connected to the X screen.
+ *
+ * An application can track a specific output by specifying its ID when
+ * creating a capture session.
+ *
+ * Only if XRandR is available.
+ */
+ NVFBC_RANDR_OUTPUT_INFO outputs[NVFBC_OUTPUT_MAX];
+ /*!
+ * [out] Number of outputs connected to the X screen.
+ *
+ * This must be used to parse the array of connected outputs.
+ *
+ * Only if XRandR is available.
+ */
+ uint32_t dwOutputNum;
+ /*!
+ * [out] Version of the NvFBC library running on this system.
+ */
+ uint32_t dwNvFBCVersion;
+ /*!
+ * [out] Whether the X server is currently in modeset.
+ *
+ * When the X server is in modeset, it must give up all its video
+ * memory allocations. It is not possible to create a capture
+ * session until the modeset is over.
+ *
+ * Note that VT-switches are considered modesets.
+ */
+ NVFBC_BOOL bInModeset;
+} NVFBC_GET_STATUS_PARAMS;
+
+/*!
+ * NVFBC_GET_STATUS_PARAMS structure version.
+ */
+#define NVFBC_GET_STATUS_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_GET_STATUS_PARAMS, 2)
+
+/*!
+ * Defines parameters for the ::NvFBCCreateCaptureSession() API call.
+ */
+typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER
+ */
+ uint32_t dwVersion;
+ /*!
+ * [in] Desired capture type.
+ *
+ * Note that when specyfing ::NVFBC_CAPTURE_SHARED_CUDA NvFBC will try to
+ * dlopen() the corresponding libraries. This means that NvFBC can run on
+ * a system without the CUDA library since it does not link against them.
+ */
+ NVFBC_CAPTURE_TYPE eCaptureType;
+ /*!
+ * [in] What region of the framebuffer should be tracked.
+ */
+ NVFBC_TRACKING_TYPE eTrackingType;
+ /*!
+ * [in] ID of the output to track if eTrackingType is set to
+ * ::NVFBC_TRACKING_OUTPUT.
+ */
+ uint32_t dwOutputId;
+ /*!
+ * [in] Crop the tracked region.
+ *
+ * The coordinates are relative to the tracked region.
+ *
+ * It can be set to 0 to capture the entire tracked region.
+ */
+ NVFBC_BOX captureBox;
+ /*!
+ * [in] Desired size of the captured frame.
+ *
+ * This parameter allow to scale the captured frame.
+ *
+ * It can be set to 0 to disable frame resizing.
+ */
+ NVFBC_SIZE frameSize;
+ /*!
+ * [in] Whether the mouse cursor should be composited to the frame.
+ *
+ * Disabling the cursor will not generate new frames when only the cursor
+ * is moved.
+ */
+ NVFBC_BOOL bWithCursor;
+ /*!
+ * [in] Whether NvFBC should not attempt to recover from modesets.
+ *
+ * NvFBC is able to detect when a modeset event occured and can automatically
+ * re-create a capture session with the same settings as before, then resume
+ * its frame capture session transparently.
+ *
+ * This option allows to disable this behavior. NVFBC_ERR_MUST_RECREATE
+ * will be returned in that case.
+ *
+ * It can be useful in the cases when an application needs to do some work
+ * between setting up a capture and grabbing the first frame.
+ *
+ * For example: an application using the ToGL interface needs to register
+ * resources with EncodeAPI prior to encoding frames.
+ *
+ * Note that during modeset recovery, NvFBC will try to re-create the
+ * capture session every second until it succeeds.
+ */
+ NVFBC_BOOL bDisableAutoModesetRecovery;
+ /*!
+ * [in] Whether NvFBC should round the requested frameSize.
+ *
+ * When disabled, NvFBC will not attempt to round the requested resolution.
+ *
+ * However, some pixel formats have resolution requirements. E.g., YUV/NV
+ * formats must have a width being a multiple of 4, and a height being a
+ * multiple of 2. RGB formats don't have such requirements.
+ *
+ * If the resolution doesn't meet the requirements of the format, then NvFBC
+ * will fail at setup time.
+ *
+ * When enabled, NvFBC will round the requested width to the next multiple
+ * of 4 and the requested height to the next multiple of 2.
+ *
+ * In this case, requesting any resolution will always work with every
+ * format. However, an NvFBC client must be prepared to handle the case
+ * where the requested resolution is different than the captured resolution.
+ *
+ * NVFBC_FRAME_GRAB_INFO::dwWidth and NVFBC_FRAME_GRAB_INFO::dwHeight should
+ * always be used for getting information about captured frames.
+ */
+ NVFBC_BOOL bRoundFrameSize;
+ /*!
+ * [in] Rate in ms at which the display server generates new frames
+ *
+ * This controls the frequency at which the display server will generate
+ * new frames if new content is available. This effectively controls the
+ * capture rate when using blocking calls.
+ *
+ * Note that lower values will increase the CPU and GPU loads.
+ *
+ * The default value is 16ms (~ 60 Hz).
+ */
+ uint32_t dwSamplingRateMs;
+ /*!
+ * [in] Enable push model for frame capture
+ *
+ * When set to NVFBC_TRUE, the display server will generate frames whenever
+ * it receives a damage event from applications.
+ *
+ * Setting this to NVFBC_TRUE will ignore ::dwSamplingRateMs.
+ *
+ * Using push model with the NVFBC_*_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY
+ * capture flag should guarantee the shortest amount of time between an
+ * application rendering a frame and an NvFBC client capturing it, provided
+ * that the NvFBC client is able to process the frames quickly enough.
+ *
+ * Note that applications running at high frame rates will increase CPU and
+ * GPU loads.
+ */
+ NVFBC_BOOL bPushModel;
+ /*!
+ * [in] Allow direct capture
+ *
+ * Direct capture allows NvFBC to attach itself to a fullscreen graphics
+ * application. Whenever that application presents a frame, it makes a copy
+ * of it directly into a buffer owned by NvFBC thus bypassing the X server.
+ *
+ * When direct capture is *not* enabled, the NVIDIA X driver generates a
+ * frame for NvFBC when it receives a damage event from an application if push
+ * model is enabled, or periodically checks if there are any pending damage
+ * events otherwise (see NVFBC_CREATE_CAPTURE_SESSION_PARAMS::dwSamplingRateMs).
+ *
+ * Direct capture is possible under the following conditions:
+ * - Direct capture is allowed
+ * - Push model is enabled (see NVFBC_CREATE_CAPTURE_SESSION_PARAMS::bPushModel)
+ * - The mouse cursor is not composited (see NVFBC_CREATE_CAPTURE_SESSION_PARAMS::bWithCursor)
+ * - No viewport transformation is required. This happens when the remote
+ * desktop is e.g. rotated.
+ *
+ * When direct capture is possible, NvFBC will automatically attach itself
+ * to a fullscreen unoccluded application, if such exists.
+ *
+ * Notes:
+ * - This includes compositing desktops such as GNOME (e.g., gnome-shell
+ * is the fullscreen unoccluded application).
+ * - There can be only one fullscreen unoccluded application at a time.
+ * - The NVIDIA X driver monitors which application qualifies or no
+ * longer qualifies.
+ *
+ * For example, if a fullscreen application is launched in GNOME, NvFBC will
+ * detach from gnome-shell and attach to that application.
+ *
+ * Attaching and detaching happens automatically from the perspective of an
+ * NvFBC client. When detaching from an application, the X driver will
+ * transparently resume generating frames for NvFBC.
+ *
+ * An application can know whether a given frame was obtained through
+ * direct capture by checking NVFBC_FRAME_GRAB_INFO::bDirectCapture.
+ */
+ NVFBC_BOOL bAllowDirectCapture;
+} NVFBC_CREATE_CAPTURE_SESSION_PARAMS;
+
+/*!
+ * NVFBC_CREATE_CAPTURE_SESSION_PARAMS structure version.
+ */
+#define NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_CREATE_CAPTURE_SESSION_PARAMS, 6)
+
+/*!
+ * Defines parameters for the ::NvFBCDestroyCaptureSession() API call.
+ */
+typedef struct _NVFBC_DESTROY_CAPTURE_SESSION_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER
+ */
+ uint32_t dwVersion;
+} NVFBC_DESTROY_CAPTURE_SESSION_PARAMS;
+
+/*!
+ * NVFBC_DESTROY_CAPTURE_SESSION_PARAMS structure version.
+ */
+#define NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_DESTROY_CAPTURE_SESSION_PARAMS, 1)
+
+/*!
+ * Defines parameters for the ::NvFBCBindContext() API call.
+ */
+typedef struct _NVFBC_BIND_CONTEXT_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_BIND_CONTEXT_PARAMS_VER
+ */
+ uint32_t dwVersion;
+} NVFBC_BIND_CONTEXT_PARAMS;
+
+/*!
+ * NVFBC_BIND_CONTEXT_PARAMS structure version.
+ */
+#define NVFBC_BIND_CONTEXT_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_BIND_CONTEXT_PARAMS, 1)
+
+/*!
+ * Defines parameters for the ::NvFBCReleaseContext() API call.
+ */
+typedef struct _NVFBC_RELEASE_CONTEXT_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_RELEASE_CONTEXT_PARAMS_VER
+ */
+ uint32_t dwVersion;
+} NVFBC_RELEASE_CONTEXT_PARAMS;
+
+/*!
+ * NVFBC_RELEASE_CONTEXT_PARAMS structure version.
+ */
+#define NVFBC_RELEASE_CONTEXT_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_RELEASE_CONTEXT_PARAMS, 1)
+
+/*!
+ * Defines flags that can be used when capturing to system memory.
+ */
+typedef enum
+{
+ /*!
+ * Default, capturing waits for a new frame or mouse move.
+ *
+ * The default behavior of blocking grabs is to wait for a new frame until
+ * after the call was made. But it's possible that there is a frame already
+ * ready that the client hasn't seen.
+ * \see NVFBC_TOSYS_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY
+ */
+ NVFBC_TOSYS_GRAB_FLAGS_NOFLAGS = 0,
+ /*!
+ * Capturing does not wait for a new frame nor a mouse move.
+ *
+ * It is therefore possible to capture the same frame multiple times.
+ * When this occurs, the dwCurrentFrame parameter of the
+ * NVFBC_FRAME_GRAB_INFO structure is not incremented.
+ */
+ NVFBC_TOSYS_GRAB_FLAGS_NOWAIT = (1 << 0),
+ /*!
+ * Forces the destination buffer to be refreshed even if the frame has not
+ * changed since previous capture.
+ *
+ * By default, if the captured frame is identical to the previous one, NvFBC
+ * will omit one copy and not update the destination buffer.
+ *
+ * Setting that flag will prevent this behavior. This can be useful e.g.,
+ * if the application has modified the buffer in the meantime.
+ */
+ NVFBC_TOSYS_GRAB_FLAGS_FORCE_REFRESH = (1 << 1),
+ /*!
+ * Similar to NVFBC_TOSYS_GRAB_FLAGS_NOFLAGS, except that the capture will
+ * not wait if there is already a frame available that the client has
+ * never seen yet.
+ */
+ NVFBC_TOSYS_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2),
+} NVFBC_TOSYS_GRAB_FLAGS;
+
+/*!
+ * Defines parameters for the ::NvFBCToSysSetUp() API call.
+ */
+typedef struct _NVFBC_TOSYS_SETUP_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_TOSYS_SETUP_PARAMS_VER
+ */
+ uint32_t dwVersion;
+ /*!
+ * [in] Desired buffer format.
+ */
+ NVFBC_BUFFER_FORMAT eBufferFormat;
+ /*!
+ * [out] Pointer to a pointer to a buffer in system memory.
+ *
+ * This buffer contains the pixel value of the requested format. Refer to
+ * the description of the buffer formats to understand the memory layout.
+ *
+ * The application does not need to allocate memory for this buffer. It
+ * should not free this buffer either. This buffer is automatically
+ * re-allocated when needed (e.g., when the resolution changes).
+ *
+ * This buffer is allocated by the NvFBC library to the proper size. This
+ * size is returned in the dwByteSize field of the
+ * ::NVFBC_FRAME_GRAB_INFO structure.
+ */
+ void **ppBuffer;
+ /*!
+ * [in] Whether differential maps should be generated.
+ */
+ NVFBC_BOOL bWithDiffMap;
+ /*!
+ * [out] Pointer to a pointer to a buffer in system memory.
+ *
+ * This buffer contains the differential map of two frames. It must be read
+ * as an array of unsigned char. Each unsigned char is either 0 or
+ * non-zero. 0 means that the pixel value at the given location has not
+ * changed since the previous captured frame. Non-zero means that the pixel
+ * value has changed.
+ *
+ * The application does not need to allocate memory for this buffer. It
+ * should not free this buffer either. This buffer is automatically
+ * re-allocated when needed (e.g., when the resolution changes).
+ *
+ * This buffer is allocated by the NvFBC library to the proper size. The
+ * size of the differential map is returned in ::diffMapSize.
+ *
+ * This option is not compatible with the ::NVFBC_BUFFER_FORMAT_YUV420P and
+ * ::NVFBC_BUFFER_FORMAT_YUV444P buffer formats.
+ */
+ void **ppDiffMap;
+ /*!
+ * [in] Scaling factor of the differential maps.
+ *
+ * For example, a scaling factor of 16 means that one pixel of the diffmap
+ * will represent 16x16 pixels of the original frames.
+ *
+ * If any of these 16x16 pixels is different between the current and the
+ * previous frame, then the corresponding pixel in the diffmap will be set
+ * to non-zero.
+ *
+ * The default scaling factor is 1. A dwDiffMapScalingFactor of 0 will be
+ * set to 1.
+ */
+ uint32_t dwDiffMapScalingFactor;
+ /*!
+ * [out] Size of the differential map.
+ *
+ * Only set if bWithDiffMap is set to NVFBC_TRUE.
+ */
+ NVFBC_SIZE diffMapSize;
+} NVFBC_TOSYS_SETUP_PARAMS;
+
+/*!
+ * NVFBC_TOSYS_SETUP_PARAMS structure version.
+ */
+#define NVFBC_TOSYS_SETUP_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_TOSYS_SETUP_PARAMS, 3)
+
+/*!
+ * Defines parameters for the ::NvFBCToSysGrabFrame() API call.
+ */
+typedef struct _NVFBC_TOSYS_GRAB_FRAME_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER
+ */
+ uint32_t dwVersion;
+ /*!
+ * [in] Flags defining the behavior of this frame capture.
+ */
+ uint32_t dwFlags;
+ /*!
+ * [out] Information about the captured frame.
+ *
+ * Can be NULL.
+ */
+ NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo;
+ /*!
+ * [in] Wait timeout in milliseconds.
+ *
+ * When capturing frames with the NVFBC_TOSYS_GRAB_FLAGS_NOFLAGS or
+ * NVFBC_TOSYS_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY flags,
+ * NvFBC will wait for a new frame or mouse move until the below timer
+ * expires.
+ *
+ * When timing out, the last captured frame will be returned. Note that as
+ * long as the NVFBC_TOSYS_GRAB_FLAGS_FORCE_REFRESH flag is not set,
+ * returning an old frame will incur no performance penalty.
+ *
+ * NvFBC clients can use the return value of the grab frame operation to
+ * find out whether a new frame was captured, or the timer expired.
+ *
+ * Note that the behavior of blocking calls is to wait for a new frame
+ * *after* the call has been made. When using timeouts, it is possible
+ * that NvFBC will return a new frame (e.g., it has never been captured
+ * before) even though no new frame was generated after the grab call.
+ *
+ * For the precise definition of what constitutes a new frame, see
+ * ::bIsNewFrame.
+ *
+ * Set to 0 to disable timeouts.
+ */
+ uint32_t dwTimeoutMs;
+} NVFBC_TOSYS_GRAB_FRAME_PARAMS;
+
+/*!
+ * NVFBC_TOSYS_GRAB_FRAME_PARAMS structure version.
+ */
+#define NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_TOSYS_GRAB_FRAME_PARAMS, 2)
+
+/*!
+ * Defines flags that can be used when capturing to a CUDA buffer in video memory.
+ */
+typedef enum
+{
+ /*!
+ * Default, capturing waits for a new frame or mouse move.
+ *
+ * The default behavior of blocking grabs is to wait for a new frame until
+ * after the call was made. But it's possible that there is a frame already
+ * ready that the client hasn't seen.
+ * \see NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY
+ */
+ NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS = 0,
+ /*!
+ * Capturing does not wait for a new frame nor a mouse move.
+ *
+ * It is therefore possible to capture the same frame multiple times.
+ * When this occurs, the dwCurrentFrame parameter of the
+ * NVFBC_FRAME_GRAB_INFO structure is not incremented.
+ */
+ NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT = (1 << 0),
+ /*!
+ * [in] Forces the destination buffer to be refreshed even if the frame
+ * has not changed since previous capture.
+ *
+ * By default, if the captured frame is identical to the previous one, NvFBC
+ * will omit one copy and not update the destination buffer.
+ *
+ * Setting that flag will prevent this behavior. This can be useful e.g.,
+ * if the application has modified the buffer in the meantime.
+ */
+ NVFBC_TOCUDA_GRAB_FLAGS_FORCE_REFRESH = (1 << 1),
+ /*!
+ * Similar to NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS, except that the capture will
+ * not wait if there is already a frame available that the client has
+ * never seen yet.
+ */
+ NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2),
+} NVFBC_TOCUDA_FLAGS;
+
+/*!
+ * Defines parameters for the ::NvFBCToCudaSetUp() API call.
+ */
+typedef struct _NVFBC_TOCUDA_SETUP_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_TOCUDA_SETUP_PARAMS_VER
+ */
+ uint32_t dwVersion;
+ /*!
+ * [in] Desired buffer format.
+ */
+ NVFBC_BUFFER_FORMAT eBufferFormat;
+} NVFBC_TOCUDA_SETUP_PARAMS;
+
+/*!
+ * NVFBC_TOCUDA_SETUP_PARAMS structure version.
+ */
+#define NVFBC_TOCUDA_SETUP_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_TOCUDA_SETUP_PARAMS, 1)
+
+/*!
+ * Defines parameters for the ::NvFBCToCudaGrabFrame() API call.
+ */
+typedef struct _NVFBC_TOCUDA_GRAB_FRAME_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER.
+ */
+ uint32_t dwVersion;
+ /*!
+ * [in] Flags defining the behavior of this frame capture.
+ */
+ uint32_t dwFlags;
+ /*!
+ * [out] Pointer to a ::CUdeviceptr
+ *
+ * The application does not need to allocate memory for this CUDA device.
+ *
+ * The application does need to create its own CUDA context to use this
+ * CUDA device.
+ *
+ * This ::CUdeviceptr will be mapped to a segment in video memory containing
+ * the frame. It is not possible to process a CUDA device while capturing
+ * a new frame. If the application wants to do so, it must copy the CUDA
+ * device using ::cuMemcpyDtoD or ::cuMemcpyDtoH beforehand.
+ */
+ void *pCUDADeviceBuffer;
+ /*!
+ * [out] Information about the captured frame.
+ *
+ * Can be NULL.
+ */
+ NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo;
+ /*!
+ * [in] Wait timeout in milliseconds.
+ *
+ * When capturing frames with the NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS or
+ * NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY flags,
+ * NvFBC will wait for a new frame or mouse move until the below timer
+ * expires.
+ *
+ * When timing out, the last captured frame will be returned. Note that as
+ * long as the NVFBC_TOCUDA_GRAB_FLAGS_FORCE_REFRESH flag is not set,
+ * returning an old frame will incur no performance penalty.
+ *
+ * NvFBC clients can use the return value of the grab frame operation to
+ * find out whether a new frame was captured, or the timer expired.
+ *
+ * Note that the behavior of blocking calls is to wait for a new frame
+ * *after* the call has been made. When using timeouts, it is possible
+ * that NvFBC will return a new frame (e.g., it has never been captured
+ * before) even though no new frame was generated after the grab call.
+ *
+ * For the precise definition of what constitutes a new frame, see
+ * ::bIsNewFrame.
+ *
+ * Set to 0 to disable timeouts.
+ */
+ uint32_t dwTimeoutMs;
+} NVFBC_TOCUDA_GRAB_FRAME_PARAMS;
+
+/*!
+ * NVFBC_TOCUDA_GRAB_FRAME_PARAMS structure version.
+ */
+#define NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_TOCUDA_GRAB_FRAME_PARAMS, 2)
+
+/*!
+ * Defines flags that can be used when capturing to an OpenGL buffer in video memory.
+ */
+typedef enum
+{
+ /*!
+ * Default, capturing waits for a new frame or mouse move.
+ *
+ * The default behavior of blocking grabs is to wait for a new frame until
+ * after the call was made. But it's possible that there is a frame already
+ * ready that the client hasn't seen.
+ * \see NVFBC_TOGL_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY
+ */
+ NVFBC_TOGL_GRAB_FLAGS_NOFLAGS = 0,
+ /*!
+ * Capturing does not wait for a new frame nor a mouse move.
+ *
+ * It is therefore possible to capture the same frame multiple times.
+ * When this occurs, the dwCurrentFrame parameter of the
+ * NVFBC_FRAME_GRAB_INFO structure is not incremented.
+ */
+ NVFBC_TOGL_GRAB_FLAGS_NOWAIT = (1 << 0),
+ /*!
+ * [in] Forces the destination buffer to be refreshed even if the frame
+ * has not changed since previous capture.
+ *
+ * By default, if the captured frame is identical to the previous one, NvFBC
+ * will omit one copy and not update the destination buffer.
+ *
+ * Setting that flag will prevent this behavior. This can be useful e.g.,
+ * if the application has modified the buffer in the meantime.
+ */
+ NVFBC_TOGL_GRAB_FLAGS_FORCE_REFRESH = (1 << 1),
+ /*!
+ * Similar to NVFBC_TOGL_GRAB_FLAGS_NOFLAGS, except that the capture will
+ * not wait if there is already a frame available that the client has
+ * never seen yet.
+ */
+ NVFBC_TOGL_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2),
+} NVFBC_TOGL_FLAGS;
+
+/*!
+ * Maximum number of GL textures that can be used to store frames.
+ */
+#define NVFBC_TOGL_TEXTURES_MAX 2
+
+/*!
+ * Defines parameters for the ::NvFBCToGLSetUp() API call.
+ */
+typedef struct _NVFBC_TOGL_SETUP_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_TOGL_SETUP_PARAMS_VER
+ */
+ uint32_t dwVersion;
+ /*!
+ * [in] Desired buffer format.
+ */
+ NVFBC_BUFFER_FORMAT eBufferFormat;
+ /*!
+ * [in] Whether differential maps should be generated.
+ */
+ NVFBC_BOOL bWithDiffMap;
+ /*!
+ * [out] Pointer to a pointer to a buffer in system memory.
+ *
+ * \see NVFBC_TOSYS_SETUP_PARAMS::ppDiffMap
+ */
+ void **ppDiffMap;
+ /*!
+ * [in] Scaling factor of the differential maps.
+ *
+ * \see NVFBC_TOSYS_SETUP_PARAMS::dwDiffMapScalingFactor
+ */
+ uint32_t dwDiffMapScalingFactor;
+ /*!
+ * [out] List of GL textures that will store the captured frames.
+ *
+ * This array is 0 terminated. The number of textures varies depending on
+ * the capture settings (such as whether diffmaps are enabled).
+ *
+ * An application wishing to interop with, for example, EncodeAPI will need
+ * to register these textures prior to start encoding frames.
+ *
+ * After each frame capture, the texture holding the current frame will be
+ * returned in NVFBC_TOGL_GRAB_FRAME_PARAMS::dwTexture.
+ */
+ uint32_t dwTextures[NVFBC_TOGL_TEXTURES_MAX];
+ /*!
+ * [out] GL target to which the texture should be bound.
+ */
+ uint32_t dwTexTarget;
+ /*!
+ * [out] GL format of the textures.
+ */
+ uint32_t dwTexFormat;
+ /*!
+ * [out] GL type of the textures.
+ */
+ uint32_t dwTexType;
+ /*!
+ * [out] Size of the differential map.
+ *
+ * Only set if bWithDiffMap is set to NVFBC_TRUE.
+ */
+ NVFBC_SIZE diffMapSize;
+} NVFBC_TOGL_SETUP_PARAMS;
+
+/*!
+ * NVFBC_TOGL_SETUP_PARAMS structure version.
+ */
+#define NVFBC_TOGL_SETUP_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_TOGL_SETUP_PARAMS, 2)
+
+/*!
+ * Defines parameters for the ::NvFBCToGLGrabFrame() API call.
+ */
+typedef struct _NVFBC_TOGL_GRAB_FRAME_PARAMS
+{
+ /*!
+ * [in] Must be set to NVFBC_TOGL_GRAB_FRAME_PARAMS_VER.
+ */
+ uint32_t dwVersion;
+ /*!
+ * [in] Flags defining the behavior of this frame capture.
+ */
+ uint32_t dwFlags;
+ /*!
+ * [out] Index of the texture storing the current frame.
+ *
+ * This is an index in the NVFBC_TOGL_SETUP_PARAMS::dwTextures array.
+ */
+ uint32_t dwTextureIndex;
+ /*!
+ * [out] Information about the captured frame.
+ *
+ * Can be NULL.
+ */
+ NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo;
+ /*!
+ * [in] Wait timeout in milliseconds.
+ *
+ * When capturing frames with the NVFBC_TOGL_GRAB_FLAGS_NOFLAGS or
+ * NVFBC_TOGL_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY flags,
+ * NvFBC will wait for a new frame or mouse move until the below timer
+ * expires.
+ *
+ * When timing out, the last captured frame will be returned. Note that as
+ * long as the NVFBC_TOGL_GRAB_FLAGS_FORCE_REFRESH flag is not set,
+ * returning an old frame will incur no performance penalty.
+ *
+ * NvFBC clients can use the return value of the grab frame operation to
+ * find out whether a new frame was captured, or the timer expired.
+ *
+ * Note that the behavior of blocking calls is to wait for a new frame
+ * *after* the call has been made. When using timeouts, it is possible
+ * that NvFBC will return a new frame (e.g., it has never been captured
+ * before) even though no new frame was generated after the grab call.
+ *
+ * For the precise definition of what constitutes a new frame, see
+ * ::bIsNewFrame.
+ *
+ * Set to 0 to disable timeouts.
+ */
+ uint32_t dwTimeoutMs;
+} NVFBC_TOGL_GRAB_FRAME_PARAMS;
+
+/*!
+ * NVFBC_TOGL_GRAB_FRAME_PARAMS structure version.
+ */
+#define NVFBC_TOGL_GRAB_FRAME_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_TOGL_GRAB_FRAME_PARAMS, 2)
+
+/*! @} FBC_STRUCT */
+
+/*!
+ * \defgroup FBC_FUNC API Entry Points
+ *
+ * Entry points are thread-safe and can be called concurrently.
+ *
+ * The locking model includes a global lock that protects session handle
+ * management (\see NvFBCCreateHandle, \see NvFBCDestroyHandle).
+ *
+ * Each NvFBC session uses a local lock to protect other entry points. Note
+ * that in certain cases, a thread can hold the local lock for an undefined
+ * amount of time, such as grabbing a frame using a blocking call.
+ *
+ * Note that a context is associated with each session. NvFBC clients wishing
+ * to share a session between different threads are expected to release and
+ * bind the context appropriately (\see NvFBCBindContext,
+ * \see NvFBCReleaseContext). This is not required when each thread uses its
+ * own NvFBC session.
+ *
+ * @{
+ */
+
+/*!
+ * Gets the last error message that got recorded for a client.
+ *
+ * When NvFBC returns an error, it will save an error message that can be
+ * queried through this API call. Only the last message is saved.
+ * The message and the return code should give enough information about
+ * what went wrong.
+ *
+ * \param [in] sessionHandle
+ * Handle to the NvFBC client.
+ * \return
+ * A NULL terminated error message, or an empty string. Its maximum length
+ * is NVFBC_ERROR_STR_LEN.
+ */
+const char* NVFBCAPI NvFBCGetLastErrorStr(const NVFBC_SESSION_HANDLE sessionHandle);
+
+/*!
+ * \brief Allocates a new handle for an NvFBC client.
+ *
+ * This function allocates a session handle used to identify an FBC client.
+ *
+ * This function implicitly calls NvFBCBindContext().
+ *
+ * \param [out] pSessionHandle
+ * Pointer that will hold the allocated session handle.
+ * \param [in] pParams
+ * ::NVFBC_CREATE_HANDLE_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_PTR \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_OUT_OF_MEMORY \n
+ * ::NVFBC_ERR_MAX_CLIENTS \n
+ * ::NVFBC_ERR_X \n
+ * ::NVFBC_ERR_GLX \n
+ * ::NVFBC_ERR_GL
+ *
+ */
+NVFBCSTATUS NVFBCAPI NvFBCCreateHandle(NVFBC_SESSION_HANDLE *pSessionHandle, NVFBC_CREATE_HANDLE_PARAMS *pParams);
+
+/*!
+ * \brief Destroys the handle of an NvFBC client.
+ *
+ * This function uninitializes an FBC client.
+ *
+ * This function implicitly calls NvFBCReleaseContext().
+ *
+ * After this fucntion returns, it is not possible to use this session handle
+ * for any further API call.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ * \param [in] pParams
+ * ::NVFBC_DESTROY_HANDLE_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_X
+ */
+NVFBCSTATUS NVFBCAPI NvFBCDestroyHandle(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_HANDLE_PARAMS *pParams);
+
+/*!
+ * \brief Gets the current status of the display driver.
+ *
+ * This function queries the display driver for various information.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ * \param [in] pParams
+ * ::NVFBC_GET_STATUS_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_X
+ */
+NVFBCSTATUS NVFBCAPI NvFBCGetStatus(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_GET_STATUS_PARAMS *pParams);
+
+/*!
+ * \brief Binds the FBC context to the calling thread.
+ *
+ * The NvFBC library internally relies on objects that must be bound to a
+ * thread. Such objects are OpenGL contexts and CUDA contexts.
+ *
+ * This function binds these objects to the calling thread.
+ *
+ * The FBC context must be bound to the calling thread for most NvFBC entry
+ * points, otherwise ::NVFBC_ERR_CONTEXT is returned.
+ *
+ * If the FBC context is already bound to a different thread,
+ * ::NVFBC_ERR_CONTEXT is returned. The other thread must release the context
+ * first by calling the ReleaseContext() entry point.
+ *
+ * If the FBC context is already bound to the current thread, this function has
+ * no effects.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ * \param [in] pParams
+ * ::NVFBC_DESTROY_CAPTURE_SESSION_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_X
+ */
+NVFBCSTATUS NVFBCAPI NvFBCBindContext(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_BIND_CONTEXT_PARAMS *pParams);
+
+/*!
+ * \brief Releases the FBC context from the calling thread.
+ *
+ * If the FBC context is bound to a different thread, ::NVFBC_ERR_CONTEXT is
+ * returned.
+ *
+ * If the FBC context is already released, this functino has no effects.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ * \param [in] pParams
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_X
+ */
+NVFBCSTATUS NVFBCAPI NvFBCReleaseContext(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_RELEASE_CONTEXT_PARAMS *pParams);
+
+/*!
+ * \brief Creates a capture session for an FBC client.
+ *
+ * This function starts a capture session of the desired type (system memory,
+ * video memory with CUDA interop, or H.264 compressed frames in system memory).
+ *
+ * Not all types are supported on all systems. Also, it is possible to use
+ * NvFBC without having the CUDA library. In this case, requesting a capture
+ * session of the concerned type will return an error.
+ *
+ * After this function returns, the display driver will start generating frames
+ * that can be captured using the corresponding API call.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ * \param [in] pParams
+ * ::NVFBC_CREATE_CAPTURE_SESSION_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_INVALID_PARAM \n
+ * ::NVFBC_ERR_OUT_OF_MEMORY \n
+ * ::NVFBC_ERR_X \n
+ * ::NVFBC_ERR_GLX \n
+ * ::NVFBC_ERR_GL \n
+ * ::NVFBC_ERR_CUDA \n
+ * ::NVFBC_ERR_MUST_RECREATE \n
+ * ::NVFBC_ERR_INTERNAL
+ */
+NVFBCSTATUS NVFBCAPI NvFBCCreateCaptureSession(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_CREATE_CAPTURE_SESSION_PARAMS *pParams);
+
+/*!
+ * \brief Destroys a capture session for an FBC client.
+ *
+ * This function stops a capture session and frees allocated objects.
+ *
+ * After this function returns, it is possible to create another capture
+ * session using the corresponding API call.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ * \param [in] pParams
+ * ::NVFBC_DESTROY_CAPTURE_SESSION_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_X
+ */
+NVFBCSTATUS NVFBCAPI NvFBCDestroyCaptureSession(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_CAPTURE_SESSION_PARAMS *pParams);
+
+/*!
+ * \brief Sets up a capture to system memory session.
+ *
+ * This function configures how the capture to system memory should behave. It
+ * can be called anytime and several times after the capture session has been
+ * created. However, it must be called at least once prior to start capturing
+ * frames.
+ *
+ * This function allocates the buffer that will contain the captured frame.
+ * The application does not need to free this buffer. The size of this buffer
+ * is returned in the ::NVFBC_FRAME_GRAB_INFO structure.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ * \param [in] pParams
+ * ::NVFBC_TOSYS_SETUP_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_UNSUPPORTED \n
+ * ::NVFBC_ERR_INVALID_PTR \n
+ * ::NVFBC_ERR_INVALID_PARAM \n
+ * ::NVFBC_ERR_OUT_OF_MEMORY \n
+ * ::NVFBC_ERR_X
+ */
+NVFBCSTATUS NVFBCAPI NvFBCToSysSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_SETUP_PARAMS *pParams);
+
+/*!
+ * \brief Captures a frame to a buffer in system memory.
+ *
+ * This function triggers a frame capture to a buffer in system memory that was
+ * registered with the ToSysSetUp() API call.
+ *
+ * Note that it is possible that the resolution of the desktop changes while
+ * capturing frames. This should be transparent for the application.
+ *
+ * When the resolution changes, the capture session is recreated using the same
+ * parameters, and necessary buffers are re-allocated. The frame counter is not
+ * reset.
+ *
+ * An application can detect that the resolution changed by comparing the
+ * dwByteSize member of the ::NVFBC_FRAME_GRAB_INFO against a previous
+ * frame and/or dwWidth and dwHeight.
+ *
+ * During a change of resolution the capture is paused even in asynchronous
+ * mode.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ * \param [in] pParams
+ * ::NVFBC_TOSYS_GRAB_FRAME_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_INVALID_PTR \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_X \n
+ * ::NVFBC_ERR_MUST_RECREATE \n
+ * \see NvFBCCreateCaptureSession \n
+ * \see NvFBCToSysSetUp
+ */
+NVFBCSTATUS NVFBCAPI NvFBCToSysGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_GRAB_FRAME_PARAMS *pParams);
+
+/*!
+ * \brief Sets up a capture to video memory session.
+ *
+ * This function configures how the capture to video memory with CUDA interop
+ * should behave. It can be called anytime and several times after the capture
+ * session has been created. However, it must be called at least once prior
+ * to start capturing frames.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ *
+ * \param [in] pParams
+ * ::NVFBC_TOCUDA_SETUP_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_UNSUPPORTED \n
+ * ::NVFBC_ERR_GL \n
+ * ::NVFBC_ERR_X
+ */
+NVFBCSTATUS NVFBCAPI NvFBCToCudaSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_SETUP_PARAMS *pParams);
+
+/*!
+ * \brief Captures a frame to a CUDA device in video memory.
+ *
+ * This function triggers a frame capture to a CUDA device in video memory.
+ *
+ * Note about changes of resolution: \see NvFBCToSysGrabFrame
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ *
+ * \param [in] pParams
+ * ::NVFBC_TOCUDA_GRAB_FRAME_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_INVALID_PTR \n
+ * ::NVFBC_ERR_CUDA \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_X \n
+ * ::NVFBC_ERR_MUST_RECREATE \n
+ * \see NvFBCCreateCaptureSession \n
+ * \see NvFBCToCudaSetUp
+ */
+NVFBCSTATUS NVFBCAPI NvFBCToCudaGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_GRAB_FRAME_PARAMS *pParams);
+
+/*!
+ * \brief Sets up a capture to OpenGL buffer in video memory session.
+ *
+ * This function configures how the capture to video memory should behave.
+ * It can be called anytime and several times after the capture session has been
+ * created. However, it must be called at least once prior to start capturing
+ * frames.
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ *
+ * \param [in] pParams
+ * ::NVFBC_TOGL_SETUP_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_UNSUPPORTED \n
+ * ::NVFBC_ERR_GL \n
+ * ::NVFBC_ERR_X
+ */
+NVFBCSTATUS NVFBCAPI NvFBCToGLSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_SETUP_PARAMS *pParams);
+
+/*!
+ * \brief Captures a frame to an OpenGL buffer in video memory.
+ *
+ * This function triggers a frame capture to a selected resource in video memory.
+ *
+ * Note about changes of resolution: \see NvFBCToSysGrabFrame
+ *
+ * \param [in] sessionHandle
+ * FBC session handle.
+ *
+ * \param [in] pParams
+ * ::NVFBC_TOGL_GRAB_FRAME_PARAMS
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_HANDLE \n
+ * ::NVFBC_ERR_API_VERSION \n
+ * ::NVFBC_ERR_BAD_REQUEST \n
+ * ::NVFBC_ERR_CONTEXT \n
+ * ::NVFBC_ERR_INVALID_PTR \n
+ * ::NVFBC_ERR_INTERNAL \n
+ * ::NVFBC_ERR_X \n
+ * ::NVFBC_ERR_MUST_RECREATE \n
+ * \see NvFBCCreateCaptureSession \n
+ * \see NvFBCToCudaSetUp
+ */
+NVFBCSTATUS NVFBCAPI NvFBCToGLGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_GRAB_FRAME_PARAMS *pParams);
+
+/*!
+ * \cond FBC_PFN
+ *
+ * Defines API function pointers
+ */
+typedef const char* (NVFBCAPI* PNVFBCGETLASTERRORSTR)(const NVFBC_SESSION_HANDLE sessionHandle);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCCREATEHANDLE)(NVFBC_SESSION_HANDLE *pSessionHandle, NVFBC_CREATE_HANDLE_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCDESTROYHANDLE)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_HANDLE_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCBINDCONTEXT)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_BIND_CONTEXT_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCRELEASECONTEXT)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_RELEASE_CONTEXT_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCGETSTATUS)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_GET_STATUS_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCCREATECAPTURESESSION)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_CREATE_CAPTURE_SESSION_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCDESTROYCAPTURESESSION)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_CAPTURE_SESSION_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOSYSSETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_SETUP_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOSYSGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_GRAB_FRAME_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOCUDASETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_SETUP_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOCUDAGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_GRAB_FRAME_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOGLSETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_SETUP_PARAMS *pParams);
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOGLGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_GRAB_FRAME_PARAMS *pParams);
+
+/// \endcond
+
+/*! @} FBC_FUNC */
+
+/*!
+ * \ingroup FBC_STRUCT
+ *
+ * Structure populated with API function pointers.
+ */
+typedef struct
+{
+ uint32_t dwVersion; //!< [in] Must be set to NVFBC_VERSION.
+ PNVFBCGETLASTERRORSTR nvFBCGetLastErrorStr; //!< [out] Pointer to ::NvFBCGetLastErrorStr().
+ PNVFBCCREATEHANDLE nvFBCCreateHandle; //!< [out] Pointer to ::NvFBCCreateHandle().
+ PNVFBCDESTROYHANDLE nvFBCDestroyHandle; //!< [out] Pointer to ::NvFBCDestroyHandle().
+ PNVFBCGETSTATUS nvFBCGetStatus; //!< [out] Pointer to ::NvFBCGetStatus().
+ PNVFBCCREATECAPTURESESSION nvFBCCreateCaptureSession; //!< [out] Pointer to ::NvFBCCreateCaptureSession().
+ PNVFBCDESTROYCAPTURESESSION nvFBCDestroyCaptureSession; //!< [out] Pointer to ::NvFBCDestroyCaptureSession().
+ PNVFBCTOSYSSETUP nvFBCToSysSetUp; //!< [out] Pointer to ::NvFBCToSysSetUp().
+ PNVFBCTOSYSGRABFRAME nvFBCToSysGrabFrame; //!< [out] Pointer to ::NvFBCToSysGrabFrame().
+ PNVFBCTOCUDASETUP nvFBCToCudaSetUp; //!< [out] Pointer to ::NvFBCToCudaSetUp().
+ PNVFBCTOCUDAGRABFRAME nvFBCToCudaGrabFrame; //!< [out] Pointer to ::NvFBCToCudaGrabFrame().
+ void* pad1; //!< [out] Retired. Do not use.
+ void* pad2; //!< [out] Retired. Do not use.
+ void* pad3; //!< [out] Retired. Do not use.
+ PNVFBCBINDCONTEXT nvFBCBindContext; //!< [out] Pointer to ::NvFBCBindContext().
+ PNVFBCRELEASECONTEXT nvFBCReleaseContext; //!< [out] Pointer to ::NvFBCReleaseContext().
+ void* pad4; //!< [out] Retired. Do not use.
+ void* pad5; //!< [out] Retired. Do not use.
+ void* pad6; //!< [out] Retired. Do not use.
+ void* pad7; //!< [out] Retired. Do not use.
+ PNVFBCTOGLSETUP nvFBCToGLSetUp; //!< [out] Pointer to ::nvFBCToGLSetup().
+ PNVFBCTOGLGRABFRAME nvFBCToGLGrabFrame; //!< [out] Pointer to ::nvFBCToGLGrabFrame().
+} NVFBC_API_FUNCTION_LIST;
+
+/*!
+ * \ingroup FBC_FUNC
+ *
+ * \brief Entry Points to the NvFBC interface.
+ *
+ * Creates an instance of the NvFBC interface, and populates the
+ * pFunctionList with function pointers to the API routines implemented by
+ * the NvFBC interface.
+ *
+ * \param [out] pFunctionList
+ *
+ * \return
+ * ::NVFBC_SUCCESS \n
+ * ::NVFBC_ERR_INVALID_PTR \n
+ * ::NVFBC_ERR_API_VERSION
+ */
+NVFBCSTATUS NVFBCAPI NvFBCCreateInstance(NVFBC_API_FUNCTION_LIST *pFunctionList);
+/*!
+ * \ingroup FBC_FUNC
+ *
+ * Defines function pointer for the ::NvFBCCreateInstance() API call.
+ */
+typedef NVFBCSTATUS (NVFBCAPI* PNVFBCCREATEINSTANCE)(NVFBC_API_FUNCTION_LIST *pFunctionList);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NVFBC_H_
diff --git a/include/NvFBCLibrary.hpp b/include/NvFBCLibrary.hpp
new file mode 100644
index 0000000..a0bf045
--- /dev/null
+++ b/include/NvFBCLibrary.hpp
@@ -0,0 +1,245 @@
+#pragma once
+
+#include "NvFBC.h"
+#include <cuda.h>
+
+#include <dlfcn.h>
+#include <string.h>
+#include <stdio.h>
+
+class NvFBCLibrary {
+public:
+ ~NvFBCLibrary() {
+ if(fbc_handle_created) {
+ NVFBC_DESTROY_CAPTURE_SESSION_PARAMS destroy_capture_params;
+ memset(&destroy_capture_params, 0, sizeof(destroy_capture_params));
+ destroy_capture_params.dwVersion = NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER;
+ nv_fbc_function_list.nvFBCDestroyCaptureSession(nv_fbc_handle, &destroy_capture_params);
+
+ NVFBC_DESTROY_HANDLE_PARAMS destroy_params;
+ memset(&destroy_params, 0, sizeof(destroy_params));
+ destroy_params.dwVersion = NVFBC_DESTROY_HANDLE_PARAMS_VER;
+ nv_fbc_function_list.nvFBCDestroyHandle(nv_fbc_handle, &destroy_params);
+ }
+
+ if(library)
+ dlclose(library);
+ }
+
+ bool load() {
+ if(library)
+ return true;
+
+ dlerror(); // clear
+ void *lib = dlopen("libnvidia-fbc.so.1", RTLD_NOW);
+ if(!lib) {
+ fprintf(stderr, "Error: failed to load libnvidia-fbc.so.1, error: %s\n", dlerror());
+ return false;
+ }
+
+ nv_fbc_create_instance = (PNVFBCCREATEINSTANCE)dlsym(lib, "NvFBCCreateInstance");
+ if(!nv_fbc_create_instance) {
+ fprintf(stderr, "Error: unable to resolve symbol 'NvFBCCreateInstance'\n");
+ dlclose(lib);
+ return false;
+ }
+
+ memset(&nv_fbc_function_list, 0, sizeof(nv_fbc_function_list));
+ nv_fbc_function_list.dwVersion = NVFBC_VERSION;
+ NVFBCSTATUS status = nv_fbc_create_instance(&nv_fbc_function_list);
+ if(status != NVFBC_SUCCESS) {
+ fprintf(stderr, "Error: failed to create NvFBC instance (status: %d)\n", status);
+ dlclose(lib);
+ return false;
+ }
+
+ library = lib;
+ return true;
+ }
+
+ // If |display_to_capture| is "screen", then the entire x11 screen is captured (all displays)
+ bool create(const char *display_to_capture, uint32_t fps, /*out*/ uint32_t *display_width, /*out*/ uint32_t *display_height, uint32_t x = 0, uint32_t y = 0, uint32_t width = 0, uint32_t height = 0) {
+ if(!library || !display_to_capture || !display_width || !display_height || fbc_handle_created)
+ return false;
+
+ const bool capture_region = (x > 0 && y > 0 && width > 0 && height > 0);
+
+ NVFBCSTATUS status;
+ NVFBC_TRACKING_TYPE tracking_type;
+ bool capture_session_created = false;
+ uint32_t output_id = 0;
+ fbc_handle_created = false;
+
+ NVFBC_CREATE_HANDLE_PARAMS create_params;
+ memset(&create_params, 0, sizeof(create_params));
+ create_params.dwVersion = NVFBC_CREATE_HANDLE_PARAMS_VER;
+
+ status = nv_fbc_function_list.nvFBCCreateHandle(&nv_fbc_handle, &create_params);
+ if(status != NVFBC_SUCCESS) {
+ fprintf(stderr, "Error: %s\n", nv_fbc_function_list.nvFBCGetLastErrorStr(nv_fbc_handle));
+ return false;
+ }
+ fbc_handle_created = true;
+
+ NVFBC_GET_STATUS_PARAMS status_params;
+ memset(&status_params, 0, sizeof(status_params));
+ status_params.dwVersion = NVFBC_GET_STATUS_PARAMS_VER;
+
+ status = nv_fbc_function_list.nvFBCGetStatus(nv_fbc_handle, &status_params);
+ if(status != NVFBC_SUCCESS) {
+ fprintf(stderr, "Error: %s\n", nv_fbc_function_list.nvFBCGetLastErrorStr(nv_fbc_handle));
+ goto error_cleanup;
+ }
+
+ if(status_params.bCanCreateNow == NVFBC_FALSE) {
+ fprintf(stderr, "Error: it's not possible to create a capture session on this system\n");
+ goto error_cleanup;
+ }
+
+ tracking_type = strcmp(display_to_capture, "screen") == 0 ? NVFBC_TRACKING_SCREEN : NVFBC_TRACKING_OUTPUT;
+ if(tracking_type == NVFBC_TRACKING_OUTPUT) {
+ if(!status_params.bXRandRAvailable) {
+ fprintf(stderr, "Error: the xrandr extension is not available\n");
+ goto error_cleanup;
+ }
+
+ if(status_params.bInModeset) {
+ fprintf(stderr, "Error: the x server is in modeset, unable to record\n");
+ goto error_cleanup;
+ }
+
+ output_id = get_output_id_from_display_name(status_params.outputs, status_params.dwOutputNum, display_to_capture, display_width, display_height);
+ if(output_id == 0) {
+ fprintf(stderr, "Error: display '%s' not found\n", display_to_capture);
+ goto error_cleanup;
+ }
+ } else {
+ *display_width = status_params.screenSize.w;
+ *display_height = status_params.screenSize.h;
+ }
+
+ NVFBC_CREATE_CAPTURE_SESSION_PARAMS create_capture_params;
+ memset(&create_capture_params, 0, sizeof(create_capture_params));
+ create_capture_params.dwVersion = NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER;
+ create_capture_params.eCaptureType = NVFBC_CAPTURE_SHARED_CUDA;
+ create_capture_params.bWithCursor = NVFBC_TRUE; // This will need to be disabled when using bAllowDirectCapture
+ if(capture_region) {
+ create_capture_params.captureBox = { x, y, width, height };
+ *display_width = width;
+ *display_height = height;
+ }
+ create_capture_params.eTrackingType = tracking_type;
+ //create_capture_params.dwSamplingRateMs = 1000 / fps;
+ if(tracking_type == NVFBC_TRACKING_OUTPUT)
+ create_capture_params.dwOutputId = output_id;
+
+ // TODO: Use create_capture_params.bAllowDirectCapture and create_capture_params.bPushModel
+ status = nv_fbc_function_list.nvFBCCreateCaptureSession(nv_fbc_handle, &create_capture_params);
+ if(status != NVFBC_SUCCESS) {
+ fprintf(stderr, "Error: %s\n", nv_fbc_function_list.nvFBCGetLastErrorStr(nv_fbc_handle));
+ goto error_cleanup;
+ }
+ capture_session_created = true;
+
+ NVFBC_TOCUDA_SETUP_PARAMS setup_params;
+ memset(&setup_params, 0, sizeof(setup_params));
+ setup_params.dwVersion = NVFBC_TOCUDA_SETUP_PARAMS_VER;
+ setup_params.eBufferFormat = NVFBC_BUFFER_FORMAT_BGRA;
+
+ status = nv_fbc_function_list.nvFBCToCudaSetUp(nv_fbc_handle, &setup_params);
+ if(status != NVFBC_SUCCESS) {
+ fprintf(stderr, "Error: %s\n", nv_fbc_function_list.nvFBCGetLastErrorStr(nv_fbc_handle));
+ goto error_cleanup;
+ }
+
+ return true;
+
+ error_cleanup:
+ if(fbc_handle_created) {
+ if(capture_session_created) {
+ NVFBC_DESTROY_CAPTURE_SESSION_PARAMS destroy_capture_params;
+ memset(&destroy_capture_params, 0, sizeof(destroy_capture_params));
+ destroy_capture_params.dwVersion = NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER;
+ nv_fbc_function_list.nvFBCDestroyCaptureSession(nv_fbc_handle, &destroy_capture_params);
+ }
+
+ NVFBC_DESTROY_HANDLE_PARAMS destroy_params;
+ memset(&destroy_params, 0, sizeof(destroy_params));
+ destroy_params.dwVersion = NVFBC_DESTROY_HANDLE_PARAMS_VER;
+ nv_fbc_function_list.nvFBCDestroyHandle(nv_fbc_handle, &destroy_params);
+ fbc_handle_created = false;
+ }
+ output_id = 0;
+ return false;
+ }
+
+ bool capture(/*out*/ CUdeviceptr *cu_device_ptr, uint32_t *byte_size) {
+ if(!library || !fbc_handle_created || !cu_device_ptr || !byte_size)
+ return false;
+
+ NVFBCSTATUS status;
+ NVFBC_FRAME_GRAB_INFO frame_info;
+ memset(&frame_info, 0, sizeof(frame_info));
+
+ NVFBC_TOCUDA_GRAB_FRAME_PARAMS grab_params;
+ memset(&grab_params, 0, sizeof(grab_params));
+ grab_params.dwVersion = NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER;
+ grab_params.dwFlags = NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT;
+ grab_params.pFrameGrabInfo = &frame_info;
+ grab_params.pCUDADeviceBuffer = cu_device_ptr;
+
+ status = nv_fbc_function_list.nvFBCToCudaGrabFrame(nv_fbc_handle, &grab_params);
+ if(status != NVFBC_SUCCESS) {
+ fprintf(stderr, "Error: %s\n", nv_fbc_function_list.nvFBCGetLastErrorStr(nv_fbc_handle));
+ return false;
+ }
+
+ *byte_size = frame_info.dwByteSize;
+ // TODO: Check bIsNewFrame
+ // TODO: Check dwWidth and dwHeight and update size in video output in ffmpeg. This can happen when xrandr is used to change monitor resolution
+
+ return true;
+ }
+private:
+ static char to_upper(char c) {
+ if(c >= 'a' && c <= 'z')
+ return c - 32;
+ else
+ return c;
+ }
+
+ static bool strcase_equals(const char *str1, const char *str2) {
+ for(;;) {
+ char c1 = to_upper(*str1);
+ char c2 = to_upper(*str2);
+ if(c1 != c2)
+ return false;
+ if(c1 == '\0' || c2 == '\0')
+ return true;
+ ++str1;
+ ++str2;
+ }
+ }
+
+ // Returns 0 on failure
+ uint32_t get_output_id_from_display_name(NVFBC_RANDR_OUTPUT_INFO *outputs, uint32_t num_outputs, const char *display_name, uint32_t *display_width, uint32_t *display_height) {
+ if(!outputs)
+ return 0;
+
+ for(uint32_t i = 0; i < num_outputs; ++i) {
+ if(strcase_equals(outputs[i].name, display_name)) {
+ *display_width = outputs[i].trackedBox.w;
+ *display_height = outputs[i].trackedBox.h;
+ return outputs[i].dwId;
+ }
+ }
+
+ return 0;
+ }
+private:
+ void *library = nullptr;
+ PNVFBCCREATEINSTANCE nv_fbc_create_instance = nullptr;
+ NVFBC_API_FUNCTION_LIST nv_fbc_function_list;
+ NVFBC_SESSION_HANDLE nv_fbc_handle;
+ bool fbc_handle_created = false;
+}; \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 782e329..5bc69b8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -52,6 +52,8 @@ extern "C" {
#include <libavutil/hwcontext.h>
}
+#include "../include/NvFBCLibrary.hpp"
+
#include <deque>
//#include <CL/cl.h>
@@ -397,12 +399,13 @@ static AVStream *add_audio_stream(AVFormatContext *av_format_context, AVCodec **
static AVStream *add_video_stream(AVFormatContext *av_format_context, AVCodec **codec,
VideoQuality video_quality,
- const WindowPixmap &window_pixmap,
+ int texture_width, int texture_height,
int fps) {
//*codec = avcodec_find_encoder(codec_id);
- *codec = avcodec_find_encoder_by_name("h264_nvenc");
+ bool using_hevc = true;
+ *codec = avcodec_find_encoder_by_name("hevc_nvenc");
if (!*codec) {
- *codec = avcodec_find_encoder_by_name("nvenc_h264");
+ *codec = avcodec_find_encoder_by_name("nvenc_hevc");
}
if (!*codec) {
fprintf(
@@ -425,8 +428,8 @@ static AVStream *add_video_stream(AVFormatContext *av_format_context, AVCodec **
assert((*codec)->type == AVMEDIA_TYPE_VIDEO);
codec_context->codec_id = (*codec)->id;
fprintf(stderr, "codec id: %d\n", (*codec)->id);
- codec_context->width = window_pixmap.texture_width & ~1;
- codec_context->height = window_pixmap.texture_height & ~1;
+ codec_context->width = texture_width & ~1;
+ codec_context->height = texture_height & ~1;
codec_context->bit_rate = 7500000 + (codec_context->width * codec_context->height) / 2;
// Timebase: This is the fundamental unit of time (in seconds) in terms
// of which frame timestamps are represented. For fixed-fps content,
@@ -439,7 +442,7 @@ static AVStream *add_video_stream(AVFormatContext *av_format_context, AVCodec **
codec_context->sample_aspect_ratio.num = 0;
codec_context->sample_aspect_ratio.den = 0;
codec_context->gop_size = fps * 2;
- codec_context->max_b_frames = 2;
+ codec_context->max_b_frames = using_hevc ? 0 : 2;
codec_context->pix_fmt = AV_PIX_FMT_CUDA;
codec_context->color_range = AVCOL_RANGE_JPEG;
switch(video_quality) {
@@ -577,24 +580,26 @@ static void open_video(AVCodec *codec, AVStream *stream,
exit(1);
}
- CUresult res;
- CUcontext old_ctx;
- res = cuCtxPopCurrent(&old_ctx);
- res = cuCtxPushCurrent(*cuda_context);
- res = cuGraphicsGLRegisterImage(
- cuda_graphics_resource, window_pixmap.target_texture_id, GL_TEXTURE_2D,
- CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY);
- // cuGraphicsUnregisterResource(*cuda_graphics_resource);
- if (res != CUDA_SUCCESS) {
- const char *err_str;
- cuGetErrorString(res, &err_str);
- fprintf(stderr,
- "Error: cuGraphicsGLRegisterImage failed, error %s, texture "
- "id: %u\n",
- err_str, window_pixmap.target_texture_id);
- exit(1);
+ if(window_pixmap.target_texture_id != 0) {
+ CUresult res;
+ CUcontext old_ctx;
+ res = cuCtxPopCurrent(&old_ctx);
+ res = cuCtxPushCurrent(*cuda_context);
+ res = cuGraphicsGLRegisterImage(
+ cuda_graphics_resource, window_pixmap.target_texture_id, GL_TEXTURE_2D,
+ CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY);
+ // cuGraphicsUnregisterResource(*cuda_graphics_resource);
+ if (res != CUDA_SUCCESS) {
+ const char *err_str;
+ cuGetErrorString(res, &err_str);
+ fprintf(stderr,
+ "Error: cuGraphicsGLRegisterImage failed, error %s, texture "
+ "id: %u\n",
+ err_str, window_pixmap.target_texture_id);
+ exit(1);
+ }
+ res = cuCtxPopCurrent(&old_ctx);
}
- res = cuCtxPopCurrent(&old_ctx);
}
static void close_video(AVStream *video_stream, AVFrame *frame) {
@@ -605,9 +610,10 @@ static void close_video(AVStream *video_stream, AVFrame *frame) {
static void usage() {
fprintf(stderr, "usage: gpu-screen-recorder -w <window_id> -c <container_format> -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-o <output_file>]\n");
fprintf(stderr, "OPTIONS:\n");
- fprintf(stderr, " -w Window to record.\n");
+ fprintf(stderr, " -w Window to record or a display or \"screen\". The display is the display name in xrandr and if \"screen\" is selected then all displays are recorded and they are recorded in h265 (aka hevc). Recording a display requires a gpu with NvFBC support.\n");
+ //fprintf(stderr, " -s The screen region to capture in format WxH+X+Y. This is only applicable when -w is a display or \"screen\". Optional, the entire window/display/screen is recorded by default.\n");
fprintf(stderr, " -c Container format for output file, for example mp4, or flv.\n");
- fprintf(stderr, " -f Framerate to record at.\n");
+ fprintf(stderr, " -f Framerate to record at. Clamped to [1,500].\n");
fprintf(stderr, " -a Audio device to record from (pulse audio device). Optional, disabled by default.\n");
fprintf(stderr, " -q Video quality. Should either be 'medium', 'high' or 'ultra'. Optional, set to 'medium' be default.\n");
fprintf(stderr, " -r Replay buffer size in seconds. If this is set, then only the last seconds as set by this option will be stored"
@@ -628,17 +634,39 @@ struct Arg {
bool optional;
};
+static bool is_hex_num(char c) {
+ return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9');
+}
+
+static bool contains_non_hex_number(const char *str) {
+ size_t len = strlen(str);
+ if(len >= 2 && memcmp(str, "0x", 2) == 0) {
+ str += 2;
+ len -= 2;
+ }
+
+ for(size_t i = 0; i < len; ++i) {
+ char c = str[i];
+ if(c == '\0')
+ return false;
+ if(!is_hex_num(c))
+ return true;
+ }
+ return false;
+}
+
int main(int argc, char **argv) {
signal(SIGINT, int_handler);
std::map<std::string, Arg> args = {
{ "-w", Arg { nullptr, false } },
+ //{ "-s", Arg { nullptr, true } },
{ "-c", Arg { nullptr, false } },
{ "-f", Arg { nullptr, false } },
{ "-a", Arg { nullptr, true } },
{ "-q", Arg { nullptr, true } },
{ "-o", Arg { nullptr, true } },
- { "-r", Arg { nullptr, true} }
+ { "-r", Arg { nullptr, true } }
};
for(int i = 1; i < argc - 1; i += 2) {
@@ -657,13 +685,30 @@ int main(int argc, char **argv) {
}
}
- Window src_window_id = strtol(args["-w"].value, nullptr, 0);
+ uint32_t region_x = 0;
+ uint32_t region_y = 0;
+ uint32_t region_width = 0;
+ uint32_t region_height = 0;
+
+ /*
+ TODO: Fix this. Doesn't work for some reason
+ const char *screen_region = args["-s"].value;
+ if(screen_region) {
+ if(sscanf(screen_region, "%ux%u+%u+%u", &region_x, &region_y, &region_width, &region_height) != 4) {
+ fprintf(stderr, "Invalid value for -s '%s', expected a value in format WxH+X+Y\n", screen_region);
+ return 1;
+ }
+ }
+ */
+
const char *container_format = args["-c"].value;
int fps = atoi(args["-f"].value);
- if(fps <= 0 || fps > 255) {
- fprintf(stderr, "invalid fps argument: %s\n", args["-f"].value);
+ if(fps == 0) {
+ fprintf(stderr, "Invalid fps argument: %s\n", args["-f"].value);
return 1;
}
+ if(fps > 500)
+ fps = 500;
const char *quality_str = args["-q"].value;
if(!quality_str)
@@ -681,12 +726,6 @@ int main(int argc, char **argv) {
usage();
}
- const char *filename = args["-o"].value;
- if(!filename)
- filename = "/dev/stdout";
-
- const double target_fps = 1.0 / (double)fps;
-
int replay_buffer_size_secs = -1;
const char *replay_buffer_size_secs_str = args["-r"].value;
if(replay_buffer_size_secs_str) {
@@ -698,70 +737,133 @@ int main(int argc, char **argv) {
replay_buffer_size_secs += 5; // Add a few seconds to account of lost packets because of non-keyframe packets skipped
}
- Display *dpy = XOpenDisplay(nullptr);
- if (!dpy) {
- fprintf(stderr, "Error: Failed to open display\n");
- return 1;
+ CUresult res;
+
+ res = cuInit(0);
+ if(res != CUDA_SUCCESS) {
+ fprintf(stderr, "Error: cuInit failed (result: %d)\n", res);
+ return {};
}
- bool has_name_pixmap = x11_supports_composite_named_window_pixmap(dpy);
- if (!has_name_pixmap) {
- fprintf(stderr, "Error: XCompositeNameWindowPixmap is not supported by "
- "your X11 server\n");
+ CUdevice cu_dev;
+ res = cuDeviceGet(&cu_dev, 0);
+ if(res != CUDA_SUCCESS) {
+ fprintf(stderr, "Unable to get CUDA device (result: %d)\n", res);
return 1;
}
- XWindowAttributes attr;
- if (!XGetWindowAttributes(dpy, src_window_id, &attr)) {
- fprintf(stderr, "Error: Invalid window id: %lu\n", src_window_id);
+ CUcontext cu_ctx;
+ res = cuCtxCreate_v2(&cu_ctx, CU_CTX_SCHED_AUTO, cu_dev);
+ if(res != CUDA_SUCCESS) {
+ fprintf(stderr, "Unable to create CUDA context (result: %d)\n", res);
return 1;
}
- XCompositeRedirectWindow(dpy, src_window_id, CompositeRedirectAutomatic);
+ uint32_t window_width = 0;
+ uint32_t window_height = 0;
- // glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read,
- // GLXContext ctx)
- if (!glfwInit()) {
- fprintf(stderr, "Error: Failed to initialize glfw\n");
- return 1;
- }
+ NvFBCLibrary nv_fbc_library;
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
- glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
- glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
+ const char *window_str = args["-w"].value;
+ Window src_window_id = None;
+ if(contains_non_hex_number(window_str)) {
+ if(!nv_fbc_library.load())
+ return 1;
- GLFWwindow *window = glfwCreateWindow(1, 1, "gpu-screen-recorder", nullptr, nullptr);
- if (!window) {
- fprintf(stderr, "Error: Failed to create glfw window\n");
- glfwTerminate();
- return 1;
+ if(!nv_fbc_library.create(window_str, fps, &window_width, &window_height, region_x, region_y, region_width, region_height))
+ return 1;
+ } else {
+ src_window_id = strtol(window_str, nullptr, 0);
+ if(src_window_id == None && errno == EINVAL) {
+ fprintf(stderr, "Invalid window number %s\n", window_str);
+ usage();
+ }
}
- glfwMakeContextCurrent(window);
- glfwSwapInterval(0);
- glfwHideWindow(window);
-
-//#if defined(DEBUG)
- XSetErrorHandler(x11_error_handler);
- XSetIOErrorHandler(x11_io_error_handler);
-//#endif
+ const char *filename = args["-o"].value;
+ if(!filename)
+ filename = "/dev/stdout";
- glewExperimental = GL_TRUE;
- GLenum nGlewError = glewInit();
- if (nGlewError != GLEW_OK) {
- fprintf(stderr, "%s - Error initializing GLEW! %s\n", __FUNCTION__,
- glewGetErrorString(nGlewError));
- return 1;
- }
- glGetError(); // to clear the error caused deep in GLEW
+ const double target_fps = 1.0 / (double)fps;
WindowPixmap window_pixmap;
- if (!recreate_window_pixmap(dpy, src_window_id, window_pixmap)) {
- fprintf(stderr, "Error: Failed to create glx pixmap for window: %lu\n",
- src_window_id);
- return 1;
+ Display *dpy = nullptr;
+ GLFWwindow *window = nullptr;
+ if(src_window_id) {
+ dpy = XOpenDisplay(nullptr);
+ if (!dpy) {
+ fprintf(stderr, "Error: Failed to open display\n");
+ return 1;
+ }
+
+ bool has_name_pixmap = x11_supports_composite_named_window_pixmap(dpy);
+ if (!has_name_pixmap) {
+ fprintf(stderr, "Error: XCompositeNameWindowPixmap is not supported by "
+ "your X11 server\n");
+ return 1;
+ }
+
+ XWindowAttributes attr;
+ if (!XGetWindowAttributes(dpy, src_window_id, &attr)) {
+ fprintf(stderr, "Error: Invalid window id: %lu\n", src_window_id);
+ return 1;
+ }
+
+ XCompositeRedirectWindow(dpy, src_window_id, CompositeRedirectAutomatic);
+
+ // glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read,
+ // GLXContext ctx)
+ if (!glfwInit()) {
+ fprintf(stderr, "Error: Failed to initialize glfw\n");
+ return 1;
+ }
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
+
+ window = glfwCreateWindow(1, 1, "gpu-screen-recorder", nullptr, nullptr);
+ if (!window) {
+ fprintf(stderr, "Error: Failed to create glfw window\n");
+ glfwTerminate();
+ return 1;
+ }
+
+ glfwMakeContextCurrent(window);
+ glfwSwapInterval(0);
+ glfwHideWindow(window);
+
+ //#if defined(DEBUG)
+ XSetErrorHandler(x11_error_handler);
+ XSetIOErrorHandler(x11_io_error_handler);
+ //#endif
+
+ glewExperimental = GL_TRUE;
+ GLenum nGlewError = glewInit();
+ if (nGlewError != GLEW_OK) {
+ fprintf(stderr, "%s - Error initializing GLEW! %s\n", __FUNCTION__,
+ glewGetErrorString(nGlewError));
+ return 1;
+ }
+ glGetError(); // to clear the error caused deep in GLEW
+
+ if (!recreate_window_pixmap(dpy, src_window_id, window_pixmap)) {
+ fprintf(stderr, "Error: Failed to create glx pixmap for window: %lu\n",
+ src_window_id);
+ return 1;
+ }
+ } else {
+ window_pixmap.texture_id = 0;
+ window_pixmap.target_texture_id = 0;
+ window_pixmap.texture_width = window_width;
+ window_pixmap.texture_height = window_height;
+
+ if (!glfwInit()) {
+ fprintf(stderr, "Error: Failed to initialize glfw\n");
+ return 1;
+ }
}
// Video start
@@ -780,8 +882,7 @@ int main(int argc, char **argv) {
AVCodec *video_codec;
AVStream *video_stream =
- add_video_stream(av_format_context, &video_codec, quality,
- window_pixmap, fps);
+ add_video_stream(av_format_context, &video_codec, quality, window_pixmap.texture_width, window_pixmap.texture_height, fps);
if (!video_stream) {
fprintf(stderr, "Error: Failed to create video stream\n");
return 1;
@@ -795,11 +896,6 @@ int main(int argc, char **argv) {
return 1;
}
- if (cuInit(0) < 0) {
- fprintf(stderr, "Error: cuInit failed\n");
- return {};
- }
-
AVBufferRef *device_ctx;
CUgraphicsResource cuda_graphics_resource;
open_video(video_codec, video_stream, window_pixmap, &device_ctx,
@@ -841,7 +937,8 @@ int main(int argc, char **argv) {
// av_frame_free(&rgb_frame);
// avcodec_close(av_codec_context);
- XSelectInput(dpy, src_window_id, StructureNotifyMask);
+ if(dpy)
+ XSelectInput(dpy, src_window_id, StructureNotifyMask);
/*
int damage_event;
@@ -857,20 +954,21 @@ int main(int argc, char **argv) {
int frame_count = 0;
- CUresult res;
CUcontext old_ctx;
- res = cuCtxPopCurrent(&old_ctx);
- res = cuCtxPushCurrent(*cuda_context);
-
- // Get texture
- res = cuGraphicsResourceSetMapFlags(
- cuda_graphics_resource, CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
- res = cuGraphicsMapResources(1, &cuda_graphics_resource, 0);
-
- // Map texture to cuda array
CUarray mapped_array;
- res = cuGraphicsSubResourceGetMappedArray(&mapped_array,
- cuda_graphics_resource, 0, 0);
+ if(src_window_id) {
+ res = cuCtxPopCurrent(&old_ctx);
+ res = cuCtxPushCurrent(*cuda_context);
+
+ // Get texture
+ res = cuGraphicsResourceSetMapFlags(
+ cuda_graphics_resource, CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
+ res = cuGraphicsMapResources(1, &cuda_graphics_resource, 0);
+
+ // Map texture to cuda array
+ res = cuGraphicsSubResourceGetMappedArray(&mapped_array,
+ cuda_graphics_resource, 0, 0);
+ }
// Release texture
// res = cuGraphicsUnmapResources(1, &cuda_graphics_resource, 0);
@@ -896,11 +994,12 @@ int main(int argc, char **argv) {
exit(1);
}
- XWindowAttributes xwa;
- XGetWindowAttributes(dpy, src_window_id, &xwa);
- int window_width = xwa.width;
- int window_height = xwa.height;
-
+ if(dpy) {
+ XWindowAttributes xwa;
+ XGetWindowAttributes(dpy, src_window_id, &xwa);
+ window_width = xwa.width;
+ window_height = xwa.height;
+ }
int original_window_width = window_width;
int original_window_height = window_height;
@@ -999,67 +1098,70 @@ int main(int argc, char **argv) {
while (running) {
double frame_start = glfwGetTime();
glfwPollEvents();
-
- glClear(GL_COLOR_BUFFER_BIT);
- if (XCheckTypedWindowEvent(dpy, src_window_id, ConfigureNotify, &e) && e.xconfigure.window == src_window_id) {
- // Window resize
- if(e.xconfigure.width != window_width || e.xconfigure.height != window_height) {
- window_width = e.xconfigure.width;
- window_height = e.xconfigure.height;
- window_resize_timer = glfwGetTime();
- window_resized = true;
- }
- }
+ if(window)
+ glClear(GL_COLOR_BUFFER_BIT);
redraw = true;
- const double window_resize_timeout = 1.0; // 1 second
- if(window_resized && glfwGetTime() - window_resize_timer >= window_resize_timeout) {
- window_resized = false;
- fprintf(stderr, "Resize window!\n");
- recreate_window_pixmap(dpy, src_window_id, window_pixmap);
- // Resolution must be a multiple of two
- //video_stream->codec->width = window_pixmap.texture_width & ~1;
- //video_stream->codec->height = window_pixmap.texture_height & ~1;
-
- cuGraphicsUnregisterResource(cuda_graphics_resource);
- res = cuGraphicsGLRegisterImage(
- &cuda_graphics_resource, window_pixmap.target_texture_id, GL_TEXTURE_2D,
- CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY);
- if (res != CUDA_SUCCESS) {
- const char *err_str;
- cuGetErrorString(res, &err_str);
- fprintf(stderr,
- "Error: cuGraphicsGLRegisterImage failed, error %s, texture "
- "id: %u\n",
- err_str, window_pixmap.target_texture_id);
- running = false;
- break;
+ if(src_window_id) {
+ if (XCheckTypedWindowEvent(dpy, src_window_id, ConfigureNotify, &e) && e.xconfigure.window == src_window_id) {
+ // Window resize
+ if(e.xconfigure.width != window_width || e.xconfigure.height != window_height) {
+ window_width = e.xconfigure.width;
+ window_height = e.xconfigure.height;
+ window_resize_timer = glfwGetTime();
+ window_resized = true;
+ }
}
- res = cuGraphicsResourceSetMapFlags(
- cuda_graphics_resource, CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
- res = cuGraphicsMapResources(1, &cuda_graphics_resource, 0);
- res = cuGraphicsSubResourceGetMappedArray(&mapped_array, cuda_graphics_resource, 0, 0);
+ const double window_resize_timeout = 1.0; // 1 second
+ if(window_resized && glfwGetTime() - window_resize_timer >= window_resize_timeout) {
+ window_resized = false;
+ fprintf(stderr, "Resize window!\n");
+ recreate_window_pixmap(dpy, src_window_id, window_pixmap);
+ // Resolution must be a multiple of two
+ //video_stream->codec->width = window_pixmap.texture_width & ~1;
+ //video_stream->codec->height = window_pixmap.texture_height & ~1;
+
+ cuGraphicsUnregisterResource(cuda_graphics_resource);
+ res = cuGraphicsGLRegisterImage(
+ &cuda_graphics_resource, window_pixmap.target_texture_id, GL_TEXTURE_2D,
+ CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY);
+ if (res != CUDA_SUCCESS) {
+ const char *err_str;
+ cuGetErrorString(res, &err_str);
+ fprintf(stderr,
+ "Error: cuGraphicsGLRegisterImage failed, error %s, texture "
+ "id: %u\n",
+ err_str, window_pixmap.target_texture_id);
+ running = false;
+ break;
+ }
- av_frame_unref(frame);
- if (av_hwframe_get_buffer(video_stream->codec->hw_frames_ctx, frame, 0) < 0) {
- fprintf(stderr, "Error: av_hwframe_get_buffer failed\n");
- running = false;
- break;
- }
+ res = cuGraphicsResourceSetMapFlags(
+ cuda_graphics_resource, CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
+ res = cuGraphicsMapResources(1, &cuda_graphics_resource, 0);
+ res = cuGraphicsSubResourceGetMappedArray(&mapped_array, cuda_graphics_resource, 0, 0);
- frame->pts = frame_count;
+ av_frame_unref(frame);
+ if (av_hwframe_get_buffer(video_stream->codec->hw_frames_ctx, frame, 0) < 0) {
+ fprintf(stderr, "Error: av_hwframe_get_buffer failed\n");
+ running = false;
+ break;
+ }
- if(window_width < original_window_width)
- frame->width = window_pixmap.texture_width & ~1;
- else
- frame->width = original_window_width;
+ frame->pts = frame_count;
- if(window_height < original_window_height)
- frame->height = window_pixmap.texture_height & ~1;
- else
- frame->height = original_window_height;
+ if(window_width < original_window_width)
+ frame->width = window_pixmap.texture_width & ~1;
+ else
+ frame->width = original_window_width;
+
+ if(window_height < original_window_height)
+ frame->height = window_pixmap.texture_height & ~1;
+ else
+ frame->height = original_window_height;
+ }
}
++fps_counter;
@@ -1078,33 +1180,64 @@ int main(int argc, char **argv) {
if (frame_time_overflow >= 0.0) {
frame_timer_start = time_now - frame_time_overflow;
+ bool frame_captured = true;
if(redraw) {
redraw = false;
- // TODO: Use a framebuffer instead. glCopyImageSubData requires
- // opengl 4.2
- glCopyImageSubData(
- window_pixmap.texture_id, GL_TEXTURE_2D, 0, 0, 0, 0,
- window_pixmap.target_texture_id, GL_TEXTURE_2D, 0, 0, 0, 0,
- window_pixmap.texture_width, window_pixmap.texture_height, 1);
- glfwSwapBuffers(window);
- // int err = glGetError();
- // fprintf(stderr, "error: %d\n", err);
-
- CUDA_MEMCPY2D memcpy_struct;
- memcpy_struct.srcXInBytes = 0;
- memcpy_struct.srcY = 0;
- memcpy_struct.srcMemoryType = CUmemorytype::CU_MEMORYTYPE_ARRAY;
-
- memcpy_struct.dstXInBytes = 0;
- memcpy_struct.dstY = 0;
- memcpy_struct.dstMemoryType = CUmemorytype::CU_MEMORYTYPE_DEVICE;
-
- memcpy_struct.srcArray = mapped_array;
- memcpy_struct.dstDevice = (CUdeviceptr)frame->data[0];
- memcpy_struct.dstPitch = frame->linesize[0];
- memcpy_struct.WidthInBytes = frame->width * 4;
- memcpy_struct.Height = frame->height;
- cuMemcpy2D(&memcpy_struct);
+ if(src_window_id) {
+ // TODO: Use a framebuffer instead. glCopyImageSubData requires
+ // opengl 4.2
+ glCopyImageSubData(
+ window_pixmap.texture_id, GL_TEXTURE_2D, 0, 0, 0, 0,
+ window_pixmap.target_texture_id, GL_TEXTURE_2D, 0, 0, 0, 0,
+ window_pixmap.texture_width, window_pixmap.texture_height, 1);
+ glfwSwapBuffers(window);
+ // int err = glGetError();
+ // fprintf(stderr, "error: %d\n", err);
+
+ CUDA_MEMCPY2D memcpy_struct;
+ memcpy_struct.srcXInBytes = 0;
+ memcpy_struct.srcY = 0;
+ memcpy_struct.srcMemoryType = CUmemorytype::CU_MEMORYTYPE_ARRAY;
+
+ memcpy_struct.dstXInBytes = 0;
+ memcpy_struct.dstY = 0;
+ memcpy_struct.dstMemoryType = CUmemorytype::CU_MEMORYTYPE_DEVICE;
+
+ memcpy_struct.srcArray = mapped_array;
+ memcpy_struct.dstDevice = (CUdeviceptr)frame->data[0];
+ memcpy_struct.dstPitch = frame->linesize[0];
+ memcpy_struct.WidthInBytes = frame->width * 4;
+ memcpy_struct.Height = frame->height;
+ cuMemcpy2D(&memcpy_struct);
+
+ frame_captured = true;
+ } else {
+ uint32_t byte_size;
+ CUdeviceptr src_cu_device_ptr;
+ frame_captured = nv_fbc_library.capture(&src_cu_device_ptr, &byte_size);
+ if(frame_captured) {
+ // TODO: Is it possible to bypass this copy?
+ /*
+ CUDA_MEMCPY2D memcpy_struct;
+ memcpy_struct.srcXInBytes = 0;
+ memcpy_struct.srcY = 0;
+ memcpy_struct.srcMemoryType = CUmemorytype::CU_MEMORYTYPE_DEVICE;
+
+ memcpy_struct.dstXInBytes = 0;
+ memcpy_struct.dstY = 0;
+ memcpy_struct.dstMemoryType = CUmemorytype::CU_MEMORYTYPE_DEVICE;
+
+ memcpy_struct.srcDevice = src_cu_device_ptr;
+ memcpy_struct.dstDevice = (CUdeviceptr)frame->data[0];
+ memcpy_struct.dstPitch = frame->linesize[0];
+ memcpy_struct.WidthInBytes = frame->width * 4;
+ memcpy_struct.Height = frame->height;
+ cuMemcpy2D(&memcpy_struct);
+ */
+ cuMemcpyDtoD((CUdeviceptr)frame->data[0], src_cu_device_ptr, byte_size);
+ //frame->data[0] = (uint8_t*)src_cu_device_ptr;
+ }
+ }
// res = cuCtxPopCurrent(&old_ctx);
}
@@ -1196,6 +1329,8 @@ int main(int argc, char **argv) {
// avformat_free_context(av_format_context);
// cleanup_window_pixmap(dpy, window_pixmap);
- XCompositeUnredirectWindow(dpy, src_window_id, CompositeRedirectAutomatic);
- XCloseDisplay(dpy);
+ if(dpy) {
+ XCompositeUnredirectWindow(dpy, src_window_id, CompositeRedirectAutomatic);
+ XCloseDisplay(dpy);
+ }
}