1 /* 2 * Copyright (C) 2016 The Android Open Source Project 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 package org.mozilla.thirdparty.com.google.android.exoplayer2.extractor; 17 18 import org.mozilla.thirdparty.com.google.android.exoplayer2.ParserException; 19 import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Log; 20 import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableByteArray; 21 import java.util.Arrays; 22 23 /** Utility methods for parsing Vorbis streams. */ 24 public final class VorbisUtil { 25 26 /** Vorbis comment header. */ 27 public static final class CommentHeader { 28 29 public final String vendor; 30 public final String[] comments; 31 public final int length; 32 CommentHeader(String vendor, String[] comments, int length)33 public CommentHeader(String vendor, String[] comments, int length) { 34 this.vendor = vendor; 35 this.comments = comments; 36 this.length = length; 37 } 38 } 39 40 /** Vorbis identification header. */ 41 public static final class VorbisIdHeader { 42 43 public final long version; 44 public final int channels; 45 public final long sampleRate; 46 public final int bitrateMax; 47 public final int bitrateNominal; 48 public final int bitrateMin; 49 public final int blockSize0; 50 public final int blockSize1; 51 public final boolean framingFlag; 52 public final byte[] data; 53 VorbisIdHeader( long version, int channels, long sampleRate, int bitrateMax, int bitrateNominal, int bitrateMin, int blockSize0, int blockSize1, boolean framingFlag, byte[] data)54 public VorbisIdHeader( 55 long version, 56 int channels, 57 long sampleRate, 58 int bitrateMax, 59 int bitrateNominal, 60 int bitrateMin, 61 int blockSize0, 62 int blockSize1, 63 boolean framingFlag, 64 byte[] data) { 65 this.version = version; 66 this.channels = channels; 67 this.sampleRate = sampleRate; 68 this.bitrateMax = bitrateMax; 69 this.bitrateNominal = bitrateNominal; 70 this.bitrateMin = bitrateMin; 71 this.blockSize0 = blockSize0; 72 this.blockSize1 = blockSize1; 73 this.framingFlag = framingFlag; 74 this.data = data; 75 } 76 getApproximateBitrate()77 public int getApproximateBitrate() { 78 return bitrateNominal == 0 ? (bitrateMin + bitrateMax) / 2 : bitrateNominal; 79 } 80 } 81 82 /** Vorbis setup header modes. */ 83 public static final class Mode { 84 85 public final boolean blockFlag; 86 public final int windowType; 87 public final int transformType; 88 public final int mapping; 89 Mode(boolean blockFlag, int windowType, int transformType, int mapping)90 public Mode(boolean blockFlag, int windowType, int transformType, int mapping) { 91 this.blockFlag = blockFlag; 92 this.windowType = windowType; 93 this.transformType = transformType; 94 this.mapping = mapping; 95 } 96 } 97 98 private static final String TAG = "VorbisUtil"; 99 100 /** 101 * Returns ilog(x), which is the index of the highest set bit in {@code x}. 102 * 103 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-1190009.2.1"> 104 * Vorbis spec</a> 105 * @param x the value of which the ilog should be calculated. 106 * @return ilog(x) 107 */ iLog(int x)108 public static int iLog(int x) { 109 int val = 0; 110 while (x > 0) { 111 val++; 112 x >>>= 1; 113 } 114 return val; 115 } 116 117 /** 118 * Reads a Vorbis identification header from {@code headerData}. 119 * 120 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-630004.2.2">Vorbis 121 * spec/Identification header</a> 122 * @param headerData a {@link ParsableByteArray} wrapping the header data. 123 * @return a {@link VorbisUtil.VorbisIdHeader} with meta data. 124 * @throws ParserException thrown if invalid capture pattern is detected. 125 */ readVorbisIdentificationHeader(ParsableByteArray headerData)126 public static VorbisIdHeader readVorbisIdentificationHeader(ParsableByteArray headerData) 127 throws ParserException { 128 129 verifyVorbisHeaderCapturePattern(0x01, headerData, false); 130 131 long version = headerData.readLittleEndianUnsignedInt(); 132 int channels = headerData.readUnsignedByte(); 133 long sampleRate = headerData.readLittleEndianUnsignedInt(); 134 int bitrateMax = headerData.readLittleEndianInt(); 135 int bitrateNominal = headerData.readLittleEndianInt(); 136 int bitrateMin = headerData.readLittleEndianInt(); 137 138 int blockSize = headerData.readUnsignedByte(); 139 int blockSize0 = (int) Math.pow(2, blockSize & 0x0F); 140 int blockSize1 = (int) Math.pow(2, (blockSize & 0xF0) >> 4); 141 142 boolean framingFlag = (headerData.readUnsignedByte() & 0x01) > 0; 143 // raw data of Vorbis setup header has to be passed to decoder as CSD buffer #1 144 byte[] data = Arrays.copyOf(headerData.data, headerData.limit()); 145 146 return new VorbisIdHeader(version, channels, sampleRate, bitrateMax, bitrateNominal, bitrateMin, 147 blockSize0, blockSize1, framingFlag, data); 148 } 149 150 /** 151 * Reads a Vorbis comment header. 152 * 153 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-640004.2.3">Vorbis 154 * spec/Comment header</a> 155 * @param headerData A {@link ParsableByteArray} wrapping the header data. 156 * @return A {@link VorbisUtil.CommentHeader} with all the comments. 157 * @throws ParserException If an error occurs parsing the comment header. 158 */ readVorbisCommentHeader(ParsableByteArray headerData)159 public static CommentHeader readVorbisCommentHeader(ParsableByteArray headerData) 160 throws ParserException { 161 return readVorbisCommentHeader( 162 headerData, /* hasMetadataHeader= */ true, /* hasFramingBit= */ true); 163 } 164 165 /** 166 * Reads a Vorbis comment header. 167 * 168 * <p>The data provided may not contain the Vorbis metadata common header and the framing bit. 169 * 170 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-640004.2.3">Vorbis 171 * spec/Comment header</a> 172 * @param headerData A {@link ParsableByteArray} wrapping the header data. 173 * @param hasMetadataHeader Whether the {@code headerData} contains a Vorbis metadata common 174 * header preceding the comment header. 175 * @param hasFramingBit Whether the {@code headerData} contains a framing bit. 176 * @return A {@link VorbisUtil.CommentHeader} with all the comments. 177 * @throws ParserException If an error occurs parsing the comment header. 178 */ readVorbisCommentHeader( ParsableByteArray headerData, boolean hasMetadataHeader, boolean hasFramingBit)179 public static CommentHeader readVorbisCommentHeader( 180 ParsableByteArray headerData, boolean hasMetadataHeader, boolean hasFramingBit) 181 throws ParserException { 182 183 if (hasMetadataHeader) { 184 verifyVorbisHeaderCapturePattern(/* headerType= */ 0x03, headerData, /* quiet= */ false); 185 } 186 int length = 7; 187 188 int len = (int) headerData.readLittleEndianUnsignedInt(); 189 length += 4; 190 String vendor = headerData.readString(len); 191 length += vendor.length(); 192 193 long commentListLen = headerData.readLittleEndianUnsignedInt(); 194 String[] comments = new String[(int) commentListLen]; 195 length += 4; 196 for (int i = 0; i < commentListLen; i++) { 197 len = (int) headerData.readLittleEndianUnsignedInt(); 198 length += 4; 199 comments[i] = headerData.readString(len); 200 length += comments[i].length(); 201 } 202 if (hasFramingBit && (headerData.readUnsignedByte() & 0x01) == 0) { 203 throw new ParserException("framing bit expected to be set"); 204 } 205 length += 1; 206 return new CommentHeader(vendor, comments, length); 207 } 208 209 /** 210 * Verifies whether the next bytes in {@code header} are a Vorbis header of the given {@code 211 * headerType}. 212 * 213 * @param headerType the type of the header expected. 214 * @param header the alleged header bytes. 215 * @param quiet if {@code true} no exceptions are thrown. Instead {@code false} is returned. 216 * @return the number of bytes read. 217 * @throws ParserException thrown if header type or capture pattern is not as expected. 218 */ verifyVorbisHeaderCapturePattern( int headerType, ParsableByteArray header, boolean quiet)219 public static boolean verifyVorbisHeaderCapturePattern( 220 int headerType, ParsableByteArray header, boolean quiet) throws ParserException { 221 if (header.bytesLeft() < 7) { 222 if (quiet) { 223 return false; 224 } else { 225 throw new ParserException("too short header: " + header.bytesLeft()); 226 } 227 } 228 229 if (header.readUnsignedByte() != headerType) { 230 if (quiet) { 231 return false; 232 } else { 233 throw new ParserException("expected header type " + Integer.toHexString(headerType)); 234 } 235 } 236 237 if (!(header.readUnsignedByte() == 'v' 238 && header.readUnsignedByte() == 'o' 239 && header.readUnsignedByte() == 'r' 240 && header.readUnsignedByte() == 'b' 241 && header.readUnsignedByte() == 'i' 242 && header.readUnsignedByte() == 's')) { 243 if (quiet) { 244 return false; 245 } else { 246 throw new ParserException("expected characters 'vorbis'"); 247 } 248 } 249 return true; 250 } 251 252 /** 253 * This method reads the modes which are located at the very end of the Vorbis setup header. 254 * That's why we need to partially decode or at least read the entire setup header to know where 255 * to start reading the modes. 256 * 257 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4">Vorbis 258 * spec/Setup header</a> 259 * @param headerData a {@link ParsableByteArray} containing setup header data. 260 * @param channels the number of channels. 261 * @return an array of {@link Mode}s. 262 * @throws ParserException thrown if bit stream is invalid. 263 */ readVorbisModes(ParsableByteArray headerData, int channels)264 public static Mode[] readVorbisModes(ParsableByteArray headerData, int channels) 265 throws ParserException { 266 267 verifyVorbisHeaderCapturePattern(0x05, headerData, false); 268 269 int numberOfBooks = headerData.readUnsignedByte() + 1; 270 271 VorbisBitArray bitArray = new VorbisBitArray(headerData.data); 272 bitArray.skipBits(headerData.getPosition() * 8); 273 274 for (int i = 0; i < numberOfBooks; i++) { 275 readBook(bitArray); 276 } 277 278 int timeCount = bitArray.readBits(6) + 1; 279 for (int i = 0; i < timeCount; i++) { 280 if (bitArray.readBits(16) != 0x00) { 281 throw new ParserException("placeholder of time domain transforms not zeroed out"); 282 } 283 } 284 readFloors(bitArray); 285 readResidues(bitArray); 286 readMappings(channels, bitArray); 287 288 Mode[] modes = readModes(bitArray); 289 if (!bitArray.readBit()) { 290 throw new ParserException("framing bit after modes not set as expected"); 291 } 292 return modes; 293 } 294 readModes(VorbisBitArray bitArray)295 private static Mode[] readModes(VorbisBitArray bitArray) { 296 int modeCount = bitArray.readBits(6) + 1; 297 Mode[] modes = new Mode[modeCount]; 298 for (int i = 0; i < modeCount; i++) { 299 boolean blockFlag = bitArray.readBit(); 300 int windowType = bitArray.readBits(16); 301 int transformType = bitArray.readBits(16); 302 int mapping = bitArray.readBits(8); 303 modes[i] = new Mode(blockFlag, windowType, transformType, mapping); 304 } 305 return modes; 306 } 307 readMappings(int channels, VorbisBitArray bitArray)308 private static void readMappings(int channels, VorbisBitArray bitArray) 309 throws ParserException { 310 int mappingsCount = bitArray.readBits(6) + 1; 311 for (int i = 0; i < mappingsCount; i++) { 312 int mappingType = bitArray.readBits(16); 313 if (mappingType != 0) { 314 Log.e(TAG, "mapping type other than 0 not supported: " + mappingType); 315 continue; 316 } 317 int submaps; 318 if (bitArray.readBit()) { 319 submaps = bitArray.readBits(4) + 1; 320 } else { 321 submaps = 1; 322 } 323 int couplingSteps; 324 if (bitArray.readBit()) { 325 couplingSteps = bitArray.readBits(8) + 1; 326 for (int j = 0; j < couplingSteps; j++) { 327 bitArray.skipBits(iLog(channels - 1)); // magnitude 328 bitArray.skipBits(iLog(channels - 1)); // angle 329 } 330 } /*else { 331 couplingSteps = 0; 332 }*/ 333 if (bitArray.readBits(2) != 0x00) { 334 throw new ParserException("to reserved bits must be zero after mapping coupling steps"); 335 } 336 if (submaps > 1) { 337 for (int j = 0; j < channels; j++) { 338 bitArray.skipBits(4); // mappingMux 339 } 340 } 341 for (int j = 0; j < submaps; j++) { 342 bitArray.skipBits(8); // discard 343 bitArray.skipBits(8); // submapFloor 344 bitArray.skipBits(8); // submapResidue 345 } 346 } 347 } 348 readResidues(VorbisBitArray bitArray)349 private static void readResidues(VorbisBitArray bitArray) throws ParserException { 350 int residueCount = bitArray.readBits(6) + 1; 351 for (int i = 0; i < residueCount; i++) { 352 int residueType = bitArray.readBits(16); 353 if (residueType > 2) { 354 throw new ParserException("residueType greater than 2 is not decodable"); 355 } else { 356 bitArray.skipBits(24); // begin 357 bitArray.skipBits(24); // end 358 bitArray.skipBits(24); // partitionSize (add one) 359 int classifications = bitArray.readBits(6) + 1; 360 bitArray.skipBits(8); // classbook 361 int[] cascade = new int[classifications]; 362 for (int j = 0; j < classifications; j++) { 363 int highBits = 0; 364 int lowBits = bitArray.readBits(3); 365 if (bitArray.readBit()) { 366 highBits = bitArray.readBits(5); 367 } 368 cascade[j] = highBits * 8 + lowBits; 369 } 370 for (int j = 0; j < classifications; j++) { 371 for (int k = 0; k < 8; k++) { 372 if ((cascade[j] & (0x01 << k)) != 0) { 373 bitArray.skipBits(8); // discard 374 } 375 } 376 } 377 } 378 } 379 } 380 readFloors(VorbisBitArray bitArray)381 private static void readFloors(VorbisBitArray bitArray) throws ParserException { 382 int floorCount = bitArray.readBits(6) + 1; 383 for (int i = 0; i < floorCount; i++) { 384 int floorType = bitArray.readBits(16); 385 switch (floorType) { 386 case 0: 387 bitArray.skipBits(8); //order 388 bitArray.skipBits(16); // rate 389 bitArray.skipBits(16); // barkMapSize 390 bitArray.skipBits(6); // amplitudeBits 391 bitArray.skipBits(8); // amplitudeOffset 392 int floorNumberOfBooks = bitArray.readBits(4) + 1; 393 for (int j = 0; j < floorNumberOfBooks; j++) { 394 bitArray.skipBits(8); 395 } 396 break; 397 case 1: 398 int partitions = bitArray.readBits(5); 399 int maximumClass = -1; 400 int[] partitionClassList = new int[partitions]; 401 for (int j = 0; j < partitions; j++) { 402 partitionClassList[j] = bitArray.readBits(4); 403 if (partitionClassList[j] > maximumClass) { 404 maximumClass = partitionClassList[j]; 405 } 406 } 407 int[] classDimensions = new int[maximumClass + 1]; 408 for (int j = 0; j < classDimensions.length; j++) { 409 classDimensions[j] = bitArray.readBits(3) + 1; 410 int classSubclasses = bitArray.readBits(2); 411 if (classSubclasses > 0) { 412 bitArray.skipBits(8); // classMasterbooks 413 } 414 for (int k = 0; k < (1 << classSubclasses); k++) { 415 bitArray.skipBits(8); // subclassBook (subtract 1) 416 } 417 } 418 bitArray.skipBits(2); // multiplier (add one) 419 int rangeBits = bitArray.readBits(4); 420 int count = 0; 421 for (int j = 0, k = 0; j < partitions; j++) { 422 int idx = partitionClassList[j]; 423 count += classDimensions[idx]; 424 for (; k < count; k++) { 425 bitArray.skipBits(rangeBits); // floorValue 426 } 427 } 428 break; 429 default: 430 throw new ParserException("floor type greater than 1 not decodable: " + floorType); 431 } 432 } 433 } 434 readBook(VorbisBitArray bitArray)435 private static CodeBook readBook(VorbisBitArray bitArray) throws ParserException { 436 if (bitArray.readBits(24) != 0x564342) { 437 throw new ParserException("expected code book to start with [0x56, 0x43, 0x42] at " 438 + bitArray.getPosition()); 439 } 440 int dimensions = bitArray.readBits(16); 441 int entries = bitArray.readBits(24); 442 long[] lengthMap = new long[entries]; 443 444 boolean isOrdered = bitArray.readBit(); 445 if (!isOrdered) { 446 boolean isSparse = bitArray.readBit(); 447 for (int i = 0; i < lengthMap.length; i++) { 448 if (isSparse) { 449 if (bitArray.readBit()) { 450 lengthMap[i] = (long) (bitArray.readBits(5) + 1); 451 } else { // entry unused 452 lengthMap[i] = 0; 453 } 454 } else { // not sparse 455 lengthMap[i] = (long) (bitArray.readBits(5) + 1); 456 } 457 } 458 } else { 459 int length = bitArray.readBits(5) + 1; 460 for (int i = 0; i < lengthMap.length;) { 461 int num = bitArray.readBits(iLog(entries - i)); 462 for (int j = 0; j < num && i < lengthMap.length; i++, j++) { 463 lengthMap[i] = length; 464 } 465 length++; 466 } 467 } 468 469 int lookupType = bitArray.readBits(4); 470 if (lookupType > 2) { 471 throw new ParserException("lookup type greater than 2 not decodable: " + lookupType); 472 } else if (lookupType == 1 || lookupType == 2) { 473 bitArray.skipBits(32); // minimumValue 474 bitArray.skipBits(32); // deltaValue 475 int valueBits = bitArray.readBits(4) + 1; 476 bitArray.skipBits(1); // sequenceP 477 long lookupValuesCount; 478 if (lookupType == 1) { 479 if (dimensions != 0) { 480 lookupValuesCount = mapType1QuantValues(entries, dimensions); 481 } else { 482 lookupValuesCount = 0; 483 } 484 } else { 485 lookupValuesCount = (long) entries * dimensions; 486 } 487 // discard (no decoding required yet) 488 bitArray.skipBits((int) (lookupValuesCount * valueBits)); 489 } 490 return new CodeBook(dimensions, entries, lengthMap, lookupType, isOrdered); 491 } 492 493 /** 494 * @see <a href="http://svn.xiph.org/trunk/vorbis/lib/sharedbook.c">_book_maptype1_quantvals</a> 495 */ mapType1QuantValues(long entries, long dimension)496 private static long mapType1QuantValues(long entries, long dimension) { 497 return (long) Math.floor(Math.pow(entries, 1.d / dimension)); 498 } 499 VorbisUtil()500 private VorbisUtil() { 501 // Prevent instantiation. 502 } 503 504 private static final class CodeBook { 505 506 public final int dimensions; 507 public final int entries; 508 public final long[] lengthMap; 509 public final int lookupType; 510 public final boolean isOrdered; 511 CodeBook(int dimensions, int entries, long[] lengthMap, int lookupType, boolean isOrdered)512 public CodeBook(int dimensions, int entries, long[] lengthMap, int lookupType, 513 boolean isOrdered) { 514 this.dimensions = dimensions; 515 this.entries = entries; 516 this.lengthMap = lengthMap; 517 this.lookupType = lookupType; 518 this.isOrdered = isOrdered; 519 } 520 521 } 522 } 523