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

Add javascript signalling #88

Merged
merged 9 commits into from
Sep 25, 2024

Conversation

aiden-jeffrey
Copy link
Contributor

@aiden-jeffrey aiden-jeffrey commented Aug 22, 2024

Based on this sample, this adds a way to signal in javascript (from the webpage) a couple of things:

  • "ready" - page is ready for rendering, so enable cefsrc to go to a PAUSED/PLAYING state
  • "eos" - page doesn't have any more content, so push an EOS event onto cefsrc

This is useful for pages that have non-trivial load times and may run out of content (webgl pages, pages with videos, etc).

There is a sample in the html/ directory that shows how to send the signals, e.g.:

window.gstSendMsg({
          request: "ready",
          onSuccess: function(response) { ... }
          },
          onFailure: function(error_code, error_message) { ... }
        });
      }

To test, run a webserver in the html directory, say:

python -m http.server 9000

Then just specify the flag in a run pointing at that page:

gst-launch-1.0 -e cefsrc url="http://localhost:9000/ready_test.html" listen-for-js-signals=true ! video/x-raw, width=1920, height=1080, framerate=60/1 ! cefdemux name=d d.video ! queue max-size-bytes=0 max-size-buffers=0 max-size-time=3000000000 ! videoconvert ! xvimagesink

@MathieuDuponchelle
Copy link
Collaborator

Whee, that looks very interesting, I'll review as soon as I find time :)

gstcefsrc.h Outdated Show resolved Hide resolved
gstcefsrc.cc Outdated Show resolved Hide resolved
@MathieuDuponchelle
Copy link
Collaborator

Can you expand on the commit message for "message: Add message routers and handlers ", explaining why this is actually needed? The code looks fine but I can't quite understand the intent behind it :)

gstcefsrc.cc Outdated Show resolved Hide resolved
@MathieuDuponchelle
Copy link
Collaborator

I know this wasn't the case before, but I think you will want to also add the posting of progress messages to gst_cef_src_start() (refer to eg rtspsrc for an example).

The start method was already blocking while waiting for cef initialization, which isn't great without posting progress messages, but if we now also (optionally) wait for an arbitrary amount of time for the web page to reach a certain state it becomes even more critical to post these :)

You should notice the difference in behavior with gst-launch after the messages are added.

@MathieuDuponchelle
Copy link
Collaborator

Sorry for not reviewing earlier ! This looks very useful to me, though admittedly not entirely generic, I'm OK with merging once my comments are addressed :)

@aiden-jeffrey
Copy link
Contributor Author

though admittedly not entirely generic

I toyed with allowing the user to send all gstreamer events from javascript, but then I'm not sure there is an event that correlates to the "ready" signal I add here. If you can think of a way of making this more flexible / generic, then I'd be happy to improve the API.

@MathieuDuponchelle
Copy link
Collaborator

If you can think of a way of making this more flexible / generic, then I'd be happy to improve the API.

No, I think that's fine for now :)

@aiden-jeffrey
Copy link
Contributor Author

I know this wasn't the case before, but I think you will want to also add the posting of progress messages to gst_cef_src_start() (refer to eg rtspsrc for an example).

Is there a reason cef_status is file level static and not in the GstCefSrc struct?

@MathieuDuponchelle
Copy link
Collaborator

Is there a reason cef_status is file level static and not in the GstCefSrc struct?

Oh hrm wait, this question makes me realize you're probably piggy-backing the wrong mechanism :)

@MathieuDuponchelle
Copy link
Collaborator

cef status is global, it tracks whether cef needs to be deinitialized (because all browsers / cef sources are done rendering).

@aiden-jeffrey
Copy link
Contributor Author

Oh hrm wait, this question makes me realize you're probably piggy-backing the wrong mechanism :)

Yeah.

@MathieuDuponchelle
Copy link
Collaborator

Here you want a separate, per source additional mechanism to wait for readiness, you can probably mirror the existing one

@aiden-jeffrey
Copy link
Contributor Author

Should I use the state_lock and state_cond for the wait mechanics?

@MathieuDuponchelle
Copy link
Collaborator

Yes, that seems appropriate

@MathieuDuponchelle
Copy link
Collaborator

@aiden-jeffrey
Copy link
Contributor Author

Re progress messages, that is not what I meant

So replace where I've added GST_DEBUG_OBJECT calls with your GST_ELEMENT_PROGRESS macro?

Define message handlers for the render and browser processes (to enable
inter-process communication - see
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-inter-process-communication-ipc).

In short this allows us to send messages from js to C++ in the main
"browser" process of cef. The two functions ("gstSendMsg" and
"gsCancelMsg") are attached to the javascript global "window" object.

Note that we need to add a RendererApp in this commit to contain and run
the javascript side of the message routing.

See cef sample for more detail:
https://github.com/chromiumembedded/cef-project/tree/aaccb78661c390a5ce6650df8db54ea5424d431c/examples/message_router
See the sample in the html/ directory for an example usage. This should
be useful for those webpages that have non-trivial load times (webgl
apps, etc) - allowing the page to signal from javascript to the gst
pipeline that the page is ready for display. We can also send an EOS
signal to be turned into an event on cefsrc.

