ffmpeginteropx / FFmpegInteropX

FFmpeg decoding library for Windows 10 UWP and WinUI 3 Apps
Apache License 2.0
210 stars 53 forks source link

Packages for UWP + WinUI #371

Closed softworkz closed 1 year ago

softworkz commented 1 year ago

Regarding:

Open questions:

How do we want to package this? We could put both UWP and WinUI/Desktop into a single NuGet package. Or we create separate packages for each target platform.

(from https://github.com/ffmpeginteropx/FFmpegInteropX/pull/364#issue-1922398418)

I would actually prefer a single package for UWP and WinUI3, because we are going forward in parallel with UWP and MAUI and all player implementation is in a single project which is multi-targeting UWP and net6. Opposed to having two packages with conditional inclusion, a single nuget package which serves both targets will be easier to handle for both - Visual Studio and the developers working on it.

Though, I haven't been able to get it working with a single package. The problem is the collision of native files, because those for UWP and those for WinUI have the same names and every attempt I made so far to put those files in different folders have failed to far. They seem to be expected to be in the .\runtime\{platform}-{architecture}\native folders. I tried to split them into native-winui and native-uwp folders, and was able to get them into the compilation output, but then they don't get registered in the appx manifest.

The only thing I haven't tried is to use different names, so that all can get into a 'native' folder. Or maybe there's another trick to get this going?

softworkz commented 1 year ago

I finally ended up with a three-package solution where one package includes the WinUI and UWP packages based on target architecture. Something like this:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>$id$</id>
        <version>$version$</version>
        <description>FFmpeg decoding library for Windows 10 UWP and WinUI Apps</description>
        <authors>FFmpegInteropX</authors>
        <projectUrl>https://github.com/ffmpeginteropx/FFmpegInteropX</projectUrl>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <license type="expression">Apache-2.0</license>
        <dependencies>
            <group targetFramework="uap10.0">
                <dependency id="FFmpegInteropX.UWP" version="$version$"/>
            </group>
            <group targetFramework="net6.0-windows10.0.19041.0">
                <dependency id="FFmpegInteropX.WinUI" version="$version$"/>
            </group>
        </dependencies>
        <repository type="git" url="$repositoryUrl$" branch="$repositoryBranch$" commit="$repositoryCommit$"/>
    </metadata>
    <files>
  </files>
</package>

Maybe this solution makes sense for this repo as well, as it would allow to update and continue the primary package?

lukasf commented 1 year ago

Thank you for the information. This made me come up with a new idea about nuget package structure. In a different way than yours, but better than what I had in mind until now.

softworkz commented 1 year ago

Okay great, I'm curious..

lukasf commented 1 year ago

The thing is that I'd like to support different ffmpeg builds. So in future, we might have something like FFmpegInteropX.FFmpeg.LGPL and FFmpegInteropX.FFmpeg.GPL. But if I add either of these as a dependency of our lib, it is cumbersome to get the other one instead. It is possible to exclude all contents from the referenced package and then add a reference to the other one, by manually editing the project file. But it is a hassle for managed projects, and for native projects it requires even more tricks to get working. Alternatively, I could upload a GPL and LGPL package of our lib, with exact same content but different dependency. But that's messy as well, it does not make much sense to have two packages with same content.

But now I thought, I could have a Lib package (UWP and Desktop/WinUI version), which contains only our lib, without dependency to FFmpeg. And then have a main package in GPL and LGPL version, which references the lib (UWP or Dekstop automatically, depending on target framework) plus the right FFmpeg build. Then I only have one package with the actual lib, but can still have GPL and LGPL versions without having to mess with project files and MSBuild conditionals. And for people who roll their own ffmpeg build, they can reference the Lib package instead of the main package, which does not have any dependency. Plus, I have one NuGet which works with both UWP and WinUI, without having all the dlls in it (only the dlls are downloaded which are actually required by build config).

Oh and about your solution: It is in fact possible to add different native dlls to a single nuget package. The trick is to put them into rutime subfolders under the lib\target folders. At least, this is what I read on the nuspec documentation website a while ago.

So you currently have these folders for your managed dlls: lib\uap10.0 lib\net6.0-windows10.0.19041.0

Now you put the native dlls into runtime subfolders below your lib target folder (only shown x86 here): lib\uap10.0 lib\uap10.0\runtimes\win10-x86\native

lib\net6.0-windows10.0.19041.0 lib\net6.0-windows10.0.19041.0\runtimes\win10-x86\native

At least according to the documentation, now only the native dlls for the target framework will be installed. Using this, you should be able to build a single package with native FFmpeg dlls for both UWP and WinUI targets. For us, I want to keep the nuget packages small, so devs only need to download the ones required for their project (UWP or Desktop/WinUI). But for your internal use, a single AiO package might be more practical.

softworkz commented 1 year ago

So you currently have these folders for your managed dlls: lib\uap10.0 lib\net6.0-windows10.0.19041.0

Now you put the native dlls into runtime subfolders below your lib target folder (only shown x86 here): lib\uap10.0 lib\uap10.0\runtimes\win10-x86\native

lib\net6.0-windows10.0.19041.0 lib\net6.0-windows10.0.19041.0\runtimes\win10-x86\native

At least according to the documentation, now only the native dlls for the target framework will be installed. Using this, you should be able to build a single package with native FFmpeg dlls for both UWP and WinUI targets. For us, I want to keep the nuget packages small, so devs only need to download the ones required for their project (UWP or Desktop/WinUI). But for your internal use, a single AiO package might be more practical.

That's interesting - I've tried to many combinations and looked at other packages, but I haven't seen this variant yet. I'll try it for sure, thanks for the hint!

The misconception I had for most of the time was that the .targets file would be relevant, but then I read somewhere that this is only used when consumed by C++ projects, which I was able to confirm. C# and VB instead are doing this purely based on conventions (= folder structure in this case).

This section in the targets file doesn't even get considered:

  <ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'UAP'">
    <Reference Include="$(MSBuildThisFileDirectory)..\..\lib\uap10.0\FFmpegInteropX.winmd">
      <Implementation>FFmpegInteropX.dll</Implementation>
    </Reference>
    <ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win10-$(PlatformTarget)\native\*.dll" />
  </ItemGroup>

Which lead to the situation that the UWP project complained on build:

No implementation file was provided for the .winmd file 'xxx.winmd'. To generate registration information in the app manifest, specify the 'Implementation' metadata on the .winmd reference item in the project file.

Which is exactly what happens in the targets file, but it is simply ignored. Any other place than like runtimes/win10-x64/native didn't work.

Let me see whether lib\uap10.0\runtimes\win10-x86\native will do the trick..

softworkz commented 1 year ago

Unfortunately it doesn't work either:

  <files>

    <file src="..\Output\FFmpegInteropX\x64\Release_UWP\FFmpegInteropX.winmd"   target="lib\uap10.0" />
    <file src="..\Output\FFmpegInteropX\x64\Release_UWP\FFmpegInteropX.xml"     target="lib\uap10.0" />

    <file src="..\Output\FFmpegInteropX\x86\Release_UWP\FFmpegInteropX.dll"     target="lib\uap10.0\runtimes\win10-x86\native" />
    <file src="..\Output\FFmpegInteropX\x86\Release_UWP\FFmpegInteropX.pdb"     target="lib\uap10.0\runtimes\win10-x86\native" />

    <file src="..\Output\FFmpegInteropX\x64\Release_UWP\FFmpegInteropX.dll"     target="lib\uap10.0\runtimes\win10-x64\native" />
    <file src="..\Output\FFmpegInteropX\x64\Release_UWP\FFmpegInteropX.pdb"     target="lib\uap10.0\runtimes\win10-x64\native" />

    <file src="..\Output\FFmpegInteropX\ARM\Release_UWP\FFmpegInteropX.dll"     target="lib\uap10.0\runtimes\win10-arm\native" />
    <file src="..\Output\FFmpegInteropX\ARM\Release_UWP\FFmpegInteropX.pdb"     target="lib\uap10.0\runtimes\win10-arm\native" />

    <file src="..\Output\FFmpegInteropX\ARM64\Release_UWP\FFmpegInteropX.dll"   target="lib\uap10.0\runtimes\win10-arm64\native" />
    <file src="..\Output\FFmpegInteropX\ARM64\Release_UWP\FFmpegInteropX.pdb"   target="lib\uap10.0\runtimes\win10-arm64\native" />

    <file src="FFmpegInteropX.UWP.targets"                          target="build\native"/>

  </files>

It doesn't even pick up the dll for the output, doesn't create the activatableClass entries in the manifest and emits the same warning:

Warning No implementation file was provided for the .winmd file 'C:\Users\asd\.nuget\packages\ffmpeginteropx.uwp\5.1.29\lib\uap10.0\FFmpegInteropX.winmd'. To generate registration information in the app manifest, specify the 'Implementation' metadata on the .winmd reference item in the project file

It would have been nice when it would have worked though, so thanks for the hint.

lukasf commented 1 year ago

Okay, that's unfortunate. Maybe it only works with pure native dlls (like ffmpeg dlls), but not with RuntimeComponents, where the .winmd file needs to be matched to a implementation dll.

softworkz commented 1 year ago

Okay, that's unfortunate. Maybe it only works with pure native dlls (like ffmpeg dlls), but not with RuntimeComponents, where the .winmd file needs to be matched to a implementation dll.

Exactly. There's some hardcoded behavior based on the folder structure which the (pretty sparse) docs even admit implicitly.