microsoft / WindowsAppSDK

The Windows App SDK empowers all Windows desktop apps with modern Windows UI, APIs, and platform features, including back-compat support, shipped via NuGet.
https://docs.microsoft.com/windows/apps/windows-app-sdk/
MIT License
3.79k stars 320 forks source link

Difficult or impossible to reliably detect whether certain video encoding types are supported (e.g. AV1) #4476

Closed benstevens48 closed 2 months ago

benstevens48 commented 3 months ago

Describe the bug

I am trying to determine whether video encoding using AV1 is supported. To do this I check ApiInformation.IsMethodPresent("Windows.Media.MediaProperties.MediaEncodingProfile", "CreateAv1"). However, this returns true on my machine, even though it says in the docs "Windows 11 Insider Preview (introduced in 10.0.23504.0)" and the method is not available when compiling (I am using the 22621 Windows SDK, not any insider preview SDK) and my machine is running 23H2. It's annoying because if I do try to encode AV1 video (not using this method but by using the "Av1" subtype string), it hangs indefinitely. I am struggling to think of how to limit the functionality. Maybe I will have to do a manual Windows version check, but I don't really want to do that.

Steps to reproduce the bug

See description

Expected behavior

ApiInformation.IsMethodPresent (and other related functions) should return false if the method is not really available. If the method is experimental or in preview, then there should be some way to determine that (and I can't see it, but please correct me if I'm wrong).

Screenshots

No response

NuGet package version

None

Packaging type

Packaged (MSIX)

Windows version

Windows 11 version 22H2 (22621, 2022 Update)

IDE

Visual Studio 2022

Additional context

Does not require WindAppSDK, is a WinRT issue.

DarranRowe commented 3 months ago

Are you sure that this is a bug and not the fact that these features could have been backported to this version of Windows 11? Checking in the latest Windows 11 SDK shows that CreateAv1 is available. The metadata also has CreateAv1 available.

Screenshot 2024-06-06 201806

That metadata is what Windows itself has in System32. So the result isn't an error.

bencambridgemaths commented 3 months ago

I just installed the new 26100 SDK, used MediaEncodingProfile.CreateAv1, and it still hangs in the same way. It really should never hang so there's a bug there anyway.

Maybe the bug is that it just doesn't work.

Alternatively, it's not supposed to work currently, in which case there ought to be a way to tell that. It's possible that in fact the encoding capability is going to be added to an update to the AV1 Video Extensions package, which may be released at the same time as the 24H2 update, and then it will work down-level, but in the meantime how can we tell whether it's supported?

DarranRowe commented 3 months ago

One of the big issues here is:

Screenshot 2024-06-13 185629

I don't see anything that you mention happening. My GPU (Radeon 7800 XT) does support hardware AV1 encoding though.

Out of curiosity, if you query for your encoders using Windows.Media.Core.CodecQuery, does that show an available AV1 encoder? I have one:

Encoder name: AMDh264Encoder

Encoder name: AMDh265Encoder

Encoder name: AMDh264Encoder

Encoder name: AMDav1Encoder <-- Here

Encoder name: AMDh265Encoder

Encoder name: HEIFImageExtension

Encoder name: VP9VideoExtensionEncoder

Encoder name: HEVCVideoExtensionEncoder

Encoder name: HEVCVideoExtensionEncoder

Encoder name: H264 Encoder MFT

Encoder name: WMVideo8 Encoder MFT

Encoder name: H263 Encoder MFT

Encoder name: WMVideo9 Encoder MFT

Encoder name: Microsoft MPEG-2 Video Encoder MFT

As I stated, my system does have hardware AV1 encoding.

bencambridgemaths commented 3 months ago

When I say it hangs, I mean encoding hangs i.e.

  var encodingProfile = MediaEncodingProfile.CreateAv1(VideoEncodingQuality.HD720p);
  var preparedTranscoder = await transcoder.PrepareStreamTranscodeAsync(inputStream, outputStream, encodingProfile);
  if (!preparedTranscoder.CanTranscode) {
      return;
  }
 await preparedTranscoder.TranscodeAsync(); //Hangs here

