Can't cast Element to StreamVolume #299

Closed
opened 2025-11-13 16:52:24 +01:00 by Nyeksenn · 6 comments
Nyeksenn commented 2025-11-13 16:52:24 +01:00 (Migrated from github.com)

I want to retrieve a GstElement from my pipeline that implements GstStreamVolume.
I use the following code:

public StreamVolume getStreamVolume() {
    Element sv = pipeline.getByInterface(StreamVolume.getType());
    if (sv != null) {
        return (StreamVolume) sv;
    }
    return null;
}

If I run my program, the cast fails with:

(java:67878): java-gi-WARNING **: 16:51:03.812: java.lang.ClassCastException: class org.freedesktop.gstreamer.gst.Element$Element$Impl cannot be cast to class org.freedesktop.gstreamer.audio.StreamVolume (org.freedesktop.gstreamer.gst.Element$Element$Impl and org.freedesktop.gstreamer.audio.StreamVolume are in unnamed module of loader 'app') in SourceFunc

According to the Gstreamer docs I should be able to use it like that.
The element is also successfully retrieved.

I want to retrieve a GstElement from my pipeline that implements GstStreamVolume. I use the following code: ```java public StreamVolume getStreamVolume() { Element sv = pipeline.getByInterface(StreamVolume.getType()); if (sv != null) { return (StreamVolume) sv; } return null; } ``` If I run my program, the cast fails with: ```bash (java:67878): java-gi-WARNING **: 16:51:03.812: java.lang.ClassCastException: class org.freedesktop.gstreamer.gst.Element$Element$Impl cannot be cast to class org.freedesktop.gstreamer.audio.StreamVolume (org.freedesktop.gstreamer.gst.Element$Element$Impl and org.freedesktop.gstreamer.audio.StreamVolume are in unnamed module of loader 'app') in SourceFunc ``` According to the Gstreamer docs I should be able to use it like that. The element is also successfully retrieved.
jwharm commented 2025-11-13 22:30:12 +01:00 (Migrated from github.com)

Could this be the same issue as #295, that GstAudio hasn't been initialized?
In other words, did you try calling GstAudio.javagi$ensureInitialized(); before using the GstStreamVolume interface?

Could this be the same issue as #295, that GstAudio hasn't been initialized? In other words, did you try calling `GstAudio.javagi$ensureInitialized();` before using the GstStreamVolume interface?
Nyeksenn commented 2025-11-14 10:45:40 +01:00 (Migrated from github.com)

Thanks for the reply.
I don't think that this is the issue.
I added GstAudio.javagi$ensureInitialized(); to my main class and this
fixes #295. I can successfully retrieve the element.
As the log shows, this is a casting exception not a NullPointerException.
For testing I also added all the gestreamer intializer:

GstAudio.javagi$ensureInitialized();
Gst.javagi$ensureInitialized();
GstBase.javagi$ensureInitialized();
GstApp.javagi$ensureInitialized();GstAudio.javagi$ensureInitialized();

This doesn't help.

Thanks for the reply. I don't think that this is the issue. I added `GstAudio.javagi$ensureInitialized();` to my main class and this fixes #295. I can successfully retrieve the element. As the log shows, this is a casting exception not a NullPointerException. For testing I also added all the gestreamer intializer: ```java GstAudio.javagi$ensureInitialized(); Gst.javagi$ensureInitialized(); GstBase.javagi$ensureInitialized(); GstApp.javagi$ensureInitialized();GstAudio.javagi$ensureInitialized(); ``` This doesn't help.
Nyeksenn commented 2025-11-14 10:56:19 +01:00 (Migrated from github.com)

I found how to fix this.
I can't cast to the Interface so I have to create a new StreamVolume.StreamVolume$Impl object like so:

return new StreamVolume.StreamVolume$Impl(sv.handle());

Maybe I should write some documentation for this.

