eyemac2011
New member
Hi, I have teensy 3.2 w/ octows2811 and an arduino sd module (https://www.amazon.com/Storage-Memory-Shield-Module-Arduino/dp/B01IPCAP72)
I am trying to do a simple test of of the videosdcard sketch with a single 50 led strip. I tried rendering the video at different resolutions and with different codecs, but cannot seem to get it to work. I am able to generate a BIN file from Processing and successfully run the addaudio commands (though I do not need audio). Com port says "File Opened / error: unable to read 5-byte header", but the lights flicker for a bit and stop before flickering again, so it seems to be reading something. Here is my code
All I did was change width and height. I rendered a MJPEG MOV at 50x8 and 30 fps to fit. I cant seem to get it to work. Is there a specific format the video must be? Am I going about "fitting" the video the wrong way? Is it possible to use the BIN generated by processing without adding audio? I added audio because I thought it would fix the header error, but it did not. Thanks
Here is my processing code
I am trying to do a simple test of of the videosdcard sketch with a single 50 led strip. I tried rendering the video at different resolutions and with different codecs, but cannot seem to get it to work. I am able to generate a BIN file from Processing and successfully run the addaudio commands (though I do not need audio). Com port says "File Opened / error: unable to read 5-byte header", but the lights flicker for a bit and stop before flickering again, so it seems to be reading something. Here is my code
Code:
/*
#include <OctoWS2811.h>
#include <SPI.h>
#include <SD.h>
#include <Audio.h>
#include <Wire.h>
#define LED_WIDTH 50 // number of LEDs horizontally
#define LED_HEIGHT 8 // number of LEDs vertically (must be multiple of 8)
#define FILENAME "VIDEO.BIN"
const int ledsPerStrip = LED_WIDTH * LED_HEIGHT / 8;
DMAMEM int displayMemory[ledsPerStrip*6];
int drawingMemory[ledsPerStrip*6];
elapsedMicros elapsedSinceLastFrame = 0;
bool playing = false;
OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, WS2811_800kHz);
File videofile;
AudioPlayQueue audio;
AudioOutputAnalog dac;
AudioConnection patchCord1(audio, dac);
void setup() {
AudioMemory(40);
//while (!Serial) ;
delay(50);
Serial.println("VideoSDcard");
leds.begin();
leds.show();
if (!SD.begin(3)) stopWithErrorMessage("Could not access SD card");
Serial.println("SD card ok");
videofile = SD.open(FILENAME, FILE_READ);
if (!videofile) stopWithErrorMessage("Could not read " FILENAME);
Serial.println("File opened");
playing = true;
elapsedSinceLastFrame = 0;
}
// read from the SD card, true=ok, false=unable to read
// the SD library is much faster if all reads are 512 bytes
// this function lets us easily read any size, but always
// requests data from the SD library in 512 byte blocks.
//
bool sd_card_read(void *ptr, unsigned int len)
{
static unsigned char buffer[512];
static unsigned int bufpos = 0;
static unsigned int buflen = 0;
unsigned char *dest = (unsigned char *)ptr;
unsigned int n;
while (len > 0) {
if (buflen == 0) {
n = videofile.read(buffer, 512);
if (n == 0) return false;
buflen = n;
bufpos = 0;
}
unsigned int n = buflen;
if (n > len) n = len;
memcpy(dest, buffer + bufpos, n);
dest += n;
bufpos += n;
buflen -= n;
len -= n;
}
return true;
}
// skip past data from the SD card
void sd_card_skip(unsigned int len)
{
unsigned char buf[256];
while (len > 0) {
unsigned int n = len;
if (n > sizeof(buf)) n = sizeof(buf);
sd_card_read(buf, n);
len -= n;
}
}
void loop()
{
unsigned char header[5];
if (playing) {
if (sd_card_read(header, 5)) {
if (header[0] == '*') {
// found an image frame
unsigned int size = (header[1] | (header[2] << 8)) * 3;
unsigned int usec = header[3] | (header[4] << 8);
unsigned int readsize = size;
// Serial.printf("v: %u %u", size, usec);
if (readsize > sizeof(drawingMemory)) {
readsize = sizeof(drawingMemory);
}
if (sd_card_read(drawingMemory, readsize)) {
//Serial.printf(", us = %u", (unsigned int)elapsedSinceLastFrame);
// Serial.println();
while (elapsedSinceLastFrame < usec) ; // wait
elapsedSinceLastFrame -= usec;
leds.show();
} else {
error("unable to read video frame data");
return;
}
if (readsize < size) {
sd_card_skip(size - readsize);
}
} else if (header[0] == '%') {
// found a chunk of audio data
unsigned int size = (header[1] | (header[2] << 8)) * 2;
// Serial.printf("a: %u", size);
// Serial.println();
while (size > 0) {
unsigned int len = size;
if (len > 256) len = 256;
int16_t *p = audio.getBuffer();
if (!sd_card_read(p, len)) {
error("unable to read audio frame data");
return;
}
if (len < 256) {
for (int i=len; i < 256; i++) {
*((char *)p + i) = 0; // fill rest of buffer with zero
}
}
audio.playBuffer();
size -= len;
}
} else {
error("unknown header");
return;
}
} else {
error("unable to read 5-byte header");
return;
}
} else {
delay(2000);
videofile = SD.open(FILENAME, FILE_READ);
if (videofile) {
Serial.println("File opened");
playing = true;
elapsedSinceLastFrame = 0;
}
}
}
// when any error happens during playback, close the file and restart
void error(const char *str)
{
Serial.print("error: ");
Serial.println(str);
videofile.close();
playing = false;
}
// when an error happens during setup, give up and print a message
// to the serial monitor.
void stopWithErrorMessage(const char *str)
{
while (1) {
Serial.println(str);
delay(1000);
}
}
All I did was change width and height. I rendered a MJPEG MOV at 50x8 and 30 fps to fit. I cant seem to get it to work. Is there a specific format the video must be? Am I going about "fitting" the video the wrong way? Is it possible to use the BIN generated by processing without adding audio? I added audio because I thought it would fix the header error, but it did not. Thanks
Here is my processing code
Code:
import processing.video.*;
import processing.serial.*;
import java.io.*;
int ledWidth = 50; // size of LED panel
int ledHeight = 8;
boolean ledLayout = true; // layout of rows, true = even is left->right
double framerate = 30; // You MUST set this to the movie's frame rate
// Processing does not seem to have a way to detect it.
Movie myMovie = new Movie(this, "D:/Videos/myvideo.mov");
FileOutputStream myFile; // edit output filename below...
float gamma = 1.8;
PImage ledImage;
int[] gammatable = new int[256];
long elapsed_picoseconds=0L;
long elapsed_microseconds=0L;
long picoseconds_per_frame = (long)(1e12 / framerate + 0.5);
boolean fileopen=true;
void setup() {
for (int i=0; i < 256; i++) {
gammatable[i] = (int)(pow((float)i / 255.0, gamma) * 255.0 + 0.5);
}
try {
myFile = new FileOutputStream("D:/Videos/myvideo.bin");
} catch (Exception e) {
exit();
}
ledImage = createImage(ledWidth, ledHeight, RGB);
size(720, 560); // create the window
myMovie.play(); // start the movie :-)
}
// movieEvent runs for each new frame of movie data
void movieEvent(Movie m) {
// read the movie's next frame
m.read();
elapsed_picoseconds += picoseconds_per_frame;
int usec = (int)((elapsed_picoseconds / 1000000L) - elapsed_microseconds);
elapsed_microseconds += (long)usec;
println("usec = " + usec);
// copy the movie's image to the LED image
ledImage.copy(m, 0, 0, m.width, m.height, 0, 0, ledWidth, ledHeight);
// convert the LED image to raw data
byte[] ledData = new byte[(ledWidth * ledHeight * 3) + 5];
image2data(ledImage, ledData, ledLayout);
ledData[0] = '*'; // first Teensy is the frame sync master
ledData[1] = (byte)(ledWidth * ledHeight);
ledData[2] = (byte)((ledWidth * ledHeight) >> 8);
ledData[3] = (byte)(usec); // request the frame sync pulse
ledData[4] = (byte)(usec >> 8); // at 75% of the frame time
// send the raw data to the LEDs :-)
//ledSerial[i].write(ledData);
try {
myFile.write(ledData);
} catch (Exception e) {
exit();
}
}
// 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 = 5;
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
}
// draw runs every time the screen is redrawn - show the movie...
void draw() {
if (myMovie.time() < myMovie.duration()) {
image(myMovie, 0, 80);
image(ledImage, 240 - ledWidth / 2, 10);
} else {
if (fileopen) {
println("movie stop, closing output file");
try {
myFile.close();
} catch (Exception e) {
exit();
}
fileopen = false;
}
}
}
// respond to mouse clicks as pause/play
boolean isPlaying = true;
void mousePressed() {
if (isPlaying) {
myMovie.pause();
isPlaying = false;
} else {
myMovie.play();
isPlaying = true;
}
}