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

win32: add Media Control support #14338

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Conversation

kasper93
Copy link
Contributor

@kasper93 kasper93 commented Jun 11, 2024

Add support for SystemMediaTransportControls interface. This allows to
control mpv from Windows media control ui.

Fixes: #9336
Fixes: #13813
Fixes: #14007

@kasper93 kasper93 force-pushed the smtc branch 2 times, most recently from 08572fa to 1cfa0a2 Compare June 11, 2024 01:13
Copy link

github-actions bot commented Jun 11, 2024

Download the artifacts for this pull request:

Windows
macOS

@hooke007
Copy link
Contributor

TO fix #14007 ?

osdep/win32/smtc.cc Outdated Show resolved Hide resolved
@kasper93
Copy link
Contributor Author

kasper93 commented Jun 11, 2024

Side note, it is possible to make it work in C using only COM interfaces, but it results in significantly more boilerplate code and is completely not worth the effort. As always patches welcome, if someone wants to rewrite it.

@na-na-hi
Copy link
Contributor

Since this implementation is just a libmpv client, can you consider making it a C plugin instead? This would avoid adding any dependency to mpv core.

@kasper93 kasper93 force-pushed the smtc branch 2 times, most recently from c0cd6f5 to fac1869 Compare June 11, 2024 10:54
@kasper93
Copy link
Contributor Author

kasper93 commented Jun 11, 2024

Since this implementation is just a libmpv client, can you consider making it a C plugin instead? This would avoid adding any dependency to mpv core.

I've considered it, but since it is pretty core player functionality to integrate into system and macOS does the same. I decided to integrate it inside.

@na-na-hi
Copy link
Contributor

na-na-hi commented Jun 11, 2024

but since it is pretty core player functionality to integrate into system and macOS does the same

In this case I would expect proper integration. At least the thumbnail needs to work, which doesn't seem to be the case for now. If this is not possible from a libmpv client, a different approach is needed.

@kasper93
Copy link
Contributor Author

kasper93 commented Jun 11, 2024

In this case I would expect proper integration. At least the thumbnail needs to work, which doesn't seem to be the case for now. If this is not possible from a libmpv client, a different approach is needed.

Thumbnails are not supported on macOS either, I don't think this is a blocker. Generally thumbnail support in mpv is another big topic and out of the scope of this PR. Note that we don't support thumbnails on OSC timeline either.

Either way, thumbnail or more likely periodic screenshot of current frame is possible in the future. (and not that difficult)

@kasper93 kasper93 force-pushed the smtc branch 2 times, most recently from 7e6fbd8 to 0d36d87 Compare June 11, 2024 11:52
@na-na-hi
Copy link
Contributor

This also fixes #13813 and #9336.

meson.build Outdated Show resolved Hide resolved
osdep/threads-win32.h Outdated Show resolved Hide resolved
osdep/win32/smtc.h Outdated Show resolved Hide resolved
osdep/win32/smtc.cc Outdated Show resolved Hide resolved
player/main.c Show resolved Hide resolved
@na-na-hi
Copy link
Contributor

Currently the SMTC interface in Windows 11 doesn't display the mpv icon and program name. The built-in Windows media player does that, and once the Windows media player is run and closed, mpv is stuck with the wrong program icon and name.

@kasper93
Copy link
Contributor Author

kasper93 commented Jun 11, 2024

Currently the SMTC interface in Windows 11 doesn't display the mpv icon and program name. The built-in Windows media player does that, and once the Windows media player is run and closed, mpv is stuck with the wrong program icon and name.

It works the same way with Firefox, if you have ideas how Windows expects this information to be given, let me know. It clearly doesn't get that information from hwnd.

EDIT: It is possible that it works only for installed application that have app id defined.

@kasper93
Copy link
Contributor Author

kasper93 commented Jun 12, 2024

At least the thumbnail needs to work, which doesn't seem to be the case for now.

