diff --git a/src/main/java/org/forkalsrud/album/exif/Dimension.java b/src/main/java/org/forkalsrud/album/exif/Dimension.java index 3a0d205..5d14fc2 100644 --- a/src/main/java/org/forkalsrud/album/exif/Dimension.java +++ b/src/main/java/org/forkalsrud/album/exif/Dimension.java @@ -99,4 +99,11 @@ public class Dimension { return outd; } + + + public Dimension even() { + return new Dimension(this.w % 2 > 0 ? this.w + 1 : this.w, + this.h % 2 > 0 ? this.h + 1 : this.h); + } + } diff --git a/src/main/java/org/forkalsrud/album/video/MovieCoder.java b/src/main/java/org/forkalsrud/album/video/MovieCoder.java index 6e66aa5..eb91507 100644 --- a/src/main/java/org/forkalsrud/album/video/MovieCoder.java +++ b/src/main/java/org/forkalsrud/album/video/MovieCoder.java @@ -283,18 +283,18 @@ public class MovieCoder { private Chunk currentChunk = null; private int chunkPos; private int remainingCapacity; - private int chunkNo = 0; + private volatile int chunkInProgress = 0; + private volatile int chunkAvailable = 0; private FlvFilter filter; private String dbKey; private long fileTimestamp; private int orientation; + private volatile boolean done = false; public EncodingProcess(File file, Thumbnail thumbnail, Dimension size) { this.file = file; this.fileTimestamp = file.lastModified(); - this.targetSize = size; - if (targetSize.getWidth() % 2 > 0) targetSize = new Dimension(targetSize.getWidth() + 1, targetSize.getHeight()); - if (targetSize.getHeight() % 2 > 0) targetSize = new Dimension(targetSize.getWidth(), targetSize.getHeight() + 1); + this.targetSize = size.even(); this.dbKey = key(file, targetSize); FlvMetadata extraMeta = new FlvMetadata(); extraMeta.setDuration(thumbnail.getDuration()); @@ -381,36 +381,54 @@ public class MovieCoder { // Generate header again to get updated metadata // TODO (knut 30 JUL 2011) Add some metadata about the file in the first chunk of the file // size of first chunk, size of last chunk, number number of chunks between first-last, size of chunks between first-last - // This will be used to determine the right chunk number from a given byte offset in a range oritented HTTP request + // This will be used to determine the right chunk number from a given byte offset in a range oriented HTTP request filter.generateHeaderChunk(); } catch (Exception e) { log.error("uh?", e); movieDb.delete(dbKey); + synchronized (this) { + this.chunkAvailable = Integer.MAX_VALUE; + } } finally { synchronized (MovieCoder.this) { currentEncodings.remove(key(file, targetSize)); } - notifyListeners(chunkNo, true); + done = true; + notifyListeners(chunkAvailable); } } + + /** + * @return number of chunks available. + * -Integer.MAX_VALUE if something went very wrong + * -1 * number of chunks available when we're done. + */ + public synchronized int waitForData() { + if (!this.done) { + try { + this.wait(2000L); + } catch (InterruptedException e) { + // ignore + } + } + int numChunks = this.chunkAvailable + 1; + return this.done ? -numChunks : numChunks; + } + + private synchronized void notifyListeners(int chunkNo) { + + this.chunkAvailable = chunkNo; + this.notifyAll(); + } @Override public void writeHeader(byte[] data) { int len = data.length; Chunk chunk0 = new Chunk(fileTimestamp, len, 0); chunk0.bits = data; + log.info("Writing " + dbKey + " seq 0 (" + chunkInProgress + ") " + data.length); movieDb.store(dbKey, 0, chunk0); - notifyListeners(0, false); - } - - private void notifyListeners(int chunkNo, boolean end) { - for (EncodingProcessListener listener : listeners) { - if (end) { - listener.codingFinished(chunkNo); - } else { - listener.chunkAvailable(chunkNo); - } - } + notifyListeners(chunkInProgress); } @Override @@ -440,7 +458,7 @@ public class MovieCoder { private void startNewChunk() { - this.chunkNo++; + this.chunkInProgress++; this.currentChunk = new Chunk(fileTimestamp, chunkSize, 0); this.chunkPos = 0; this.remainingCapacity = chunkSize; @@ -448,10 +466,11 @@ public class MovieCoder { private void endChunk() { - log.info("Receiving chunk " + chunkNo); - movieDb.store(dbKey, chunkNo, currentChunk); + log.info("store chunk " + chunkInProgress); + movieDb.store(dbKey, chunkInProgress, currentChunk); + log.info("Writing " + dbKey + " seq " + chunkInProgress + " (" + chunkInProgress + ") " + currentChunk.bits.length); currentChunk = null; - notifyListeners(chunkNo, false); + notifyListeners(chunkInProgress); } private void endLastChunk() { @@ -462,16 +481,16 @@ public class MovieCoder { // reallocate Chunk last = new Chunk(fileTimestamp, chunkPos, 0); System.arraycopy(currentChunk.bits, 0, last.bits, 0, chunkPos); - movieDb.store(key(file, targetSize), chunkNo, last); + movieDb.store(dbKey, chunkInProgress, last); + log.info("Writing " + dbKey + " seq " + chunkInProgress + " (" + chunkInProgress + ") " + last.bits.length); currentChunk = null; - notifyListeners(chunkNo, true); } public synchronized void addListener(EncodingProcessListener videoStreamer) { listeners.add(videoStreamer); } - public void removeListener(VideoStreamer videoStreamer) { + public synchronized void removeListener(VideoStreamer videoStreamer) { listeners.remove(videoStreamer); } } @@ -522,7 +541,7 @@ public class MovieCoder { synchronized VideoStreamer grabStream(File file, Thumbnail thumbnail, String size) { - Dimension targetSize = thumbnail.getSize().scale(size); + Dimension targetSize = thumbnail.getSize().scale(size).even(); String key = key(file, targetSize); // See if we have (at least the beginning of) the file in the DB @@ -547,21 +566,18 @@ public class MovieCoder { } - class VideoStreamer implements EncodingProcessListener { + class VideoStreamer { private int chunkNo = 0; - private int lastChunkNo = -1; private EncodingProcess ep; private Chunk chunk; private String key; + private boolean done = false; private VideoStreamer(String key, EncodingProcess ep, Chunk chunk0) { this.key = key; this.ep = ep; this.chunk = chunk0; - if (ep != null) { - ep.addListener(this); - } // Range requests can hook in here // if we have chunk metadata in chunk0 we can use that to compute the first // chunk we want and set this.chunkNo accordingly. Otherwise (not likely @@ -571,16 +587,12 @@ public class MovieCoder { // The end of the range is also a matter of counting the bytes. } - private boolean done() { - return lastChunkNo >= 0 && chunkNo > lastChunkNo; - } - public void stream(OutputStream out) throws IOException, InterruptedException { - while (!done()) { + while (!done) { if (chunk == null) { - log.info("Looking for " + chunkNo); + log.info("Looking for " + key + " - " + chunkNo); chunk = movieDb.load(key, chunkNo); } if (chunk != null) { @@ -590,35 +602,23 @@ public class MovieCoder { chunkNo++; continue; } - if (!done()) { - if (ep != null) { - synchronized (this) { - wait(2000L); - } - } else { - // We ran out of chunks, so we must be done. - break; - } + if (!done) { + if (ep != null) { + int numChunks = ep.waitForData(); + System.out.println("Got " + numChunks); + if (numChunks < 0) { + this.done = true; + } + } else { + // We ran out of chunks, so we must be done. + this.done = true; + break; + } } } - if (ep != null) { - ep.removeListener(this); - } log.info("done sending"); } - - @Override - public synchronized void chunkAvailable(int chunkNo) { - notify(); - } - - @Override - public synchronized void codingFinished(int lastCunkNo) { - this.lastChunkNo = lastCunkNo; - ep = null; - notify(); - } } diff --git a/src/main/java/org/forkalsrud/album/web/AlbumServlet.java b/src/main/java/org/forkalsrud/album/web/AlbumServlet.java index 635b66b..3e96134 100644 --- a/src/main/java/org/forkalsrud/album/web/AlbumServlet.java +++ b/src/main/java/org/forkalsrud/album/web/AlbumServlet.java @@ -466,6 +466,7 @@ public class AlbumServlet boolean notModified(HttpServletRequest req, File f) { long reqDate = req.getDateHeader("If-Modified-Since"); long fDate = f.lastModified(); + System.out.println("reqDate is " + new Date(reqDate) + " fDate is " + new Date(fDate) + " " + req.getHeader("If-Modified-Since")); return reqDate > 0 && fDate > 0 && fDate <= reqDate; }