blob: 724ff3066ff2cacf89347b96a986cf2b34643216 [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
wimba.comc23aeb02004-09-16 00:00:00 +000024package com.HorizonLive.RfbPlayer;
25
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 Kaplinsky903009e2002-05-20 10:55:47 +000046 //
47 // Constructors.
48 //
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000049 FbsInputStream() throws IOException {
50 throw new IOException("FbsInputStream: no such constructor");
51 }
52
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000053 //
54 // Construct FbsInputStream object, begin playback.
55 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000056 FbsInputStream(InputStream in) throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000057 this.in = in;
58 startTime = System.currentTimeMillis();
59 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000060 seekOffset = -1;
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +000061 seekBackwards = false;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000062 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +000063 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000064
65 byte[] b = new byte[12];
66 readFully(b);
67
68 if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' ||
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000069 b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' ||
70 b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' ||
71 b[10] < '0' || b[10] > '9' || b[11] != '\n') {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000072 throw new IOException("Incorrect protocol version");
73 }
74
75 buffer = null;
76 bufferSize = 0;
77 bufferPos = 0;
78 }
79
wimba.comd1f56df2004-11-01 16:18:54 +000080 // Force stream to finish any wait.
81 public void quit() {
82 isQuitting = true;
83 synchronized(this) {
84 notify();
85 }
86 }
87
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000088 //
89 // Basic methods overriding InputStream's methods.
90 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000091 public int read() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000092 while (bufferSize == 0) {
93 if (!fillBuffer())
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000094 return -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000095 }
96 bufferSize--;
97 return buffer[bufferPos++] & 0xFF;
98 }
99
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000100 public int available() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000101 // FIXME: This will work incorrectly if our caller will wait until
102 // some amount of data is available when the buffer contains less
103 // data than then that. Current implementation never reads more
104 // data until the buffer is fully exhausted.
105 return bufferSize;
106 }
107
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000108 public synchronized void close() throws IOException {
wimba.com275f6072005-01-11 19:02:12 +0000109 if (in != null)
110 in.close();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000111 in = null;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000112 startTime = -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000113 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000114 seekOffset = -1;
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000115 seekBackwards = false;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000116 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000117 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000118
119 buffer = null;
120 bufferSize = 0;
121 bufferPos = 0;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000122
123 obs = null;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000124 }
125
126 //
127 // Methods providing additional functionality.
128 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000129 public synchronized long getTimeOffset() {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000130 long off = Math.max(seekOffset, timeOffset);
131 return (long)(off * playbackSpeed);
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000132 }
133
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000134 public synchronized void setTimeOffset(long pos) {
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000135 seekOffset = (long)(pos / playbackSpeed);
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000136 if (seekOffset < timeOffset) {
137 seekBackwards = true;
138 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000139 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000140 }
141
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000142 public synchronized void setSpeed(double newSpeed) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000143 long newOffset = (long)(timeOffset * playbackSpeed / newSpeed);
144 startTime += timeOffset - newOffset;
145 timeOffset = newOffset;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000146 if (isSeeking()) {
147 seekOffset = (long)(seekOffset * playbackSpeed / newSpeed);
148 }
149 playbackSpeed = newSpeed;
150 }
151
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000152 public boolean isSeeking() {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000153 return (seekOffset >= 0);
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000154 }
155
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000156 public long getSeekOffset() {
Constantin Kaplinsky7cc77622002-09-22 12:36:20 +0000157 return (long)(seekOffset * playbackSpeed);
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000158 }
159
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000160 public boolean isPaused() {
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000161 return paused;
162 }
163
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000164 public synchronized void pausePlayback() {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000165 paused = true;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000166 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000167 }
168
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000169 public synchronized void resumePlayback() {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000170 paused = false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000171 startTime = System.currentTimeMillis() - timeOffset;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000172 notify();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000173 }
174
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000175 public void addObserver(Observer target) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000176 obs = target;
177 }
178
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000179 //
180 // Methods for internal use.
181 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000182 private synchronized boolean fillBuffer() throws IOException {
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000183 // The reading thread should be interrupted on backward seeking.
184 if (seekBackwards)
185 throw new EOFException("[REWIND]");
186
187 // Just wait unless we are performing playback OR seeking.
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000188 waitWhilePaused();
189
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000190 bufferSize = (int)readUnsigned32();
191 if (bufferSize >= 0) {
192 int realSize = (bufferSize + 3) & 0xFFFFFFFC;
193 buffer = new byte[realSize];
194 readFully(buffer);
195 bufferPos = 0;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000196 timeOffset = (long)(readUnsigned32() / playbackSpeed);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000197 }
198
199 if (bufferSize < 0 || timeOffset < 0) {
200 buffer = null;
201 bufferSize = 0;
202 bufferPos = 0;
203 return false;
204 }
205
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000206 if (seekOffset >= 0) {
207 if (timeOffset >= seekOffset) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000208 startTime = System.currentTimeMillis() - seekOffset;
209 seekOffset = -1;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000210 } else {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000211 return true;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000212 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000213 }
214
wimba.comd1f56df2004-11-01 16:18:54 +0000215 while (!isQuitting) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000216 long timeDiff = startTime + timeOffset - System.currentTimeMillis();
217 if (timeDiff <= 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000218 break;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000219 }
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000220 try {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000221 wait(timeDiff);
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000222 } catch (InterruptedException e) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000223 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000224 waitWhilePaused();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000225 }
226
227 return true;
228 }
229
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000230 //
231 // In paused mode, wait for external notification on this object.
232 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000233 private void waitWhilePaused() {
wimba.comd1f56df2004-11-01 16:18:54 +0000234 while (paused && !isSeeking() && !isQuitting) {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000235 synchronized(this) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000236 try {
237 // Note: we call Observer.update(Observable,Object) method
238 // directly instead of maintaining an Observable object.
239 obs.update(null, null);
240 wait();
241 } catch (InterruptedException e) {
242 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000243 }
244 }
245 }
246
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000247 private long readUnsigned32() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000248 byte[] buf = new byte[4];
249 if (!readFully(buf))
250 return -1;
251
252 return ((long)(buf[0] & 0xFF) << 24 |
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000253 (buf[1] & 0xFF) << 16 |
254 (buf[2] & 0xFF) << 8 |
255 (buf[3] & 0xFF));
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000256 }
257
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000258 private boolean readFully(byte[] b) throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000259 int off = 0;
260 int len = b.length;
261
262 while (off != len) {
263 int count = in.read(b, off, len - off);
264 if (count < 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000265 return false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000266 }
267 off += count;
268 }
269
270 return true;
271 }
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000272
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000273}
274