001
014
015 package com.liferay.portlet.documentlibrary.util;
016
017 import com.liferay.portal.kernel.log.Log;
018 import com.liferay.portal.kernel.log.LogFactoryUtil;
019 import com.liferay.portal.kernel.util.FileUtil;
020
021 import java.io.File;
022 import java.io.IOException;
023 import java.io.RandomAccessFile;
024
025
034 public class JQTFastStart {
035
036 public static void convert(File inputFile, File outputFile)
037 throws IOException {
038
039 _instance.doConvert(inputFile, outputFile);
040 }
041
042 protected void doConvert(File inputFile, File outputFile)
043 throws IOException {
044
045 validate(inputFile, outputFile);
046
047 RandomAccessFile randomAccessInputFile = null;
048 RandomAccessFile randomAccessOutputFile = null;
049
050 try {
051 randomAccessInputFile = new RandomAccessFile(inputFile, "r");
052
053 Atom atom = null;
054 Atom ftypAtom = null;
055
056 boolean ftypFound = false;
057 boolean mdatFound = false;
058 boolean isFastStart = false;
059
060 while (randomAccessInputFile.getFilePointer() <
061 randomAccessInputFile.length()) {
062
063 atom = new Atom(randomAccessInputFile);
064
065 if (!atom.isTopLevelAtom()) {
066 throw new IOException(
067 "Non top level atom was found " + atom.getType());
068 }
069
070 if (ftypFound && !mdatFound && atom.isMOOV()) {
071 isFastStart = true;
072
073 break;
074 }
075
076 if (atom.isFTYP()) {
077 ftypAtom = atom;
078
079 ftypAtom.fillBuffer(randomAccessInputFile);
080
081 ftypFound = true;
082 }
083 else if (atom.isMDAT()) {
084 mdatFound = true;
085
086 randomAccessInputFile.skipBytes((int)atom.getSize());
087 }
088 else {
089 randomAccessInputFile.skipBytes((int)atom.getSize());
090 }
091 }
092
093 if (isFastStart) {
094 if (_log.isInfoEnabled()) {
095 _log.info("The movie is already a fast start MP4");
096 }
097
098 FileUtil.move(inputFile, outputFile);
099
100 return;
101 }
102
103 if (!atom.isMOOV()) {
104 throw new IOException("Last atom was not of type MOOV");
105 }
106
107 randomAccessInputFile.seek(atom.getOffset());
108
109 Atom moovAtom = atom;
110
111 moovAtom.fillBuffer(randomAccessInputFile);
112
113 if (moovAtom.hasCompressedMoovAtom()) {
114 throw new IOException("Compressed MOOV atoms are unsupported");
115 }
116
117 moovAtom.patchAtom();
118
119 randomAccessInputFile.seek(
120 ftypAtom.getOffset() + ftypAtom.getSize());
121
122 randomAccessOutputFile = new RandomAccessFile(outputFile, "rw");
123
124 randomAccessOutputFile.setLength(0);
125
126 randomAccessOutputFile.write(ftypAtom.getBuffer());
127 randomAccessOutputFile.write(moovAtom.getBuffer());
128
129 byte[] buffer = new byte[1024 * 1024];
130
131 while ((randomAccessInputFile.getFilePointer() + buffer.length) <
132 moovAtom.getOffset()) {
133
134 int read = randomAccessInputFile.read(buffer);
135
136 randomAccessOutputFile.write(buffer, 0, read);
137 }
138
139 int bufferSize = (int)
140 (moovAtom.getOffset() - randomAccessInputFile.getFilePointer());
141
142 buffer = new byte[bufferSize];
143
144 randomAccessInputFile.readFully(buffer);
145
146 randomAccessOutputFile.write(buffer);
147 }
148 finally {
149 if (randomAccessInputFile != null) {
150 randomAccessInputFile.close();
151 }
152
153 if (randomAccessOutputFile != null) {
154 randomAccessOutputFile.close();
155 }
156 }
157 }
158
159 protected void validate(File inputFile, File outputFile)
160 throws IOException {
161
162 if (!inputFile.exists() || !inputFile.canRead()) {
163 throw new IOException("Input file cannot be read " + inputFile);
164 }
165
166 if (outputFile.exists()) {
167 throw new IOException("Output file alread exists " + outputFile);
168 }
169
170 if (inputFile.getAbsolutePath().equals(outputFile.getAbsolutePath())) {
171 throw new IOException(
172 "Input file and output file cannot be the same " + inputFile);
173 }
174 }
175
176 private static Log _log = LogFactoryUtil.getLog(JQTFastStart.class);
177
178 private static JQTFastStart _instance = new JQTFastStart();
179
180 }