|
1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "constants.h" |
|
17 #include "parsers.h" |
|
18 |
|
19 // |
|
20 // Matroska 'XML' tags. |
|
21 // |
|
22 static const TUint32 KEBML = 0x1A45DFA3; |
|
23 static const TUint32 KSegment = 0x18538067; |
|
24 static const TUint32 KTracks = 0x1654AE6B; |
|
25 static const TUint32 KTrackEntry = 0xAE; |
|
26 static const TUint32 KTrackType = 0x83; |
|
27 static const TUint32 KVoid = 0xEC; |
|
28 static const TUint32 KSeekHead = 0x114D9B74; |
|
29 static const TUint32 KSegmentInfo = 0x1549A966; |
|
30 static const TUint32 KCluster = 0x1F43B675; |
|
31 static const TUint32 KCues = 0x1C53BB6B; |
|
32 static const TUint32 KAttachments = 0x1941A469; |
|
33 static const TUint32 KChapters = 0x1043A770; |
|
34 static const TUint32 KTags = 0x1254C367; |
|
35 |
|
36 // |
|
37 // Matroska Track Types. |
|
38 // |
|
39 static const TUint8 KTrackTypeVideo = 0x01; |
|
40 |
|
41 #define KMatroskaConfidenceMask 0x07 // 00000111 |
|
42 #define KMatroskaEBMLBit KBit1 |
|
43 #define KMatroskaSegmentBit KBit2 |
|
44 #define KMatroskaVideoBit KBit3 |
|
45 |
|
46 |
|
47 // |
|
48 // This truth table maps the following flags to confidence levels. |
|
49 // |
|
50 // A: Extension match. |
|
51 // B: EBML tag found. |
|
52 // C: Segment tag found. |
|
53 // |
|
54 // C B A -> Confidence |
|
55 // -------------------- |
|
56 // 0 0 0 -> NotRecognised |
|
57 // 0 0 1 -> EPossible |
|
58 // 0 1 0 -> EPossible |
|
59 // 0 1 1 -> EProbable |
|
60 // 1 0 0 -> ENotRecognised (EBML should be present) |
|
61 // 1 0 1 -> ENotRecognised (EBML should be present) |
|
62 // 1 1 0 -> EProbable |
|
63 // 1 1 1 -> ECertain |
|
64 // |
|
65 static const TInt KMatroskaFlagsToConfidence[8] = |
|
66 { |
|
67 KConfNotRecognised, |
|
68 KConfPossible, |
|
69 KConfPossible, |
|
70 KConfProbable, |
|
71 KConfNotRecognised, |
|
72 KConfNotRecognised, |
|
73 KConfProbable, |
|
74 KConfCertain |
|
75 }; |
|
76 |
|
77 static const TInt KMatExtensionOnlyIndex = 1; // See truth table. |
|
78 |
|
79 |
|
80 typedef struct |
|
81 { |
|
82 const TText* iExt; |
|
83 const TText8* iMime; |
|
84 } |
|
85 TMatroskaExt; |
|
86 |
|
87 // |
|
88 // Known Matroska extensions and their corresponding MIME-types. |
|
89 // |
|
90 static const TMatroskaExt KMatroskaExt[] = |
|
91 { |
|
92 {KExtMAT_A, KMimeMAT_A }, |
|
93 {KExtMAT_V, KMimeMAT_V } |
|
94 }; |
|
95 |
|
96 static const TInt KMatroskaExtCount = sizeof(KMatroskaExt) / sizeof(TMatroskaExt); |
|
97 |
|
98 |
|
99 // |
|
100 // |
|
101 // |
|
102 TMatroskaParser::TMatroskaParser(CReader& aReader, TFlags& aFlags) |
|
103 : iReader(aReader), |
|
104 iFlags(aFlags) |
|
105 { |
|
106 } |
|
107 |
|
108 |
|
109 // |
|
110 // |
|
111 // |
|
112 const TText8* TMatroskaParser::MatchExtension(const TDesC& aExt) |
|
113 { |
|
114 for (TInt i = 0; i < KMatroskaExtCount; i++) |
|
115 { |
|
116 if (aExt.MatchF(TPtrC(KMatroskaExt[i].iExt)) != KErrNotFound) |
|
117 { |
|
118 return KMatroskaExt[i].iMime; |
|
119 } |
|
120 } |
|
121 |
|
122 return NULL; |
|
123 } |
|
124 |
|
125 |
|
126 // |
|
127 // This function calls the parser and turns the results |
|
128 // into a MIME-type. |
|
129 // |
|
130 void TMatroskaParser::DoRecognise(const TDesC& aExt, CReader& aReader, TMatch& aMatch) |
|
131 { |
|
132 TFlags flags; |
|
133 TMatroskaParser parser(aReader, flags); |
|
134 |
|
135 // Try to match the extension. |
|
136 const TText8* extMime = parser.MatchExtension(aExt); |
|
137 if (extMime != NULL) |
|
138 { |
|
139 flags.SetExtensionFlag(); |
|
140 } |
|
141 |
|
142 // Try to parse the content. |
|
143 TRAP_IGNORE(parser.ParseL()); |
|
144 TInt confIndex = flags.GetBitField(KMatroskaConfidenceMask); |
|
145 aMatch.iConfidence = KMatroskaFlagsToConfidence[confIndex]; |
|
146 if (aMatch.iConfidence != KConfNotRecognised) |
|
147 { |
|
148 // If any header data has been recognised trust that, |
|
149 // otherwise go with the extension. |
|
150 if (confIndex == KMatExtensionOnlyIndex) |
|
151 { |
|
152 aMatch.iMime = extMime; |
|
153 } |
|
154 else |
|
155 { |
|
156 aMatch.iMime = (flags.GetBitField(KMatroskaVideoBit) ? KMimeMAT_V : KMimeMAT_A); |
|
157 } |
|
158 } |
|
159 } |
|
160 |
|
161 |
|
162 // |
|
163 // This function does the parsing. |
|
164 // |
|
165 void TMatroskaParser::ParseL() |
|
166 { |
|
167 TUint64 id; |
|
168 TInt64 size; |
|
169 TBool haveElement = EFalse; |
|
170 |
|
171 // Assume there's video content if we only have buffer data. |
|
172 if (iReader.Type() == CReader::EBuffer) |
|
173 { |
|
174 iFlags.SetBit(KMatroskaVideoBit); |
|
175 } |
|
176 |
|
177 FOREVER |
|
178 { |
|
179 if (!haveElement) |
|
180 { |
|
181 ReadElementL(id, size); |
|
182 } |
|
183 |
|
184 switch (id) |
|
185 { |
|
186 case KEBML: |
|
187 iFlags.SetBit(KMatroskaEBMLBit); |
|
188 break; |
|
189 |
|
190 case KSegment: |
|
191 haveElement = ReadSegmentL(id, size); |
|
192 break; |
|
193 |
|
194 default: |
|
195 // Skip it. |
|
196 iReader.SeekL(size); |
|
197 } |
|
198 } |
|
199 } |
|
200 |
|
201 |
|
202 // |
|
203 // This function returns ETrue if the aNextID and aNextSize parameters |
|
204 // contain valid values, EFalse otherwise. |
|
205 // |
|
206 TBool TMatroskaParser::ReadSegmentL(TUint64& aNextID, TInt64& aNextSize) |
|
207 { |
|
208 TUint64 id; |
|
209 TInt64 size; |
|
210 TBool videoContent = EFalse; |
|
211 |
|
212 aNextID = 0; |
|
213 aNextSize = 0; |
|
214 |
|
215 iFlags.SetBit(KMatroskaSegmentBit); |
|
216 |
|
217 while (!videoContent) |
|
218 { |
|
219 ReadElementL(id, size); |
|
220 |
|
221 switch (id) |
|
222 { |
|
223 case KTracks: |
|
224 videoContent = ReadTrackL(size); |
|
225 break; |
|
226 |
|
227 case KSeekHead: |
|
228 case KSegmentInfo: |
|
229 case KCluster: |
|
230 case KCues: |
|
231 case KAttachments: |
|
232 case KChapters: |
|
233 case KTags: |
|
234 iReader.SeekL(size); |
|
235 break; |
|
236 |
|
237 default: |
|
238 // Unrecognised element id. Pass it back to the caller |
|
239 aNextID = id; |
|
240 aNextSize = size; |
|
241 return ETrue; |
|
242 } |
|
243 } |
|
244 |
|
245 // Tell the caller they must read the next element themselves. |
|
246 return EFalse; |
|
247 } |
|
248 |
|
249 |
|
250 // |
|
251 // The Track. This lets us know if there's video content present. |
|
252 // |
|
253 TBool TMatroskaParser::ReadTrackL(TInt64 aTrackSize) |
|
254 { |
|
255 TUint64 id; |
|
256 TInt64 size; |
|
257 TInt startPos = iReader.Position(); |
|
258 |
|
259 while (iReader.Position() - startPos < aTrackSize) |
|
260 { |
|
261 ReadElementL(id, size); |
|
262 |
|
263 switch (id) |
|
264 { |
|
265 case KTrackEntry: |
|
266 break; |
|
267 |
|
268 case KTrackType: |
|
269 TUint8 trackType; |
|
270 iReader.ReadByteL(trackType); |
|
271 if (trackType == KTrackTypeVideo) |
|
272 { |
|
273 // We found video content so we can stop parsing. |
|
274 iFlags.SetBit(KMatroskaVideoBit); |
|
275 return ETrue; |
|
276 } |
|
277 break; |
|
278 |
|
279 default: |
|
280 iReader.SeekL(size); |
|
281 } |
|
282 } |
|
283 |
|
284 return EFalse; |
|
285 } |
|
286 |
|
287 |
|
288 // |
|
289 // |
|
290 // |
|
291 void TMatroskaParser::ReadElementL(TUint64& aElementID, TInt64& aSize) |
|
292 { |
|
293 do |
|
294 { |
|
295 aElementID = ReadDataL(); |
|
296 aSize = ReadDataL(ETrue); |
|
297 |
|
298 // Void elements are used for padding and |
|
299 // can be ignored. |
|
300 if (aElementID == KVoid) |
|
301 { |
|
302 iReader.SeekL(aSize); |
|
303 } |
|
304 } |
|
305 while (aElementID == KVoid); |
|
306 } |
|
307 |
|
308 |
|
309 // |
|
310 // |
|
311 // |
|
312 TUint64 TMatroskaParser::ReadDataL(TBool aTurnOffHighestSetBit) |
|
313 { |
|
314 TUint64 retval; |
|
315 TUint8 byte; |
|
316 TUint8 mask = 0x80; // [1000 0000]. It will be shifted right 1 in each 'i' iteration. |
|
317 TUint8 size = 1; // It will be incremented in each 'i' iteration. |
|
318 |
|
319 iReader.ReadByteL(byte); |
|
320 |
|
321 for (TInt i = 0; i < sizeof(TUint64); i++) |
|
322 { |
|
323 if (byte & mask) |
|
324 { |
|
325 retval = byte; |
|
326 if (aTurnOffHighestSetBit) |
|
327 { |
|
328 retval &= ~mask; // Turn off the highest set bit. |
|
329 } |
|
330 |
|
331 // Now read the real data. |
|
332 // Start from 1 because we've already read a byte. |
|
333 for (TInt j = 1; j < size; j++) |
|
334 { |
|
335 iReader.ReadByteL(byte); |
|
336 retval <<= 8; |
|
337 retval |= byte; |
|
338 } |
|
339 |
|
340 return retval; |
|
341 } |
|
342 else |
|
343 { |
|
344 mask >>= 1; |
|
345 size++; |
|
346 } |
|
347 } |
|
348 |
|
349 User::Leave(KErrCorrupt); |
|
350 return 0; // Keep the compiler happy. |
|
351 } |
|
352 |