[video] Play random video sequence

edited July 2017 in Library Questions

Hi everyone, I'm new to Processing and I've been working on this video-art / installation project for my graduation.

My question is: How can I set a random video to be played next?

I managed to get the videos playing, but I can't set another random video to follow. My intention is to have a sequence of videos (maybe 10 or more) that will play in a shuffled order. I have looked around this and some other forums but couldn't find a specific answer. Hope you guys can help me out. Thanks!

ps. if something doesn't make sense in my code please tell me :)

import processing.video.*;
import processing.sound.*;

AudioIn input;
Amplitude analyzer;
int scale=4;

Movie mov1, mov2;
Capture cam;

int n = 5; //total number of videos

float vidN = random(1, n+1);
int x = int (vidN);

float vidN2 = random(1, n+1);
int x2 = int (vidN2);

void setup() {
  //frameRate (30);
  size(1280, 720);
  colorMode (HSB);
  //fullScreen();
  //background(0);

  mov1 = new Movie(this, nf(x, 2)+".mp4");
  mov1.loop();
  mov1.volume(100);

  mov2 = new Movie(this, nf(x2, 2)+".mp4");
  mov2.loop();
  mov2.volume(00);

  cam = new Capture(this, width, height);
  cam.start();  

  blendMode(DIFFERENCE);

  imageMode(CENTER);

  //Create an Audio input and grab the 1st channel
  input = new AudioIn(this, 0);

  // start the Audio Input
  input.start();

  // create a new Amplitude analyzer
  analyzer = new Amplitude(this);

  // Patch the input to an volume analyzer
  analyzer.input(input);
}

void movieEvent(Movie m) {
  if (m == mov1) {
    mov1.read();
  } else if (m == mov2) {
    mov2.read();
  }
}

void draw() {    
  float vol = analyzer.analyze();

  noStroke ();

  tint (255, 80);
  if (cam.available()) {
    cam.read();
    image(cam, width/2, height/2, width, height); // Draw the webcam cam onto the screen

    vol = vol*scale;

    tint (255, 100-vol*100);
    image(mov1, width/2, height/2, width, height);

    tint (255, 100-vol*100);
    image(mov2, width/2, height/2, width, height);
  }
}  

