blob: 4057aca9a7a05523addd2a3e1dc3c75f3252c993 [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
24import java.io.*;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +000025import java.util.*;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000026
27class FbsInputStream extends InputStream {
28
29 protected InputStream in;
30 protected long startTime;
31 protected long timeOffset;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000032 protected long seekOffset;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000033 protected boolean paused;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +000034 protected double playbackSpeed;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000035
36 protected byte[] buffer;
37 protected int bufferSize;
38 protected int bufferPos;
39
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +000040 protected Observer obs;
41
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000042 //
43 // Constructors.
44 //
45
46 FbsInputStream() throws IOException {
47 throw new IOException("FbsInputStream: no such constructor");
48 }
49
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000050 //
51 // Construct FbsInputStream object, begin playback.
52 //
53
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000054 FbsInputStream(InputStream in) throws IOException
55 {
56 this.in = in;
57 startTime = System.currentTimeMillis();
58 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000059 seekOffset = -1;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000060 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +000061 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000062
63 byte[] b = new byte[12];
64 readFully(b);
65
66 if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' ||
67 b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' ||
68 b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' ||
69 b[10] < '0' || b[10] > '9' || b[11] != '\n') {
70 throw new IOException("Incorrect protocol version");
71 }
72
73 buffer = null;
74 bufferSize = 0;
75 bufferPos = 0;
76 }
77
78 //
79 // Basic methods overriding InputStream's methods.
80 //
81
82 public int read() throws IOException
83 {
84 while (bufferSize == 0) {
85 if (!fillBuffer())
86 return -1;
87 }
88 bufferSize--;
89 return buffer[bufferPos++] & 0xFF;
90 }
91
92 public int available() throws IOException
93 {
94 // FIXME: This will work incorrectly if our caller will wait until
95 // some amount of data is available when the buffer contains less
96 // data than then that. Current implementation never reads more
97 // data until the buffer is fully exhausted.
98 return bufferSize;
99 }
100
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000101 public synchronized void close() throws IOException
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000102 {
103 in.close();
104 in = null;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000105 startTime = -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000106 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000107 seekOffset = -1;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000108 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000109 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000110
111 buffer = null;
112 bufferSize = 0;
113 bufferPos = 0;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000114
115 obs = null;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000116 }
117
118 //
119 // Methods providing additional functionality.
120 //
121
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000122 public synchronized long getTimeOffset()
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000123 {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000124 long off = Math.max(seekOffset, timeOffset);
125 return (long)(off * playbackSpeed);
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000126 }
127
Constantin Kaplinsky972c2572002-05-30 14:19:02 +0000128 public synchronized void setTimeOffset(long pos)
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000129 {
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000130 seekOffset = (long)(pos / playbackSpeed);
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000131 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000132 }
133
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000134 public synchronized void setSpeed(double newSpeed)
135 {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000136 long newOffset = (long)(timeOffset * playbackSpeed / newSpeed);
137 startTime += timeOffset - newOffset;
138 timeOffset = newOffset;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000139 if (isSeeking()) {
140 seekOffset = (long)(seekOffset * playbackSpeed / newSpeed);
141 }
142 playbackSpeed = newSpeed;
143 }
144
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000145 public boolean isSeeking()
146 {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000147 return (seekOffset >= 0);
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000148 }
149
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000150 public synchronized void pausePlayback()
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000151 {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000152 paused = true;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000153 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000154 }
155
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000156 public synchronized void resumePlayback()
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000157 {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000158 paused = false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000159 startTime = System.currentTimeMillis() - timeOffset;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000160 notify();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000161 }
162
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000163 public void addObserver(Observer target)
164 {
165 obs = target;
166 }
167
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000168 //
169 // Methods for internal use.
170 //
171
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000172 private synchronized boolean fillBuffer() throws IOException
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000173 {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000174 waitWhilePaused();
175
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000176 bufferSize = (int)readUnsigned32();
177 if (bufferSize >= 0) {
178 int realSize = (bufferSize + 3) & 0xFFFFFFFC;
179 buffer = new byte[realSize];
180 readFully(buffer);
181 bufferPos = 0;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000182 timeOffset = (long)(readUnsigned32() / playbackSpeed);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000183 }
184
185 if (bufferSize < 0 || timeOffset < 0) {
186 buffer = null;
187 bufferSize = 0;
188 bufferPos = 0;
189 return false;
190 }
191
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000192 if (seekOffset >= 0) {
193 if (timeOffset >= seekOffset) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000194 startTime = System.currentTimeMillis() - seekOffset;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000195 seekOffset = -1;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000196 } else {
197 return true;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000198 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000199 }
200
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000201 while (true) {
202 long timeDiff = startTime + timeOffset - System.currentTimeMillis();
203 if (timeDiff <= 0) {
204 break;
205 }
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000206 try {
207 wait(timeDiff);
208 } catch (InterruptedException e) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000209 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000210 waitWhilePaused();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000211 }
212
213 return true;
214 }
215
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000216 //
217 // In paused mode, wait for external notification on this object.
218 //
219
220 private void waitWhilePaused()
221 {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000222 while (paused && !isSeeking()) {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000223 synchronized(this) {
224 try {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000225 // Note: we call Observer.update(Observable,Object) method
226 // directly instead of maintaining an Observable object.
227 obs.update(null, null);
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000228 wait();
229 } catch (InterruptedException e) {
230 }
231 }
232 }
233 }
234
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000235 private long readUnsigned32() throws IOException
236 {
237 byte[] buf = new byte[4];
238 if (!readFully(buf))
239 return -1;
240
241 return ((long)(buf[0] & 0xFF) << 24 |
242 (buf[1] & 0xFF) << 16 |
243 (buf[2] & 0xFF) << 8 |
244 (buf[3] & 0xFF));
245 }
246
247 private boolean readFully(byte[] b) throws IOException
248 {
249 int off = 0;
250 int len = b.length;
251
252 while (off != len) {
253 int count = in.read(b, off, len - off);
254 if (count < 0) {
255 return false;
256 }
257 off += count;
258 }
259
260 return true;
261 }
262}
263