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