I have experimented a little and it works pretty good now. I need to glue everything together, because there are multiple cases, local files, external files, embedded files, etc. I will finish it when I get time, but you can expect proper support.'

EDIT: To be honest, I find thumbnail itself not that useful. Compared to other features of this PR, so not sure why this is required, but it will work.

@na-na-hi
Copy link
Contributor

It is possible that it works only for installed application that have app id defined.

From a look at the API this seems to be the case.

To be honest, I find thumbnail itself not that useful.

Personally I expect this feature to behave in a similar way as on mobile platforms. So I at least expect album arts to work. For videos, I find most apps don't attempt to do this, or only show a random frame, so missing thumbnail isn't too bad.

The above problem of icon and program name might also be alleviated with the "thumbnail" by displaying the mpv icon when no thumbnail is applicable.

osdep/win32/smtc.cc Outdated Show resolved Hide resolved
@zhongfly
Copy link

Perhaps related, would it be possible to add thumbnail-toolbars(prev,play/pause,next) to the taskbar?
https://learn.microsoft.com/en-us/windows/win32/shell/taskbar-extensions#thumbnail-toolbars

image

@kasper93
Copy link
Contributor Author

kasper93 commented Jun 12, 2024

The above problem of icon and program name might also be alleviated with the "thumbnail" by displaying the mpv icon when no thumbnail is applicable.

I found it "cheap", if you get what I mean. Like pushing logo in the place where it shouldn't have been.

From a look at the API this seems to be the case.

I've taken a look and I'm almost certain they are getting app info for installed packages. Something like

// id for mpv is `mpv.exe`
using winrt::Windows::ApplicationModel::AppInfo;
using winrt::Windows::Foundation::Size;
auto app = AppInfo::GetFromAppUserModelId(id);
auto di = app.DisplayInfo();
auto name = di.DisplayName();
auto logo = di.GetLogo(Size(96, 96));

There is no app registered for mpv. There are possibilities like https://blogs.windows.com/windowsdeveloper/2019/10/29/identity-registration-and-activation-of-non-packaged-win32-apps/ but it still would need to be installed and have dedicated package. I don't think it is feasible for mpv in any way.

Perhaps related, would it be possible to add thumbnail-toolbars(prev,play/pause,next) to the taskbar?

No, this is completely not related. This is completely different interface, with basically custom buttons that you can put there. I'm not interested in supporting this. Maybe one day, this taskbar stuff could be merged into one place, but this is out of the scope of this PR.

@Andarwinux
Copy link
Contributor

There is no app registered for mpv. There are possibilities like https://blogs.windows.com/windowsdeveloper/2019/10/29/identity-registration-and-activation-of-non-packaged-win32-apps/ but it still would need to be installed and have dedicated package. I don't think it is feasible for mpv in any way.

But this is feasible for libmpv. In addition, it is possible to use Add-AppxPackage -Register to register mpv in-place without signature, and mpv-packaging can do this for end-users.

@kasper93
Copy link
Contributor Author

In this case it will possibly work or not. I don't know.

@kasper93
Copy link
Contributor Author

not sure why you direct that towards me. i didn't argue against this, neither did i say it's a boogeyman, nor did i say it's bad in any way. i am just saying that it's not completely comparable to the macOS situation.

I don't aim directly towards you. Just describing the situation. As for macOS comparison, I compared mostly the usage of client api. Which I think it a good thing, as it abstract a lot of synchronization details that would have to be handled otherwise.

@Dudemanguy
Copy link
Member

C++ is not some meme language and all of us already have c++ compilers installed anyway so I have no problem with it being in the codebase if it's needed for something. I don't know anything about windows so I don't think my review here would be of any real use.

@na-na-hi
Copy link
Contributor

na-na-hi commented Jun 16, 2024

Wouldn't this only increase the complexity of the solution from both a development and user perspective?

It doesn't increase the complexity on the user side if properly done. VLC for example has everything as plugins, yet it doesn't affect its "just works" reputation because they are all bundled in the official distribution. mplayer also had lots of parts "modular" by making libao and libvo separate libraries, but mpv moved away from it. Efforts like #11145 are needed if mpv wants to be modular again.

