blob: 431ee21e696f805c55c56d8a34aa5b97e9d7aad2 [file] [log] [blame]
Constantin Kaplinsky903009e2002-05-20 10:55:47 +00001//
2// Copyright (C) 2002 HorizonLive.com, Inc. All Rights Reserved.
3//
4// This is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This software is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this software; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17// USA.
18//
19
20//
21// FbsInputStream.java
22//
23
Constantin Kaplinskycf689b32008-04-30 12:50:34 +000024package com.tightvnc.rfbplayer;
wimba.comc23aeb02004-09-16 00:00:00 +000025
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000026import java.io.*;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +000027import java.util.*;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000028
29class FbsInputStream extends InputStream {
30
31 protected InputStream in;
32 protected long startTime;
33 protected long timeOffset;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000034 protected long seekOffset;
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +000035 protected boolean seekBackwards;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000036 protected boolean paused;
wimba.comd1f56df2004-11-01 16:18:54 +000037 protected boolean isQuitting = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +000038 protected double playbackSpeed;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000039
40 protected byte[] buffer;
41 protected int bufferSize;
42 protected int bufferPos;
43
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +000044 protected Observer obs;
45
Constantin Kaplinskyd9ade182008-06-18 11:27:50 +000046 /**
47 * Construct FbsInputStream object based on the given InputStream.
48 *
49 * @param in InputStream object that will be used as a base
50 * @throws java.io.IOException thrown on read error or on incorrect file
51 * header signature.
52 */
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000053 FbsInputStream(InputStream in) throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000054 this.in = in;
55 startTime = System.currentTimeMillis();
56 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000057 seekOffset = -1;
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +000058 seekBackwards = false;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000059 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +000060 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000061
62 byte[] b = new byte[12];
63 readFully(b);
64
65 if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' ||
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000066 b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' ||
67 b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' ||
68 b[10] < '0' || b[10] > '9' || b[11] != '\n') {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000069 throw new IOException("Incorrect protocol version");
70 }
71
72 buffer = null;
73 bufferSize = 0;
74 bufferPos = 0;
75 }
76
wimba.comd1f56df2004-11-01 16:18:54 +000077 // Force stream to finish any wait.
78 public void quit() {
79 isQuitting = true;
80 synchronized(this) {
81 notify();
82 }
83 }
84
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000085 //
86 // Basic methods overriding InputStream's methods.
87 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000088 public int read() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000089 while (bufferSize == 0) {
90 if (!fillBuffer())
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000091 return -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000092 }
93 bufferSize--;
94 return buffer[bufferPos++] & 0xFF;
95 }
96
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000097 public int available() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000098 // FIXME: This will work incorrectly if our caller will wait until
99 // some amount of data is available when the buffer contains less
100 // data than then that. Current implementation never reads more
101 // data until the buffer is fully exhausted.
102 return bufferSize;
103 }
104
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000105 public synchronized void close() throws IOException {
wimba.com275f6072005-01-11 19:02:12 +0000106 if (in != null)
107 in.close();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000108 in = null;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000109 startTime = -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000110 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000111 seekOffset = -1;
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000112 seekBackwards = false;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000113 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000114 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000115
116 buffer = null;
117 bufferSize = 0;
118 bufferPos = 0;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000119
120 obs = null;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000121 }
122
123 //
124 // Methods providing additional functionality.
125 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000126 public synchronized long getTimeOffset() {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000127 long off = Math.max(seekOffset, timeOffset);
128 return (long)(off * playbackSpeed);
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000129 }
130
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000131 public synchronized void setTimeOffset(long pos) {
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000132 seekOffset = (long)(pos / playbackSpeed);
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000133 if (seekOffset < timeOffset) {
134 seekBackwards = true;
135 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000136 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000137 }
138
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000139 public synchronized void setSpeed(double newSpeed) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000140 long newOffset = (long)(timeOffset * playbackSpeed / newSpeed);
141 startTime += timeOffset - newOffset;
142 timeOffset = newOffset;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000143 if (isSeeking()) {
144 seekOffset = (long)(seekOffset * playbackSpeed / newSpeed);
145 }
146 playbackSpeed = newSpeed;
147 }
148
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000149 public boolean isSeeking() {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000150 return (seekOffset >= 0);
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000151 }
152
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000153 public long getSeekOffset() {
Constantin Kaplinsky7cc77622002-09-22 12:36:20 +0000154 return (long)(seekOffset * playbackSpeed);
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000155 }
156
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000157 public boolean isPaused() {
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000158 return paused;
159 }
160
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000161 public synchronized void pausePlayback() {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000162 paused = true;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000163 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000164 }
165
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000166 public synchronized void resumePlayback() {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000167 paused = false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000168 startTime = System.currentTimeMillis() - timeOffset;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000169 notify();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000170 }
171
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000172 public void addObserver(Observer target) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000173 obs = target;
174 }
175
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000176 //
177 // Methods for internal use.
178 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000179 private synchronized boolean fillBuffer() throws IOException {
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000180 // The reading thread should be interrupted on backward seeking.
181 if (seekBackwards)
182 throw new EOFException("[REWIND]");
183
184 // Just wait unless we are performing playback OR seeking.
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000185 waitWhilePaused();
186
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000187 bufferSize = (int)readUnsigned32();
188 if (bufferSize >= 0) {
189 int realSize = (bufferSize + 3) & 0xFFFFFFFC;
190 buffer = new byte[realSize];
191 readFully(buffer);
192 bufferPos = 0;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000193 timeOffset = (long)(readUnsigned32() / playbackSpeed);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000194 }
195
196 if (bufferSize < 0 || timeOffset < 0) {
197 buffer = null;
198 bufferSize = 0;
199 bufferPos = 0;
200 return false;
201 }
202
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000203 if (seekOffset >= 0) {
204 if (timeOffset >= seekOffset) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000205 startTime = System.currentTimeMillis() - seekOffset;
206 seekOffset = -1;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000207 } else {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000208 return true;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000209 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000210 }
211
wimba.comd1f56df2004-11-01 16:18:54 +0000212 while (!isQuitting) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000213 long timeDiff = startTime + timeOffset - System.currentTimeMillis();
214 if (timeDiff <= 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000215 break;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000216 }
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000217 try {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000218 wait(timeDiff);
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000219 } catch (InterruptedException e) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000220 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000221 waitWhilePaused();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000222 }
223
224 return true;
225 }
226
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000227 //
228 // In paused mode, wait for external notification on this object.
229 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000230 private void waitWhilePaused() {
wimba.comd1f56df2004-11-01 16:18:54 +0000231 while (paused && !isSeeking() && !isQuitting) {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000232 synchronized(this) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000233 try {
234 // Note: we call Observer.update(Observable,Object) method
235 // directly instead of maintaining an Observable object.
236 obs.update(null, null);
237 wait();
238 } catch (InterruptedException e) {
239 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000240 }
241 }
242 }
243
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000244 private long readUnsigned32() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000245 byte[] buf = new byte[4];
246 if (!readFully(buf))
247 return -1;
248
249 return ((long)(buf[0] & 0xFF) << 24 |
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000250 (buf[1] & 0xFF) << 16 |
251 (buf[2] & 0xFF) << 8 |
252 (buf[3] & 0xFF));
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000253 }
254
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000255 private boolean readFully(byte[] b) throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000256 int off = 0;
257 int len = b.length;
258
259 while (off != len) {
260 int count = in.read(b, off, len - off);
261 if (count < 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000262 return false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000263 }
264 off += count;
265 }
266
267 return true;
268 }
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000269
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000270}
271