Skip to content

Commit 6f69aa1

Browse files
author
pytorchbot
committed
2026-05-16 nightly release (42c8e86)
1 parent 7b982fa commit 6f69aa1

6 files changed

Lines changed: 874 additions & 13 deletions

File tree

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,8 @@ norecursedirs = ["third-party-interface"]
5454
# the CI, where we definitely want the 'slow' tests to run.
5555
addopts = "-v -m 'not slow'"
5656

57+
# Tells pytest to keep around temporary paths (via tmp_path fixture) for
58+
# *failed tests* only. Otherwise, the /tmp folder can grow too large.
59+
tmp_path_retention_policy = "failed"
60+
5761
testpaths = ["test"]

src/torchcodec/_core/BetaCudaDeviceInterface.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,33 @@ void BetaCudaDeviceInterface::convertAVFrameToFrameOutput(
903903
UniqueAVFrame& avFrame,
904904
FrameOutput& frameOutput,
905905
std::optional<torch::stable::Tensor> preAllocatedOutputTensor) {
906+
if (cpuFallback_) {
907+
// When the CPU fallabck happens, we'll try to run the color-conversion on
908+
// GPU by sending those CPU frames to the GPU as NV12 (See
909+
// transferCpuFrameToGpuNV12() below). However, it's not always possible:
910+
// NV12 would downsample 4:4:4 frames and lose chroma resolution, resulting
911+
// in poorly decoded frames. So for those, we still do the color conversion
912+
// on the CPU and then send the full RGB frame to the GPU.
913+
const AVPixFmtDescriptor* desc =
914+
av_pix_fmt_desc_get(static_cast<AVPixelFormat>(avFrame->format));
915+
if (desc && desc->log2_chroma_w == 0 && desc->log2_chroma_h == 0) {
916+
// 4:4:4: converting through NV12 (4:2:0) would lose chroma resolution.
917+
FrameOutput cpuFrameOutput;
918+
cpuFallback_->convertAVFrameToFrameOutput(avFrame, cpuFrameOutput);
919+
if (preAllocatedOutputTensor.has_value()) {
920+
torch::stable::copy_(
921+
preAllocatedOutputTensor.value(), cpuFrameOutput.data);
922+
frameOutput.data = preAllocatedOutputTensor.value();
923+
} else {
924+
frameOutput.data = torch::stable::to(cpuFrameOutput.data, device_);
925+
}
926+
if (rotation_ != Rotation::NONE) {
927+
applyRotation(frameOutput, preAllocatedOutputTensor);
928+
}
929+
return;
930+
}
931+
}
932+
906933
// Capture original dimensions before transferCpuFrameToGpuNV12 may
907934
// round them up to even for NV12.
908935
FrameDims originalDims(avFrame->height, avFrame->width);

src/torchcodec/_core/Encoder.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,6 @@ torch::stable::Tensor validateFrames(
573573
framesDevice.index());
574574
}
575575
if (avCodecContext) {
576-
// TODO MultiStreamEncoder: Enable tensors in NHWC shape
577576
STD_TORCH_CHECK(
578577
static_cast<int>(frames.sizes()[2]) == avCodecContext->height &&
579578
static_cast<int>(frames.sizes()[3]) == avCodecContext->width,
@@ -832,13 +831,11 @@ void VideoEncoder::initializeEncoder(
832831
avCodecContext_.reset(avCodecContext);
833832

834833
// Store dimensions of input frames
835-
// TODO-VideoEncoder: (P2) Enable tensors in NHWC shape
836834
auto sizes = frames_.sizes();
837835
int inHeight = static_cast<int>(sizes[2]);
838836
int inWidth = static_cast<int>(sizes[3]);
839837

840838
// Always use input dimensions as output dimensions
841-
// TODO-VideoEncoder: (P2) Allow height and width to be set
842839
int outWidth = inWidth;
843840
int outHeight = inHeight;
844841
AVPixelFormat outPixelFormat = AV_PIX_FMT_NONE;
@@ -1153,6 +1150,13 @@ int MultiStreamEncoder::addAudioStream(
11531150
STD_TORCH_CHECK(sampleRate > 0, "sample_rate must be > 0, got ", sampleRate);
11541151
STD_TORCH_CHECK(
11551152
numChannels > 0, "num_channels must be > 0, got ", numChannels);
1153+
STD_TORCH_CHECK(
1154+
numChannels <= AV_NUM_DATA_POINTERS,
1155+
"Trying to encode ",
1156+
numChannels,
1157+
" channels, but FFmpeg only supports ",
1158+
AV_NUM_DATA_POINTERS,
1159+
" channels per frame.");
11561160

11571161
AudioStream audioStream;
11581162
audioStream.inSampleRate = sampleRate;
@@ -1210,7 +1214,6 @@ void MultiStreamEncoder::initializeVideoStream(VideoStream& videoStream) {
12101214
avCodecContext != nullptr, "Couldn't allocate codec context.");
12111215
videoStream.avCodecContext.reset(avCodecContext);
12121216

1213-
// TODO MultiStreamEncoder: Allow output height and width to be set
12141217
int outHeight = videoStream.inHeight;
12151218
int outWidth = videoStream.inWidth;
12161219
AVPixelFormat outPixelFormat = AV_PIX_FMT_NONE;
@@ -1247,7 +1250,6 @@ void MultiStreamEncoder::initializeVideoStream(VideoStream& videoStream) {
12471250
videoStream.avCodecContext->width = outWidth;
12481251
videoStream.avCodecContext->height = outHeight;
12491252
videoStream.avCodecContext->pix_fmt = outPixelFormat;
1250-
// TODO MultiStreamEncoder: Add and utilize output frame_rate option
12511253
videoStream.avCodecContext->framerate =
12521254
av_d2q(videoStream.inFrameRate, INT_MAX);
12531255
videoStream.avCodecContext->time_base =
@@ -1419,7 +1421,6 @@ void MultiStreamEncoder::openStreamsAndWriteHeader() {
14191421
void MultiStreamEncoder::addFrames(
14201422
const torch::stable::Tensor& frames,
14211423
int streamIndex) {
1422-
// TODO MultiStreamEncoder: Specify which video stream to add frames to
14231424
STD_TORCH_CHECK(!closed_, "Cannot add frames after close() was called.");
14241425
STD_TORCH_CHECK(headerWritten_, "Call open() before addFrames().");
14251426
STD_TORCH_CHECK(

0 commit comments

Comments
 (0)