Answers

  • Answer ✓

    In the code above, you are playing a camera and a video1 and a video2.

    You need to consider you have:

    void captureEvent(Capture webCam) {
      webCam.read();
    }
    

    and

    void movieEvent(Movie m) {
     ...
    }
    

    These two functions manage the movie and webcam resp. Now, notice above that you have

    if (cam.available()) {
      ...
    }
    

    In this function you are managing all your images (never mind they all overlap and only the last one is shown). What is important to keep in mind is that you show the movie's images only when a new image is available from your webcam device.

    Consider these two links for playing multiple videos:

    https://forum.processing.org/two/discussion/8109/prepare-a-second-video-to-play-without-slowing-down-the-first-video#latest

    https://forum.processing.org/two/discussion/comment/85389/#Comment_85389

    I hope this helps,

    Kf

  • edited July 2017

    Hi there, kfrajer!

    Yes, I want 2 videos + the webcam to be playing at the same time, because I like the blending effect. I managed to set 2 random videos at the start, but the point is to have a shuffled sequence of videos, so that it's more varied and doesn't get repetitive.

    Anyway, I think what you pointed out was related to performance, am I right? So I disabled this:

    if (cam.available()) {
      ...
    }
    

    and included this:

    void captureEvent(Capture cam) {
      cam.read();
    }
    

    Apparently playback is smoother now. Thank you!

    I will have a look at the links you provided, and hope someone's figured out this - apparently simple - task :) Cheers!

  • I didn't run your code, but yes... the tint effect will provide a good effect thinking about it. About performance, yes... it happens that you are only refreshing the sketch only when the webcam is available. You should treat them independently as you are doing now.

    Kf

  • edited July 2017

    kfrajer, I looked at the links you posted, and I got some results by adapting the code by GoToLoop :) https://forum.processing.org/two/discussion/8109/prepare-a-second-video-to-play-without-slowing-down-the-first-video#latest ... As I would like videos to take up the whole screen, as well as to maintain blendMode() and tint(), I used image() instead of set():

      image (clips[A], 0, 0, width, height);
      image (clips[B], 0, 0, width, height);
    

    Oh, just for the record. At this line

      int rnd, idx = toIdx(id), num = QTY>1? toNum(id) : -1;
    

    I get a "Dead code" warning. It doesn't stop the sketch from running though :) Is it a problem? I'm also worried about performance in the long run, since I'll leave the installation running for a couple hours. Do you have any tips on that? :B

    Thanks again! :)

    ...

    /**
     * Preloaded Clips (v3.13)
     * by GoToLoop (2014/Nov/18)
     *
     * forum.processing.org/two/discussion/8109/
     * prepare-a-second-video-to-play-
     * without-slowing-down-the-first-video
     */
    
    import processing.video.Movie;
    
    import org.gstreamer.Bus.EOS;
    import org.gstreamer.GstObject;
    
    final EOS finished = new EOS() {
      @ Override void endOfStream(GstObject gst) {
        transferLoadedToActive(int(gst.get("name").toString()));
      }
    };
    
    static final String EXT = ".mp4";
    static final int FPS = 30, MARGIN = 8, PAUSE = 1000/FPS;
    static final int NUM = 2, QTY = 3, A = 0, B = 1; //QTY = number of videos
    
    final Movie[] clips = new Movie[NUM], loads = new Movie[NUM];
    final int[] x = new int[NUM], y = new int[NUM];
    
    boolean isPlaying = true;
    
    void setup() {
      size(1280, 720, JAVA2D);
    
      noSmooth();
      noLoop();
      frameRate(FPS);
    
      blendMode(BLEND);
      //imageMode(CENTER);
    
      transferLoadedToActive(A);
      transferLoadedToActive(B);
    }
    
    void draw() {
      tint (255, 60);
      //  set(x[A], y[A], clips[A]);
      //  set(x[B], y[B], clips[B]);
      //  set(0, 0, clips[A]);
      //  set(0, 0, clips[B]);
      image (clips[A], 0, 0, width, height);
      image (clips[B], 0, 0, width, height);
    }
    
    //void keyTyped() {
    //  if (isPlaying ^= true) {
    //    clips[A].play();
    //    clips[B].play();
    //  } else {
    //    clips[A].pause();
    //    clips[B].pause();
    //  }
    //}
    
    //void mousePressed() {
    //  keyTyped();
    //}
    
    void movieEvent(Movie m) {
      m.read();
      redraw();
    }
    
    static final int toIdx(int id) {
      return (short) id;
    }
    
    static final int toNum(int id) {
      return id>>>020;
    }
    
    Movie loadClip(int id, String name) {
      Movie m = new Movie(this, name);
      m.playbin.set("name", id);
      m.playbin.getBus().connect(finished);
      println(name, "loaded  for index", toIdx(id));
      return m;
    }
    
    Movie preloadRandomClip(int id) {
      int rnd, idx = toIdx(id), num = QTY>1? toNum(id) : -1;
      while  (num == (rnd = (int)random(QTY) + 1));
      return loads[idx] = loadClip(idx | rnd<<020, rnd + EXT);
    }
    
    Movie transferLoadedToActive(int id) {
      int idx = toIdx(id);
      if (loads[idx] == null)  preloadRandomClip(id);
    
      Movie old = clips[idx], now = clips[idx] = loads[idx];
      if (isPlaying)    now.play();
      if (old == null)  while (now.width == 0)  delay(PAUSE);
    
      centralizeClip(idx);
      println(now.filename, "started for index", idx);
    
      id = int(now.playbin.get("name").toString());
      preloadRandomClip(id);
    
      if (old != null)  old.dispose();
      return now;
    }
    
    Movie centralizeClip(int id) {
      int idx = toIdx(id);
      Movie m = clips[idx];
      x[idx] = idx == A? MARGIN : width - m.width - MARGIN;
      y[idx] = height - m.height >> 1;
      clear();
      return m;
    }
    
  • edited July 2017

    I tried to clean up the code, removing parts that aren't essential. But as I said, I'm new to programming, so if anyone is able to further improve it, either on performance or code logic itself, it'd be greatly appreciated. Comments in the code would be nice as well, explaining what does what ;) Thanks again to kfrajer and GoToLoop.

    /**
     * Preloaded random Clips
     * adapted from GoToLoop (2017/Jul/13)
     *
     * forum.processing.org/two/discussion/8109/
     * prepare-a-second-video-to-play-
     * without-slowing-down-the-first-video
     */
    
    import processing.video.Movie;
    
    import org.gstreamer.Bus.EOS;
    import org.gstreamer.GstObject;
    
    final EOS finished = new EOS() {
      @ Override void endOfStream(GstObject gst) {
        transferLoadedToActive(int(gst.get("name").toString()));
      }
    };
    
    static final String EXT = ".mp4";
    static final int FPS = 30, MARGIN = 8, PAUSE = 1000/FPS;
    static final int NUM = 2, QTY = 7, A = 0, B = 1; //QTY = total amount of videos in data folder
    
    final Movie[] clips = new Movie[NUM], loads = new Movie[NUM];
    final int[] x = new int[NUM], y = new int[NUM];
    
    boolean isPlaying = true;
    
    void setup() {
      size(1280, 720);
      noSmooth();
      noLoop();
      frameRate(FPS);
    
      transferLoadedToActive(A);
    //  transferLoadedToActive(B);
    }
    
    void draw() {
      image (clips[A], 0, 0, width, height);
    //  image (clips[B], 0, 0, width, height);
    }
    
    void movieEvent(Movie m) {
      m.read();
      redraw();
    }
    
    static final int toIdx(int id) {
      return (short) id;
    }
    
    static final int toNum(int id) {
      return id>>>020;
    }
    
    Movie loadClip(int id, String name) {
      Movie m = new Movie(this, name);
      m.playbin.set("name", id);
      m.playbin.getBus().connect(finished);
      println(name, "loaded  for index", toIdx(id));
      return m;
    }
    
    Movie preloadRandomClip(int id) {
      int rnd, idx = toIdx(id), num = QTY>1? toNum(id) : -1;
      while  (num == (rnd = (int)random(QTY) + 1));
      return loads[idx] = loadClip(idx | rnd<<020, rnd + EXT);
    }
    
    Movie transferLoadedToActive(int id) {
      int idx = toIdx(id);
      if (loads[idx] == null)  preloadRandomClip(id);
    
      Movie old = clips[idx], now = clips[idx] = loads[idx];
      if (isPlaying)    now.play();
      if (old == null)  while (now.width == 0)  delay(PAUSE);
    
      println(now.filename, "started for index", idx);
    
      id = int(now.playbin.get("name").toString());
      preloadRandomClip(id);
    
      if (old != null)  old.dispose();
      return now;
    }
    
  • edited July 2017

    Hey GoToLoop! Thanks for showing up! Apparently, the link you posted guides to a simpler way of indicating the end of a video stream. Is it so? - I tried and striped down your new code, removing stuff I don't need (code below). ~ Maybe I'm being too ambitious to do this in Processing, but how can I merge both codes to get a random sequence of videos (from a set) + webcam playing simultaneously?

    /**
     * Hacked Movie EoS Event (v1.02)
     * by GoToLoop (2016-Feb-18)
     * forum.Processing.org/two/discussion/14990/movie-begin-and-end-events#Item_1
     */
    
    import processing.video.Movie;
    Movie vid;
    boolean ended;
    
    void setup() {
      frameRate(30);
      size(1280,720);
      noSmooth();
    
      vid = new Movie(this, "1.mp4") {
        @ Override public void eosEvent() {
          super.eosEvent();
          myEoS();
        }
      };
    
      vid.play();
     }
    
    void draw() {
      if (!ended)  image(vid,0,0,width,height);
      else {
        println("Playback has finished!");  
      }
      println("Frame rate: " + int(frameRate));
    }
    
    void movieEvent(final Movie m) {
      m.read();
    }
    
    void myEoS() {
      ended = true;
    }
    
  • REFERENCE: https://forum.processing.org/two/discussion/4333/how-can-i-get-multiple-videos-to-play-one-after-another-and-triggered-by-specific-keys

    I modify the previous code and provide a demo that should do what you need.

    The code does the following:

    • It divides the sketch in 3 sections along its width
    • In the first section, it plays movies randomly, one after another and only when a movie in that section comes to an end. When it stops playing, it plays a new movie selected randomly from my extensive list of movies: 3
    • The second and third section play a random movie as well, but only when you press the s or d key. When it finishes playing a movie. It comes to a stop showing the last frame. You can press the key again to start playing the movie again.
    • REMARK: at any time you can press the keys a s d to reset and play a movie in their respective section.

    In your case, you want the behavior displayed in the first section: Consecutive and continuous playing of random movies.

    Kf

    import processing.video.*;
    
    final int[] XPOS={0, 260, 520};
    final int N=3;
    
    final String[] names = {"shortrun.mp4", "shortrun.mp4", "transit.mov"};
    
    Movie myMovie[];
    float[] targetTime;
    int index = 0;
    
    int[] currIdx;
    
    void setup() {
      size(720, 576);
    
      myMovie = new Movie[N];
      targetTime=new float[N];
      currIdx=new int[N];
    
      myMovie[0]  = new Movie(this, "shortrun.mp4");
      myMovie[1]  = new Movie(this, "shortrun.mp4");
      myMovie[2]  = new Movie(this, "transit.mov");
    
      textAlign(LEFT, TOP);
      textSize(16);
    }
    
    
    void draw() {
      background(0);
    
      text("Current time=" + millis(), 0, 0);
    
      for (int i=0; i<N; i++) {
    
        boolean playFlag=targetTime[i]-millis()>0;
    
        text("Playing:"+playFlag, XPOS[i], 40); 
        text("End time:"+targetTime[i], XPOS[i], 80); 
    
        if (playFlag) {
          image(myMovie[i], XPOS[i], 100, 260, myMovie[i].height);
        }
    
        if(i==0 && playFlag==false){
          setMovie(0);
        }
      }
    }
    
    void movieEvent(Movie m) {
      m.read();
    }
    
    void keyPressed() {
      if (key == 'a') {
        index = 0;
        setMovie(index);
      }
    
      if (key == 's') {
        index = 1;
        setMovie(index);
      }
      if (key == 'd') {
        index = 2;
        setMovie(index);
      }
    }
    
    void setMovie(int idx) {
      myMovie[idx]  = new Movie(this, getMovieName(idx));
      myMovie[idx].stop();
      myMovie[idx].play();
      myMovie[idx].volume(0);
      targetTime[idx]= millis()+myMovie[idx].duration()*1000;
    }
    
    String getMovieName(int pos) {
      currIdx[pos]=(int)random(N);
      return names[currIdx[pos]];
    }
    
Sign In or Register to comment.