-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Solve CUDA AV1 decoding #448
Conversation
Hi @hugo-ijw! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
@@ -256,4 +256,36 @@ void convertAVFrameToDecodedOutputOnCuda( | |||
<< " took: " << duration.count() << "us" << std::endl; | |||
} | |||
|
|||
// inspired by https://github.com/FFmpeg/FFmpeg/commit/ad67ea9 | |||
void forceCudaCodec( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's change the semantics of this function so that it returns the codec, if found. Then the caller is responsible for doing the assignment. That would make this signature:
std::optional<AVCodecPtr> findCudaCodec(
const torch::Device& decive,
const AVCodecID& codecId);
Then, inside the function, when we find the right codec, we just return it. If we loop through all available codecs and never find it, we return std::nullopt
.
const torch::Device& device, | ||
AVCodecPtr* codec, | ||
const AVCodecID& codecId) { | ||
if (device.type() != torch::kCUDA) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace this check with throwErrorIfNonCudaDevice(device)
. That's the convention used by the other functions in this file, and it also enforces that calling this function in a non-CUDA context is an error.
return; | ||
} | ||
|
||
const AVCodec* c; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this definition into the while loop condition itself. With the change in semantics, we no longer need to reference the AVCodec
outside of a single loop iteration, so it's fine (and actually good) that its scope is limited to the while loop only.
|
||
if (options.device.type() == torch::kCUDA) { | ||
forceCudaCodec( | ||
options.device, &codec, streamInfo.stream->codecpar->codec_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the change in semantics, we can make this call:
codec = findCudaCodec(options.device, streamInfo.stream->codecpar->codec_id).value_or(codec);
test/decoders/test_video_decoder.py
Outdated
# We don't parametrize with CUDA because the current GPUs on CI do not | ||
# support AV1: | ||
decoder = VideoDecoder(AV1_VIDEO.path, device="cpu") | ||
ref_frame11 = AV1_VIDEO.get_frame_data_by_index(10) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention we're following in these tests is that the variable name number matches the index number, so this should be ref_frame10
, ref_frame_info10
and decoded_frame10
.
@hugo-ijw, thank you for digging into this and fixing it! I made a bunch of minor comments on code structure and style. But my main concern right now is it appears that we don't have AV1 support on our GPU CPUs in CI, based on the comment you made. Can you re-enable that in your code so we can dig into what exactly is failing? Since CUDA support for AV1 is the main purpose of this PR, I want to spend some time seeing if we can actually test that scenario. Unfortunately, it also looks like the CUDA tests may be failing because of some outdated actions. I'll dig into that myself. |
PR #450 should solve the CUDA test problems. |
Thanks for the comments, I'll try to work on them this morning. |
@hugo-ijw, I dug into the problem with our runners a little bit in #451. That is, I tried enabling the H265 test on a different kind of runner, and notably, it gets the same error message you're fixing: https://github.com/pytorch/torchcodec/actions/runs/12713611963/job/35442310631?pr=451#step:16:364. But it doesn't get the error message about the decoder not being available. I'm going to push to your branch to see if that fixes the problem. |
// we have to do this because of an FFmpeg bug where hardware decoding is not | ||
// appropriately set, so we just go off and find the matching codec for the CUDA | ||
// device | ||
std::optional<AVCodecPtr> forceCudaCodec( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I think findCudaCodec
is a more appropriate name now.
const AVCodecID& codecId) { | ||
throwErrorIfNonCudaDevice(device); | ||
|
||
AVCodecPtr c; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: we can move this declaration into the while loop condition.
@hugo-ijw, good news! By changing the kinds of runners we use, I was able to get your new test to run on GPUs in our CI. Things look great now; the FFmpeg version 5 failures a known problem. I'm okay merging as-is, but if you made the small style changes I'd greatly appreciate it. |
Uh-oh, looks like I was wrong about the while loop declaration! I'll push a fix so we can merge. |
fixes #443
Hello there, love the library!
It looks like there is an FFmpeg bug when selecting an AV1 decoder while specifying an HW decoder; a SW decoder is always chosen (cf. FFmpeg/FFmpeg@ad67ea9).
I also added small details to the contributing doc and an AV1 sample video for tests (I do not know if the GPU used in your CI/CD is compatible with CUDA AV1 decoding, let's see).