• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.google.android.exoplayer2.extractor.mkv;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import androidx.test.ext.junit.runners.AndroidJUnit4;
21 import com.google.android.exoplayer2.extractor.ExtractorInput;
22 import com.google.android.exoplayer2.testutil.FakeExtractorInput;
23 import com.google.android.exoplayer2.testutil.TestUtil;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 
31 /** Tests {@link DefaultEbmlReader}. */
32 @RunWith(AndroidJUnit4.class)
33 public class DefaultEbmlReaderTest {
34 
35   @Test
masterElement()36   public void masterElement() throws IOException {
37     ExtractorInput input = createTestInput(0x1A, 0x45, 0xDF, 0xA3, 0x84, 0x42, 0x85, 0x81, 0x01);
38     TestProcessor expected = new TestProcessor();
39     expected.startMasterElement(TestProcessor.ID_EBML, 5, 4);
40     expected.integerElement(TestProcessor.ID_DOC_TYPE_READ_VERSION, 1);
41     expected.endMasterElement(TestProcessor.ID_EBML);
42     assertEvents(input, expected.events);
43   }
44 
45   @Test
masterElementEmpty()46   public void masterElementEmpty() throws IOException {
47     ExtractorInput input = createTestInput(0x18, 0x53, 0x80, 0x67, 0x80);
48     TestProcessor expected = new TestProcessor();
49     expected.startMasterElement(TestProcessor.ID_SEGMENT, 5, 0);
50     expected.endMasterElement(TestProcessor.ID_SEGMENT);
51     assertEvents(input, expected.events);
52   }
53 
54   @Test
unsignedIntegerElement()55   public void unsignedIntegerElement() throws IOException {
56     // 0xFE is chosen because for signed integers it should be interpreted as -2
57     ExtractorInput input = createTestInput(0x42, 0xF7, 0x81, 0xFE);
58     TestProcessor expected = new TestProcessor();
59     expected.integerElement(TestProcessor.ID_EBML_READ_VERSION, 254);
60     assertEvents(input, expected.events);
61   }
62 
63   @Test
unsignedIntegerElementLarge()64   public void unsignedIntegerElementLarge() throws IOException {
65     ExtractorInput input =
66         createTestInput(0x42, 0xF7, 0x88, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
67     TestProcessor expected = new TestProcessor();
68     expected.integerElement(TestProcessor.ID_EBML_READ_VERSION, Long.MAX_VALUE);
69     assertEvents(input, expected.events);
70   }
71 
72   @Test
unsignedIntegerElementTooLargeBecomesNegative()73   public void unsignedIntegerElementTooLargeBecomesNegative() throws IOException {
74     ExtractorInput input =
75         createTestInput(0x42, 0xF7, 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
76     TestProcessor expected = new TestProcessor();
77     expected.integerElement(TestProcessor.ID_EBML_READ_VERSION, -1);
78     assertEvents(input, expected.events);
79   }
80 
81   @Test
stringElement()82   public void stringElement() throws IOException {
83     ExtractorInput input = createTestInput(0x42, 0x82, 0x86, 0x41, 0x62, 0x63, 0x31, 0x32, 0x33);
84     TestProcessor expected = new TestProcessor();
85     expected.stringElement(TestProcessor.ID_DOC_TYPE, "Abc123");
86     assertEvents(input, expected.events);
87   }
88 
89   @Test
stringElementWithZeroPadding()90   public void stringElementWithZeroPadding() throws IOException, InterruptedException {
91     ExtractorInput input = createTestInput(0x42, 0x82, 0x86, 0x41, 0x62, 0x63, 0x00, 0x00, 0x00);
92     TestProcessor expected = new TestProcessor();
93     expected.stringElement(TestProcessor.ID_DOC_TYPE, "Abc");
94     assertEvents(input, expected.events);
95   }
96 
97   @Test
stringElementEmpty()98   public void stringElementEmpty() throws IOException {
99     ExtractorInput input = createTestInput(0x42, 0x82, 0x80);
100     TestProcessor expected = new TestProcessor();
101     expected.stringElement(TestProcessor.ID_DOC_TYPE, "");
102     assertEvents(input, expected.events);
103   }
104 
105   @Test
floatElementFourBytes()106   public void floatElementFourBytes() throws IOException {
107     ExtractorInput input =
108         createTestInput(0x44, 0x89, 0x84, 0x3F, 0x80, 0x00, 0x00);
109     TestProcessor expected = new TestProcessor();
110     expected.floatElement(TestProcessor.ID_DURATION, 1.0);
111     assertEvents(input, expected.events);
112   }
113 
114   @Test
floatElementEightBytes()115   public void floatElementEightBytes() throws IOException {
116     ExtractorInput input =
117         createTestInput(0x44, 0x89, 0x88, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
118     TestProcessor expected = new TestProcessor();
119     expected.floatElement(TestProcessor.ID_DURATION, -2.0);
120     assertEvents(input, expected.events);
121   }
122 
123   @Test
binaryElement()124   public void binaryElement() throws IOException {
125     ExtractorInput input =
126         createTestInput(0xA3, 0x88, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
127     TestProcessor expected = new TestProcessor();
128     expected.binaryElement(
129         TestProcessor.ID_SIMPLE_BLOCK,
130         8,
131         createTestInput(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08));
132     assertEvents(input, expected.events);
133   }
134 
assertEvents(ExtractorInput input, List<String> expectedEvents)135   private static void assertEvents(ExtractorInput input, List<String> expectedEvents)
136       throws IOException {
137     DefaultEbmlReader reader = new DefaultEbmlReader();
138     TestProcessor output = new TestProcessor();
139     reader.init(output);
140 
141     // We expect the number of successful reads to equal the number of expected events.
142     for (int i = 0; i < expectedEvents.size(); i++) {
143       assertThat(reader.read(input)).isTrue();
144     }
145     // The next read should be unsuccessful.
146     assertThat(reader.read(input)).isFalse();
147     // Check that we really did get to the end of input.
148     assertThat(input.readFully(new byte[1], 0, 1, true)).isFalse();
149 
150     assertThat(output.events).containsExactlyElementsIn(expectedEvents).inOrder();
151   }
152 
153   /**
154    * Helper to build an {@link ExtractorInput} from byte data.
155    *
156    * @param data Zero or more integers with values between {@code 0x00} and {@code 0xFF}.
157    * @return An {@link ExtractorInput} from which the data can be read.
158    */
createTestInput(int... data)159   private static ExtractorInput createTestInput(int... data) {
160     return new FakeExtractorInput.Builder()
161         .setData(TestUtil.createByteArray(data))
162         .setSimulateUnknownLength(true)
163         .build();
164   }
165 
166   /** An {@link EbmlProcessor} that records each event callback. */
167   private static final class TestProcessor implements EbmlProcessor {
168 
169     // Element IDs
170     private static final int ID_EBML = 0x1A45DFA3;
171     private static final int ID_EBML_READ_VERSION = 0x42F7;
172     private static final int ID_DOC_TYPE = 0x4282;
173     private static final int ID_DOC_TYPE_READ_VERSION = 0x4285;
174 
175     private static final int ID_SEGMENT = 0x18538067;
176     private static final int ID_DURATION = 0x4489;
177     private static final int ID_SIMPLE_BLOCK = 0xA3;
178 
179     private final List<String> events = new ArrayList<>();
180 
181     @Override
182     @EbmlProcessor.ElementType
getElementType(int id)183     public int getElementType(int id) {
184       switch (id) {
185         case ID_EBML:
186         case ID_SEGMENT:
187           return EbmlProcessor.ELEMENT_TYPE_MASTER;
188         case ID_EBML_READ_VERSION:
189         case ID_DOC_TYPE_READ_VERSION:
190           return EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT;
191         case ID_DOC_TYPE:
192           return EbmlProcessor.ELEMENT_TYPE_STRING;
193         case ID_SIMPLE_BLOCK:
194           return EbmlProcessor.ELEMENT_TYPE_BINARY;
195         case ID_DURATION:
196           return EbmlProcessor.ELEMENT_TYPE_FLOAT;
197         default:
198           return EbmlProcessor.ELEMENT_TYPE_UNKNOWN;
199       }
200     }
201 
202     @Override
isLevel1Element(int id)203     public boolean isLevel1Element(int id) {
204       return false;
205     }
206 
207     @Override
startMasterElement(int id, long contentPosition, long contentSize)208     public void startMasterElement(int id, long contentPosition, long contentSize) {
209       events.add(formatEvent(id, "start contentPosition=" + contentPosition
210           + " contentSize=" + contentSize));
211     }
212 
213     @Override
endMasterElement(int id)214     public void endMasterElement(int id) {
215       events.add(formatEvent(id, "end"));
216     }
217 
218     @Override
integerElement(int id, long value)219     public void integerElement(int id, long value) {
220       events.add(formatEvent(id, "integer=" + value));
221     }
222 
223     @Override
floatElement(int id, double value)224     public void floatElement(int id, double value) {
225       events.add(formatEvent(id, "float=" + value));
226     }
227 
228     @Override
stringElement(int id, String value)229     public void stringElement(int id, String value) {
230       events.add(formatEvent(id, "string=" + value));
231     }
232 
233     @Override
binaryElement(int id, int contentSize, ExtractorInput input)234     public void binaryElement(int id, int contentSize, ExtractorInput input) throws IOException {
235       byte[] bytes = new byte[contentSize];
236       input.readFully(bytes, 0, contentSize);
237       events.add(formatEvent(id, "bytes=" + Arrays.toString(bytes)));
238     }
239 
formatEvent(int id, String event)240     private static String formatEvent(int id, String event) {
241       return "[" + Integer.toHexString(id) + "] " + event;
242     }
243 
244   }
245 
246 }
247