Skip to content

Add video encoding support with SYCL RGB→NV12 kernel and CPU fallback - #103

Open
eromomon wants to merge 4 commits into
intel:mainfrom
eromomon:eromomon/encoding_no_patch
Open

Add video encoding support with SYCL RGB→NV12 kernel and CPU fallback#103
eromomon wants to merge 4 commits into
intel:mainfrom
eromomon:eromomon/encoding_no_patch

Conversation

@eromomon

Copy link
Copy Markdown
Contributor

Adds a hardware video encoding path to the torchcodec-xpu plugin (supersedes #58)

What changes:

  • XpuDeviceInterface.{h,cpp} — implements three encoder overrides:
    • get_encoding_pixel_format (pinned to NV12).
    • setup_hardware_frame_context_for_encoding (VAAPI hw_frames_ctx, BT.709 + JPEG-range defaults, B-frames disabled, low_power=0, quality=1).
    • convert_tensor_to_av_frame_for_encoding dispatching to:

      • -SYCL fast path — exports the VA surface as DMA-BUF, imports via Level Zero USM, runs convertRGBToNV12 directly on the surface (handles linear + Intel Tile-Y, and both NV12 export layouts on iHD/i915).
      • -CPU fallback — GBRP→NV12 via libswscale, then av_hwframe_transfer_data onto the VA surface. Used when SYCL is disabled or FP64 is unavailable (DG2 / ATS-M).
    • find_codec extended to resolve VAAPI encoders, with H.264 / HEVC / AV1 fallback when the requested codec id has no VAAPI encoder.
  • ColorConversionKernel.{h,cpp} — adds convertRGBToNV12 SYCL kernel (BT.601 / BT.709, limited + full range); get_tile_offset reused for encode.

  • patches/0002-Add-XPU-support-to-video-encoder-tests.patch — parametrizes upstream torchcodec encoder tests over CUDA + XPU, and primes the encoder with one frame in test_write_frames_different_devices_errors (VAAPI segfaults on empty flush; NVENC doesn't). Depends on 0001-Add-XPU-support-to-tests.patch.

Compatibility
No change to decode behavior, plugin loading, or public API.

Testing
Encoder test suite (patches 0001 + 0002) passes on Intel Arc / Battlemage.

eromomon added 3 commits July 27, 2026 08:32
Signed-off-by: Edgar Romo Montiel <edgar.romo.montiel@intel.com>
Signed-off-by: Edgar Romo Montiel <edgar.romo.montiel@intel.com>
Signed-off-by: Edgar Romo Montiel <edgar.romo.montiel@intel.com>
@eromomon
eromomon requested review from Copilot and removed request for dvrogozh and luis-real July 27, 2026 22:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an Intel XPU (VAAPI) hardware video encoding path to the torchcodec-xpu plugin, including an on-device SYCL RGB→NV12 conversion kernel with a CPU/libswscale fallback, and updates upstream TorchCodec encoder tests (via patch) to run on XPU as well as CUDA.

Changes:

  • Implement XPU encoding overrides in XpuDeviceInterface (pixel format selection, VAAPI hw_frames_ctx setup, tensor→AVFrame upload path with SYCL fast-path + CPU fallback).
  • Add SYCL convertRGBToNV12 kernel supporting BT.601/BT.709 and full/limited range, including tiled (Intel Tile-Y) surface handling.
  • Extend/parameterize upstream encoder tests (patch) to exercise XPU and avoid VAAPI flush-on-empty segfault by priming with a frame.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/torchcodec-xpu/src/torchcodec_xpu/XpuDeviceInterface.h Declares XPU encoding overrides and helper conversion entry points.
packages/torchcodec-xpu/src/torchcodec_xpu/XpuDeviceInterface.cpp Implements VAAPI frames context setup, VAAPI encoder discovery logic, and SYCL/CPU tensor→NV12 surface conversion.
packages/torchcodec-xpu/src/torchcodec_xpu/ColorConversionKernel.h Exposes the new SYCL RGB→NV12 conversion API.
packages/torchcodec-xpu/src/torchcodec_xpu/ColorConversionKernel.cpp Implements RGB→NV12 SYCL kernel and supporting YUV conversion matrices.
packages/torchcodec-xpu/patches/0002-Add-XPU-support-to-video-encoder-tests.patch Updates upstream TorchCodec encoder tests to include XPU device coverage and VAAPI-specific priming behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/torchcodec-xpu/src/torchcodec_xpu/XpuDeviceInterface.cpp
Comment on lines +610 to 628
// Encoder-only fallback: if no VAAPI encoder exists for codecId
// substitute a HW-capable alternative
if (!is_decoder) {
static constexpr AVCodecID kHwEncoderFallbacks[] = {
AV_CODEC_ID_H264,
AV_CODEC_ID_HEVC,
AV_CODEC_ID_AV1,
};
for (AVCodecID fb : kHwEncoderFallbacks) {
if (fb == codec_id) {
continue;
}
if (const AVCodec* c = findVaapiForId(fb)) {
VLOG(1) << "No VAAPI encoder for codec id " << codec_id
<< ", substituting " << c->name;
return c;
}
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment

Comment thread packages/torchcodec-xpu/src/torchcodec_xpu/XpuDeviceInterface.cpp Outdated
Comment thread packages/torchcodec-xpu/src/torchcodec_xpu/XpuDeviceInterface.cpp Outdated
hwFramesCtx->width = codec_context->width;
hwFramesCtx->height = codec_context->height;

// XPU quality is matterially better when using BT.709 color space and full range
Comment on lines +38 to +40
+import torchcodec
+import torchcodec_xpu
+

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patch updated

@eromomon
eromomon requested a review from dvrogozh July 27, 2026 23:29
Comment on lines +704 to +705
// Disable B-frames (mirrors CUDA/NVENC delay=0) to avoid reorder issues
// with fragmented containers; callers can restore via extra_options={"bf":"2"}.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reordering is not an issue - that's how media codecs behave. I don't understand what you mean by "fragmented containers", so drop it.

Suggested change
// Disable B-frames (mirrors CUDA/NVENC delay=0) to avoid reorder issues
// with fragmented containers; callers can restore via extra_options={"bf":"2"}.
// Disable B-frames (mirrors CUDA/NVENC delay=0) to avoid frames reordering.
// Callers can restore via extra_options={"bf": "2"}.

Question: extra_options is a TorchCodec option?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, extra_options is a TorchCodec public API parameter on _video_encoder.py

Comment thread packages/torchcodec-xpu/src/torchcodec_xpu/XpuDeviceInterface.cpp
Comment thread packages/torchcodec-xpu/src/torchcodec_xpu/XpuDeviceInterface.cpp Outdated
Comment thread packages/torchcodec-xpu/src/torchcodec_xpu/XpuDeviceInterface.cpp Outdated
Comment thread packages/torchcodec-xpu/patches/0002-Add-XPU-support-to-video-encoder-tests.patch Outdated
return c;
}

// Encoder-only fallback: if no VAAPI encoder exists for codecId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remind me, please, why do we need this? What will start to fail if we will drop this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the fallback here regresses XPU encoding on any FFmpeg build that lacks h264_vaapi. The block is not a CPU fallback: it substitutes a different VAAPI encoder from the same family, so frames continue to be encoded on the Intel Media Engine — only the bitstream codec ID changes, and Encoder.cpp propagates that through codec_context->codec_id = av_codec->id, so the MP4 muxer writes the correct sample entries.

The reason this cannot simply be dropped today is that torchcodec's initialize_video_stream unconditionally invokes our setup_hardware_frame_context_for_encoding, which forces pix_fmt = AV_PIX_FMT_VAAPI and attaches an AVHWFramesContext. When find_codec returns nullopt, torchcodec falls back to avcodec_find_encoder() — a software encoder (e.g. libx264) — and avcodec_open2 then rejects the VAAPI pixel format with AVERROR(EINVAL) ("Invalid argument"), which is exactly the reproduced failure.

RuntimeError: initialize_video_stream, /__w/torchcodec/torchcodec/meta-pytorch/torchcodec/src/torchcodec/_core/Encoder.cpp:500, avcodec_open2 failed: Invalid argument

@dvrogozh dvrogozh Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When find_codec returns nullopt

This is in the assumption that ffmpeg was built without h264_vaapi, right? find_codec should not return nullopt if ffmpeg was built with h264_vaapi, correct?

…e clarity.

Update patch file for test encoding to align with autoloader changes.

Signed-off-by: Edgar Romo Montiel <edgar.romo.montiel@intel.com>
Co-authored-by: Dmitry Rogozhkin <dmitry.v.rogozhkin@intel.com>
@eromomon
eromomon requested a review from dvrogozh July 29, 2026 00:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants