diff options
Diffstat (limited to 'src/video-support/QuicktimeMetdataLoader.vala')
-rw-r--r-- | src/video-support/QuicktimeMetdataLoader.vala | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/src/video-support/QuicktimeMetdataLoader.vala b/src/video-support/QuicktimeMetdataLoader.vala new file mode 100644 index 0000000..0a831d2 --- /dev/null +++ b/src/video-support/QuicktimeMetdataLoader.vala @@ -0,0 +1,127 @@ +public class QuickTimeMetadataLoader { + + // Quicktime calendar date/time format is number of seconds since January 1, 1904. + // This converts to UNIX time (66 years + 17 leap days). + public const int64 QUICKTIME_EPOCH_ADJUSTMENT = 2082844800; + + private File file = null; + + public QuickTimeMetadataLoader(File file) { + this.file = file; + } + + public MetadataDateTime? get_creation_date_time() { + var dt = get_creation_date_time_for_quicktime(); + if (dt == null) { + return null; + } else { + return new MetadataDateTime(dt); + } + } + + public string? get_title() { + // Not supported. + return null; + } + + // Checks if the given file is a QuickTime file. + public bool is_supported() { + QuickTimeAtom test = new QuickTimeAtom(file); + + bool ret = false; + try { + test.open_file(); + test.read_atom(); + + // Look for the header. + if ("ftyp" == test.get_current_atom_name()) { + ret = true; + } else { + // Some versions of QuickTime don't have + // an ftyp section, so we'll just look + // for the mandatory moov section. + while(true) { + if ("moov" == test.get_current_atom_name()) { + ret = true; + break; + } + test.next_atom(); + test.read_atom(); + if (test.is_last_atom()) { + break; + } + } + } + } catch (GLib.Error e) { + debug("Error while testing for QuickTime file for %s: %s", file.get_path(), e.message); + } + + try { + test.close_file(); + } catch (GLib.Error e) { + debug("Error while closing Quicktime file: %s", e.message); + } + return ret; + } + + private DateTime? get_creation_date_time_for_quicktime() { + QuickTimeAtom test = new QuickTimeAtom(file); + DateTime? timestamp = null; + + try { + test.open_file(); + bool done = false; + while(!done) { + // Look for "moov" section. + test.read_atom(); + if (test.is_last_atom()) break; + if ("moov" == test.get_current_atom_name()) { + QuickTimeAtom child = test.get_first_child_atom(); + while (!done) { + // Look for "mvhd" section, or break if none is found. + child.read_atom(); + if (child.is_last_atom() || 0 == child.section_size_remaining()) { + done = true; + break; + } + + if ("mvhd" == child.get_current_atom_name()) { + // Skip 4 bytes (version + flags) + child.read_uint32(); + // Grab the timestamp. + + // Some Android phones package videos recorded with their internal cameras in a 3GP + // container that looks suspiciously like a QuickTime container but really isn't -- for + // the timestamps of these Android 3GP videos are relative to the UNIX epoch + // (January 1, 1970) instead of the QuickTime epoch (January 1, 1904). So, if we detect a + // QuickTime movie with a negative timestamp, we can be pretty sure it isn't a valid + // QuickTime movie that was shot before 1904 but is instead a non-compliant 3GP video + // file. If we detect such a video, we correct its time. See this Redmine ticket + // (https://bugzilla.gnome.org/show_bug.cgi?id=717384) for more information. + + if ((child.read_uint32() - QUICKTIME_EPOCH_ADJUSTMENT) < 0) { + timestamp = new DateTime.from_unix_utc(child.read_uint32()); + } else { + timestamp = new DateTime.from_unix_utc(child.read_uint32() - QUICKTIME_EPOCH_ADJUSTMENT); + } + done = true; + break; + } + child.next_atom(); + } + } + test.next_atom(); + } + } catch (GLib.Error e) { + debug("Error while testing for QuickTime file: %s", e.message); + } + + try { + test.close_file(); + } catch (GLib.Error e) { + debug("Error while closing Quicktime file: %s", e.message); + } + + return timestamp; + } +} |