13#include <cuda_runtime_api.h>
14#include <opencv2/cudaarithm.hpp>
16#include <nppi_color_conversion.h>
17#include <nppi_data_exchange_and_initialization.h>
25 if (rational.num <= 0 || rational.den <= 0) {
28 return av_q2d(rational);
32 if (source ==
nullptr || source->data[0] ==
nullptr || source->data[1] ==
nullptr) {
35 const bool p010 = source->format == AV_PIX_FMT_P010LE;
36 if (!p010 && (source->format != AV_PIX_FMT_YUV420P10LE || source->data[2] ==
nullptr)) {
40 width = source->width;
41 height = source->height;
43 if (width <= 0 || height <= 0) {
46 rgba.resize(
static_cast<size_t>(width) *
static_cast<size_t>(height) * 4U);
48 const int sampleShift = p010 ? 6 : 0;
49 const auto sample10 = [sampleShift](
const uint8_t *plane,
int stride,
int x,
int y) {
51 std::memcpy(&raw, plane +
static_cast<size_t>(y) * stride +
static_cast<size_t>(x) * 2U,
sizeof(raw));
52 return static_cast<int>(raw >> sampleShift);
55 constexpr float CR_R = 1.4746F;
56 constexpr float CB_G = -0.16455312684366F;
57 constexpr float CR_G = -0.57135313725490F;
58 constexpr float CB_B = 1.8814F;
59 constexpr float INV_Y = 1.0F / 876.0F;
60 constexpr float INV_C = 1.0F / 896.0F;
62 for (
int y = 0; y < height; ++y) {
63 uint16_t *destination = rgba.data() +
static_cast<size_t>(y) * width * 4U;
64 const int chromaY = y >> 1;
65 for (
int x = 0; x < width; ++x) {
66 const int chromaX = x >> 1;
67 const int ySample = sample10(source->data[0], source->linesize[0], x, y);
71 cbSample = sample10(source->data[1], source->linesize[1], chromaX * 2, chromaY);
72 crSample = sample10(source->data[1], source->linesize[1], chromaX * 2 + 1, chromaY);
74 cbSample = sample10(source->data[1], source->linesize[1], chromaX, chromaY);
75 crSample = sample10(source->data[2], source->linesize[2], chromaX, chromaY);
78 const float yValue = (ySample - 64) * INV_Y;
79 const float cb = (cbSample - 512) * INV_C;
80 const float cr = (crSample - 512) * INV_C;
81 const float red = std::clamp(yValue + CR_R * cr, 0.0F, 1.0F);
82 const float green = std::clamp(yValue + CB_G * cb + CR_G * cr, 0.0F, 1.0F);
83 const float blue = std::clamp(yValue + CB_B * cb, 0.0F, 1.0F);
85 destination[x * 4 + 0] =
static_cast<uint16_t
>(red * 65535.0F + 0.5F);
86 destination[x * 4 + 1] =
static_cast<uint16_t
>(green * 65535.0F + 0.5F);
87 destination[x * 4 + 2] =
static_cast<uint16_t
>(blue * 65535.0F + 0.5F);
88 destination[x * 4 + 3] = UINT16_MAX;
93 std::vector<uint16_t> row(
static_cast<size_t>(width) * 4U);
94 for (
int y = 0; y < height / 2; ++y) {
95 uint16_t *top = rgba.data() +
static_cast<size_t>(y) * width * 4U;
96 uint16_t *bottom = rgba.data() +
static_cast<size_t>(height - 1 - y) * width * 4U;
97 std::copy_n(top, row.size(), row.data());
98 std::copy_n(bottom, row.size(), top);
99 std::copy_n(row.data(), row.size(), bottom);
105#if defined(MXVK_CUDA) && defined(MXVK_CUDA_NPP)
106 [[nodiscard]] NppStreamContext makeNppStreamContext(cudaStream_t stream) {
107 NppStreamContext context{};
108 context.hStream = stream;
109 cudaGetDevice(&context.nCudaDeviceId);
111 cudaDeviceProp properties{};
112 cudaGetDeviceProperties(&properties, context.nCudaDeviceId);
113 context.nMultiProcessorCount = properties.multiProcessorCount;
114 context.nMaxThreadsPerMultiProcessor = properties.maxThreadsPerMultiProcessor;
115 context.nMaxThreadsPerBlock = properties.maxThreadsPerBlock;
116 context.nSharedMemPerBlock = properties.sharedMemPerBlock;
118 cudaDeviceGetAttribute(&context.nCudaDevAttrComputeCapabilityMajor, cudaDevAttrComputeCapabilityMajor, context.nCudaDeviceId);
119 cudaDeviceGetAttribute(&context.nCudaDevAttrComputeCapabilityMinor, cudaDevAttrComputeCapabilityMinor, context.nCudaDeviceId);
120 cudaStreamGetFlags(stream, &context.nStreamFlags);
133 if (avformat_open_input(&formatCtx, filename.c_str(),
nullptr,
nullptr) < 0) {
134 std::cout << std::format(
"mxvk_ff_capture: failed to open file: {}\n", filename);
139 if (avformat_find_stream_info(formatCtx,
nullptr) < 0) {
140 std::cout << std::format(
"mxvk_ff_capture: failed to read stream info: {}\n", filename);
145 const int streamIndex = av_find_best_stream(formatCtx, AVMEDIA_TYPE_VIDEO, -1, -1,
nullptr, 0);
146 if (streamIndex < 0) {
147 std::cout << std::format(
"mxvk_ff_capture: no video stream found: {}\n", filename);
151 videoStream = streamIndex;
153 AVStream *stream = formatCtx->streams[videoStream];
154 const AVCodecParameters *codecParams = stream->codecpar;
155 const AVCodec *decoder = avcodec_find_decoder(codecParams->codec_id);
156 if (decoder ==
nullptr) {
157 std::cout <<
"mxvk_ff_capture: no decoder found for video stream\n";
162 codecCtx = avcodec_alloc_context3(decoder);
163 if (codecCtx ==
nullptr || avcodec_parameters_to_context(codecCtx, codecParams) < 0) {
164 std::cout <<
"mxvk_ff_capture: failed to create decoder context\n";
169 codecCtx->opaque =
this;
170 if (initHardwareDevice(decoder, cuda_device)) {
171 codecCtx->get_format = &VK_FF_Capture::chooseHwFormat;
172 codecCtx->hw_device_ctx = av_buffer_ref(hwDeviceCtx);
173 hardwareDecode = codecCtx->hw_device_ctx !=
nullptr;
176 if (avcodec_open2(codecCtx, decoder,
nullptr) < 0) {
177 std::cout <<
"mxvk_ff_capture: failed to open decoder\n";
182 packet = av_packet_alloc();
183 frame = av_frame_alloc();
184 swFrame = av_frame_alloc();
185 if (packet ==
nullptr || frame ==
nullptr || swFrame ==
nullptr) {
186 std::cout <<
"mxvk_ff_capture: failed to allocate decoder frames\n";
191 frameWidth = codecCtx->width;
192 frameHeight = codecCtx->height;
193 frameFps = rationalToDouble(stream->avg_frame_rate);
194 if (frameFps <= 0.0) {
195 frameFps = rationalToDouble(stream->r_frame_rate);
197 if (frameFps <= 0.0) {
201 const std::string decodeMode = hardwareDecode && hardwareDecodeDevice >= 0 ? std::format(
"cuda:{}", hardwareDecodeDevice) : hardwareDecode ?
"cuda" :
"software";
202 std::cout << std::format(
"mxvk_ff_capture: opened {} ({}x{}, {:.3f} fps, decode={})\n", filename, frameWidth, frameHeight, frameFps, decodeMode);
207 if (!
is_open() || videoStream < 0) {
211 AVStream *stream = formatCtx->streams[videoStream];
212 const int64_t timestamp = stream->start_time == AV_NOPTS_VALUE ? 0 : stream->start_time;
213 if (av_seek_frame(formatCtx, videoStream, timestamp, AVSEEK_FLAG_BACKWARD) < 0) {
214 std::cout <<
"mxvk_ff_capture: failed to seek to start\n";
218 avcodec_flush_buffers(codecCtx);
219 av_packet_unref(packet);
220 av_frame_unref(frame);
221 av_frame_unref(swFrame);
226 if (!
is_open() || !decodeNextFrame()) {
229 av_frame_unref(frame);
235 if (decoder_surface_copy_event !=
nullptr) {
236 cudaEventDestroy(decoder_surface_copy_event);
237 decoder_surface_copy_event =
nullptr;
239 decoder_surface_barrier_logged =
false;
241 if (swsCtx !=
nullptr) {
242 sws_freeContext(swsCtx);
245 rgba16_conversion_logged =
false;
246 if (hwDeviceCtx !=
nullptr) {
247 av_buffer_unref(&hwDeviceCtx);
249 if (swFrame !=
nullptr) {
250 av_frame_free(&swFrame);
252 if (frame !=
nullptr) {
253 av_frame_free(&frame);
255 if (packet !=
nullptr) {
256 av_packet_free(&packet);
258 if (codecCtx !=
nullptr) {
259 avcodec_free_context(&codecCtx);
261 if (formatCtx !=
nullptr) {
262 avformat_close_input(&formatCtx);
268 hwPixFmt = AV_PIX_FMT_NONE;
269 hardwareDecode =
false;
270 hardwareDecodeDevice = -1;
278 if (!decodeNextFrame()) {
281 const bool converted = convertFrameToRgba(frame, rgba,
width,
height, pitch, flipY);
282 av_frame_unref(frame);
291 if (!decodeNextFrame()) {
294 const bool converted = convertFrameToRgba16(frame, rgba,
width,
height, pitch, flipY);
295 av_frame_unref(frame);
300 bool VK_FF_Capture::readGpuRgba(cv::cuda::GpuMat &rgba, cv::cuda::Stream &stream,
bool flipY) {
305 if (!decodeNextFrame()) {
308 const bool converted = convertFrameToGpuRgba(frame, rgba, stream, flipY);
309 av_frame_unref(frame);
314 bool VK_FF_Capture::decodeNextFrame() {
315 bool draining =
false;
317 const int receiveResult = avcodec_receive_frame(codecCtx, frame);
318 if (receiveResult == 0) {
321 if (receiveResult == AVERROR_EOF) {
324 if (receiveResult != AVERROR(EAGAIN)) {
332 const int readResult = av_read_frame(formatCtx, packet);
333 if (readResult < 0) {
335 avcodec_send_packet(codecCtx,
nullptr);
339 if (packet->stream_index == videoStream) {
340 const int sendResult = avcodec_send_packet(codecCtx, packet);
341 av_packet_unref(packet);
342 if (sendResult == 0 || sendResult == AVERROR(EAGAIN)) {
347 av_packet_unref(packet);
352 AVPixelFormat VK_FF_Capture::chooseHwFormat(AVCodecContext *ctx,
const AVPixelFormat *formats) {
353 const auto *capture =
static_cast<const VK_FF_Capture *
>(ctx->opaque);
354 if (capture ==
nullptr) {
357 return capture->selectHwFormat(formats);
360 AVPixelFormat VK_FF_Capture::selectHwFormat(
const AVPixelFormat *formats)
const {
361 for (
const AVPixelFormat *format = formats; *format != AV_PIX_FMT_NONE; ++format) {
362 if (*format == hwPixFmt) {
366 std::cout <<
"mxvk_ff_capture: requested CUDA pixel format is unavailable; decoder will use software frames\n";
370 bool VK_FF_Capture::initHardwareDevice(
const AVCodec *decoder,
int cuda_device) {
371 const AVHWDeviceType deviceType = av_hwdevice_find_type_by_name(
"cuda");
372 if (deviceType == AV_HWDEVICE_TYPE_NONE) {
376 for (
int index = 0;; ++index) {
377 const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, index);
378 if (config ==
nullptr) {
381 const bool hasDeviceCtx = (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0;
382 if (hasDeviceCtx && config->device_type == deviceType) {
383 hwPixFmt = config->pix_fmt;
388 const std::string deviceName = cuda_device >= 0 ? std::to_string(cuda_device) : std::string{};
389 const char *device = deviceName.empty() ? nullptr : deviceName.c_str();
390 if (av_hwdevice_ctx_create(&hwDeviceCtx, deviceType, device,
nullptr, 0) < 0) {
391 hwPixFmt = AV_PIX_FMT_NONE;
395 hardwareDecodeDevice = cuda_device;
399 bool VK_FF_Capture::convertFrameToRgba(
const AVFrame *decodedFrame, std::vector<uint8_t> &rgba,
int &width,
int &height,
int &pitch,
bool flipY) {
400 const AVFrame *sourceFrame = decodedFrame;
401 if (decodedFrame->format == hwPixFmt && hwPixFmt != AV_PIX_FMT_NONE) {
402 av_frame_unref(swFrame);
403 if (av_hwframe_transfer_data(swFrame, decodedFrame, 0) < 0) {
404 std::cout <<
"mxvk_ff_capture: failed to transfer CUDA decoded frame to host memory\n";
407 sourceFrame = swFrame;
410 width = sourceFrame->width;
411 height = sourceFrame->height;
417 rgba.resize(
static_cast<size_t>(pitch) *
static_cast<size_t>(
height));
418 uint8_t *dstData[4] = {rgba.data(),
nullptr,
nullptr,
nullptr};
419 int dstLinesize[4] = {pitch, 0, 0, 0};
421 swsCtx = sws_getCachedContext(swsCtx, sourceFrame->width, sourceFrame->height,
static_cast<AVPixelFormat
>(sourceFrame->format),
width,
height, AV_PIX_FMT_RGBA, SWS_BILINEAR,
nullptr,
nullptr,
nullptr);
422 if (swsCtx ==
nullptr) {
426 const int scaledRows = sws_scale(swsCtx, sourceFrame->data, sourceFrame->linesize, 0, sourceFrame->height, dstData, dstLinesize);
427 if (scaledRows !=
height) {
431 flipRows(rgba, pitch);
436 bool VK_FF_Capture::convertFrameToRgba16(
const AVFrame *decodedFrame, std::vector<uint16_t> &rgba,
int &width,
int &height,
int &pitch,
bool flipY) {
437 const AVFrame *sourceFrame = decodedFrame;
438 if (decodedFrame->format == hwPixFmt && hwPixFmt != AV_PIX_FMT_NONE) {
439 av_frame_unref(swFrame);
440 if (av_hwframe_transfer_data(swFrame, decodedFrame, 0) < 0) {
441 std::cout <<
"mxvk_ff_capture: failed to transfer CUDA decoded "
442 "HDR frame to host memory\n";
445 sourceFrame = swFrame;
448 const AVColorSpace colorSpace = sourceFrame->colorspace != AVCOL_SPC_UNSPECIFIED ? sourceFrame->colorspace : codecCtx->colorspace;
449 const AVColorRange colorRange = sourceFrame->color_range != AVCOL_RANGE_UNSPECIFIED ? sourceFrame->color_range : codecCtx->color_range;
451 if (!rgba16_conversion_logged) {
452 std::cout <<
"mxvk_ff_capture: preserving 10-bit BT.2020 NCL input "
453 "through native RGBA16 conversion\n";
454 rgba16_conversion_logged =
true;
459 width = sourceFrame->width;
460 height = sourceFrame->height;
466 rgba.resize(
static_cast<size_t>(
width) *
static_cast<size_t>(
height) * 4U);
467 auto *bytes =
reinterpret_cast<uint8_t *
>(rgba.data());
468 uint8_t *dstData[4] = {bytes,
nullptr,
nullptr,
nullptr};
469 int dstLinesize[4] = {pitch, 0, 0, 0};
471 swsCtx = sws_getCachedContext(swsCtx, sourceFrame->width, sourceFrame->height,
static_cast<AVPixelFormat
>(sourceFrame->format),
width,
height, AV_PIX_FMT_RGBA64, SWS_BILINEAR,
nullptr,
nullptr,
nullptr);
472 if (swsCtx ==
nullptr) {
476 int sourceColorSpace = SWS_CS_DEFAULT;
477 switch (colorSpace) {
478 case AVCOL_SPC_BT2020_NCL:
479 case AVCOL_SPC_BT2020_CL:
480 sourceColorSpace = SWS_CS_BT2020;
482 case AVCOL_SPC_BT709:
483 sourceColorSpace = SWS_CS_ITU709;
485 case AVCOL_SPC_SMPTE170M:
486 case AVCOL_SPC_BT470BG:
487 sourceColorSpace = SWS_CS_ITU601;
492 const int *sourceCoefficients = sws_getCoefficients(sourceColorSpace);
493 const int *destinationCoefficients = sws_getCoefficients(SWS_CS_BT2020);
494 sws_setColorspaceDetails(swsCtx, sourceCoefficients, colorRange == AVCOL_RANGE_JPEG ? 1 : 0, destinationCoefficients, 1, 0, 1 << 16, 1 << 16);
495 if (!rgba16_conversion_logged) {
496 std::cout <<
"mxvk_ff_capture: preserving high-bit-depth input through "
497 "FFmpeg RGBA64 conversion\n";
498 rgba16_conversion_logged =
true;
501 const int scaledRows = sws_scale(swsCtx, sourceFrame->data, sourceFrame->linesize, 0, sourceFrame->height, dstData, dstLinesize);
502 if (scaledRows !=
height) {
506 std::vector<uint8_t> row(
static_cast<size_t>(pitch));
507 for (
int y = 0; y <
height / 2; ++y) {
508 uint8_t *top = bytes +
static_cast<size_t>(y) * pitch;
509 uint8_t *bottom = bytes +
static_cast<size_t>(
height - 1 - y) * pitch;
510 std::memcpy(row.data(), top,
static_cast<size_t>(pitch));
511 std::memcpy(top, bottom,
static_cast<size_t>(pitch));
512 std::memcpy(bottom, row.data(),
static_cast<size_t>(pitch));
519 bool VK_FF_Capture::convertFrameToGpuRgba(
const AVFrame *decodedFrame, cv::cuda::GpuMat &rgba, cv::cuda::Stream &stream,
bool flipY) {
520 if (decodedFrame->format == hwPixFmt && hwPixFmt != AV_PIX_FMT_NONE && decodedFrame->hw_frames_ctx !=
nullptr) {
521 const auto *framesContext =
reinterpret_cast<const AVHWFramesContext *
>(decodedFrame->hw_frames_ctx->data);
522 if (framesContext !=
nullptr && framesContext->sw_format == AV_PIX_FMT_NV12) {
523 const int width = decodedFrame->width;
524 const int height = decodedFrame->height;
530 cudaStream_t cudaStream = mxvk::cuda_stream_handle(stream);
531 if (decoder_surface_copy_event ==
nullptr) {
532 const cudaError_t event_result = cudaEventCreateWithFlags(&decoder_surface_copy_event, cudaEventDisableTiming);
533 if (event_result != cudaSuccess) {
534 std::cout <<
"mxvk_ff_capture: CUDA decoder-surface event creation failed: " << cudaGetErrorString(event_result) <<
"\n";
539 const auto synchronize_decoder_surface_copy = [&]() {
540 cudaError_t result = cudaEventSynchronize(decoder_surface_copy_event);
541 if (result != cudaSuccess) {
542 std::cout <<
"mxvk_ff_capture: CUDA decoder-surface copy barrier failed: " << cudaGetErrorString(result) <<
"\n";
543 result = cudaStreamSynchronize(cudaStream);
544 if (result != cudaSuccess) {
545 std::cout <<
"mxvk_ff_capture: CUDA capture-stream synchronization failed: " << cudaGetErrorString(result) <<
"\n";
549 if (!decoder_surface_barrier_logged) {
550 std::cout <<
"mxvk_ff_capture: asynchronous NVDEC surface-copy barrier active\n";
551 decoder_surface_barrier_logged =
true;
556 cudaError_t result = cudaMemcpy2DAsync(gpuNv12.ptr(), gpuNv12.step, decodedFrame->data[0],
static_cast<size_t>(decodedFrame->linesize[0]),
static_cast<size_t>(
width),
static_cast<size_t>(
height), cudaMemcpyDeviceToDevice, cudaStream);
557 if (result != cudaSuccess) {
558 std::cout <<
"mxvk_ff_capture: CUDA NV12 luma copy failed: " << cudaGetErrorString(result) <<
"\n";
559 cudaStreamSynchronize(cudaStream);
563 result = cudaMemcpy2DAsync(gpuNv12.ptr(
height), gpuNv12.step, decodedFrame->data[1],
static_cast<size_t>(decodedFrame->linesize[1]),
static_cast<size_t>(
width),
static_cast<size_t>(
height / 2), cudaMemcpyDeviceToDevice, cudaStream);
564 if (result != cudaSuccess) {
565 std::cout <<
"mxvk_ff_capture: CUDA NV12 chroma copy failed: " << cudaGetErrorString(result) <<
"\n";
566 cudaStreamSynchronize(cudaStream);
570 result = cudaEventRecord(decoder_surface_copy_event, cudaStream);
571 if (result != cudaSuccess) {
572 std::cout <<
"mxvk_ff_capture: CUDA decoder-surface event record failed: " << cudaGetErrorString(result) <<
"\n";
573 cudaStreamSynchronize(cudaStream);
581 const Npp8u *srcPlanes[2] = {
582 static_cast<const Npp8u *
>(gpuNv12.ptr()),
583 static_cast<const Npp8u *
>(gpuNv12.ptr(
height)),
586 const NppStreamContext nppContext = makeNppStreamContext(cudaStream);
587 NppStatus nppStatus = nppiNV12ToRGB_8u_P2C3R_Ctx(srcPlanes,
static_cast<int>(gpuNv12.step),
static_cast<Npp8u *
>(gpuRgb.ptr()),
static_cast<int>(gpuRgb.step), roi, nppContext);
588 if (nppStatus != NPP_SUCCESS) {
589 std::cout <<
"mxvk_ff_capture: NPP NV12 to RGB conversion failed: " <<
static_cast<int>(nppStatus) <<
"\n";
590 synchronize_decoder_surface_copy();
594 const int rgbaOrder[4] = {0, 1, 2, 3};
595 nppStatus = nppiSwapChannels_8u_C3C4R_Ctx(
static_cast<const Npp8u *
>(gpuRgb.ptr()),
static_cast<int>(gpuRgb.step),
static_cast<Npp8u *
>(gpuRgba.ptr()),
static_cast<int>(gpuRgba.step), roi, rgbaOrder, 255, nppContext);
596 if (nppStatus != NPP_SUCCESS) {
597 std::cout <<
"mxvk_ff_capture: NPP RGB to RGBA conversion failed: " <<
static_cast<int>(nppStatus) <<
"\n";
598 synchronize_decoder_surface_copy();
603 cv::cuda::flip(gpuRgba, gpuFlippedRgba, 0, stream);
604 rgba = gpuFlippedRgba;
608 if (!synchronize_decoder_surface_copy()) {
614 cv::cuda::cvtColor(gpuNv12, gpuRgba, cv::COLOR_YUV2RGBA_NV12, 0, stream);
616 cv::cuda::flip(gpuRgba, gpuFlippedRgba, 0, stream);
617 rgba = gpuFlippedRgba;
621 if (!synchronize_decoder_surface_copy()) {
625 }
catch (
const cv::Exception &e) {
626 if (!synchronize_decoder_surface_copy()) {
629 std::cout <<
"mxvk_ff_capture: CUDA NV12 to RGBA conversion failed; falling back to host conversion: " << e.what() <<
"\n";
635 std::vector<uint8_t> hostRgba;
639 if (!convertFrameToRgba(decodedFrame, hostRgba,
width,
height, pitch, flipY) || pitch !=
width * 4) {
642 cv::Mat hostFrame(
height,
width, CV_8UC4, hostRgba.data(),
static_cast<size_t>(pitch));
643 gpuRgba.upload(hostFrame, stream);
644 stream.waitForCompletion();
650 void VK_FF_Capture::flipRows(std::vector<uint8_t> &rgba,
int pitch)
const {
651 std::vector<uint8_t> row(
static_cast<size_t>(pitch));
652 for (
int top = 0, bottom = frameHeight - 1; top < bottom; ++top, --bottom) {
653 auto *topPtr = rgba.data() +
static_cast<size_t>(top) *
static_cast<size_t>(pitch);
654 auto *bottomPtr = rgba.data() +
static_cast<size_t>(bottom) *
static_cast<size_t>(pitch);
655 std::memcpy(row.data(), topPtr,
static_cast<size_t>(pitch));
656 std::memcpy(topPtr, bottomPtr,
static_cast<size_t>(pitch));
657 std::memcpy(bottomPtr, row.data(),
static_cast<size_t>(pitch));
void close()
Close the active file and release decoder resources.
bool is_open() const
Check whether a decoder is open.
int width() const
Source width in pixels.
int height() const
Source height in pixels.
bool open(const std::string &filename)
Open a video file.
bool readRgba16(std::vector< uint16_t > &rgba, int &width, int &height, int &pitch, bool flipY=false)
Decode the next frame as native-endian RGBA16.
VK_FF_Capture()=default
Construct a closed capture source.
bool readRgba(std::vector< uint8_t > &rgba, int &width, int &height, int &pitch, bool flipY=false)
Decode the next frame as tightly packed RGBA8.
bool skip()
Decode and discard the next video frame.
bool seek_start()
Seek the active video stream back to its beginning.
~VK_FF_Capture()
Close and release FFmpeg resources.
FFmpeg video-file capture with optional CUDA hardware decoding.
Small compatibility wrappers around OpenCV CUDA APIs.
double rationalToDouble(AVRational rational)
bool convertBt2020Yuv10LimitedToRgba16(const AVFrame *source, std::vector< uint16_t > &rgba, int &width, int &height, int &pitch, bool flipY)
Utilities for loading and saving PNG images.