Transpiled version (4914L) is out of date.
1 | !include once #1032914 // VorbisJava |
2 | |
3 | /* |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | import java.io.ByteArrayOutputStream; |
18 | import java.io.IOException; |
19 | import java.io.InputStream; |
20 | import java.io.OutputStream; |
21 | import java.util.Iterator; |
22 | |
23 | import static org.gagravarr.ogg.IOUtils.readOrEOF; |
24 | |
25 | // replaces OggPage |
26 | sclass OggStreamPage { |
27 | private int sid; |
28 | private int seqNum; |
29 | private long checksum; |
30 | private long granulePosition; |
31 | |
32 | private boolean isBOS; |
33 | private boolean isEOS; |
34 | private boolean isContinue; |
35 | |
36 | private int numLVs = 0; |
37 | private byte[] lvs = new byte[255]; |
38 | private byte[] data; |
39 | private ByteArrayOutputStream tmpData; |
40 | |
41 | *(int sid, int seqNum) { |
42 | ifdef OggStreamPage_debug |
43 | printVars("OggStreamPage.init", +sid, +seqNum); |
44 | endifdef |
45 | this.sid = sid; |
46 | this.seqNum = seqNum; |
47 | this.tmpData = new ByteArrayOutputStream(); |
48 | } |
49 | /** |
50 | * InputStream should be positioned *just after* |
51 | * the OggS capture pattern. |
52 | */ |
53 | *(InputStream inp) throws IOException { |
54 | ifdef OggStreamPage_debug |
55 | printVars("OggStreamPage.init(stream)"); |
56 | endifdef |
57 | |
58 | int version = readOrEOF(inp); |
59 | if(version != 0) { |
60 | throw new UnsupportedOperationException("Found Ogg page in format " + version + " but we only support version 0"); |
61 | } |
62 | |
63 | int flags = readOrEOF(inp); |
64 | if((flags & 0x01) == 0x01) { |
65 | isContinue = true; |
66 | } |
67 | if((flags & 0x02) == 0x02) { |
68 | isBOS = true; |
69 | } |
70 | if((flags & 0x04) == 0x04) { |
71 | isEOS = true; |
72 | } |
73 | |
74 | granulePosition = IOUtils.getInt( |
75 | readOrEOF(inp), readOrEOF(inp), readOrEOF(inp), readOrEOF(inp), |
76 | readOrEOF(inp), readOrEOF(inp), readOrEOF(inp), readOrEOF(inp) |
77 | ); |
78 | sid = (int)IOUtils.getInt( |
79 | readOrEOF(inp), readOrEOF(inp), readOrEOF(inp), readOrEOF(inp) |
80 | ); |
81 | seqNum = (int)IOUtils.getInt( |
82 | readOrEOF(inp), readOrEOF(inp), readOrEOF(inp), readOrEOF(inp) |
83 | ); |
84 | checksum = IOUtils.getInt( |
85 | readOrEOF(inp), readOrEOF(inp), readOrEOF(inp), readOrEOF(inp) |
86 | ); |
87 | |
88 | numLVs = readOrEOF(inp); |
89 | lvs = new byte[numLVs]; |
90 | |
91 | ifdef OggStreamPage_debug |
92 | printVars("OggStreamPage", +numLVs); |
93 | endifdef |
94 | readFully(inp, lvs); |
95 | ifdef OggStreamPage_debug |
96 | print("OggStreamPage lvs read"); |
97 | endifdef |
98 | |
99 | data = new byte[ getDataSize() ]; |
100 | ifdef OggStreamPage_debug |
101 | printVars("OggStreamPage", dataLength := l(data)); |
102 | endifdef |
103 | readFully(inp, data); |
104 | ifdef OggStreamPage_debug |
105 | print("OggStreamPage data read"); |
106 | endifdef |
107 | } |
108 | |
109 | void readFully(InputStream inp, byte[] buf) throws IOException { |
110 | if (inp cast InputStreamPlusReadFully) |
111 | inp.readFully(buf); |
112 | else |
113 | IOUtils.readFully(inp, buf); |
114 | } |
115 | |
116 | /** |
117 | * Adds as much of the packet's data as |
118 | * we can do. |
119 | */ |
120 | protected int addPacket(OggStreamPacket packet, int offset) { |
121 | if(packet.isBeginningOfStream()) { |
122 | isBOS = true; |
123 | } |
124 | if(packet.isEndOfStream()) { |
125 | isEOS = true; |
126 | } |
127 | |
128 | // Add on in 255 byte chunks |
129 | int size = packet.getData().length; |
130 | for(int i = numLVs; i< 255; i++) { |
131 | int remains = size - offset; |
132 | |
133 | int toAdd = 255; |
134 | if(remains < 255) { |
135 | toAdd = remains; |
136 | } |
137 | lvs[i] = IOUtils.fromInt(toAdd); |
138 | tmpData.write(packet.getData(), offset, toAdd); |
139 | |
140 | numLVs++; |
141 | offset += toAdd; |
142 | if(toAdd < 255) { |
143 | break; |
144 | } |
145 | } |
146 | |
147 | return offset; |
148 | } |
149 | |
150 | /** |
151 | * Is the checksum for the page valid? |
152 | */ |
153 | public boolean isChecksumValid() { |
154 | if(checksum == 0) |
155 | return true; |
156 | |
157 | int crc = CRCUtils.getCRC(getHeader()); |
158 | if(data != null && data.length > 0) { |
159 | crc = CRCUtils.getCRC(data, crc); |
160 | } |
161 | |
162 | return (checksum == crc); |
163 | } |
164 | protected long getChecksum() { |
165 | return checksum; |
166 | } |
167 | |
168 | /** |
169 | * Does this Page have space for the given |
170 | * number of bytes? |
171 | */ |
172 | protected boolean hasSpaceFor(int bytes) { |
173 | // Do we have enough lvs spare? |
174 | // (Each LV holds up to 255 bytes, and we're |
175 | // not allowed more than 255 of them) |
176 | int reqLVs = (int)Math.ceil(bytes / 255.0); |
177 | |
178 | if(numLVs + reqLVs > 255) { |
179 | return false; |
180 | } |
181 | return true; |
182 | } |
183 | |
184 | /** |
185 | * Returns the minimum size of a page, which is 27 |
186 | * bytes for the headers |
187 | */ |
188 | public static int getMinimumPageSize() { |
189 | return MINIMUM_PAGE_SIZE; |
190 | } |
191 | private static final int MINIMUM_PAGE_SIZE = 27; |
192 | |
193 | /** |
194 | * How big is the page, including headers? |
195 | */ |
196 | public int getPageSize() { |
197 | // Header is 27 bytes + number of headers |
198 | int size = MINIMUM_PAGE_SIZE + numLVs; |
199 | // Data size is given by lvs |
200 | size += getDataSize(); |
201 | return size; |
202 | } |
203 | /** |
204 | * How big is the page, excluding headers? |
205 | */ |
206 | public int getDataSize() { |
207 | // Data size is given by lvs |
208 | int size = 0; |
209 | for(int i=0; i<numLVs; i++) { |
210 | size += IOUtils.toInt(lvs[i]); |
211 | } |
212 | return size; |
213 | } |
214 | |
215 | |
216 | public int getSid() { |
217 | return sid; |
218 | } |
219 | public int getSequenceNumber () { |
220 | return seqNum; |
221 | } |
222 | public long getGranulePosition() { |
223 | return granulePosition; |
224 | } |
225 | public byte[] getData() { |
226 | if(tmpData != null) { |
227 | if(data == null || tmpData.size() != data.length) { |
228 | data = tmpData.toByteArray(); |
229 | } |
230 | } |
231 | return data; |
232 | } |
233 | |
234 | protected void setGranulePosition(long position) { |
235 | this.granulePosition = position; |
236 | } |
237 | |
238 | /** |
239 | * Is there a subsequent page containing the |
240 | * remainder of the packets? |
241 | */ |
242 | public boolean hasContinuation() { |
243 | // Has a continuation if the last LV |
244 | // is 255. |
245 | // Normally one would expect to have |
246 | // the full 255 LVs, with the |
247 | // last one at 255, but technically |
248 | // you can force a continue without |
249 | // using all your LVs up |
250 | if(numLVs == 0) { |
251 | return false; |
252 | } |
253 | if(IOUtils.toInt( lvs[numLVs-1] ) == 255) { |
254 | return true; |
255 | } |
256 | return false; |
257 | } |
258 | /** |
259 | * Is this carrying on the packets from |
260 | * a previous page? |
261 | */ |
262 | public boolean isContinuation() { |
263 | return isContinue; |
264 | } |
265 | protected void setIsContinuation() { |
266 | isContinue = true; |
267 | } |
268 | |
269 | /** |
270 | * This should only ever be called by |
271 | * {@link OggPacketWriter#close()} ! |
272 | */ |
273 | protected void setIsEOS() { |
274 | isEOS = true; |
275 | } |
276 | |
277 | /** |
278 | * For unit testing only! |
279 | */ |
280 | protected int getNumLVs() { |
281 | return numLVs; |
282 | } |
283 | |
284 | |
285 | public void writeHeader(OutputStream out) throws IOException { |
286 | byte[] header = getHeader(); |
287 | |
288 | // Ensure we've moved from tmpdata to data |
289 | getData(); |
290 | |
291 | // Generate the checksum and store |
292 | int crc = CRCUtils.getCRC(header); |
293 | if(data != null && data.length > 0) { |
294 | crc = CRCUtils.getCRC(data, crc); |
295 | } |
296 | IOUtils.putInt4(header, 22, crc); |
297 | checksum = crc; |
298 | |
299 | // Write out |
300 | out.write(header); |
301 | } |
302 | /** |
303 | * Gets the header, but with a blank CRC field |
304 | */ |
305 | protected byte[] getHeader() { |
306 | byte[] header = new byte[MINIMUM_PAGE_SIZE + numLVs]; |
307 | header[0] = (byte)'O'; |
308 | header[1] = (byte)'g'; |
309 | header[2] = (byte)'g'; |
310 | header[3] = (byte)'S'; |
311 | |
312 | header[4] = 0; // Version |
313 | |
314 | byte flags = 0; |
315 | if(isContinue) { |
316 | flags += 1; |
317 | } |
318 | if(isBOS) { |
319 | flags += 2; |
320 | } |
321 | if(isEOS) { |
322 | flags += 4; |
323 | } |
324 | header[5] = flags; |
325 | |
326 | IOUtils.putInt8(header, 6, granulePosition); |
327 | IOUtils.putInt4(header, 14, sid); |
328 | IOUtils.putInt4(header, 18, seqNum); |
329 | |
330 | // Checksum @ 22 left blank for now |
331 | |
332 | header[26] = IOUtils.fromInt(numLVs); |
333 | System.arraycopy(lvs, 0, header, MINIMUM_PAGE_SIZE, numLVs); |
334 | |
335 | return header; |
336 | } |
337 | |
338 | |
339 | public String toString() { |
340 | return "Ogg Page - " + getSid() + " @ " + getSequenceNumber() + |
341 | " - " + numLVs + " LVs"; |
342 | } |
343 | |
344 | |
345 | public OggPacketIterator getPacketIterator() { |
346 | return new OggPacketIterator(null); |
347 | } |
348 | public OggPacketIterator getPacketIterator(OggPacketData previousPart) { |
349 | return new OggPacketIterator(previousPart); |
350 | } |
351 | /** |
352 | * Returns a full {@link OggPacket} if it can, otherwise |
353 | * just the {@link OggPacketData} if the rest of the |
354 | * packet is in another {@link OggPage} |
355 | */ |
356 | protected class OggPacketIterator implements Iterator<OggPacketData> { |
357 | private OggPacketData prevPart; |
358 | private int currentLV = 0; |
359 | private int currentOffset = 0; |
360 | |
361 | private OggPacketIterator(OggPacketData previousPart) { |
362 | this.prevPart = previousPart; |
363 | |
364 | ifdef OggStreamPage_debug |
365 | printVars("OggPacketIterator", lvsRemaining := numLVs-currentLV, +prevPart); |
366 | endifdef |
367 | } |
368 | |
369 | public boolean hasNext() { |
370 | if(currentLV < numLVs) { |
371 | return true; |
372 | } |
373 | // Special case for an empty page |
374 | if(currentLV == 0 && numLVs == 0) { |
375 | return true; |
376 | } |
377 | |
378 | return false; |
379 | } |
380 | |
381 | public OggPacketData next() { |
382 | boolean continues = false; |
383 | int packetLVs = 0; |
384 | int packetSize = 0; |
385 | |
386 | // How much data to we have? |
387 | for(int i=currentLV; i< numLVs; i++) { |
388 | int size = IOUtils.toInt( lvs[i] ); |
389 | packetSize += size; |
390 | packetLVs++; |
391 | |
392 | if(size < 255) { |
393 | break; |
394 | } |
395 | if(i == (numLVs-1) && size == 255) { |
396 | continues = true; |
397 | } |
398 | } |
399 | |
400 | // Get the data |
401 | byte[] pd = new byte[packetSize]; |
402 | for(int i=currentLV; i<(currentLV + packetLVs); i++) { |
403 | int size = IOUtils.toInt( lvs[i] ); |
404 | int offset = (i-currentLV)*255; |
405 | System.arraycopy(data, currentOffset+offset, pd, offset, size); |
406 | } |
407 | // Tack on anything spare from last time too |
408 | if(prevPart != null) { |
409 | int prevSize = prevPart.getData().length; |
410 | byte[] fpd = new byte[prevSize+pd.length]; |
411 | System.arraycopy(prevPart.getData(), 0, fpd, 0, prevSize); |
412 | System.arraycopy(pd, 0, fpd, prevSize, pd.length); |
413 | |
414 | prevPart = null; |
415 | pd = fpd; |
416 | } |
417 | |
418 | // Create |
419 | OggPacketData packet; |
420 | if(continues) { |
421 | packet = new OggPacketData(pd) {}; |
422 | } else { |
423 | boolean packetBOS = false; |
424 | boolean packetEOS = false; |
425 | if(isBOS && currentLV == 0) { |
426 | packetBOS = true; |
427 | } |
428 | if(isEOS && (currentLV+packetLVs) == numLVs) { |
429 | packetEOS = true; |
430 | } |
431 | |
432 | packet = new OggStreamPacket(OggStreamPage.this, pd, packetBOS, packetEOS); |
433 | } |
434 | |
435 | // Wind on |
436 | currentLV += packetLVs; |
437 | currentOffset += packetSize; |
438 | // Empty page special case wind-on |
439 | if(currentLV == 0) |
440 | currentLV = 1; |
441 | |
442 | // Done! |
443 | return packet; |
444 | } |
445 | |
446 | public void remove() { |
447 | throw new IllegalStateException("Remove not supported"); |
448 | } |
449 | } |
450 | } |
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): bhatertpkbcr, ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1032913 |
Snippet name: | OggStreamPage |
Eternal ID of this version: | #1032913/11 |
Text MD5: | 0085171b4cc2c98b4bacddfcd2badd03 |
Author: | stefan |
Category: | javax / audio |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-10-10 21:51:13 |
Source code size: | 13006 bytes / 450 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 159 / 463 |
Version history: | 10 change(s) |
Referenced in: | [show references] |