Movie Library Reverse Video Looping Example

Hi guys,

Have been playing around with reverse video playback and looping.

Found the following notes:

  • check that video is actually available and playing before calling mov.jump()
  • avoid setting speed = 0.0 (gstreamer error)
  • before calling mov.jump() set speed to positive value, after .jump() restore speed
  • skip some frames before calling mov.speed() again. otherwise gstreamer errors pop up and framerate drops.

At least these steps worked for me as a workaround with Processing Video library Release 3.

Working example attached (add the default launch2.mp4 to the sketch when testing, H.264 MP4).

Best,
Arne.

/**
 * Reverse playback example.
 *
 * The Movie.speed() method allows to change the playback speed. 
 * Use negative values for backwards playback. Note that not all 
 * video formats support backwards playback. This depends on the 
 * underlying gstreamer plugins used by gsvideo. For example, the 
 * theora codec does support backward playback, but not so the H264 
 * codec, at least in its current version.
 * 
 */
 
/**
 * Working with Processing-Video Release 3
 * https://github.com/processing/processing-video/releases/tag/r3-v2.0-beta1
 * Note: download from github and put in libraries folder, replacing manager version
 */

import processing.video.*;

Movie mov;

float speed = 1.0;
float pSpeed = speed;
boolean bReady = false;
boolean bReverse = false;
int countdownMax = 10; //--num frames before allowing speed change
int countdownSpeed = countdownMax;

void setup() {
  size(560, 406);
  background(0);
  mov = new Movie(this, "launch2.mp4");
  mov.play();
  mov.loop();
}

void draw() {
  updateMovie();
  
  image(mov, 0, 0, width, height);

  fill(0);
  text(speed, 10, 20);
  text(mov.time(), 10, 40);
  text("bReverse: " + (bReverse ? "true" : "false"), 10, 60); 
  text("countdownSpeed: " + countdownSpeed, 10, 80);
  text("fps: " + Math.round(frameRate), 10, 100);
}

void keyPressed() {
  //--test jump
  mov.speed(0.1); //NOTE: essential to set speed to 1.0 before jump
  mov.jump(mov.duration()/2);
  mov.speed(speed);
  //mov.play(); //--not required?
}

void updateMovie() {
  if (mov.available()) {
    //--read next frame
    mov.read();
    
    //--when movie ready at init jump to middle of video
    if(!bReady) {
      bReady=true;
      mov.jump(mov.duration() / 2.0);
      //mov.play(); //not required?
    }
      
    //--loop video to end when reaching begin
    if(bReady && bReverse && mov.time() <= 0.5) { //--0.5 sec as save value at high speeds
      mov.speed(0.1); //NOTE: essential to set speed to positive before jump
      mov.jump(mov.duration()); //--jump to end
      mov.speed(speed); //--reset to input speed
      //mov.play(); //not required?
    }
    
    //--update counter for speed change allowance
    if(countdownSpeed > 0) {
      countdownSpeed--;
    }
  }
  
  //--map input to speed level
  speed = map(mouseX, 0, width, -10, 10);
  speed = Math.round(speed * 10.0) / 10.0; //--round number to 2 decimals
  
  //--check for reverse speed
  if(speed < 0.0) {
    bReverse = true;
  } else {
    bReverse = false;
  }
  
  //--avoid speed 0
  if(speed > -0.5 && speed < 0.5) {
    if(bReverse) {
      speed = -0.5;
    } else {
      speed = 0.5;
    }
  }

  //--check for allowing speed change
  if(speed != pSpeed && countdownSpeed == 0) {
    println("change speed");
    countdownSpeed = countdownMax;
    mov.speed(speed);
    //mov.play(); //not required?
  }
  pSpeed = speed; //--update counter
}

3 Likes

Yes, there’s some annoying workarounds required for some of this at the moment. You should keep an eye on the GSoC for the Video library. Hopefully these issues will be fully addressed, and I’ve been discussing bindings usage with the person taking it on. Definitely the right time for you to be feeding your thoughts into it.

All this already works correctly in PraxisLIVE incidentally! :smile:

1 Like