I found how to fix this. I can't cast to the Interface so I have to create a new `StreamVolume.StreamVolume$Impl` object like so: ```java return new StreamVolume.StreamVolume$Impl(sv.handle()); ``` Maybe I should write some documentation for this.
jwharm commented 2025-11-14 18:20:33 +01:00 (Migrated from github.com)

Yes that will work. But it’s a workaround and shouldn’t be necessary. I suspect that there’s a bug related to GstElement being an abstract class. I’ll have to investigate a bit further.

Yes that will work. But it’s a workaround and shouldn’t be necessary. I suspect that there’s a bug related to GstElement being an abstract class. I’ll have to investigate a bit further.
jwharm commented 2025-11-15 11:06:10 +01:00 (Migrated from github.com)

OK I found the cause.

The element that is returned by pipeline.getByInterface(StreamVolume.getType()) is of a type that isn't publicly documented in the GObject-Introspection data; basically a "private class". Java-GI tries to wrap that into a Java proxy class. Because the exact type isn't registered, I have to return a supertype: either a parent class or an implemented interface. Java's static typing doesn't allow instances of "unknown class that extends X and implements Y". It has to be a concrete Java class, but which one should it be? I haven't been able to come up with a foolproof solution yet.

Currently I implemented this:

  • When the parent class isn't a fundamental type (like GObject), choose that one. (This happens in your case.)
  • Otherwise, choose one of its implemented interfaces.
  • If that also fails, fallback to the return type of the function.

I frankly don't know how to properly resolve this. If I prioritize the implemented interfaces instead of the superclass, then other functions could break instead.

OK I found the cause. The element that is returned by `pipeline.getByInterface(StreamVolume.getType())` is of a type that isn't publicly documented in the GObject-Introspection data; basically a "private class". Java-GI tries to wrap that into a Java proxy class. Because the exact type isn't registered, I have to return a supertype: either a parent class or an implemented interface. Java's static typing doesn't allow instances of "unknown class that extends X and implements Y". It has to be a concrete Java class, but which one should it be? I haven't been able to come up with a foolproof solution yet. Currently I implemented this: - When the parent class isn't a fundamental type (like GObject), choose that one. (This happens in your case.) - Otherwise, choose one of its implemented interfaces. - If that also fails, fallback to the return type of the function. I frankly don't know how to properly resolve this. If I prioritize the implemented interfaces instead of the superclass, then other functions could break instead.
jwharm commented 2025-11-23 21:32:02 +01:00 (Migrated from github.com)

One other thing that complicates this issue: The return type of getByInterface() is Element. But the StreamVolume interface can be applied to any GObject-derived class. So I cannot change getByInterface() to return a StreamVolume instance; it would not be an Element and it would throw a ClassCastException, just like you saw when you tried to cast it in your own code.

Of course, the actual object that getByInterface() returns, IS most probably an Element subclass that implements StreamVolume. It's just not a type that java-gi knows about, so there is no wrapper class available on the Java side.

I'm afraid I can't fix this issue cleanly. The only way to handle this, is like you already mentioned:

Element sv = pipeline.getByInterface(StreamVolume.getType());
if (sv != null) {
    new StreamVolume.StreamVolume$Impl(sv.handle());
}
One other thing that complicates this issue: The return type of `getByInterface()` is `Element`. But the `StreamVolume` interface can be applied to any GObject-derived class. So I cannot change `getByInterface()` to return a `StreamVolume` instance; it would not be an `Element` and it would throw a ClassCastException, just like you saw when you tried to cast it in your own code. Of course, the actual object that `getByInterface()` returns, IS most probably an `Element` subclass that implements `StreamVolume`. It's just not a type that java-gi knows about, so there is no wrapper class available on the Java side. I'm afraid I can't fix this issue cleanly. The only way to handle this, is like you already mentioned: ```java Element sv = pipeline.getByInterface(StreamVolume.getType()); if (sv != null) { new StreamVolume.StreamVolume$Impl(sv.handle()); } ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
java-gi/java-gi#299
No description provided.