blob: 48c5b410c62acc9c412bbea9a9df47c33c3f4138 [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;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000030 protected long pausedTime;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000031 protected long timeOffset;
32
33 protected byte[] buffer;
34 protected int bufferSize;
35 protected int bufferPos;
36
37 //
38 // Constructors.
39 //
40
41 FbsInputStream() throws IOException {
42 throw new IOException("FbsInputStream: no such constructor");
43 }
44
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000045 //
46 // Construct FbsInputStream object, begin playback.
47 //
48
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000049 FbsInputStream(InputStream in) throws IOException
50 {
51 this.in = in;
52 startTime = System.currentTimeMillis();
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000053 pausedTime = -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000054 timeOffset = 0;
55
56 byte[] b = new byte[12];
57 readFully(b);
58
59 if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' ||
60 b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' ||
61 b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' ||
62 b[10] < '0' || b[10] > '9' || b[11] != '\n') {
63 throw new IOException("Incorrect protocol version");
64 }
65
66 buffer = null;
67 bufferSize = 0;
68 bufferPos = 0;
69 }
70
71 //
72 // Basic methods overriding InputStream's methods.
73 //
74
75 public int read() throws IOException
76 {
77 while (bufferSize == 0) {
78 if (!fillBuffer())
79 return -1;
80 }
81 bufferSize--;
82 return buffer[bufferPos++] & 0xFF;
83 }
84
85 public int available() throws IOException
86 {
87 // FIXME: This will work incorrectly if our caller will wait until
88 // some amount of data is available when the buffer contains less
89 // data than then that. Current implementation never reads more
90 // data until the buffer is fully exhausted.
91 return bufferSize;
92 }
93
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000094 public synchronized void close() throws IOException
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000095 {
96 in.close();
97 in = null;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000098 startTime = -1;
99 pausedTime = -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000100 timeOffset = 0;
101
102 buffer = null;
103 bufferSize = 0;
104 bufferPos = 0;
105 }
106
107 //
108 // Methods providing additional functionality.
109 //
110
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000111 public long getTimeOffset()
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000112 {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000113 return timeOffset;
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000114 }
115
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000116 public void setTimeOffset(int pos)
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000117 {
118 }
119
120 public boolean isSeeking()
121 {
122 return false;
123 }
124
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000125 public synchronized void pausePlayback()
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000126 {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000127 // FIXME: There is no need to remember the time?
128 pausedTime = System.currentTimeMillis();
129 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000130 }
131
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000132 public synchronized void resumePlayback()
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000133 {
134 startTime = System.currentTimeMillis() - timeOffset;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000135 pausedTime = -1;
136 notify();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000137 }
138
139 //
140 // Methods for internal use.
141 //
142
143 private boolean fillBuffer() throws IOException
144 {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000145 waitWhilePaused();
146
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000147 bufferSize = (int)readUnsigned32();
148 if (bufferSize >= 0) {
149 int realSize = (bufferSize + 3) & 0xFFFFFFFC;
150 buffer = new byte[realSize];
151 readFully(buffer);
152 bufferPos = 0;
153
154 timeOffset = readUnsigned32();
155 }
156
157 if (bufferSize < 0 || timeOffset < 0) {
158 buffer = null;
159 bufferSize = 0;
160 bufferPos = 0;
161 return false;
162 }
163
164 while (true) {
165 long timeDiff = startTime + timeOffset - System.currentTimeMillis();
166 if (timeDiff <= 0) {
167 break;
168 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000169 synchronized(this) {
170 try {
171 wait(timeDiff);
172 } catch (InterruptedException e) {
173 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000174 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000175 waitWhilePaused();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000176 }
177
178 return true;
179 }
180
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000181 //
182 // In paused mode, wait for external notification on this object.
183 //
184
185 private void waitWhilePaused()
186 {
187 while (pausedTime >= 0) {
188 synchronized(this) {
189 try {
190 wait();
191 } catch (InterruptedException e) {
192 }
193 }
194 }
195 }
196
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000197 private long readUnsigned32() throws IOException
198 {
199 byte[] buf = new byte[4];
200 if (!readFully(buf))
201 return -1;
202
203 return ((long)(buf[0] & 0xFF) << 24 |
204 (buf[1] & 0xFF) << 16 |
205 (buf[2] & 0xFF) << 8 |
206 (buf[3] & 0xFF));
207 }
208
209 private boolean readFully(byte[] b) throws IOException
210 {
211 int off = 0;
212 int len = b.length;
213
214 while (off != len) {
215 int count = in.read(b, off, len - off);
216 if (count < 0) {
217 return false;
218 }
219 off += count;
220 }
221
222 return true;
223 }
224}
225