Skip to content
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

Merged
merged 12 commits into from
Jan 13, 2025
Merged

fix: Solve CUDA AV1 decoding #448

merged 12 commits into from
Jan 13, 2025

Conversation

hugo-ijw
Copy link
Contributor

@hugo-ijw hugo-ijw commented Jan 9, 2025

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).

@facebook-github-bot
Copy link
Contributor

Hi @hugo-ijw!

Thank you for your pull request and welcome to our community.

Action Required

In 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.

Process

In 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 CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Meta Open Source bot. label Jan 9, 2025
@facebook-github-bot
Copy link
Contributor

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(
Copy link
Contributor

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) {
Copy link
Contributor

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;
Copy link
Contributor

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);
Copy link
Contributor

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);

# 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)
Copy link
Contributor

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.

@scotts
Copy link
Contributor

scotts commented Jan 9, 2025

@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.

@scotts
Copy link
Contributor

scotts commented Jan 9, 2025

PR #450 should solve the CUDA test problems.

@hugo-ijw
Copy link
Contributor Author

@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.

Thanks for the comments, I'll try to work on them this morning.
Regarding the GPU tests, I believe it's the same case as h265 (#350), the GPU used in the CI/CD does not support AV1 decoding:
https://github.com/pytorch/torchcodec/actions/runs/12690884678/job/35383538150

@scotts
Copy link
Contributor

scotts commented Jan 10, 2025

@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(
Copy link
Contributor

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;
Copy link
Contributor

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.

@scotts
Copy link
Contributor

scotts commented Jan 10, 2025

@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.

@scotts
Copy link
Contributor

scotts commented Jan 13, 2025

Uh-oh, looks like I was wrong about the while loop declaration! I'll push a fix so we can merge.

@scotts scotts merged commit d8dde5c into pytorch:main Jan 13, 2025
48 of 51 checks passed
@scotts scotts mentioned this pull request Jan 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Meta Open Source bot.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

AV1 support for CUDA backend
3 participants