blob: 1dcdd0936da1d2773cd8181f222202cb10f04cda [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 Kaplinskyd8770f62002-08-16 16:01:37 +000033 protected boolean seekBackwards;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000034 protected boolean paused;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +000035 protected double playbackSpeed;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000036
37 protected byte[] buffer;
38 protected int bufferSize;
39 protected int bufferPos;
40
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +000041 protected Observer obs;
42
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000043 //
44 // Constructors.
45 //
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000046 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 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000053 FbsInputStream(InputStream in) throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000054 this.in = in;
55 startTime = System.currentTimeMillis();
56 timeOffset = 0;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000057 seekOffset = -1;
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +000058 seekBackwards = false;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +000059 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +000060 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000061
62 byte[] b = new byte[12];
63 readFully(b);
64
65 if (b[0] != 'F' || b[1] != 'B' || b[2] != 'S' || b[3] != ' ' ||
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000066 b[4] != '0' || b[5] != '0' || b[6] != '1' || b[7] != '.' ||
67 b[8] < '0' || b[8] > '9' || b[9] < '0' || b[9] > '9' ||
68 b[10] < '0' || b[10] > '9' || b[11] != '\n') {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000069 throw new IOException("Incorrect protocol version");
70 }
71
72 buffer = null;
73 bufferSize = 0;
74 bufferPos = 0;
75 }
76
77 //
78 // Basic methods overriding InputStream's methods.
79 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000080 public int read() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000081 while (bufferSize == 0) {
82 if (!fillBuffer())
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000083 return -1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000084 }
85 bufferSize--;
86 return buffer[bufferPos++] & 0xFF;
87 }
88
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000089 public int available() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000090 // FIXME: This will work incorrectly if our caller will wait until
91 // some amount of data is available when the buffer contains less
92 // data than then that. Current implementation never reads more
93 // data until the buffer is fully exhausted.
94 return bufferSize;
95 }
96
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +000097 public synchronized void close() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000098 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 Kaplinskyd8770f62002-08-16 16:01:37 +0000103 seekBackwards = false;
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000104 paused = false;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000105 playbackSpeed = 1.0;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000106
107 buffer = null;
108 bufferSize = 0;
109 bufferPos = 0;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000110
111 obs = null;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000112 }
113
114 //
115 // Methods providing additional functionality.
116 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000117 public synchronized long getTimeOffset() {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000118 long off = Math.max(seekOffset, timeOffset);
119 return (long)(off * playbackSpeed);
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000120 }
121
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000122 public synchronized void setTimeOffset(long pos) {
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000123 seekOffset = (long)(pos / playbackSpeed);
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000124 if (seekOffset < timeOffset) {
125 seekBackwards = true;
126 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000127 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000128 }
129
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000130 public synchronized void setSpeed(double newSpeed) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000131 long newOffset = (long)(timeOffset * playbackSpeed / newSpeed);
132 startTime += timeOffset - newOffset;
133 timeOffset = newOffset;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000134 if (isSeeking()) {
135 seekOffset = (long)(seekOffset * playbackSpeed / newSpeed);
136 }
137 playbackSpeed = newSpeed;
138 }
139
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000140 public boolean isSeeking() {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000141 return (seekOffset >= 0);
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000142 }
143
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000144 public long getSeekOffset() {
Constantin Kaplinsky7cc77622002-09-22 12:36:20 +0000145 return (long)(seekOffset * playbackSpeed);
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000146 }
147
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000148 public boolean isPaused() {
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000149 return paused;
150 }
151
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000152 public synchronized void pausePlayback() {
Constantin Kaplinskyba0c4df2002-05-30 12:23:25 +0000153 paused = true;
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000154 notify();
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000155 }
156
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000157 public synchronized void resumePlayback() {
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 Kaplinsky72e47ef2008-04-18 17:48:16 +0000163 public void addObserver(Observer target) {
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000164 obs = target;
165 }
166
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000167 //
168 // Methods for internal use.
169 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000170 private synchronized boolean fillBuffer() throws IOException {
Constantin Kaplinskyd8770f62002-08-16 16:01:37 +0000171 // The reading thread should be interrupted on backward seeking.
172 if (seekBackwards)
173 throw new EOFException("[REWIND]");
174
175 // Just wait unless we are performing playback OR seeking.
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000176 waitWhilePaused();
177
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000178 bufferSize = (int)readUnsigned32();
179 if (bufferSize >= 0) {
180 int realSize = (bufferSize + 3) & 0xFFFFFFFC;
181 buffer = new byte[realSize];
182 readFully(buffer);
183 bufferPos = 0;
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000184 timeOffset = (long)(readUnsigned32() / playbackSpeed);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000185 }
186
187 if (bufferSize < 0 || timeOffset < 0) {
188 buffer = null;
189 bufferSize = 0;
190 bufferPos = 0;
191 return false;
192 }
193
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000194 if (seekOffset >= 0) {
195 if (timeOffset >= seekOffset) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000196 startTime = System.currentTimeMillis() - seekOffset;
197 seekOffset = -1;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000198 } else {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000199 return true;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000200 }
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000201 }
202
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000203 while (true) {
204 long timeDiff = startTime + timeOffset - System.currentTimeMillis();
205 if (timeDiff <= 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000206 break;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000207 }
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000208 try {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000209 wait(timeDiff);
Constantin Kaplinskyce39d3b2002-05-30 15:54:56 +0000210 } catch (InterruptedException e) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000211 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000212 waitWhilePaused();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000213 }
214
215 return true;
216 }
217
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000218 //
219 // In paused mode, wait for external notification on this object.
220 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000221 private void waitWhilePaused() {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000222 while (paused && !isSeeking()) {
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000223 synchronized(this) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000224 try {
225 // Note: we call Observer.update(Observable,Object) method
226 // directly instead of maintaining an Observable object.
227 obs.update(null, null);
228 wait();
229 } catch (InterruptedException e) {
230 }
Constantin Kaplinskyac6420d2002-05-29 17:05:39 +0000231 }
232 }
233 }
234
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000235 private long readUnsigned32() throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000236 byte[] buf = new byte[4];
237 if (!readFully(buf))
238 return -1;
239
240 return ((long)(buf[0] & 0xFF) << 24 |
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000241 (buf[1] & 0xFF) << 16 |
242 (buf[2] & 0xFF) << 8 |
243 (buf[3] & 0xFF));
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000244 }
245
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000246 private boolean readFully(byte[] b) throws IOException {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000247 int off = 0;
248 int len = b.length;
249
250 while (off != len) {
251 int count = in.read(b, off, len - off);
252 if (count < 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000253 return false;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000254 }
255 off += count;
256 }
257
258 return true;
259 }
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000260
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000261}
262