Processing Movie Question!

edited April 2017 in Arduino

Hello everyone! I am a student at Penn State University. I am making a project that is a large LED display screen, controlled by a Teensy 3.6 microcontroller. Paul Stoffregen from PJRC Teensy has given me great guidance so far in the project, and has suggested to me that I use a code that he has already had success with for an LED video display, that is made in Processing. It is called movie2serial for converting a video file to the LED display screen. I am however struggling with the addressing for the movie file. I will post the code, so maybe I can get some folks to take a look at this and help me out. These might be pretty basic questions, so I apologize for my lack of understanding, but I am sort of a coding novice!! Thank you so much for anyone willing to view this post and hopefully respond!

2 problems I am having:

1.) I am trying to address the file for the video and I am getting an error. Error is shown in picture.

2.) I am trying to address the COM port that it is plugged into my computer (I'm using Microsoft). I put in "COM4" because that's the location it said in device manager, but I am not sure if it needs more detailed addressing than that.

Processing.error2 Processing.error3 Processing.error4

Answers

  • edited April 2017

    Here's the full source code, The writer gave instructions in notes at the beginning as to how to adapt it. NOTE: this is not my work. It belongs to Paul Stoffregen of PJRC

    // To configure this program, edit the following sections:
    //
    //  1: change myMovie to open a video file of your choice    ;-)
    //
    //  2: edit the serialConfigure() lines in setup() for your
    //     serial device names (Mac, Linux) or COM ports (Windows)
    //
    //  3: if your LED strips have unusual color configuration,
    //     edit colorWiring().  Nearly all strips have GRB wiring,
    //     so normally you can leave this as-is.
    //
    //  4: if playing 50 or 60 Hz progressive video (or faster),
    //     edit framerate in movieEvent().
    
    import processing.video.*;
    import processing.serial.*;
    import java.awt.Rectangle;
    
    Movie myMovie = new Movie(this, "c:/users/wes/pictures/Sr Proj/psufootball.avi");
    
    float gamma = 1.7;
    
    
    
    int numPorts=0;  // the number of serial ports in use
    int maxPorts=24; // maximum number of serial ports
    
    Serial[] ledSerial = new Serial[maxPorts];     // each port's actual Serial port
    Rectangle[] ledArea = new Rectangle[maxPorts]; // the area of the movie each port gets, in % (0-100)
    boolean[] ledLayout = new boolean[maxPorts];   // layout of rows, true = even is left->right
    PImage[] ledImage = new PImage[maxPorts];      // image sent to each port
    int[] gammatable = new int[256];
    int errorCount=0;
    float framerate=0;
    
    void setup() {
      String[] list = Serial.list();
      delay(20);
      println("Serial Ports List:");
      println(list);
      serialConfigure("COM4");  // change these to your port names
      serialConfigure("COM4");
      if (errorCount > 0) exit();
      for (int i=0; i < 256; i++) {
        gammatable[i] = (int)(pow((float)i / 255.0, gamma) * 255.0 + 0.5);
      }
      size(480, 400);  // create the window
      myMovie.loop();  // start the movie :-)
    }
    
    
    // movieEvent runs for each new frame of movie data
    void movieEvent(Movie m) {
      // read the movie's next frame
      m.read();
    
      //if (framerate == 0) framerate = m.getSourceFrameRate();
      framerate = 30.0; // TODO, how to read the frame rate???
    
      for (int i=0; i < numPorts; i++) {    
        // copy a portion of the movie's image to the LED image
        int xoffset = percentage(m.width, ledArea[i].x);
        int yoffset = percentage(m.height, ledArea[i].y);
        int xwidth =  percentage(m.width, ledArea[i].width);
        int yheight = percentage(m.height, ledArea[i].height);
        ledImage[i].copy(m, xoffset, yoffset, xwidth, yheight,
                         0, 0, ledImage[i].width, ledImage[i].height);
        // convert the LED image to raw data
        byte[] ledData =  new byte[(ledImage[i].width * ledImage[i].height * 3) + 3];
        image2data(ledImage[i], ledData, ledLayout[i]);
        if (i == 0) {
          ledData[0] = '*';  // first Teensy is the frame sync master
          int usec = (int)((1000000.0 / framerate) * 0.75);
          ledData[1] = (byte)(usec);   // request the frame sync pulse
          ledData[2] = (byte)(usec >> 8); // at 75% of the frame time
        } else {
          ledData[0] = '%';  // others sync to the master board
          ledData[1] = 0;
          ledData[2] = 0;
        }
        // send the raw data to the LEDs  :-)
        ledSerial[i].write(ledData); 
      }
    }
    
    // image2data converts an image to OctoWS2811's raw data format.
    // The number of vertical pixels in the image must be a multiple
    // of 8.  The data array must be the proper size for the image.
    void image2data(PImage image, byte[] data, boolean layout) {
      int offset = 3;
      int x, y, xbegin, xend, xinc, mask;
      int linesPerPin = image.height / 8;
      int pixel[] = new int[8];
    
      for (y = 0; y < linesPerPin; y++) {
        if ((y & 1) == (layout ? 0 : 1)) {
          // even numbered rows are left to right
          xbegin = 0;
          xend = image.width;
          xinc = 1;
        } else {
          // odd numbered rows are right to left
          xbegin = image.width - 1;
          xend = -1;
          xinc = -1;
        }
        for (x = xbegin; x != xend; x += xinc) {
          for (int i=0; i < 8; i++) {
            // fetch 8 pixels from the image, 1 for each pin
            pixel[i] = image.pixels[x + (y + linesPerPin * i) * image.width];
            pixel[i] = colorWiring(pixel[i]);
          }
          // convert 8 pixels to 24 bytes
          for (mask = 0x800000; mask != 0; mask >>= 1) {
            byte b = 0;
            for (int i=0; i < 8; i++) {
              if ((pixel[i] & mask) != 0) b |= (1 << i);
            }
            data[offset++] = b;
          }
        }
      } 
    }
    
    // translate the 24 bit color from RGB to the actual
    // order used by the LED wiring.  GRB is the most common.
    int colorWiring(int c) {
      int red = (c & 0xFF0000) >> 16;
      int green = (c & 0x00FF00) >> 8;
      int blue = (c & 0x0000FF);
      red = gammatable[red];
      green = gammatable[green];
      blue = gammatable[blue];
      return (green << 16) | (red << 8) | (blue); // GRB - most common wiring
    }
    
    // ask a Teensy board for its LED configuration, and set up the info for it.
    void serialConfigure(String portName) {
      if (numPorts >= maxPorts) {
        println("too many serial ports, please increase maxPorts");
        errorCount++;
        return;
      }
      try {
        ledSerial[numPorts] = new Serial(this, portName);
        if (ledSerial[numPorts] == null) throw new NullPointerException();
        ledSerial[numPorts].write('?');
      } catch (Throwable e) {
        println("Serial port " + portName + " does not exist or is non-functional");
        errorCount++;
        return;
      }
      delay(50);
      String line = ledSerial[numPorts].readStringUntil(10);
      if (line == null) {
        println("Serial port " + portName + " is not responding.");
        println("Is it really a Teensy 3.0 running VideoDisplay?");
        errorCount++;
        return;
      }
      String param[] = line.split(",");
      if (param.length != 12) {
        println("Error: port " + portName + " did not respond to LED config query");
        errorCount++;
        return;
      }
      // only store the info and increase numPorts if Teensy responds properly
      ledImage[numPorts] = new PImage(Integer.parseInt(param[0]), Integer.parseInt(param[1]), RGB);
      ledArea[numPorts] = new Rectangle(Integer.parseInt(param[5]), Integer.parseInt(param[6]),
                         Integer.parseInt(param[7]), Integer.parseInt(param[8]));
      ledLayout[numPorts] = (Integer.parseInt(param[5]) == 0);
      numPorts++;
    }
    
    // draw runs every time the screen is redrawn - show the movie...
    void draw() {
      // show the original video
      image(myMovie, 0, 80);
    
      // then try to show what was most recently sent to the LEDs
      // by displaying all the images for each port.
      for (int i=0; i < numPorts; i++) {
        // compute the intended size of the entire LED array
        int xsize = percentageInverse(ledImage[i].width, ledArea[i].width);
        int ysize = percentageInverse(ledImage[i].height, ledArea[i].height);
        // computer this image's position within it
        int xloc =  percentage(xsize, ledArea[i].x);
        int yloc =  percentage(ysize, ledArea[i].y);
        // show what should appear on the LEDs
        image(ledImage[i], 240 - xsize / 2 + xloc, 10 + yloc);
      } 
    }
    
    // respond to mouse clicks as pause/play
    boolean isPlaying = true;
    void mousePressed() {
      if (isPlaying) {
        myMovie.pause();
        isPlaying = false;
      } else {
        myMovie.play();
        isPlaying = true;
      }
    }
    
    // scale a number by a percentage, from 0 to 100
    int percentage(int num, int percent) {
      double mult = percentageFloat(percent);
      double output = num * mult;
      return (int)output;
    }
    
    // scale a number by the inverse of a percentage, from 0 to 100
    int percentageInverse(int num, int percent) {
      double div = percentageFloat(percent);
      double output = num / div;
      return (int)output;
    }
    
    // convert an integer from 0 to 100 to a float percentage
    // from 0.0 to 1.0.  Special cases for 1/3, 1/6, 1/7, etc
    // are handled automatically to fix integer rounding.
    double percentageFloat(int percent) {
      if (percent == 33) return 1.0 / 3.0;
      if (percent == 17) return 1.0 / 6.0;
      if (percent == 14) return 1.0 / 7.0;
      if (percent == 13) return 1.0 / 8.0;
      if (percent == 11) return 1.0 / 9.0;
      if (percent ==  9) return 1.0 / 11.0;
      if (percent ==  8) return 1.0 / 12.0;
      return (double)percent / 100.0;
    }
    
  • Answer ✓

    Please edit your post, select your code and hit ctrl+o to format your code. Make sure there is an empty line above and below your code.

    The class Movie doesn't exist

    This implies the library is not installed. You need to install the library. To do that, open Processing and go to Sketch>>Import Library>>Add library and then search for the video library and install it.

    Also, please no screenshots of your code or errors. They are hard to read and they are not searchable in the forum, so other ppl in the community will not be able to reach your post.

    Related to the COM port, stick to COM4 if that is what the device manager says where your device is connected. I see you have serialCoinfigure twice (Line 62 and 63) in your code. Is that from the original code or did you add that?

    Kf

  • Hey sorry about that, I am new to this, I will avoid the screenshots. Thanks for the fix though. I formatted the code.

    Thank you! I added the library and it looks like the error is gone!! Going to try to finalize this and see if it continues to work.

    In regards to the COM ports, yes the original code does include serialConfigure in both lines 62 and 63.

  • edited April 2017
    // To configure this program, edit the following sections:
    //
    //  1: change myMovie to open a video file of your choice    ;-)
    //
    //  2: edit the serialConfigure() lines in setup() for your
    //     serial device names (Mac, Linux) or COM ports (Windows)
    //
    //  3: if your LED strips have unusual color configuration,
    //     edit colorWiring().  Nearly all strips have GRB wiring,
    //     so normally you can leave this as-is.
    //
    //  4: if playing 50 or 60 Hz progressive video (or faster),
    //     edit framerate in movieEvent().
    
    import processing.video.*;
    import processing.serial.*;
    import java.awt.Rectangle;
    
    Movie myMovie = new Movie(this, "/tmp/Toy_Story.avi");
    
    float gamma = 1.7;
    
    int numPorts=0;  // the number of serial ports in use
    int maxPorts=24; // maximum number of serial ports
    
    Serial[] ledSerial = new Serial[maxPorts];     // each port's actual Serial port
    Rectangle[] ledArea = new Rectangle[maxPorts]; // the area of the movie each port gets, in % (0-100)
    boolean[] ledLayout = new boolean[maxPorts];   // layout of rows, true = even is left->right
    PImage[] ledImage = new PImage[maxPorts];      // image sent to each port
    int[] gammatable = new int[256];
    int errorCount=0;
    float framerate=0;
    
    void setup() {
      String[] list = Serial.list();
      delay(20);
      println("Serial Ports List:");
      println(list);
      serialConfigure("/dev/ttyACM0");  // change these to your port names
      serialConfigure("/dev/ttyACM1");
      if (errorCount > 0) exit();
      for (int i=0; i < 256; i++) {
        gammatable[i] = (int)(pow((float)i / 255.0, gamma) * 255.0 + 0.5);
      }
      size(480, 400);  // create the window
      myMovie.loop();  // start the movie :-)
    }
    
    
    // movieEvent runs for each new frame of movie data
    void movieEvent(Movie m) {
      // read the movie's next frame
      m.read();
    
      //if (framerate == 0) framerate = m.getSourceFrameRate();
      framerate = 30.0; // TODO, how to read the frame rate???
    
      for (int i=0; i < numPorts; i++) {    
        // copy a portion of the movie's image to the LED image
        int xoffset = percentage(m.width, ledArea[i].x);
        int yoffset = percentage(m.height, ledArea[i].y);
        int xwidth =  percentage(m.width, ledArea[i].width);
        int yheight = percentage(m.height, ledArea[i].height);
        ledImage[i].copy(m, xoffset, yoffset, xwidth, yheight,
                         0, 0, ledImage[i].width, ledImage[i].height);
        // convert the LED image to raw data
        byte[] ledData =  new byte[(ledImage[i].width * ledImage[i].height * 3) + 3];
        image2data(ledImage[i], ledData, ledLayout[i]);
        if (i == 0) {
          ledData[0] = '*';  // first Teensy is the frame sync master
          int usec = (int)((1000000.0 / framerate) * 0.75);
          ledData[1] = (byte)(usec);   // request the frame sync pulse
          ledData[2] = (byte)(usec >> 8); // at 75% of the frame time
        } else {
          ledData[0] = '%';  // others sync to the master board
          ledData[1] = 0;
          ledData[2] = 0;
        }
        // send the raw data to the LEDs  :-)
        ledSerial[i].write(ledData); 
      }
    }
    
    // image2data converts an image to OctoWS2811's raw data format.
    // The number of vertical pixels in the image must be a multiple
    // of 8.  The data array must be the proper size for the image.
    void image2data(PImage image, byte[] data, boolean layout) {
      int offset = 3;
      int x, y, xbegin, xend, xinc, mask;
      int linesPerPin = image.height / 8;
      int pixel[] = new int[8];
    
      for (y = 0; y < linesPerPin; y++) {
        if ((y & 1) == (layout ? 0 : 1)) {
          // even numbered rows are left to right
          xbegin = 0;
          xend = image.width;
          xinc = 1;
        } else {
          // odd numbered rows are right to left
          xbegin = image.width - 1;
          xend = -1;
          xinc = -1;
        }
        for (x = xbegin; x != xend; x += xinc) {
          for (int i=0; i < 8; i++) {
            // fetch 8 pixels from the image, 1 for each pin
            pixel[i] = image.pixels[x + (y + linesPerPin * i) * image.width];
            pixel[i] = colorWiring(pixel[i]);
          }
          // convert 8 pixels to 24 bytes
          for (mask = 0x800000; mask != 0; mask >>= 1) {
            byte b = 0;
            for (int i=0; i < 8; i++) {
              if ((pixel[i] & mask) != 0) b |= (1 << i);
            }
            data[offset++] = b;
          }
        }
      } 
    }
    
    // translate the 24 bit color from RGB to the actual
    // order used by the LED wiring.  GRB is the most common.
    int colorWiring(int c) {
      int red = (c & 0xFF0000) >> 16;
      int green = (c & 0x00FF00) >> 8;
      int blue = (c & 0x0000FF);
      red = gammatable[red];
      green = gammatable[green];
      blue = gammatable[blue];
      return (green << 16) | (red << 8) | (blue); // GRB - most common wiring
    }
    
    // ask a Teensy board for its LED configuration, and set up the info for it.
    void serialConfigure(String portName) {
      if (numPorts >= maxPorts) {
        println("too many serial ports, please increase maxPorts");
        errorCount++;
        return;
      }
      try {
        ledSerial[numPorts] = new Serial(this, portName);
        if (ledSerial[numPorts] == null) throw new NullPointerException();
        ledSerial[numPorts].write('?');
      } catch (Throwable e) {
        println("Serial port " + portName + " does not exist or is non-functional");
        errorCount++;
        return;
      }
      delay(50);
      String line = ledSerial[numPorts].readStringUntil(10);
      if (line == null) {
        println("Serial port " + portName + " is not responding.");
        println("Is it really a Teensy 3.0 running VideoDisplay?");
        errorCount++;
        return;
      }
      String param[] = line.split(",");
      if (param.length != 12) {
        println("Error: port " + portName + " did not respond to LED config query");
        errorCount++;
        return;
      }
      // only store the info and increase numPorts if Teensy responds properly
      ledImage[numPorts] = new PImage(Integer.parseInt(param[0]), Integer.parseInt(param[1]), RGB);
      ledArea[numPorts] = new Rectangle(Integer.parseInt(param[5]), Integer.parseInt(param[6]),
                         Integer.parseInt(param[7]), Integer.parseInt(param[8]));
      ledLayout[numPorts] = (Integer.parseInt(param[5]) == 0);
      numPorts++;
    }
    
    // draw runs every time the screen is redrawn - show the movie...
    void draw() {
      // show the original video
      image(myMovie, 0, 80);
    
      // then try to show what was most recently sent to the LEDs
      // by displaying all the images for each port.
      for (int i=0; i < numPorts; i++) {
        // compute the intended size of the entire LED array
        int xsize = percentageInverse(ledImage[i].width, ledArea[i].width);
        int ysize = percentageInverse(ledImage[i].height, ledArea[i].height);
        // computer this image's position within it
        int xloc =  percentage(xsize, ledArea[i].x);
        int yloc =  percentage(ysize, ledArea[i].y);
        // show what should appear on the LEDs
        image(ledImage[i], 240 - xsize / 2 + xloc, 10 + yloc);
      } 
    }
    
    // respond to mouse clicks as pause/play
    boolean isPlaying = true;
    void mousePressed() {
      if (isPlaying) {
        myMovie.pause();
        isPlaying = false;
      } else {
        myMovie.play();
        isPlaying = true;
      }
    }
    
    // scale a number by a percentage, from 0 to 100
    int percentage(int num, int percent) {
      double mult = percentageFloat(percent);
      double output = num * mult;
      return (int)output;
    }
    
    // scale a number by the inverse of a percentage, from 0 to 100
    int percentageInverse(int num, int percent) {
      double div = percentageFloat(percent);
      double output = num / div;
      return (int)output;
    }
    
    // convert an integer from 0 to 100 to a float percentage
    // from 0.0 to 1.0.  Special cases for 1/3, 1/6, 1/7, etc
    // are handled automatically to fix integer rounding.
    double percentageFloat(int percent) {
      if (percent == 33) return 1.0 / 3.0;
      if (percent == 17) return 1.0 / 6.0;
      if (percent == 14) return 1.0 / 7.0;
      if (percent == 13) return 1.0 / 8.0;
      if (percent == 11) return 1.0 / 9.0;
      if (percent ==  9) return 1.0 / 11.0;
      if (percent ==  8) return 1.0 / 12.0;
      return (double)percent / 100.0;
    }
    
  • I posted the full original code in the comment above. Maybe that will help shed some light on the ports situation

  • Any suggestions on what to do for the Ports? I'm using Windows

  • edited April 2017

    serialConfigure("/dev/ttyACM0"); // change these to your port names
    serialConfigure("/dev/ttyACM1");

    I don't think these are COM ports' definitions in Windows. I thought you said they were in COM4.

    For testing and debugging purposes, you should copy your code and make it more simple. Bring it down to the essentials of communication with the device and some simple operations. It will help with the debugging part. You could also run one of the examples either in the processing foundation page (under libraries >> serial) or available with the serial library in the Processing IDE (Go to File>>Examples>>Libraries>>Serial (I think that is the folder hierarchy...)

    One thing about the serialConfig, I think that function allows you to open multiple ports at the same time. I don't think it is valid to open the same port more than once. If you use this code, start by calling serialConfig only once, again for debugging purposes.

    Kf

  • Hey Kf,

    I figured it out, it was COM4 but a tiny syntax errorI had made. Unfortunatly I am now getting an error. I have no clue what thus error means??

    Error appears as:

    ** (java.exe:3312): WARNING **: gstvideo: failed to get caps of pad playsink0:video_raw_sink

  • When running the program, the video player pops up separately but is grayed out, and the above error comes up under the console tab next to errors at the bottom of the Processing window.

  • You might need to strip down your code to a barely minimum where you test your movie calls. It is easier to debug if you decouple the movie from the serial communication part. An alternative, run the sample code that comes with the video library and test it using your video file.

    One thing I can see is that you don't have a call back method for your movie object as presented in the examples. Check here for example: https://processing.org/reference/libraries/video/index.html

    Specifically, add this to your code:

    void movieEvent(Movie m) {
      m.read();
    }
    

    Kf

Sign In or Register to comment.