Yes, apparently I do have a hardware encoder - Intel® Hardware Accelerated AV1 Encoder MFT.

DarranRowe commented 3 months ago

That means that, if there is a bug, the bug isn't in the AV1 functionality exposed by WinRT. The transcoding functionality should be built upon Windows Media Foundation, and it is the Media Foundation object that is responsible for all of the work here. Now, there are two major questions, first how large is the file that you are transcoding? Transcoding isn't a fast process, and on my system a file that I was using for testing does take around a minute.

Another thing to remember is that TranscodeAsync returns an IAsyncActionWithProgress. This means that you can hook a progress routine up to it and have it report progress. Have you done that?

An example of this is:

    winrt::Windows::Foundation::IAsyncAction MainWindow::Click_Click(winrt::IInspectable const &, winrt::RoutedEventArgs const &)
    {
        auto profile = Windows::Media::MediaProperties::MediaEncodingProfile::CreateAv1(Windows::Media::MediaProperties::VideoEncodingQuality::HD720p);
        auto in_file = co_await Windows::Storage::StorageFile::GetFileFromPathAsync(LR"(C:\Users\Darran\Documents\wpin.mkv)");
        auto out_folder = co_await Windows::Storage::StorageFolder::GetFolderFromPathAsync(LR"(C:\Users\Darran\Documents)");
        Windows::Storage::StorageFile out_file{ nullptr };
        auto out_file_list = co_await out_folder.GetFilesAsync();
        for (auto file : out_file_list)
        {
            if (file.Name() == L"wpout.mp4")
            {
                out_file = file;
            }
        }

        if (out_file == nullptr)
        {
            out_file = co_await out_folder.CreateFileAsync(L"wpout.mp4");
        }

        Windows::Media::Transcoding::MediaTranscoder transcoder{};
        auto prep_transcoder = co_await transcoder.PrepareFileTranscodeAsync(in_file, out_file, profile);
        if (prep_transcoder.CanTranscode())
        {
            //Note that there is no co_await here.
            auto transcode_async = prep_transcoder.TranscodeAsync();
            //Add a callback to the Progress member, which is IAsyncActionWithProgress<double>.Progress.
            transcode_async.Progress([this](const Windows::Foundation::IAsyncActionWithProgress<double> &async_info, const double &progress)
                {
                    DispatcherQueue().TryEnqueue([this, progress]() {
                        auto fmt_string = std::format(L"{}", progress);
                        Content().Text(fmt_string);
                        }
                    );
                });
            //Now we co_await the async transcoder.
            co_await transcode_async;

        }

        Content().Text(L"File encode complete");

        co_return;
    }

It is C++ due to that being more comfortable for me, but this basically hooks up a progress callback, and this sets the contents of a TextBox to the stringified version of that progress.

Screenshot 2024-06-14 190317

As you can see, it is progressing. Finally, after it does report the transcode as complete:

Screenshot 2024-06-14 190814

The out file has a pretty substantial file size. The video track is around 20 minutes and CBR, so that is what I would expect.

Secondly, are your video drivers up to date? Since this is hardware encoding and if you really are seeing a hang, then this would mean that the Intel video drivers are a good candidate for the bug.

benstevens48 commented 3 months ago

The video is only small and takes a few seconds to encode to H.264 or H.265. I have been using the progress. Some interesting notes on this.

Also, on a related note, while testing supported container and encoding types, I found that trying to use NV12 with a MPEG-4 container (not designed to be supported I guess) will freeze the app.

In conclusion, it seems like AV1 video encoding is not really currently supported, at least on my machine. Probably a software encoder is needed (i.e. an updated AV1 Video Extension package that supports encoding).

I thought that maybe this was the reason that the API was not available in an earlier SDK, but I guess perhaps the IsMethodPresent etc check will never be enough for this sort of scenario so I am going to change the title to reflect how to reliably test for encoding support. Meanwile, in my app I will just mark AV1 encoding as experimental.

codendone commented 2 months ago

Logged an internal bug for the media team. Closing as External.