versuche mich gerade an einer kleinen Anwendung welche ich auf meinem Raspberry Pi laufen lassen will:
Ich rufe “raspstill” auf um Bilder mit der Raspberry Camera zu machen. Es wird alle x Millisekunden ein Bild gemacht. Statt die Bilddaten auf die Platte zu schreiben und wieder einzulesen, will ich die Ausgabe auf STDOUT verwenden (Parameter “-o -”).
Mit der Javaanwendugn starte ich das raspstill und lese die Ausgabe.
Aber mir ist gerade aufgefallen: ich weiß ja gar nicht wie groß ein Bild ist. “Messen” kann ich’s auch nicht. Die Bilder sind ja (da JPG) nicht alle gleich groß.
Wie unterscheidet man jetzt ein JPG Bild vom nächsten wenn eins nach dem anderen durch den InputStream kommt? Any Ideas?
Ein JPG-Stream beginnt mit FFD8h (SOI-Marker) und endet mit FFD9h (EOI-Marker). Diese Marker sollten im JPG-Stream auch sonst nicht mehr vorkommen, so dass man Einzelbilder damit trennen könnte.
edit: Allerdings weis ich nicht, ob diese Werte nicht auch als Höhe und/oder Breite auftauchen können. Evtl. musst du doch den Stream nach allen möglichen Markern absuchen. Hier mal ein PDF zum Aufbau solcher Dateien. http://www.jpeg.org/public/jfif.pdf
Scheint zu gehen. Aber irgendwie steh ich mal wieder wie der Ochs vorm Berg: Wie finde ich effizientesten innerhalb des Streams den End-Marker, ohne jedes byte einzeln zu lesen?
Normalerweise lese ich ja blockweise, was schneller ist. Aber es kann ja durchaus sein dass die hälfte des Markers in einen Block, oder der rest im nächsten Block drin ist. Gibt es da ein effizientes Standardvorgehen um im Stream einen Marker zu finden?
Auf einem “normalen PC” wäre das wohl weniger tragisch wenn ich mir da was “wildes” zusammen bastel. Aber da das ganze auf einem Raspberry Pi laufen soll, will ich da nicht von vornherein verschwenderisch mit der CPU sein.
import java.util.Collection;
import javax.datatypes.DataTypeReader;
final class JPGMarker {
final long pos;
final int length, id, hi, lo;
private final String name;
JPGMarker(DataTypeReader f) throws IOException {
long pos = f.getPosition() + 3;
id = f.read();
int l = 0;
String name = "";
int b = (id == 0xD9)? 2 : (id == 0x00 || id == 0xFF)? 0 : f.readUnsignedShort();
// BEGIN Brauchst du wahrscheinlich nicht
if(id == 0xE0) {
while(l < b && f.read() != 0) {
l++;
}
f.setPosition(pos);
byte[] buf = new byte[l];
l++;
b -= l;
f.read(buf);
name = new String(buf, "ASCII");
f.read();
}
// END Brauchst du wahrscheinlich nicht
this.name = name;
this.pos = pos + l;
hi = (id >> 4) & 0xF;
lo = id & 0xF;
if((b & 0xFF00) == 0xFF00) {
b = 0;
f.skip(-2);
} else if(id == 0x00 || id == 0xFF) {
f.skip(-1);
} else {
b -= 2;
}
length = b;
f.skip(length);
}
}```
und der folgenden Methode:
``` private static List<JPGMarker> scanForMarkers(DataTypeReader f) throws IOException {
f.setOrder(ByteOrder.BIG_ENDIAN);
List<JPGMarker> markerList = new ArrayList<JPGMarker>();
boolean soi = false;
boolean eoi = false;
boolean bogus = false;
JPGMarker marker = null;
int r;
do {
r = f.read();
if(r == 0xFF) {
marker = new JPGMarker(f);
if(marker.id == 0x00 || marker.id == 0xFF) {
marker = null;
}
}
if(marker != null) {
if(!soi) {
soi = marker.id == 0xD8;
} else if(marker.id == 0xD9) {
soi = false;
eoi = true;
}
markerList.add(marker);
marker = null;
}
bogus = (!soi && !eoi) || r < 0;
} while(soi && !bogus);
if(bogus) {
markerList.clear();
}
return markerList;
}```Wobei der DatatypeReader ein erweiterter RandomAccessFile ist. Aber evtl. kannst es dir ja umbauen. Du musst sie möglicherweise ja nichtmal in einer Liste speichern. Die Marker müssen ja nur gelesen werden, um deren Länge zu überspringen.