Flag for enabling this feature is listen-for-js-signals, and then on the
javascript side you can send signals (see html sample for detail):

window.gstSendMsg({ request: "ready" | "eos", ...})
@aiden-jeffrey aiden-jeffrey force-pushed the aiden/signal branch 2 times, most recently from 4eb1feb to c4f59c8 Compare September 24, 2024 15:47
@SteveMcFarlin
Copy link

btw. Thanks for the patch.

@aiden-jeffrey
Copy link
Contributor Author

I'm getting a seg fault when starting another pipeline with a cefsrc in after the first cefsrc has emmited the eos due to the javascript signal. It segfaults somewhere in CefInitialize, but i can't work out where because I can't get gstreamer to pick up the plugin when built with Debug symbols.

@aiden-jeffrey
Copy link
Contributor Author

I'm getting a seg fault

Managed to trick the loader to load the debug version - this is what's failing:

[1881725:1889451:1002/150401.084126:FATAL:startup_metric_utils.cc(54)] Check failed: application_start_ticks_.is_null(). 
Trace/breakpoint trap (core dumped)

@MathieuDuponchelle - do you have any ideas? It looks like I'm not shutting things down properly (the cef_status goes to SHUTDOWN after the eos, so I'm not sure where the cleanup is broken).

@MathieuDuponchelle
Copy link
Collaborator

ouch, I don't have an idea right now no

@MathieuDuponchelle
Copy link
Collaborator

can you post a test case?

@aiden-jeffrey
Copy link
Contributor Author

can you post a test case?

I don't have a gst-launch command that allows for this behaviour. Let me see if I can make a simplified version of our application to replicate.

But the basic idea is:

  • create two pipelines
  • start one
  • wait for the eos triggered via the js signal
  • watch the cef_status go to 0
  • start second one
  • see CefInitialize call segfault

@MathieuDuponchelle
Copy link
Collaborator

OK, yes please make a reproducer application so I can take a look on my end, otherwise I'm afraid we'll have to revert this until we come with a fix for the issue :)

@aiden-jeffrey
Copy link
Contributor Author

@MathieuDuponchelle - my colleague set up a repro example last night - I think this is a bug that pre-existed this signals work. See https://github.com/aiden-jeffrey/test-cefsrc for the sample. There's two in there. I managed to repro the segfault on the sha prior to the signals work (002f660) using the sleep binary.

@aiden-jeffrey
Copy link
Contributor Author

It's possible I need to do more cleanup on the rust side. I'm not super confident in rust.

@aiden-jeffrey
Copy link
Contributor Author

aiden-jeffrey commented Oct 3, 2024

@MathieuDuponchelle - I think it might be a regression. 5643c02bda8a67f9397aa15d4905a52c00d6cc16 doesn't have this crash.

I performed a bisect between 5643c02 and 002f660, I had to skip some commits, this was the result of my bisect:

There are only 'skip'ped commits left to test.
The first bad commit could be any of:
189f7bcca308dd0a2f93e8090ac3b153cd542bce
bf0c9420aac6d58446e5638b7803c8e0ef21e034
4e79b292a6eff3f1179b24329fc263bb84e979c5
bc511024498892cb271da756833c8c4fb674dbc2
6e469dc064cef5d78431285784fc20b0f2108a15
779761f653a7d6d95344699aa534723c95ab7a4e
1e4f194c5546bb1fa70e139d28277e9cfcc6f1cd
fb9be4f9e5a88c7ca3880ce0edce7dd7ec51de65
4b8a7447e154ef16327f4bcc3c38a01ef6c31f9e
8383a4c3a8012d9919aa7e1000807c2671b06fef
187b280ddadf388d29b83d57331d83602a193707
bfa3de0fdbcbc22d26e29076595c1ff7742e3b64
6f1823cf71234c7fd19261f53bb6754cadc7e380
We cannot bisect more!

@MathieuDuponchelle
Copy link
Collaborator

MathieuDuponchelle commented Oct 3, 2024

@aiden-jeffrey yeah I was wondering if this was a regression from #83 , it looks likely that it is related. Why did you need to skip these commits? cc @amyspark

@MathieuDuponchelle
Copy link
Collaborator

Thanks for the reproducer by the way

@aiden-jeffrey
Copy link
Contributor Author

Why did you need to skip these commits?

I got some unrelated errors trying to run the sleep binary.

For half of these (for example 189f7bc) I got:

$ cargo run --bin sleep
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/sleep`

(gst-plugin-scanner:1632010): GLib-GObject-CRITICAL **: 13:33:35.045: g_param_spec_enum: assertion 'g_enum_get_value (enum_class, default_value) != NULL' failed

(gst-plugin-scanner:1632010): GLib-GObject-CRITICAL **: 13:33:35.045: validate_pspec_to_install: assertion 'G_IS_PARAM_SPEC (pspec)' failed

(gst-plugin-scanner:1632010): GLib-GObject-CRITICAL **: 13:33:35.045: g_param_spec_ref_sink: assertion 'G_IS_PARAM_SPEC (pspec)' failed

(gst-plugin-scanner:1632010): GLib-GObject-CRITICAL **: 13:33:35.045: g_param_spec_unref: assertion 'G_IS_PARAM_SPEC (pspec)' failed

(sleep:1632002): GLib-GObject-CRITICAL **: 13:33:35.098: g_param_spec_enum: assertion 'g_enum_get_value (enum_class, default_value) != NULL' failed

(sleep:1632002): GLib-GObject-CRITICAL **: 13:33:35.098: validate_pspec_to_install: assertion 'G_IS_PARAM_SPEC (pspec)' failed

(sleep:1632002): GLib-GObject-CRITICAL **: 13:33:35.098: g_param_spec_ref_sink: assertion 'G_IS_PARAM_SPEC (pspec)' failed

(sleep:1632002): GLib-GObject-CRITICAL **: 13:33:35.098: g_param_spec_unref: assertion 'G_IS_PARAM_SPEC (pspec)' failed
GStreamer-WARNING **: 13:36:37.263: Failed to load plugin '/home/aiden/-/gstcefsrc/build/Release/libgstcef.so': /home/aiden/-/gstcefsrc/build/Release/libgstcef.so: undefined symbol: cef_string_multimap_free

@MathieuDuponchelle
Copy link
Collaborator

I see, annoying that the history doesn't build cleanly

@aiden-jeffrey
Copy link
Contributor Author

yeah I was wondering if this was a regression from #83 ,

I tried reverting this merge commit and the issue still persists.

@aiden-jeffrey
Copy link
Contributor Author

I tried reverting this merge commit and the issue still persists.

See https://github.com/aiden-jeffrey/gstcefsrc/tree/aiden/try-revert.

@MathieuDuponchelle
Copy link
Collaborator

Hm, there was no merge commit, but 14 commits merged fast forward, I'm currently building the branch right before the first one of those

@MathieuDuponchelle
Copy link
Collaborator

That's #86 , not #83 :)

@aiden-jeffrey
Copy link
Contributor Author

Just spotted my mistake! Doh.

@MathieuDuponchelle
Copy link
Collaborator

yeah, some commit in #83 introduced the issue, @amyspark do you mind taking a look? cargo run --bin sleep in https://github.com/aiden-jeffrey/test-cefsrc segfaults with master, completes successfully with d33494c and prior :)

@aiden-jeffrey
Copy link
Contributor Author

Pushed a small change (removing log-severity) to that test repo. Now my bisect completes and identifies bc511024498892cb271da756833c8c4fb674dbc2 as the first bad commit.

@aiden-jeffrey
Copy link
Contributor Author

I imagine it relates to cef_inited.

@MathieuDuponchelle
Copy link
Collaborator

I imagine it relates to cef_inited.

In some fashion for sure :)

@aiden-jeffrey
Copy link
Contributor Author

I think the issue here is CefShutdown is getting called, which according to the header should only be called on application exit. I've removed that, but now can't get the MakeBrowser CefTask to run in the second pipeline.

MathieuDuponchelle added a commit that referenced this pull request Oct 4, 2024
Calling CefInitialize multiple times during a process lifetime is not
supported:

* https://magpcss.org/ceforum/viewtopic.php?f=6&t=16430
* https://www.magpcss.org/ceforum/viewtopic.php?f=6&t=466
* https://stackoverflow.com/questions/73172139/cef-can-only-be-initialized-once-per-process-this-is-a-limitation-of-the-underl

Prior to #83 we used to have a ShutdownEnforcer class, but as it
was causing issues on OSX we will simply no longer shut down CEF at
all.

We can revisit if needed, but this is preferable to crashing.

See #88 (comment)
and below
@aiden-jeffrey
Copy link
Contributor Author

Ok. Multiple pipelines just aren't really supported the way this element is currently written. If you run two cefsrc elements at the same time (without calling CefShutdown), Initialize never gets acalled, which means that the second cefsrc just has the properties of the first.

@MathieuDuponchelle
Copy link
Collaborator

@aiden-jeffrey CefInitialize being called a single time is not a problem, but yes the javascript flags property is only used for the first source, which isn't great, other properties don't affect CefInitialize afaict

@MathieuDuponchelle
Copy link
Collaborator

Ah, sandbox too, perhaps others, anyway see #90 , CefInitialize simply can't be called more than once in a given process

MathieuDuponchelle added a commit that referenced this pull request Oct 14, 2024
Calling CefInitialize multiple times during a process lifetime is not
supported:

* https://magpcss.org/ceforum/viewtopic.php?f=6&t=16430
* https://www.magpcss.org/ceforum/viewtopic.php?f=6&t=466
* https://stackoverflow.com/questions/73172139/cef-can-only-be-initialized-once-per-process-this-is-a-limitation-of-the-underl

Prior to #83 we used to have a ShutdownEnforcer class, but as it
was causing issues on OSX we will simply no longer shut down CEF at
all.

We can revisit if needed, but this is preferable to crashing.

See #88 (comment)
and below
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants