blob: f5aeb2ded9dc47673936f3915e067f0b2ec56ddf [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.*;
25
26class FbsInputStream extends InputStream {
27
28 protected InputStream in;
29 protected long startTime;
30 protected long timeOffset;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000031 protected long seekOffset;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000032 protected boolean paused;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000033
34 protected byte[] buffer;
35 protected int bufferSize;
36 protected int bufferPos;
37
38 //
39 // Constructors.
40 //
41
42 FbsInputStream() throws IOException {
43 throw new IOException("FbsInputStream: no such constructor");
44 }
45
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000046 //
47 // Construct FbsInputStream object, begin playback.
48 //
49
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000050 FbsInputStream(InputStream in) throws IOException
51 {
52 this.in = in;
53 startTime = System.currentTimeMillis();
54 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000055 seekOffset = -1;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000056 paused = false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000057
58 byte[] b = new byte[12];
59 readFully(b);
60
61 if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' ||
62 b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' ||
63 b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' ||
64 b[10] < '0' || b[10] > '9' || b[11] != '\n') {
65 throw new IOException("Incorrect protocol version");
66 }
67
68 buffer = null;
69 bufferSize = 0;
70 bufferPos = 0;
71 }
72
73 //
74 // Basic methods overriding InputStream's methods.
75 //
76
77 public int read() throws IOException
78 {
79 while (bufferSize == 0) {
80 if (!fillBuffer())
81 return -1;
82 }
83 bufferSize--;
84 return buffer[bufferPos++] & 0xFF;
85 }
86
87 public int available() throws IOException
88 {
89 // FIXME: This will work incorrectly if our caller will wait until
90 // some amount of data is available when the buffer contains less
91 // data than then that. Current implementation never reads more
92 // data until the buffer is fully exhausted.
93 return bufferSize;
94 }
95
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000096 public synchronized void close() throws IOException
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000097 {
98 in.close();
99 in = null;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000100 startTime = -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000101 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000102 seekOffset = -1;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000103 paused = false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000104
105 buffer = null;
106 bufferSize = 0;
107 bufferPos = 0;
108 }
109
110 //
111 // Methods providing additional functionality.
112 //
113
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000114 public long getTimeOffset()
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000115 {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000116 return timeOffset;
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000117 }
118
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000119 public synchronized void setTimeOffset(int pos)
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000120 {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000121 // FIXME: Seeking works only in paused mode.
122 paused = true;
123 seekOffset = pos;
124 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000125 }
126
127 public boolean isSeeking()
128 {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000129 return (seekOffset >= 0);
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000130 }
131
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000132 public synchronized void pausePlayback()
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000133 {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000134 paused = true;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000135 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000136 }
137
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000138 public synchronized void resumePlayback()
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000139 {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000140 paused = false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000141 startTime = System.currentTimeMillis() - timeOffset;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000142 notify();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000143 }
144
145 //
146 // Methods for internal use.
147 //
148
149 private boolean fillBuffer() throws IOException
150 {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000151 waitWhilePaused();
152
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000153 bufferSize = (int)readUnsigned32();
154 if (bufferSize >= 0) {
155 int realSize = (bufferSize + 3) & 0xFFFFFFFC;
156 buffer = new byte[realSize];
157 readFully(buffer);
158 bufferPos = 0;
159
160 timeOffset = readUnsigned32();
161 }
162
163 if (bufferSize < 0 || timeOffset < 0) {
164 buffer = null;
165 bufferSize = 0;
166 bufferPos = 0;
167 return false;
168 }
169
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000170 if (seekOffset >= 0) {
171 if (timeOffset >= seekOffset) {
172 seekOffset = -1;
173 }
174 return true;
175 }
176
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000177 while (true) {
178 long timeDiff = startTime + timeOffset - System.currentTimeMillis();
179 if (timeDiff <= 0) {
180 break;
181 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000182 synchronized(this) {
183 try {
184 wait(timeDiff);
185 } catch (InterruptedException e) {
186 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000187 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000188 waitWhilePaused();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000189 }
190
191 return true;
192 }
193
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000194 //
195 // In paused mode, wait for external notification on this object.
196 //
197
198 private void waitWhilePaused()
199 {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000200 while (paused && !isSeeking()) {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000201 synchronized(this) {
202 try {
203 wait();
204 } catch (InterruptedException e) {
205 }
206 }
207 }
208 }
209
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000210 private long readUnsigned32() throws IOException
211 {
212 byte[] buf = new byte[4];
213 if (!readFully(buf))
214 return -1;
215
216 return ((long)(buf[0] & 0xFF) << 24 |
217 (buf[1] & 0xFF) << 16 |
218 (buf[2] & 0xFF) << 8 |
219 (buf[3] & 0xFF));
220 }
221
222 private boolean readFully(byte[] b) throws IOException
223 {
224 int off = 0;
225 int len = b.length;
226
227 while (off != len) {
228 int count = in.read(b, off, len - off);
229 if (count < 0) {
230 return false;
231 }
232 off += count;
233 }
234
235 return true;
236 }
237}
238