# Hacked Movie EoS Event Library

**URL:** <https://discourse.processing.org/t/hacked-movie-eos-event-library/39375>\
**Category:** Gallery\
**Tags:** contributions\
**Created:** [October 20, 2022, 5:23pm UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375 "2022-10-20T17:23:48Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)\
**Post date:** [October 20, 2022, 5:23pm UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375/1 "2022-10-20T17:23:48Z")

</div>

Processing’s [Video library](https://processing.org/reference/libraries/video/index.html) doesn’t offer any means to flag a Movie stream has ended.

A long time ago I’ve posted a hack solution that used to work back then:

- [Movie begin and end events - Processing 2.x and 3.x Forum](http://Forum.Processing.org/two/discussion/14990/movie-begin-and-end-events#Item_1)

But more recent changes to the library made that hackish workaround to stop working.

This time around I’ve decided to create a HackedMovie subclass which `extends` the [Movie class](https://GitHub.com/processing/processing-video/blob/latest-processing3/src/processing/video/Movie.java):

- [Hacked Movie EoS Event · GitHub](http://Gist.GitHub.com/GoToLoop/67023ba3e9ba1d0301c40f7c030d176a)

It features a new `boolean` field _ended_ which is set to `true` when a Movie stream has finished.

Also a new callback **eosEvent()**, in addition to **movieEvent()**, which is invoked right after a Movie stream has finished.

More EoS listeners can be added via new method HackedMovie::**addEOSListener()**.

Just add the [**“HackedMovie.java”**](https://Gist.GitHub.com/GoToLoop/67023ba3e9ba1d0301c40f7c030d176a#file-hackedmovie-java) file to your sketch and `import` it like this:  
`import processing.video.HackedMovie;`

In short, HackedMovie subclass is used as a replacement to the Movie class.

P.S.: That “.java” file relies on the original video library, so you have to install it so subclass HackedMovie can work.

Important update (v2.1.0):  
Callback **eosEvent()** requires now a Movie or HackedMovie parameter, just like **movieEvent()**:

## “HackedMovie.java”:

```java
/**
 * Hacked Movie EoS Event (v2.1.0)
 * by GoToLoop (2022-Oct-20)
 *
 * Gist.GitHub.com/GoToLoop/67023ba3e9ba1d0301c40f7c030d176a
 *
 * Discourse.Processing.org/t/hacked-movie-eos-event-library/39375
 * Forum.Processing.org/two/discussion/14990/movie-begin-and-end-events#Item_1
 */

package processing.video;

import processing.core.PApplet;
import java.lang.reflect.Method;

import org.freedesktop.gstreamer.GstObject;
import static org.freedesktop.gstreamer.Bus.EOS;

public class HackedMovie extends Movie {
  public Method eosEventMethod;
  public boolean ended;

  public HackedMovie(final PApplet p, final String filename) {
    super(p, filename);
    addEOSListener(new MyEOS()).lookUpAndActivateEOSEventCallback();
  }

  public HackedMovie addEOSListener(final EOS eos) {
    playbin.getBus().connect(eos);
    return this;
  }

  public HackedMovie lookUpAndActivateEOSEventCallback() {
    final Class<?> p = eventHandler.getClass();

    try {
      eosEventMethod = p.getMethod("eosEvent", Movie.class);
    }
    catch (final NoSuchMethodException e) {
    }

    try {
      eosEventMethod = p.getMethod("eosEvent", HackedMovie.class);
    }
    catch (final NoSuchMethodException e) {
    }

    return this;
  }

  @Override public void setEventHandlerObject(final Object o) {
    super.setEventHandlerObject(o);

    final Class<?> p = o.getClass();

    try {
      movieEventMethod = p.getMethod("movieEvent", HackedMovie.class);
    }
    catch (final NoSuchMethodException e) {
    }
  }

  @Override public void play() {
    super.play();
    ended = false;
  }

  class MyEOS implements EOS {
    @Override public void endOfStream(final GstObject _) {
      ended = true;

      if (eosEventMethod != null) try {
        eosEventMethod.invoke(eventHandler, HackedMovie.this);
      }
      catch (final ReflectiveOperationException e) {
      }
    }
  }
}

```

---

<div class="post-metadata">

**Author:** ![neilcsmith](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/neilcsmith/32/144_2.png) [@neilcsmith](https://discourse.processing.org/u/neilcsmith)\
**Post date:** [October 21, 2022, 1:42pm UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375/2 "2022-10-21T13:42:02Z")

</div>

You know you can just use `movie.playbin.getBus()` already, right? That field is public [processing-video/Movie.java at master · processing/processing-video · GitHub](https://github.com/processing/processing-video/blob/master/src/processing/video/Movie.java#L59)

Which also means you can do things like change the movie file, etc.

---

<div class="post-metadata">

**Author:** ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)\
**Post date:** [October 21, 2022, 4:53pm UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375/3 "2022-10-21T16:53:00Z")

</div>

> [@neilcsmith](#):
>
> You know you can just use `movie.playbin.getBus()` already, right?

It’s not “already”! AFAIK, it’s always been so. Field _playbin_ has always been `public`.

BtW, that’s exactly what I’ve used in my **addEOSListener()** method:

```java
public HackedMovie addEOSListener(final EOS eos) {
  playbin.getBus().connect(eos);
  return this;
}

```

> [@neilcsmith](#):
>
> Which also means you can do things like change the Movie file, etc.

The idea behind this Processing video library, which includes classes such as Movie & Capture, is to be a simplified wrapper for the GStreamer framework library:

> **[GitHub - gstreamer-java/gst1-java-core: Java bindings for GStreamer 1.x](https://github.com/gstreamer-java/gst1-java-core)**
>
> Java bindings for GStreamer 1.x. Contribute to gstreamer-java/gst1-java-core development by creating an account on GitHub.

Seems like you’re the lead maintainer of the repo above, and probably wrote this:

> Please note, this is not an easy-to-use multimedia framework for beginners. It currently requires people to know the Java language and be familiar with the GStreamer framework (and possibly be prepared to apply things from tutorials on GStreamer programming in other languages to the Java bindings).

So I had to study how the Movie class was creating its own end-of-stream listener via method Bus::**conect()** and peruse the GStreamer source in order to create my HackedMovie subclass, which relies on the _playbin_ `public` field as well.

We shouldn’t expect Processing’s target public to do all that by their own just to add such basic end-of-stream listener to the Movie class.

This is an example of a sketch using my HackedMovie subclass in place of the original Movie class:

```java
/**
 * Hacked Movie EoS Event (v2.1.0)
 * by GoToLoop (2022-Oct-20)
 *
 * Gist.GitHub.com/GoToLoop/67023ba3e9ba1d0301c40f7c030d176a
 *
 * Discourse.Processing.org/t/hacked-movie-eos-event-library/39375
 * Forum.Processing.org/two/discussion/14990/movie-begin-and-end-events#Item_1
 */

import processing.video.HackedMovie;
HackedMovie vid;

static final String FILENAME = "transit.mov";

void setup() {
  frameRate(30);
  textSize(050);
  textAlign(CENTER, BASELINE);
  fill(#FFFF00);

  vid = new HackedMovie(this, FILENAME);
  vid.play();

  while (vid.width == 0 | vid.height == 0) delay(5);
  surface.setSize(vid.width, vid.height);
}

void draw() {
  if (!vid.ended) background(vid);

  else {
    background((color) random(#000000));
    text("Playback has finished!", width >> 1, height >> 1);
  }
}

void movieEvent(final HackedMovie m) {
  m.read();
}

void eosEvent(final HackedMovie m) {
  frameRate(1);
  println(m);
}

```

---

<div class="post-metadata">

**Author:** ![neilcsmith](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/neilcsmith/32/144_2.png) [@neilcsmith](https://discourse.processing.org/u/neilcsmith)\
**Post date:** [October 21, 2022, 6:04pm UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375/4 "2022-10-21T18:04:24Z")

</div>

> [@GoToLoop](#):
>
> The idea behind this Processing video library, which includes classes such as Movie & Capture, is to be a simplified wrapper for the GStreamer framework library:

I’m well aware of what Processing Video library is and how it uses our code and code originally from PraxisLIVE. Unfortunately, it’s missing some quite obvious functionality from PlayBin, like changing the playing file.

_Many_ parts of the GStreamer wrapper are complicated (yes, I know that text), but the PlayBin side is not that complicated, particularly with addition of lambdas in Processing 4. Still, you have to be really careful with threading using the Bus though, so you’re not simplifying that. What’s wrong with querying whether the Movie is playing anyway btw?

---

<div class="post-metadata">

**Author:** ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)\
**Post date:** [October 21, 2022, 6:53pm UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375/5 "2022-10-21T18:53:18Z")

</div>

> [@neilcsmith](#):
>
> , but the _playbin_ side is not that complicated, particularly with addition of lambdas in Processing 4.

A pity I’m still on P3, which even it’s based on Java 8 it surprisingly lacks lambdas!

On P3, in order to invoke method Bus::**connect()**, I’d need to import both class GstObject & interface Bus.EOS in order to construct its required parameter.

On a P4 lambda I could omit both datatypes b/c interface Bus.EOS fits the definition of a [@FunctionalInterface](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/FunctionalInterface.html).

> [@neilcsmith](#):
>
> … you have to be really careful with threading using the Bus though, so you’re not simplifying that.

I’m just adding a very useful EOS functionality to the Movie class, got nothing to do w/ threading.

Adding an **eosEvent()** callback plus an _ended_ `public boolean` field is the very definition of simplification!

> [@neilcsmith](#):
>
> What’s wrong with querying whether the Movie is _playing_ anyway btw?

A Movie can be in a _paused_ state instead of _playing_.

Also there’s a _repeat_ state, which we’ll make us miss the EOS event if we’re relying on checking the _playing_ state.

If we have a list of Movie objects to **play()** 1 after the other, it’s very convenient to have an EOS event callback.

---

<div class="post-metadata">

**Author:** ![neilcsmith](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/neilcsmith/32/144_2.png) [@neilcsmith](https://discourse.processing.org/u/neilcsmith)\
**Post date:** [October 22, 2022, 8:45am UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375/6 "2022-10-22T08:45:03Z")

</div>

> [@GoToLoop](#):
>
> I’m just adding a very useful EOS functionality to the Movie class, got nothing to do w/ threading.
> 
> Adding an **eosEvent()** callback plus an _ended_ `public boolean` field is the very definition of simplification!

Of course it’s got everything to do with threading. You’ve just introduced one more thread into the user’s sketch. The methods `draw()`, `movieEvent()` and `eosEvent()` are all called on different threads, the latter two in contexts with implications.

I’ve done a lot of work on beginner APIs and teaching non-programmers. Concurrency is hard! If they can understand the implications of that, they’re past the stage they can write a callback. If you want to make Video more beginner friendly, I’d suggest queuing and executing both `movieEvent()` and `eosEvent()` in the animation thread in a pre-draw handler.

Polling a boolean flag from draw() is much easier, but you can already get the required logic for that. If you actually need an EOS on a repeating video, you could just manually repeat.

> [@GoToLoop](#):
>
> If we have a list of Movie objects to **play()** 1 after the other, it’s very convenient to have an EOS event callback.

It’s also very convenient to use one Movie object and access the PlayBin to change the file it’s playing - `movie.playbin.setInputFile()` or `movie.playbin.setURI()`.

---

<div class="post-metadata">

**Author:** ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)\
**Post date:** [October 22, 2022, 9:56am UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375/7 "2022-10-22T09:56:20Z")

</div>

> [@neilcsmith](#):
>
> If you want to make Video more beginner friendly, I’d suggest queuing and executing both **movieEvent()** and **eosEvent()** in the animation thread in a pre-draw handler.

This hackish subclass is about adding a simple quick & easy-to-use feature in addition to the already provided **movieEvent()**.

Therefore **eosEvent()** does indeed have the same common limitations as any pre-defined callbacks from any other Processing libraries such as [**serialEvent()**](https://processing.org/reference/libraries/serial/Serial_serialEvent_.html), [**disconnectEvent()**](https://processing.org/reference/libraries/net/disconnectEvent_.html), etc.

Adding event queues and then force their dequeues to happen within the sketch’s “Animation” Thread (like **keyPressed()**, **mousePressed()**, etc.) via **pre()** or **post()** is beyond the scope of not only my hack subclass but also for most Processing 3rd-party wrapper libraries that deal w/ hardware bindings.

> [@neilcsmith](#):
>
> … and access the _playbin_ to change the file it’s playing…

That’d be a really nice addition for the devs to implement in the Movie class.

But considering Movie is a subclass of PImage I wonder if changing files w/ different dimensions would go smoothly w/ just **setInputFile()** alone.

Anyways, in order to know when is the time to invoke methods **setInputFile()** or **setURI()** to swap the current playing video after finished, we still need to catch the end-of-stream event.

---

<div class="post-metadata">

**Author:** ![neilcsmith](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/neilcsmith/32/144_2.png) [@neilcsmith](https://discourse.processing.org/u/neilcsmith)\
**Post date:** [October 22, 2022, 11:13am UTC](https://discourse.processing.org/t/hacked-movie-eos-event-library/39375/8 "2022-10-22T11:13:29Z")

</div>

> [@GoToLoop](#):
>
> Adding event queues and then force their dequeues to happen within the sketch’s “Animation” Thread (like **keyPressed()**, **mousePressed()**, etc.) via **pre()** or **post()** is beyond the scope of not only my hack subclass but also for most Processing 3rd-party wrapper libraries that deal w/ hardware bindings.

Yes, I’ve pointed out before that I consider this to probably be a general failing in the Processing ecosystem. It’s rarely necessary to expose this problem to the end user in a system designed for beginners. When it’s with a library I wrote the threading and memory management for, then yes, I’ll call it out!

You quoted - “Please note, this is not an easy-to-use multimedia framework for beginners. It currently requires people to know the Java language and be familiar with the GStreamer framework” but seem to think this doesn’t apply to understanding the threading context.

Like I said, pass the event into the animation thread, or provide more ways to poll the playbin from draw(), if the existing movie class doesn’t have the required flags.

> [@GoToLoop](#):
>
> But considering Movie is a subclass of PImage I wonder if changing files w/ different dimensions would go smoothly w/ just **setInputFile()** alone.

Yes, it looks like callback registers the new width and height correctly, but perhaps doesn’t pass it up to the superclass. Possibly a mistake that can be rectified - it is handled correctly in PraxisLIVE from where most of the existing callback code came from.

The fact that Movie _is_ an image, rather than providing a stream of images is a problem with the Video library API in my opinion. Creates a whole bunch of issues, including this.

> [@GoToLoop](#):
>
> Anyways, in order to know when is the time to invoke methods **setInputFile()** or **setURI()** to swap the current playing video after finished, we still need to catch the end-of-stream event.

No you don’t, just check if it’s playing in draw() and move on to the next.