About C++ usage, in this particular case it's well separated and optional, and is needed to reasonably interface with OS APIs, so I think it's OK. But I would avoid adding it to platform-independent code for now, especially since a significant portion of *nix users would be certainly driven away by it.

@kasper93
Copy link
Contributor Author

It doesn't increase the complexity on the user side if properly done.

Well of course. I'm saying in the context of current mpv ecosystem. We do not provide any official binaries/packages. All plugins are fully on the user side to find/copy/install. Also in this case it would be cplugin, so user has to build it or download some pre-compiled binary. I think it is clear that the process is not that accessible. Especially for Windows users, who generally are less tech-savvy, than *nix users. Of course there are solutions to everything I say here, but we should take and step back first and think what is the real problem that we are solving here.

About the modularization of mpv. It would be possible, but in the same time not sure there is much gain. We are just to small to partition things into smaller entities. And wm4 really disliked this idea, looking back at his opinion about libplacebo.

But I would avoid adding it to platform-independent code for now, especially since a significant portion of *nix users would be certainly driven away by it.

Agreed, generally mpv structure doesn't fit C++, it is C code base at is heart. Usage of it should be limited to where strictly beneficial, like imho in the current PR. Spreading this further is not on the table.

@Andarwinux
Copy link
Contributor

Almost every windows user is using mpv.exe single static executable, which makes mpv/libmpv easy to distribute and maintain, so we want to avoid non-system dlls as much as possible, especially for core media functions like SMTC.

@kasper93
Copy link
Contributor Author

especially since a significant portion of *nix users would be certainly driven away by it.

No offense to anyone who might feel strongly about this, but I think "significant portion" is an exaggeration. Additionally, libplacebo already uses C++, so I hope this "significant portion" of users are already not using mpv, for better or worse. Sorry.

@na-na-hi
Copy link
Contributor

na-na-hi commented Jun 16, 2024

My post was mostly about potential contributors. I doubt normal users care about this much. *nix has most of its API in C so modern C++ knowledge isn't required to make the most use out of *nix. If mpv code were all converted to modern C++ they will simply stop contributing. Just saying this since most mpv developers have *nix background.

Additionally, libplacebo already uses C++

It's also in a limited fashion, and doesn't make the rest of code require modern C++ knowledge for contribution.

@kasper93
Copy link
Contributor Author

My post was mostly about potential contributors. I doubt normal users care about this much. *nix has most of its API in C so modern C++ knowledge isn't required to make the most use out of *nix. If mpv code were all converted to modern C++ they will simply stop contributing. Just saying this since most mpv developers have *nix background.

Fair, but no one suggesting that. As you noticed *nix has most of its API in C, so it is natural to use C as a base language.

It's also in a limited fashion, and doesn't make the rest of code require modern C++ knowledge for contribution.

Exactly, and that's the idea. Everything needs context when talking about those things.

DOCS/man/options.rst Outdated Show resolved Hide resolved
@sfan5
Copy link
Member

sfan5 commented Jun 16, 2024

Wouldn't this only increase the complexity of the solution from both a development and user perspective? I might be missing something, so please let me know what issues or problems this solution would resolve.

It solves no problems. The point of this discussion is how careful we want to be with pulling bigger dependencies into the mpv codebase.

EDIT: It is in separate meson.build in osdep/win32. And only if it is enabled C++ language is added. Just in case, it wasn't clear.

Sure, that's good. I didn't check the changes closely before commenting.

Because it is literally separated as much as possibly can if we would like to link into mpv.exe still.

Well if I look into smtc.cc right now I can see that it includes a bunch of internal headers instead of just libmpv/client.h. This means we have to ensure that a bunch of internal headers are also valid C++20 (not that I expect this to be a problem).
But this is the kind of separation that can be useful to keep to avoid other code being "infected".

I don't understand the inconvenience you are referring to. Focusing on this PR, it only adds an optional dependency on a few headers, which greatly simplifies the code. As for C++, we already have all the necessary components; it's not like we're adding Rust or Swift.

The inconvenience relating to mpris is that AFAIR interacting with dbus requires pulling in big dependencies and there's no good standalone library for it.
I guess calling C++ an inconvenience is inaccurate. You indeed get it for free with the compiler that is already installed.

Since you have it integrated within mpv-android too, it seems you agree that it's a feature deserving of integration in the media player frontend. I assume you don't require users to install an external application to handle this basic feature.

Aside from technical limitations that make this the only workable solution I think this argument misses its mark. mpv-android is an mpv frontend and we're discussing what to include in mpv itself here.
I do agree that for media control integration thumbnails are an obvious feature that should exist.

I think the main question is, what is mpv.exe? Is it a media player or just a toolkit to DIY one?

This is indeed the main question and we don't even have any design or direction document on it.

My personal opinion is that mpv is something inbetween. Pretty bare-bones with some obvious tweaks/plugins/scripts you can add to make it into more of a full featured player. (Though this was more true years ago than it is today).

I'm not sure where this modularization requirement is coming from. Not to mention the entire macOS integration, but that's another topic.

I don't have a strong opinion on whether mpv should or should not ship this feature, but as someone who's been with mpv for a few years my aim is for mpv to keep its identity the way it was.
mpv during the wm4 era held (perceived) code quality/architecture/cleanliness in high regard, so much that otherwise useful features suffered (see: DVD support). The macOS sitation is indeed an exception...
This sounds vague, might be obvious to state but I wanted illustrate where my opinion came from. Maybe trying to stay on the "old ways" is also irrational.

We even embed lua source into the mpv binary.

I suppose convenience of distribution was considered important, since on Unix it's not unusual at all for a program to ship data files separately and to require them to run.

Why isn't youtube-dl an external feature?

Good point. I think it's due to the close interworking that is required for some formats.
I think the ytdl_hook is a good example of how a feature is nicely separated from the rest of the player.

How are users supposed to know that? They just want to download a player and have it work.

But as @na-na-hi now pointed out this is not necessarily a conflict. These things could be separated and be the default.

osdep/win32/smtc.cc Outdated Show resolved Hide resolved
osdep/win32/smtc.cc Outdated Show resolved Hide resolved
osdep/win32/smtc.cc Outdated Show resolved Hide resolved
osdep/win32/smtc.cc Outdated Show resolved Hide resolved
@kasper93
Copy link
Contributor Author

kasper93 commented Jun 17, 2024

Well if I look into smtc.cc right now I can see that it includes a bunch of internal headers instead of just libmpv/client.h. This means we have to ensure that a bunch of internal headers are also valid C++20 (not that I expect this to be a problem).
But this is the kind of separation that can be useful to keep to avoid other code being "infected".

I had that in mind, which is why it mostly uses the standard client.h and a few helper functions, like thread, path normalization, and node helpers. I wanted to keep also the style of mpv code, even though it is C++. I would easily use std::thread, but since it is internal currently I used our helpers as much as possible.

Side note, I think node.h should even be a public header for libmpv, as they are nice wrappers over the node_map thing.

The inconvenience relating to mpris is that AFAIR interacting with dbus requires pulling in big dependencies and there's no good standalone library for it.

Understandable. I was also a little reluctant with cppwinrt, even though it is header-only. While I would prefer to avoid it, as I mentioned before, it greatly simplifies our code.

This is indeed the main question and we don't even have any design or direction document on it.

¯\(ツ)/¯ I get the minimalism, but at the same time, it shouldn't hold us back on features. Generally, I think we should follow common sense. In this case, I don't think the burden of the added code/dependency is significant. It may look like it, but in practice, it's just another language mode and a few headers. Of course, it would be different if it added Boost or Qt as a dependency or something equally insane.

To complement this, SMTC plugins actually exist for mpv, although I have never tested any of them. Since there was no cplugin support for mpv on Windows, they resort to a lua script talking to another application that talks to the SMTC. I feel like this is completely unnecessary bloat.

Is it required to be inside mpv.exe? No, but I think it is a cleaner way to handle it, both from a code and user perspective.

I would expect mpv.exe to be a minimalistic yet powerful player.

This sounds vague, might be obvious to state but I wanted illustrate where my opinion came from. Maybe trying to stay on the "old ways" is also irrational.

This sounds perfectly reasonable, and I don't want to be this guy who topples the sandcastle built over the years. At the same time, I think we should evaluate things individually as they come and decide accordingly.

That said, I'm okay with moving this to a plugin in another repository separate from mpv. I would prefer to have it inside mpv, but if that's not compatible with mpv core values, I'm okay with that too. My responses before were mostly motivated by a desire to understand the reasoning behind this.

I suppose convenience of distribution was considered important, since on Unix it's not unusual at all for a program to ship data files separately and to require them to run.

It's similar on Windows, where mpv is often distributed as a single binary. To make it easier, it would require an installer, which is the common Windows way to install programs with their dependencies. However, we already know that we are not going to ship or maintain any packaging solutions. Hence, having it directly inside is for convenience of distribution too.

Good point. I think it's due to the close interworking that is required for some formats.
I think the ytdl_hook is a good example of how a feature is nicely separated from the rest of the player.

I think the main reason is that it is a highly desired feature and makes mpv much more convenient to use with internet videos. Some features just deserve to be included even in a minimalistic player video.

- Move C only warnings to own test
- Add missing native arg for add_languages
- Undef __STRICT_ANSI__ only for c/objc
Not really needed currently for our limited use of winrt.
Root directory is added to include directories and `VERSION` conflicts
with `#include <version>` which is standard library header.
Will be useful for future commits.
event->event_id == MPV_EVENT_PLAYBACK_RESTART)
{
if (ctx.hwnd)
SendMessageW(ctx.hwnd, WM_MP_EVENT, WPARAM(event), 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues:

  • The convention for window messages is to use lparam for pointers. Using wparam looks weird to me.
  • TOCTOU with ctx.hwnd: by the time this function is called the HWND might be no longer valid, or even a nullptr. SendMessageW might block indefinitely here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention for window messages is to use lparam for pointers. Using wparam looks weird to me.

I've been using both of them before, changed.

TOCTOU

I don't see how. This check only performs a check once the window is created on the other thread. The only way to initiate window destruction is from this thread, so no way it disappears while we are sending message. Unless destroyed externally, but this is not supported case.

I can add a mutex for initialization though, so we don't miss some events from mpv, but frankly, I doubt we manage to load file faster than this window is created, so I don't think it is really necessary.

Copy link
Contributor

@na-na-hi na-na-hi Jun 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way to initiate window destruction is from this thread, so no way it disappears while we are sending message.

True, but it's by posting a WM_CLOSE message which is processed asynchronously in the win event thread. Then ctx.close will be set only after it exits from the message loop, which can be after the ctx.close check in the mpv event thread. If this happens, ctx.hwnd is read from the mpv event thread, which the win event thread no longer processes. If the ctx.hwnd is set to null by the win event thread between it's null checked and used in the mpv event thread, SendMessageW will be called with nullptr as HWND.

EDIT: I saw that the mpv event loop is ended after the WM_CLOSE message so it shouldn't be a problem if the window is terminated by mpv and not from some outside source.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that the mpv event loop is ended after the WM_CLOSE message

Exactly, there is a break there. And yes, if something externally closes this window, thing would break, but I don't consider this a valid scenario. Else we would need to notify the other thread about it and in this case probably whole thing would need to be refactored to work.

@kasper93 kasper93 force-pushed the smtc branch 2 times, most recently from 0cbcae3 to d08e182 Compare June 23, 2024 15:23
Add support for SystemMediaTransportControls interface. This allows to
control mpv from Windows media control ui.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
8 participants