You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2018/12/13 15:31:12 UTC

[tika] branch master updated (614190b -> 8187f21)

This is an automated email from the ASF dual-hosted git repository.

tallison pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/tika.git.


    from 614190b  TIKA-2637 -- ParsingReader should return -1 for a zero byte file
     new f9dcd20  handful of dependency upgrades
     new 8187f21  TIKA-2792 -- revert mp4parser dependency

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 tika-example/pom.xml                               |   6 +
 tika-parsers/pom.xml                               |  19 +-
 .../tika/parser/mp4/DirectFileReadDataSource.java  | 127 ++++++++
 .../java/org/apache/tika/parser/mp4/MP4Parser.java | 337 +++++++++++----------
 tika-server/pom.xml                                |   6 +
 5 files changed, 323 insertions(+), 172 deletions(-)
 create mode 100644 tika-parsers/src/main/java/org/apache/tika/parser/mp4/DirectFileReadDataSource.java


[tika] 01/02: handful of dependency upgrades

Posted by ta...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

tallison pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tika.git

commit f9dcd2073d8984d52105fda1299facf84a683f60
Author: TALLISON <ta...@apache.org>
AuthorDate: Thu Dec 13 10:22:01 2018 -0500

    handful of dependency upgrades
---
 tika-example/pom.xml |  6 ++++++
 tika-parsers/pom.xml | 11 +++++++++++
 tika-server/pom.xml  |  6 ++++++
 3 files changed, 23 insertions(+)

diff --git a/tika-example/pom.xml b/tika-example/pom.xml
index bfd3892..7bf2ef4 100644
--- a/tika-example/pom.xml
+++ b/tika-example/pom.xml
@@ -61,6 +61,12 @@
       <groupId>org.apache.tika</groupId>
       <artifactId>tika-translate</artifactId>
       <version>${project.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.annotation</groupId>
+          <artifactId>javax.annotation-api</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
diff --git a/tika-parsers/pom.xml b/tika-parsers/pom.xml
index dace1b9..230ff1b 100644
--- a/tika-parsers/pom.xml
+++ b/tika-parsers/pom.xml
@@ -104,6 +104,11 @@
       <artifactId>commons-lang3</artifactId>
       <version>${commons.lang3.version}</version>
     </dependency>
+    <dependency>
+      <groupId>javax.annotation</groupId>
+      <artifactId>javax.annotation-api</artifactId>
+      <version>1.3.2</version>
+    </dependency>
 
     <!-- Externally Maintained Parsers -->
     <dependency>
@@ -368,6 +373,12 @@
       <groupId>org.apache.cxf</groupId>
       <artifactId>cxf-rt-rs-client</artifactId>
       <version>${cxf.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.annotation</groupId>
+          <artifactId>javax.annotation-api</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <!-- TIKA-2021: Tesseract OCR Parser dependencies,
     used for executing image processing script -->
diff --git a/tika-server/pom.xml b/tika-server/pom.xml
index abf23fa..6c4a800 100644
--- a/tika-server/pom.xml
+++ b/tika-server/pom.xml
@@ -74,6 +74,12 @@
       <groupId>org.apache.cxf</groupId>
       <artifactId>cxf-rt-frontend-jaxrs</artifactId>
       <version>${cxf.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.annotation</groupId>
+          <artifactId>javax.annotation-api</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.apache.cxf</groupId>


[tika] 02/02: TIKA-2792 -- revert mp4parser dependency

Posted by ta...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

tallison pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tika.git

commit 8187f21aecf17d6796c36f2ef2f3d39ca6364ab0
Author: TALLISON <ta...@apache.org>
AuthorDate: Thu Dec 13 10:24:47 2018 -0500

    TIKA-2792 -- revert mp4parser dependency
---
 tika-parsers/pom.xml                               |   8 +-
 .../tika/parser/mp4/DirectFileReadDataSource.java  | 127 ++++++++
 .../java/org/apache/tika/parser/mp4/MP4Parser.java | 337 +++++++++++----------
 3 files changed, 300 insertions(+), 172 deletions(-)

diff --git a/tika-parsers/pom.xml b/tika-parsers/pom.xml
index 230ff1b..248aea9 100644
--- a/tika-parsers/pom.xml
+++ b/tika-parsers/pom.xml
@@ -292,14 +292,10 @@
       <version>7.0</version>
     </dependency>
     <dependency>
-      <!-- 1.9.37 was built with jdk9 and
-           causes: Method rewind()Ljava/nio/ByteBuffer;
-           does not exist in class java.nio.ByteBuffer -->
-      <groupId>org.mp4parser</groupId>
+      <groupId>com.googlecode.mp4parser</groupId>
       <artifactId>isoparser</artifactId>
-      <version>1.9.36</version>
+      <version>1.1.22</version>
     </dependency>
-
     <dependency>
       <groupId>com.drewnoakes</groupId>
       <artifactId>metadata-extractor</artifactId>
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/mp4/DirectFileReadDataSource.java b/tika-parsers/src/main/java/org/apache/tika/parser/mp4/DirectFileReadDataSource.java
new file mode 100644
index 0000000..698a106
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/mp4/DirectFileReadDataSource.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.parser.mp4;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+
+import com.googlecode.mp4parser.DataSource;
+
+/**
+ * A {@link DataSource} implementation that relies on direct reads from a {@link RandomAccessFile}.
+ * It should be slower than {@link com.googlecode.mp4parser.FileDataSourceImpl} but does not incur the implicit file locks of
+ * memory mapped I/O on some JVMs. This implementation allows for a more controlled deletion of files
+ * and might be preferred when working with temporary files.
+ * @see <a href="http://bugs.java.com/view_bug.do?bug_id=4724038">JDK-4724038 : (fs) Add unmap method to MappedByteBuffer</a>
+ * @see <a href="http://bugs.java.com/view_bug.do?bug_id=6359560">JDK-6359560 : (fs) File.deleteOnExit() doesn't work when MappedByteBuffer exists (win)</a>
+ */
+public class DirectFileReadDataSource implements DataSource {
+
+    private static final int TRANSFER_SIZE = 8192;
+
+    private RandomAccessFile raf;
+
+    public DirectFileReadDataSource(File f) throws IOException {
+        this.raf = new RandomAccessFile(f, "r");
+    }
+
+    public int read(ByteBuffer byteBuffer) throws IOException {
+        int len = byteBuffer.remaining();
+        int totalRead = 0;
+        int bytesRead = 0;
+        byte[] buf = new byte[TRANSFER_SIZE];
+        while (totalRead < len) {
+            int bytesToRead = Math.min((len - totalRead), TRANSFER_SIZE);
+            bytesRead = raf.read(buf, 0, bytesToRead);
+            if (bytesRead < 0) {
+                break;
+            } else {
+                totalRead += bytesRead;
+            }
+            byteBuffer.put(buf, 0, bytesRead);
+        }
+        if (bytesRead < 0 && position() == size() && byteBuffer.hasRemaining()) {
+            throw new IOException("End of stream reached earlier than expected");
+        }
+        return ((bytesRead < 0) && (totalRead == 0)) ? -1 : totalRead;
+    }
+
+    public int readAllInOnce(ByteBuffer byteBuffer) throws IOException {
+        if (byteBuffer.remaining() > raf.length()) {
+            throw new IOException("trying to readAllInOnce past end of stream");
+        }
+        byte[] buf = new byte[byteBuffer.remaining()];
+        int read = raf.read(buf);
+        byteBuffer.put(buf, 0, read);
+        return read;
+    }
+
+    public long size() throws IOException {
+        return raf.length();
+    }
+
+    public long position() throws IOException {
+        return raf.getFilePointer();
+    }
+
+    public void position(long nuPos) throws IOException {
+        if (nuPos > raf.length()) {
+            throw new IOException("requesting seek past end of stream");
+        }
+        raf.seek(nuPos);
+    }
+
+    public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
+        return target.write(map(position, count));
+    }
+
+    public ByteBuffer map(long startPosition, long size) throws IOException {
+        if (startPosition < 0 || size < 0) {
+            throw new IOException("startPosition and size must both be >= 0");
+        }
+        //make sure that start+size aren't greater than avail size
+        //in raf.
+        BigInteger end = BigInteger.valueOf(startPosition);
+        end = end.add(BigInteger.valueOf(size));
+        if (end.compareTo(BigInteger.valueOf(raf.length())) > 0) {
+            throw new IOException("requesting read past end of stream");
+        }
+
+        raf.seek(startPosition);
+        int payLoadSize = l2i(size);
+        //hack to check for potential overflow
+        if (Long.MAX_VALUE-payLoadSize < startPosition ||
+                Long.MAX_VALUE-payLoadSize > raf.length()) {
+            throw new IOException("requesting read past end of stream");
+        }
+        byte[] payload = new byte[payLoadSize];
+        raf.readFully(payload);
+        return ByteBuffer.wrap(payload);
+    }
+
+    @Override
+    public void close() throws IOException {
+        raf.close();
+    }
+
+}
\ No newline at end of file
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java b/tika-parsers/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java
index c483433..afa63b8 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java
@@ -16,6 +16,36 @@
  */
 package org.apache.tika.parser.mp4;
 
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.Container;
+import com.coremedia.iso.boxes.FileTypeBox;
+import com.coremedia.iso.boxes.MetaBox;
+import com.coremedia.iso.boxes.MovieBox;
+import com.coremedia.iso.boxes.MovieHeaderBox;
+import com.coremedia.iso.boxes.SampleDescriptionBox;
+import com.coremedia.iso.boxes.SampleTableBox;
+import com.coremedia.iso.boxes.TrackBox;
+import com.coremedia.iso.boxes.TrackHeaderBox;
+import com.coremedia.iso.boxes.UserDataBox;
+import com.coremedia.iso.boxes.apple.AppleItemListBox;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.DataSource;
+import com.googlecode.mp4parser.boxes.apple.AppleAlbumBox;
+import com.googlecode.mp4parser.boxes.apple.AppleArtist2Box;
+import com.googlecode.mp4parser.boxes.apple.AppleArtistBox;
+import com.googlecode.mp4parser.boxes.apple.AppleCommentBox;
+import com.googlecode.mp4parser.boxes.apple.AppleCompilationBox;
+import com.googlecode.mp4parser.boxes.apple.AppleDiskNumberBox;
+import com.googlecode.mp4parser.boxes.apple.AppleEncoderBox;
+import com.googlecode.mp4parser.boxes.apple.AppleGenreBox;
+import com.googlecode.mp4parser.boxes.apple.AppleNameBox;
+import com.googlecode.mp4parser.boxes.apple.AppleRecordingYear2Box;
+import com.googlecode.mp4parser.boxes.apple.AppleTrackAuthorBox;
+import com.googlecode.mp4parser.boxes.apple.AppleTrackNumberBox;
+import com.googlecode.mp4parser.boxes.apple.Utf8AppleDataBox;
+import org.apache.poi.ss.formula.functions.T;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
@@ -28,33 +58,6 @@ import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.AbstractParser;
 import org.apache.tika.parser.ParseContext;
 import org.apache.tika.sax.XHTMLContentHandler;
-import org.mp4parser.Box;
-import org.mp4parser.Container;
-import org.mp4parser.IsoFile;
-import org.mp4parser.boxes.apple.AppleAlbumBox;
-import org.mp4parser.boxes.apple.AppleArtist2Box;
-import org.mp4parser.boxes.apple.AppleArtistBox;
-import org.mp4parser.boxes.apple.AppleCommentBox;
-import org.mp4parser.boxes.apple.AppleCompilationBox;
-import org.mp4parser.boxes.apple.AppleDiskNumberBox;
-import org.mp4parser.boxes.apple.AppleEncoderBox;
-import org.mp4parser.boxes.apple.AppleGenreBox;
-import org.mp4parser.boxes.apple.AppleItemListBox;
-import org.mp4parser.boxes.apple.AppleNameBox;
-import org.mp4parser.boxes.apple.AppleRecordingYear2Box;
-import org.mp4parser.boxes.apple.AppleTrackAuthorBox;
-import org.mp4parser.boxes.apple.AppleTrackNumberBox;
-import org.mp4parser.boxes.apple.Utf8AppleDataBox;
-import org.mp4parser.boxes.iso14496.part12.FileTypeBox;
-import org.mp4parser.boxes.iso14496.part12.MetaBox;
-import org.mp4parser.boxes.iso14496.part12.MovieBox;
-import org.mp4parser.boxes.iso14496.part12.MovieHeaderBox;
-import org.mp4parser.boxes.iso14496.part12.SampleDescriptionBox;
-import org.mp4parser.boxes.iso14496.part12.SampleTableBox;
-import org.mp4parser.boxes.iso14496.part12.TrackBox;
-import org.mp4parser.boxes.iso14496.part12.TrackHeaderBox;
-import org.mp4parser.boxes.iso14496.part12.UserDataBox;
-import org.mp4parser.boxes.sampleentry.AudioSampleEntry;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
@@ -73,7 +76,7 @@ import java.util.Set;
 /**
  * Parser for the MP4 media container format, as well as the older
  *  QuickTime format that MP4 is based on.
- *
+ * 
  * This uses the MP4Parser project from http://code.google.com/p/mp4parser/
  *  to do the underlying parsing
  */
@@ -81,12 +84,12 @@ public class MP4Parser extends AbstractParser {
     /** Serial version UID */
     private static final long serialVersionUID = 84011216792285L;
     /** TODO Replace this with a 2dp Duration Property Converter */
-    private static final DecimalFormat DURATION_FORMAT =
-            (DecimalFormat)NumberFormat.getNumberInstance(Locale.ROOT);
+    private static final DecimalFormat DURATION_FORMAT = 
+            (DecimalFormat)NumberFormat.getNumberInstance(Locale.ROOT); 
     static {
         DURATION_FORMAT.applyPattern("0.0#");
     }
-
+    
     // Ensure this stays in Sync with the entries in tika-mimetypes.xml
     private static final Map<MediaType,List<String>> typesMap = new HashMap<MediaType, List<String>>();
     static {
@@ -125,41 +128,42 @@ public class MP4Parser extends AbstractParser {
         TemporaryResources tmp = new TemporaryResources();
         TikaInputStream tstream = TikaInputStream.get(stream, tmp);
 
-        try (IsoFile isoFile = new IsoFile(tstream.getFile())) {
-            tmp.addResource(isoFile);
-
-            // Grab the file type box
-            FileTypeBox fileType = getOrNull(isoFile, FileTypeBox.class);
-            if (fileType != null) {
-                // Identify the type
-                MediaType type = MediaType.application("mp4");
-                for (Map.Entry<MediaType, List<String>> e : typesMap.entrySet()) {
-                    if (e.getValue().contains(fileType.getMajorBrand())) {
-                        type = e.getKey();
-                        break;
+        try (DataSource dataSource = new DirectFileReadDataSource(tstream.getFile())) {
+            try (IsoFile isoFile = new IsoFile(dataSource)) {
+                tmp.addResource(isoFile);
+
+                // Grab the file type box
+                FileTypeBox fileType = getOrNull(isoFile, FileTypeBox.class);
+                if (fileType != null) {
+                    // Identify the type
+                    MediaType type = MediaType.application("mp4");
+                    for (Map.Entry<MediaType, List<String>> e : typesMap.entrySet()) {
+                        if (e.getValue().contains(fileType.getMajorBrand())) {
+                            type = e.getKey();
+                            break;
+                        }
                     }
-                }
-                metadata.set(Metadata.CONTENT_TYPE, type.toString());
+                    metadata.set(Metadata.CONTENT_TYPE, type.toString());
 
-                if (type.getType().equals("audio")) {
-                    metadata.set(XMPDM.AUDIO_COMPRESSOR, fileType.getMajorBrand().trim());
+                    if (type.getType().equals("audio")) {
+                        metadata.set(XMPDM.AUDIO_COMPRESSOR, fileType.getMajorBrand().trim());
+                    }
+                } else {
+                    // Some older QuickTime files lack the FileType
+                    metadata.set(Metadata.CONTENT_TYPE, "video/quicktime");
                 }
-            } else {
-                // Some older QuickTime files lack the FileType
-                metadata.set(Metadata.CONTENT_TYPE, "video/quicktime");
-            }
 
 
-            // Get the main MOOV box
-            MovieBox moov = getOrNull(isoFile, MovieBox.class);
-            if (moov == null) {
-                // Bail out
-                return;
-            }
+                // Get the main MOOV box
+                MovieBox moov = getOrNull(isoFile, MovieBox.class);
+                if (moov == null) {
+                    // Bail out
+                    return;
+                }
 
 
-            XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata);
-            xhtml.startDocument();
+                XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata);
+                xhtml.startDocument();
 
 
             // Pull out some information from the header box
@@ -169,131 +173,132 @@ public class MP4Parser extends AbstractParser {
                 metadata.set(TikaCoreProperties.CREATED, mHeader.getCreationTime());
                 metadata.set(TikaCoreProperties.MODIFIED, mHeader.getModificationTime());
 
-                // Get the duration
-                double durationSeconds = ((double) mHeader.getDuration()) / mHeader.getTimescale();
-                metadata.set(XMPDM.DURATION, DURATION_FORMAT.format(durationSeconds));
-
-                // The timescale is normally the sampling rate
-                metadata.set(XMPDM.AUDIO_SAMPLE_RATE, (int) mHeader.getTimescale());
-            }
+                    // Get the duration
+                    double durationSeconds = ((double) mHeader.getDuration()) / mHeader.getTimescale();
+                    metadata.set(XMPDM.DURATION, DURATION_FORMAT.format(durationSeconds));
 
+                    // The timescale is normally the sampling rate
+                    metadata.set(XMPDM.AUDIO_SAMPLE_RATE, (int) mHeader.getTimescale());
+                }
 
-            // Get some more information from the track header
-            // TODO Decide how to handle multiple tracks
-            List<TrackBox> tb = moov.getBoxes(TrackBox.class);
-            if (tb.size() > 0) {
-                TrackBox track = tb.get(0);
 
-                TrackHeaderBox header = track.getTrackHeaderBox();
-                // Get the creation and modification dates
-                metadata.set(TikaCoreProperties.CREATED, header.getCreationTime());
-                metadata.set(TikaCoreProperties.MODIFIED, header.getModificationTime());
-
-                // Get the video with and height
-                metadata.set(Metadata.IMAGE_WIDTH, (int) header.getWidth());
-                metadata.set(Metadata.IMAGE_LENGTH, (int) header.getHeight());
-
-                // Get the sample information
-                SampleTableBox samples = track.getSampleTableBox();
-                SampleDescriptionBox sampleDesc = samples.getSampleDescriptionBox();
-                if (sampleDesc != null) {
-                    // Look for the first Audio Sample, if present
-                    AudioSampleEntry sample = getOrNull(sampleDesc, AudioSampleEntry.class);
-                    if (sample != null) {
-                        XMPDM.ChannelTypePropertyConverter.convertAndSet(metadata, sample.getChannelCount());
-                        //metadata.set(XMPDM.AUDIO_SAMPLE_TYPE, sample.getSampleSize());    // TODO Num -> Type mapping
-                        metadata.set(XMPDM.AUDIO_SAMPLE_RATE, (int) sample.getSampleRate());
-                        //metadata.set(XMPDM.AUDIO_, sample.getSamplesPerPacket());
-                        //metadata.set(XMPDM.AUDIO_, sample.getBytesPerSample());
+                // Get some more information from the track header
+                // TODO Decide how to handle multiple tracks
+                List<TrackBox> tb = moov.getBoxes(TrackBox.class);
+                if (tb.size() > 0) {
+                    TrackBox track = tb.get(0);
+
+                    TrackHeaderBox header = track.getTrackHeaderBox();
+                    // Get the creation and modification dates
+                    metadata.set(TikaCoreProperties.CREATED, header.getCreationTime());
+                    metadata.set(TikaCoreProperties.MODIFIED, header.getModificationTime());
+
+                    // Get the video with and height
+                    metadata.set(Metadata.IMAGE_WIDTH, (int) header.getWidth());
+                    metadata.set(Metadata.IMAGE_LENGTH, (int) header.getHeight());
+
+                    // Get the sample information
+                    SampleTableBox samples = track.getSampleTableBox();
+                    SampleDescriptionBox sampleDesc = samples.getSampleDescriptionBox();
+                    if (sampleDesc != null) {
+                        // Look for the first Audio Sample, if present
+                        AudioSampleEntry sample = getOrNull(sampleDesc, AudioSampleEntry.class);
+                        if (sample != null) {
+                            XMPDM.ChannelTypePropertyConverter.convertAndSet(metadata, sample.getChannelCount());
+                            //metadata.set(XMPDM.AUDIO_SAMPLE_TYPE, sample.getSampleSize());    // TODO Num -> Type mapping
+                            metadata.set(XMPDM.AUDIO_SAMPLE_RATE, (int) sample.getSampleRate());
+                            //metadata.set(XMPDM.AUDIO_, sample.getSamplesPerPacket());
+                            //metadata.set(XMPDM.AUDIO_, sample.getBytesPerSample());
+                        }
                     }
                 }
-            }
 
-            // Get metadata from the User Data Box
-            UserDataBox userData = getOrNull(moov, UserDataBox.class);
-            if (userData != null) {
-                MetaBox meta = getOrNull(userData, MetaBox.class);
-
-                // Check for iTunes Metadata
-                // See http://atomicparsley.sourceforge.net/mpeg-4files.html and
-                //  http://code.google.com/p/mp4v2/wiki/iTunesMetadata for more on these
-                AppleItemListBox apple = getOrNull(meta, AppleItemListBox.class);
-                if (apple != null) {
-                    // Title
-                    AppleNameBox title = getOrNull(apple, AppleNameBox.class);
-                    addMetadata(TikaCoreProperties.TITLE, metadata, title);
-
-                    // Artist
-                    AppleArtistBox artist = getOrNull(apple, AppleArtistBox.class);
-                    addMetadata(TikaCoreProperties.CREATOR, metadata, artist);
-                    addMetadata(XMPDM.ARTIST, metadata, artist);
-
-                    // Album Artist
-                    AppleArtist2Box artist2 = getOrNull(apple, AppleArtist2Box.class);
-                    addMetadata(XMPDM.ALBUM_ARTIST, metadata, artist2);
-
-                    // Album
-                    AppleAlbumBox album = getOrNull(apple, AppleAlbumBox.class);
-                    addMetadata(XMPDM.ALBUM, metadata, album);
-
-                    // Composer
-                    AppleTrackAuthorBox composer = getOrNull(apple, AppleTrackAuthorBox.class);
-                    addMetadata(XMPDM.COMPOSER, metadata, composer);
-
-                    // Genre
-                    AppleGenreBox genre = getOrNull(apple, AppleGenreBox.class);
-                    addMetadata(XMPDM.GENRE, metadata, genre);
-
-                    // Year
-                    AppleRecordingYear2Box year = getOrNull(apple, AppleRecordingYear2Box.class);
-                    if (year != null) {
-                        metadata.set(XMPDM.RELEASE_DATE, year.getValue());
-                    }
+                // Get metadata from the User Data Box
+                UserDataBox userData = getOrNull(moov, UserDataBox.class);
+                if (userData != null) {
+                    MetaBox meta = getOrNull(userData, MetaBox.class);
+
+                    // Check for iTunes Metadata
+                    // See http://atomicparsley.sourceforge.net/mpeg-4files.html and
+                    //  http://code.google.com/p/mp4v2/wiki/iTunesMetadata for more on these
+                    AppleItemListBox apple = getOrNull(meta, AppleItemListBox.class);
+                    if (apple != null) {
+                        // Title
+                        AppleNameBox title = getOrNull(apple, AppleNameBox.class);
+                        addMetadata(TikaCoreProperties.TITLE, metadata, title);
+
+                        // Artist
+                        AppleArtistBox artist = getOrNull(apple, AppleArtistBox.class);
+                        addMetadata(TikaCoreProperties.CREATOR, metadata, artist);
+                        addMetadata(XMPDM.ARTIST, metadata, artist);
+
+                        // Album Artist
+                        AppleArtist2Box artist2 = getOrNull(apple, AppleArtist2Box.class);
+                        addMetadata(XMPDM.ALBUM_ARTIST, metadata, artist2);
+
+                        // Album
+                        AppleAlbumBox album = getOrNull(apple, AppleAlbumBox.class);
+                        addMetadata(XMPDM.ALBUM, metadata, album);
+
+                        // Composer
+                        AppleTrackAuthorBox composer = getOrNull(apple, AppleTrackAuthorBox.class);
+                        addMetadata(XMPDM.COMPOSER, metadata, composer);
+
+                        // Genre
+                        AppleGenreBox genre = getOrNull(apple, AppleGenreBox.class);
+                        addMetadata(XMPDM.GENRE, metadata, genre);
+
+                        // Year
+                        AppleRecordingYear2Box year = getOrNull(apple, AppleRecordingYear2Box.class);
+                        if (year != null) {
+                            metadata.set(XMPDM.RELEASE_DATE, year.getValue());
+                        }
 
-                    // Track number
-                    AppleTrackNumberBox trackNum = getOrNull(apple, AppleTrackNumberBox.class);
-                    if (trackNum != null) {
-                        metadata.set(XMPDM.TRACK_NUMBER, trackNum.getA());
-                        //metadata.set(XMPDM.NUMBER_OF_TRACKS, trackNum.getB()); // TODO
-                    }
+                        // Track number
+                        AppleTrackNumberBox trackNum = getOrNull(apple, AppleTrackNumberBox.class);
+                        if (trackNum != null) {
+                            metadata.set(XMPDM.TRACK_NUMBER, trackNum.getA());
+                            //metadata.set(XMPDM.NUMBER_OF_TRACKS, trackNum.getB()); // TODO
+                        }
 
-                    // Disc number
-                    AppleDiskNumberBox discNum = getOrNull(apple, AppleDiskNumberBox.class);
-                    if (discNum != null) {
-                        metadata.set(XMPDM.DISC_NUMBER, discNum.getA());
-                    }
+                        // Disc number
+                        AppleDiskNumberBox discNum = getOrNull(apple, AppleDiskNumberBox.class);
+                        if (discNum != null) {
+                            metadata.set(XMPDM.DISC_NUMBER, discNum.getA());
+                        }
 
-                    // Compilation
-                    AppleCompilationBox compilation = getOrNull(apple, AppleCompilationBox.class);
-                    if (compilation != null) {
-                        metadata.set(XMPDM.COMPILATION, (int) compilation.getValue());
-                    }
+                        // Compilation
+                        AppleCompilationBox compilation = getOrNull(apple, AppleCompilationBox.class);
+                        if (compilation != null) {
+                            metadata.set(XMPDM.COMPILATION, (int) compilation.getValue());
+                        }
 
-                    // Comment
-                    AppleCommentBox comment = getOrNull(apple, AppleCommentBox.class);
-                    addMetadata(XMPDM.LOG_COMMENT, metadata, comment);
+                        // Comment
+                        AppleCommentBox comment = getOrNull(apple, AppleCommentBox.class);
+                        addMetadata(XMPDM.LOG_COMMENT, metadata, comment);
 
-                    // Encoder
-                    AppleEncoderBox encoder = getOrNull(apple, AppleEncoderBox.class);
-                    if (encoder != null) {
-                        metadata.set(XMP.CREATOR_TOOL, encoder.getValue());
-                    }
+                        // Encoder
+                        AppleEncoderBox encoder = getOrNull(apple, AppleEncoderBox.class);
+                        if (encoder != null) {
+                            metadata.set(XMP.CREATOR_TOOL, encoder.getValue());
+                        }
 
 
-                    // As text
-                    for (Box box : apple.getBoxes()) {
-                        if (box instanceof Utf8AppleDataBox) {
-                            xhtml.element("p", ((Utf8AppleDataBox) box).getValue());
+                        // As text
+                        for (Box box : apple.getBoxes()) {
+                            if (box instanceof Utf8AppleDataBox) {
+                                xhtml.element("p", ((Utf8AppleDataBox) box).getValue());
+                            }
                         }
                     }
-                }
 
-                // TODO Check for other kinds too
-            }
+                    // TODO Check for other kinds too
+                }
 
-            // All done
-            xhtml.endDocument();
 
+                // All done
+                xhtml.endDocument();
+            }
         } finally {
             tmp.dispose();
         }