diff --git a/photos/video/IMG_0841.MOV b/photos/video/IMG_0841.MOV
new file mode 100644
index 0000000..63ff917
Binary files /dev/null and b/photos/video/IMG_0841.MOV differ
diff --git a/photos/video/IMG_0842.MOV b/photos/video/IMG_0842.MOV
new file mode 100644
index 0000000..f4498f6
Binary files /dev/null and b/photos/video/IMG_0842.MOV differ
diff --git a/photos/video/IMG_0843.MOV b/photos/video/IMG_0843.MOV
new file mode 100644
index 0000000..a08e8b5
Binary files /dev/null and b/photos/video/IMG_0843.MOV differ
diff --git a/photos/video/IMG_0844.MOV b/photos/video/IMG_0844.MOV
new file mode 100644
index 0000000..87fd2fb
Binary files /dev/null and b/photos/video/IMG_0844.MOV differ
diff --git a/pom.xml b/pom.xml
index ce5e676..61dc2f3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -174,22 +174,22 @@
org.slf4j
slf4j-api
- 1.5.10
+ 1.6.6
org.slf4j
slf4j-log4j12
- 1.5.10
+ 1.6.6
org.slf4j
jul-to-slf4j
- 1.5.10
+ 1.6.6
org.slf4j
jcl-over-slf4j
- 1.5.10
+ 1.6.6
commons-io
@@ -198,6 +198,23 @@
jar
compile
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+ 1.9.11
+
+
+ org.eclipse.jetty
+ jetty-server
+ 7.6.8.v20121106
+ test
+
+
+ org.eclipse.jetty
+ jetty-webapp
+ 7.6.8.v20121106
+ test
+
diff --git a/src/main/java/org/forkalsrud/album/video/FlvMetadata.java b/src/main/java/org/forkalsrud/album/video/FlvMetadata.java
index ea4438e..8f1aa93 100644
--- a/src/main/java/org/forkalsrud/album/video/FlvMetadata.java
+++ b/src/main/java/org/forkalsrud/album/video/FlvMetadata.java
@@ -9,9 +9,12 @@ import java.util.LinkedList;
import java.util.List;
import org.forkalsrud.album.exif.Dimension;
+import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FlvMetadata {
+
+ private static Logger log = LoggerFactory.getLogger(FlvMetadata.class);
private abstract class Attr {
@@ -98,7 +101,7 @@ public class FlvMetadata {
public void read(InputStream in) throws FlvFormatException, IOException {
int type = in.read();
if (type != 0) {
- throw new FlvFormatException("Not a boolean: " + type);
+ throw new FlvFormatException("Not a number: " + type);
}
set(readDouble(in));
}
@@ -167,7 +170,7 @@ public class FlvMetadata {
throw new FlvFormatException("Not an object: " + type);
}
if (!in.markSupported()) {
- throw new FlvFormatException("Need redahead");
+ throw new FlvFormatException("Need readahead");
}
List filePositions = null;
List times = null;
@@ -203,6 +206,14 @@ public class FlvMetadata {
private StringAttr creator = new StringAttr("creator");
private StringAttr metadataCreator = new StringAttr("metadatacreator");
+ private StringAttr majorBrand = new StringAttr("major_brand");
+ private StringAttr minorVersion = new StringAttr("minor_version");
+ private StringAttr compatibleBrands = new StringAttr("compatible_brands");
+ private StringAttr creationTime = new StringAttr("creation_time");
+ private StringAttr encoder = new StringAttr("encoder");
+ private StringAttr encoderEng = new StringAttr( "encoder-eng");
+ private StringAttr date = new StringAttr("date");
+ private StringAttr dateEng = new StringAttr("date-eng");
private BooleanAttr hasKeyframes = new BooleanAttr("hasKeyframes");
private BooleanAttr hasVideo = new BooleanAttr("hasVideo");
@@ -247,6 +258,15 @@ public class FlvMetadata {
attrs.add(creator);
attrs.add(metadataCreator);
+ attrs.add(majorBrand);
+ attrs.add(minorVersion);
+ attrs.add(compatibleBrands);
+ attrs.add(creationTime);
+ attrs.add(date);
+ attrs.add(dateEng);
+ attrs.add(encoder);
+ attrs.add(encoderEng);
+
attrs.add(hasKeyframes);
attrs.add(hasVideo);
attrs.add(hasAudio);
@@ -511,10 +531,10 @@ public class FlvMetadata {
throw new FlvFormatException("Not an ECMA array: " + type);
}
int arrayLen = readEcmaArrayHeader(in);
- for (int i = 0; i < arrayLen; i++) {
+ while (!isLookingAtEnd(in)) {
readProperty(in);
}
- readEcmaArrayFooter(in);
+ readEcmaArrayFoter(in);
} catch (FlvFormatException e) {
LoggerFactory.getLogger(getClass()).error("invalid", e);
}
@@ -583,7 +603,7 @@ public class FlvMetadata {
return len;
}
- protected void readEcmaArrayFooter(InputStream in) throws FlvFormatException, IOException {
+ protected void readEcmaArrayFoter(InputStream in) throws FlvFormatException, IOException {
readObjectEnd(in);
}
@@ -607,10 +627,26 @@ public class FlvMetadata {
String name = readString(in);
Attr attr = findAttrByName(name);
- if (attr == null) {
- throw new FlvFormatException("Unknown attribute: " + name);
- }
- attr.read(in);
+ if (attr != null) {
+ attr.read(in);
+ } else {
+ log.warn("Unknown metadata property: " + name);
+ readUnknownProperty(in, name);
+ }
+ }
+
+ protected void readUnknownProperty(InputStream in, String name) throws IOException, FlvFormatException {
+
+ in.mark(1);
+ int type = in.read();
+ in.reset();
+ switch (type) {
+ case 2:
+ new StringAttr(name).read(in);
+ break;
+ default:
+ break;
+ }
}
private Attr findAttrByName(String name) {
diff --git a/src/main/java/org/forkalsrud/album/video/MovieCoder.java b/src/main/java/org/forkalsrud/album/video/MovieCoder.java
index 63576b4..6e66aa5 100644
--- a/src/main/java/org/forkalsrud/album/video/MovieCoder.java
+++ b/src/main/java/org/forkalsrud/album/video/MovieCoder.java
@@ -14,6 +14,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
+import org.codehaus.jackson.map.ObjectMapper;
import org.forkalsrud.album.db.Chunk;
import org.forkalsrud.album.db.MovieDatabase;
import org.forkalsrud.album.exif.Dimension;
@@ -27,6 +28,7 @@ public class MovieCoder {
private String ffmpegExecutable;
private String mplayerExecutable;
+ private String exiftoolExecutable;
private PictureScaler pictureScaler;
private MovieDatabase movieDb;
private HashMap currentEncodings = new HashMap();
@@ -39,15 +41,16 @@ public class MovieCoder {
public void init() throws Exception {
ExecUtil util = new ExecUtil();
- this.ffmpegExecutable = util.findExecutableInShellPath("ffmpeg");
+ if (this.ffmpegExecutable == null) {
+ this.ffmpegExecutable = util.findExecutableInShellPath("ffmpeg");
+ }
this.mplayerExecutable = util.findExecutableInShellPath("mplayer");
+ this.exiftoolExecutable = util.findExecutableInShellPath("exiftool");
}
-
-
/**
*
* @param f the movie file
@@ -57,35 +60,159 @@ public class MovieCoder {
*/
public Map generateVideoProperties(File f) throws IOException, InterruptedException {
+ /*
+ * [{
+ "SourceFile": "/home/erik/local/IMG_0837.mov",
+ "ExifToolVersion": 9.12,
+ "FileName": "IMG_0837.mov",
+ "Directory": "/home/erik/local",
+ "FileSize": "1438 kB",
+ "FileModifyDate": "2013:01:19 18:36:53-08:00",
+ "FileAccessDate": "2013:01:19 18:36:57-08:00",
+ "FileInodeChangeDate": "2013:01:19 18:36:53-08:00",
+ "FilePermissions": "rw-rw-r--",
+ "FileType": "MOV",
+ "MIMEType": "video/quicktime",
+ "MajorBrand": "Apple QuickTime (.MOV/QT)",
+ "MinorVersion": "0.0.0",
+ "CompatibleBrands": ["qt "],
+ "MovieDataSize": 1467141,
+ "MovieHeaderVersion": 0,
+ "ModifyDate": "2012:12:30 06:44:31",
+ "TimeScale": 600,
+ "Duration": "9.07 s",
+ "PreferredRate": 1,
+ "PreferredVolume": "100.00%",
+ "PreviewTime": "0 s",
+ "PreviewDuration": "0 s",
+ "PosterTime": "0 s",
+ "SelectionTime": "0 s",
+ "SelectionDuration": "0 s",
+ "CurrentTime": "0 s",
+ "NextTrackID": 3,
+ "TrackHeaderVersion": 0,
+ "TrackCreateDate": "2012:12:30 06:44:26",
+ "TrackModifyDate": "2012:12:30 06:44:31",
+ "TrackID": 1,
+ "TrackDuration": "9.05 s",
+ "TrackLayer": 0,
+ "TrackVolume": "100.00%",
+ "Balance": 0,
+ "AudioChannels": 1,
+ "AudioBitsPerSample": 16,
+ "AudioSampleRate": 44100,
+ "AudioFormat": "chan",
+ "MatrixStructure": "0 1 0 -1 0 0 540 0 1",
+ "ImageWidth": 960,
+ "ImageHeight": 540,
+ "CleanApertureDimensions": "960x540",
+ "ProductionApertureDimensions": "960x540",
+ "EncodedPixelsDimensions": "960x540",
+ "MediaHeaderVersion": 0,
+ "MediaCreateDate": "2012:12:30 06:44:26",
+ "MediaModifyDate": "2012:12:30 06:44:31",
+ "MediaTimeScale": 600,
+ "MediaDuration": "9.10 s",
+ "MediaLanguageCode": "und",
+ "GraphicsMode": "ditherCopy",
+ "OpColor": "32768 32768 32768",
+ "HandlerClass": "Data Handler",
+ "HandlerVendorID": "Apple",
+ "HandlerDescription": "Core Media Data Handler",
+ "CompressorID": "avc1",
+ "SourceImageWidth": 960,
+ "SourceImageHeight": 540,
+ "XResolution": 72,
+ "YResolution": 72,
+ "CompressorName": "H.264",
+ "BitDepth": 24,
+ "VideoFrameRate": 30,
+ "CameraIdentifier": "Back",
+ "FrameReadoutTime": "28512 microseconds",
+ "Make": "Apple",
+ "SoftwareVersion": "6.0.1",
+ "CreateDate": "2012:12:29 16:30:21-08:00",
+ "Model": "iPhone 4S",
+ "HandlerType": "Metadata Tags",
+ "Make-und-US": "Apple",
+ "CreationDate-und-US": "2012:12:29 16:30:21-08:00",
+ "Software-und-US": "6.0.1",
+ "Model-und-US": "iPhone 4S",
+ "AvgBitrate": "1.29 Mbps",
+ "ImageSize": "960x540",
+ "Rotation": 90
+}]
+ */
Map props = new HashMap();
ProcessBuilder pb = new ProcessBuilder().command(
- mplayerExecutable, "-vo", "null", "-ao", "null", "-frames", "0", "-identify", f.getAbsolutePath());
+ this.exiftoolExecutable, "-j", f.getAbsolutePath());
pb.redirectErrorStream(false);
Process p = pb.start();
p.getOutputStream().close();
- List lines = IOUtils.readLines(p.getInputStream());
+
+ ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
+ @SuppressWarnings("unchecked")
+ List