blob: 7baa145fbca4cd55d6a0921cdf2f9fc9c75cf23e [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 Kaplinskyce39d3b2002-05-30 15:54:56 +000033 protected double playbackSpeed;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000034
35 protected byte[] buffer;
36 protected int bufferSize;
37 protected int bufferPos;
38
39 //
40 // Constructors.
41 //
42
43 FbsInputStream() throws IOException {
44 throw new IOException("FbsInputStream: no such constructor");
45 }
46
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000047 //
48 // Construct FbsInputStream object, begin playback.
49 //
50
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000051 FbsInputStream(InputStream in) throws IOException
52 {
53 this.in = in;
54 startTime = System.currentTimeMillis();
55 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000056 seekOffset = -1;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000057 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +000058 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000059
60 byte[] b = new byte[12];
61 readFully(b);
62
63 if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' ||
64 b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' ||
65 b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' ||
66 b[10] < '0' || b[10] > '9' || b[11] != '\n') {
67 throw new IOException("Incorrect protocol version");
68 }
69
70 buffer = null;
71 bufferSize = 0;
72 bufferPos = 0;
73 }
74
75 //
76 // Basic methods overriding InputStream's methods.
77 //
78
79 public int read() throws IOException
80 {
81 while (bufferSize == 0) {
82 if (!fillBuffer())
83 return -1;
84 }
85 bufferSize--;
86 return buffer[bufferPos++] & 0xFF;
87 }
88
89 public int available() throws IOException
90 {
91 // FIXME: This will work incorrectly if our caller will wait until
92 // some amount of data is available when the buffer contains less
93 // data than then that. Current implementation never reads more
94 // data until the buffer is fully exhausted.
95 return bufferSize;
96 }
97
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +000098 public synchronized void close() throws IOException
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000099 {
100 in.close();
101 in = null;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000102 startTime = -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000103 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000104 seekOffset = -1;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000105 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000106 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000107
108 buffer = null;
109 bufferSize = 0;
110 bufferPos = 0;
111 }
112
113 //
114 // Methods providing additional functionality.
115 //
116
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000117 public synchronized long getTimeOffset()
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000118 {
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000119 return (long)(timeOffset * playbackSpeed);
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000120 }
121
Constantin Kaplinsky972c2572002-05-30 14:19:02 +0000122 public synchronized void setTimeOffset(long pos)
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000123 {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000124 // FIXME: Seeking works only in paused mode.
125 paused = true;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000126 seekOffset = (long)(pos / playbackSpeed);
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000127 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000128 }
129
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000130 public synchronized void setSpeed(double newSpeed)
131 {
132 timeOffset = (long)(timeOffset * playbackSpeed / newSpeed);
133 startTime = System.currentTimeMillis() - timeOffset;
134 if (isSeeking()) {
135 seekOffset = (long)(seekOffset * playbackSpeed / newSpeed);
136 }
137 playbackSpeed = newSpeed;
138 }
139
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000140 public boolean isSeeking()
141 {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000142 return (seekOffset >= 0);
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000143 }
144
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000145 public synchronized void pausePlayback()
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000146 {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000147 paused = true;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000148 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000149 }
150
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000151 public synchronized void resumePlayback()
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000152 {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000153 paused = false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000154 startTime = System.currentTimeMillis() - timeOffset;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000155 notify();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000156 }
157
158 //
159 // Methods for internal use.
160 //
161
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000162 private synchronized boolean fillBuffer() throws IOException
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000163 {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000164 waitWhilePaused();
165
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000166 bufferSize = (int)readUnsigned32();
167 if (bufferSize >= 0) {
168 int realSize = (bufferSize + 3) & 0xFFFFFFFC;
169 buffer = new byte[realSize];
170 readFully(buffer);
171 bufferPos = 0;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000172 timeOffset = (long)(readUnsigned32() / playbackSpeed);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000173 }
174
175 if (bufferSize < 0 || timeOffset < 0) {
176 buffer = null;
177 bufferSize = 0;
178 bufferPos = 0;
179 return false;
180 }
181
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000182 if (seekOffset >= 0) {
183 if (timeOffset >= seekOffset) {
184 seekOffset = -1;
185 }
186 return true;
187 }
188
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000189 while (true) {
190 long timeDiff = startTime + timeOffset - System.currentTimeMillis();
191 if (timeDiff <= 0) {
192 break;
193 }
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000194 try {
195 wait(timeDiff);
196 } catch (InterruptedException e) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000197 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000198 waitWhilePaused();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000199 }
200
201 return true;
202 }
203
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000204 //
205 // In paused mode, wait for external notification on this object.
206 //
207
208 private void waitWhilePaused()
209 {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000210 while (paused && !isSeeking()) {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000211 synchronized(this) {
212 try {
213 wait();
214 } catch (InterruptedException e) {
215 }
216 }
217 }
218 }
219
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000220 private long readUnsigned32() throws IOException
221 {
222 byte[] buf = new byte[4];
223 if (!readFully(buf))
224 return -1;
225
226 return ((long)(buf[0] & 0xFF) << 24 |
227 (buf[1] & 0xFF) << 16 |
228 (buf[2] & 0xFF) << 8 |
229 (buf[3] & 0xFF));
230 }
231
232 private boolean readFully(byte[] b) throws IOException
233 {
234 int off = 0;
235 int len = b.length;
236
237 while (off != len) {
238 int count = in.read(b, off, len - off);
239 if (count < 0) {
240 return false;
241 }
242 off += count;
243 }
244
245 return true;
246 }
247}
248