blob: 6df38ddfc56392f8c82f62b5a338dd0ea53998cf [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 {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000109 in.close();
110 in = null;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000111 startTime = -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000112 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000113 seekOffset = -1;
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000114 seekBackwards = false;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000115 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000116 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000117
118 buffer = null;
119 bufferSize = 0;
120 bufferPos = 0;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000121
122 obs = null;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000123 }
124
125 //
126 // Methods providing additional functionality.
127 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000128 public synchronized long getTimeOffset() {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000129 long off = Math.max(seekOffset, timeOffset);
130 return (long)(off * playbackSpeed);
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000131 }
132
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000133 public synchronized void setTimeOffset(long pos) {
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000134 seekOffset = (long)(pos / playbackSpeed);
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000135 if (seekOffset < timeOffset) {
136 seekBackwards = true;
137 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000138 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000139 }
140
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000141 public synchronized void setSpeed(double newSpeed) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000142 long newOffset = (long)(timeOffset * playbackSpeed / newSpeed);
143 startTime += timeOffset - newOffset;
144 timeOffset = newOffset;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000145 if (isSeeking()) {
146 seekOffset = (long)(seekOffset * playbackSpeed / newSpeed);
147 }
148 playbackSpeed = newSpeed;
149 }
150
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000151 public boolean isSeeking() {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000152 return (seekOffset >= 0);
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000153 }
154
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000155 public long getSeekOffset() {
Constantin Kaplinsky7cc77622002-09-22 12:36:20 +0000156 return (long)(seekOffset * playbackSpeed);
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000157 }
158
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000159 public boolean isPaused() {
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000160 return paused;
161 }
162
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000163 public synchronized void pausePlayback() {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000164 paused = true;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000165 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000166 }
167
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000168 public synchronized void resumePlayback() {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000169 paused = false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000170 startTime = System.currentTimeMillis() - timeOffset;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000171 notify();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000172 }
173
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000174 public void addObserver(Observer target) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000175 obs = target;
176 }
177
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000178 //
179 // Methods for internal use.
180 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000181 private synchronized boolean fillBuffer() throws IOException {
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000182 // The reading thread should be interrupted on backward seeking.
183 if (seekBackwards)
184 throw new EOFException("[REWIND]");
185
186 // Just wait unless we are performing playback OR seeking.
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000187 waitWhilePaused();
188
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000189 bufferSize = (int)readUnsigned32();
190 if (bufferSize >= 0) {
191 int realSize = (bufferSize + 3) & 0xFFFFFFFC;
192 buffer = new byte[realSize];
193 readFully(buffer);
194 bufferPos = 0;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000195 timeOffset = (long)(readUnsigned32() / playbackSpeed);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000196 }
197
198 if (bufferSize < 0 || timeOffset < 0) {
199 buffer = null;
200 bufferSize = 0;
201 bufferPos = 0;
202 return false;
203 }
204
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000205 if (seekOffset >= 0) {
206 if (timeOffset >= seekOffset) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000207 startTime = System.currentTimeMillis() - seekOffset;
208 seekOffset = -1;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000209 } else {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000210 return true;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000211 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000212 }
213
wimba.comd1f56df2004-11-01 16:18:54 +0000214 while (!isQuitting) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000215 long timeDiff = startTime + timeOffset - System.currentTimeMillis();
216 if (timeDiff <= 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000217 break;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000218 }
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000219 try {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000220 wait(timeDiff);
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000221 } catch (InterruptedException e) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000222 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000223 waitWhilePaused();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000224 }
225
226 return true;
227 }
228
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000229 //
230 // In paused mode, wait for external notification on this object.
231 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000232 private void waitWhilePaused() {
wimba.comd1f56df2004-11-01 16:18:54 +0000233 while (paused && !isSeeking() && !isQuitting) {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000234 synchronized(this) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000235 try {
236 // Note: we call Observer.update(Observable,Object) method
237 // directly instead of maintaining an Observable object.
238 obs.update(null, null);
239 wait();
240 } catch (InterruptedException e) {
241 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000242 }
243 }
244 }
245
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000246 private long readUnsigned32() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000247 byte[] buf = new byte[4];
248 if (!readFully(buf))
249 return -1;
250
251 return ((long)(buf[0] & 0xFF) << 24 |
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000252 (buf[1] & 0xFF) << 16 |
253 (buf[2] & 0xFF) << 8 |
254 (buf[3] & 0xFF));
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000255 }
256
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000257 private boolean readFully(byte[] b) throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000258 int off = 0;
259 int len = b.length;
260
261 while (off != len) {
262 int count = in.read(b, off, len - off);
263 if (count < 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000264 return false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000265 }
266 off += count;
267 }
268
269 return true;
270 }
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000271
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000272}
273