1 /*
2 * $Id$
3 * Copyright (c) 2010-2015, Matroska (non-profit organisation)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Matroska assocation nor the
14 * names of its contributors may be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY the Matroska association ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL The Matroska Foundation BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include "mkvalidator_stdafx.h"
29 #include "mkvalidator_project.h"
30 #ifndef CONFIG_EBML_UNICODE
31 #define CONFIG_EBML_UNICODE
32 #endif
33 #include "matroska/matroska.h"
34 #include "matroska/matroska_sem.h"
35
36 /*!
37 * \todo verify the track timecode scale is not null
38 * \todo verify that the size of frames inside a lace is legit (ie the remaining size for the last must be > 0)
39 * \todo verify that items with a limited set of values don't use other values
40 * \todo verify that timecodes for each track are increasing (for keyframes and p frames)
41 * \todo optionally show the use of deprecated elements
42 * \todo support concatenated segments
43 */
44
45 static textwriter *StdErr = NULL;
46 static ebml_master *RSegmentInfo = NULL, *RTrackInfo = NULL, *RChapters = NULL, *RTags = NULL, *RCues = NULL, *RAttachments = NULL, *RSeekHead = NULL, *RSeekHead2 = NULL;
47 static array RClusters;
48 static array Tracks;
49 static size_t TrackMax=0;
50 static bool_t Warnings = 1;
51 static bool_t Live = 0;
52 static bool_t Details = 0;
53 static bool_t DivX = 0;
54 static bool_t Quiet = 0;
55 static timecode_t MinTime = INVALID_TIMECODE_T, MaxTime = INVALID_TIMECODE_T;
56 static timecode_t ClusterTime = INVALID_TIMECODE_T;
57
58 // some macros for code readability
59 #define EL_Pos(elt) EBML_ElementPosition((const ebml_element*)elt)
60 #define EL_Int(elt) EBML_IntegerValue((const ebml_integer*)elt)
61 #define EL_Type(elt, type) EBML_ElementIsType((const ebml_element*)elt, type)
62 #define EL_DataSize(elt) EBML_ElementDataSize((const ebml_element*)elt, 1)
63
64 typedef struct track_info
65 {
66 int Num;
67 int Kind;
68 filepos_t DataLength;
69 ebml_string *CodecID;
70
71 } track_info;
72
73 #ifdef TARGET_WIN
74 #include <windows.h>
DebugMessage(const tchar_t * Msg,...)75 void DebugMessage(const tchar_t* Msg,...)
76 {
77 #if !defined(NDEBUG) || defined(LOGFILE) || defined(LOGTIME)
78 va_list Args;
79 tchar_t Buffer[1024],*s=Buffer;
80
81 va_start(Args,Msg);
82 vstprintf_s(Buffer,TSIZEOF(Buffer), Msg, Args);
83 va_end(Args);
84 tcscat_s(Buffer,TSIZEOF(Buffer),T("\r\n"));
85 #endif
86
87 #ifdef LOGTIME
88 {
89 tchar_t timed[1024];
90 SysTickToString(timed,TSIZEOF(timed),GetTimeTick(),1,1,0);
91 stcatprintf_s(timed,TSIZEOF(timed),T(" %s"),s);
92 s = timed;
93 }
94 #endif
95
96 #if !defined(NDEBUG)
97 OutputDebugString(s);
98 #endif
99
100 #if defined(LOGFILE)
101 {
102 static FILE* f=NULL;
103 static char s8[1024];
104 size_t i;
105 if (!f)
106 #if defined(TARGET_WINCE)
107 {
108 tchar_t DocPath[MAXPATH];
109 char LogPath[MAXPATH];
110 charconv *ToStr = CharConvOpen(NULL,CHARSET_DEFAULT);
111 GetDocumentPath(NULL,DocPath,TSIZEOF(DocPath),FTYPE_LOG); // more visible via ActiveSync
112 if (!DocPath[0])
113 tcscpy_s(DocPath,TSIZEOF(DocPath),T("\\My Documents"));
114 if (!PathIsFolder(NULL,DocPath))
115 FolderCreate(NULL,DocPath);
116 tcscat_s(DocPath,TSIZEOF(DocPath),T("\\corelog.txt"));
117 CharConvST(ToStr,LogPath,sizeof(LogPath),DocPath);
118 CharConvClose(ToStr);
119 f=fopen(LogPath,"a+b");
120 if (!f)
121 f=fopen("\\corelog.txt","a+b");
122 }
123 #else
124 f=fopen("\\corelog.txt","a+b");
125 #endif
126 for (i=0;s[i];++i)
127 s8[i]=(char)s[i];
128 s8[i]=0;
129 fputs(s8,f);
130 fflush(f);
131 }
132 #endif
133 }
134 #endif
135
GetProfileName(size_t ProfileNum)136 static const tchar_t *GetProfileName(size_t ProfileNum)
137 {
138 static const tchar_t *Profile[7] = {T("unknown"), T("matroska v1"), T("matroska v2"), T("matroska v3"), T("webm"), T("matroska+DivX"), T("matroska v4")};
139 switch (ProfileNum)
140 {
141 default: return Profile[0];
142 case PROFILE_MATROSKA_V1: return Profile[1];
143 case PROFILE_MATROSKA_V2: return Profile[2];
144 case PROFILE_MATROSKA_V3: return Profile[3];
145 case PROFILE_WEBM: return Profile[4];
146 case PROFILE_DIVX: return Profile[5];
147 case PROFILE_MATROSKA_V4: return Profile[6];
148 }
149 }
150
OutputError(int ErrCode,const tchar_t * ErrString,...)151 static int OutputError(int ErrCode, const tchar_t *ErrString, ...)
152 {
153 tchar_t Buffer[MAXLINE];
154 va_list Args;
155 va_start(Args,ErrString);
156 vstprintf_s(Buffer,TSIZEOF(Buffer), ErrString, Args);
157 va_end(Args);
158 TextPrintf(StdErr,T("\rERR%03X: %s\r\n"),ErrCode,Buffer);
159 return -ErrCode;
160 }
161
OutputWarning(int ErrCode,const tchar_t * ErrString,...)162 static void OutputWarning(int ErrCode, const tchar_t *ErrString, ...)
163 {
164 if (Warnings)
165 {
166 tchar_t Buffer[MAXLINE];
167 va_list Args;
168 va_start(Args,ErrString);
169 vstprintf_s(Buffer,TSIZEOF(Buffer), ErrString, Args);
170 va_end(Args);
171 TextPrintf(StdErr,T("\rWRN%03X: %s\r\n"),ErrCode,Buffer);
172 }
173 }
174
CheckUnknownElements(ebml_element * Elt)175 static filepos_t CheckUnknownElements(ebml_element *Elt)
176 {
177 tchar_t IdStr[32], String[MAXPATH];
178 ebml_element *SubElt;
179 filepos_t VoidAmount = 0;
180 for (SubElt = EBML_MasterChildren(Elt); SubElt; SubElt = EBML_MasterNext(SubElt))
181 {
182 if (Node_IsPartOf(SubElt,EBML_DUMMY_ID))
183 {
184 EBML_ElementGetName(Elt,String,TSIZEOF(String));
185 EBML_IdToString(IdStr,TSIZEOF(IdStr),EBML_ElementClassID(SubElt));
186 OutputWarning(12,T("Unknown element in %s %s at %") TPRId64 T(" (size %") TPRId64 T(" total %") TPRId64 T(")"),String,IdStr,EL_Pos(SubElt),EL_DataSize(SubElt), EBML_ElementFullSize(SubElt, 0));
187 }
188 else if (Node_IsPartOf(SubElt,EBML_VOID_CLASS))
189 {
190 VoidAmount = EBML_ElementFullSize(SubElt,0);
191 }
192 else if (Node_IsPartOf(SubElt,EBML_MASTER_CLASS))
193 {
194 VoidAmount += CheckUnknownElements(SubElt);
195 }
196 }
197 return VoidAmount;
198 }
199
gcd(int64_t a,int64_t b)200 static int64_t gcd(int64_t a, int64_t b)
201 {
202 for (;;)
203 {
204 int64_t c = a % b;
205 if(!c) return b;
206 a = b;
207 b = c;
208 }
209 }
210
CheckVideoTrack(ebml_master * Track,int TrackNum,int ProfileNum)211 static int CheckVideoTrack(ebml_master *Track, int TrackNum, int ProfileNum)
212 {
213 int Result = 0;
214 ebml_element *Elt, *PixelW, *PixelH;
215 ebml_integer *Unit;
216 ebml_master *Video;
217 Video = (ebml_master*)EBML_MasterFindChild(Track,&MATROSKA_ContextVideo);
218 if (!Video)
219 Result = OutputError(0xE0,T("Video track at %") TPRId64 T(" is missing a Video element"),EL_Pos(Track));
220 // check the DisplayWidth and DisplayHeight are correct
221 else
222 {
223 int64_t DisplayW = 0,DisplayH = 0;
224 PixelW = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelWidth);
225 if (!PixelW)
226 Result |= OutputError(0xE1,T("Video track #%d at %") TPRId64 T(" has no pixel width"),TrackNum,EL_Pos(Track));
227 PixelH = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelHeight);
228 if (!PixelH)
229 Result |= OutputError(0xE2,T("Video track #%d at %") TPRId64 T(" has no pixel height"),TrackNum,EL_Pos(Track));
230
231 Unit = (ebml_integer*)EBML_MasterGetChild(Video,&MATROSKA_ContextDisplayUnit);
232 assert(Unit!=NULL);
233
234 Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextDisplayWidth);
235 if (Elt)
236 DisplayW = EL_Int(Elt);
237 else if (EL_Int(Unit)!=MATROSKA_DISPLAY_UNIT_PIXEL)
238 Result |= OutputError(0xE2,T("Video track #%d at %") TPRId64 T(" has an implied non pixel width"),TrackNum,EL_Pos(Track));
239 else if (PixelW)
240 DisplayW = EL_Int(PixelW);
241
242 Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextDisplayHeight);
243 if (Elt)
244 DisplayH = EL_Int(Elt);
245 else if (EL_Int(Unit)!=MATROSKA_DISPLAY_UNIT_PIXEL)
246 Result |= OutputError(0xE2,T("Video track #%d at %") TPRId64 T(" has an implied non pixel height"),TrackNum,EL_Pos(Track));
247 else if (PixelH)
248 DisplayH = EL_Int(PixelH);
249
250 if (DisplayH==0)
251 Result |= OutputError(0xE7,T("Video track #%d at %") TPRId64 T(" has a null display height"),TrackNum,EL_Pos(Track));
252 if (DisplayW==0)
253 Result |= OutputError(0xE7,T("Video track #%d at %") TPRId64 T(" has a null display width"),TrackNum,EL_Pos(Track));
254
255 if (EL_Int(Unit)==MATROSKA_DISPLAY_UNIT_PIXEL && PixelW && PixelH)
256 {
257 // check if the pixel sizes appear valid
258 if (DisplayW < EL_Int(PixelW) && DisplayH < EL_Int(PixelH))
259 {
260 int Serious = gcd(DisplayW,DisplayH)==1; // the DAR values were reduced as much as possible
261 if (DisplayW*EL_Int(PixelH) == DisplayH*EL_Int(PixelW))
262 Serious++; // same aspect ratio as the source
263 if (8*DisplayW <= EL_Int(PixelW) && 8*DisplayH <= EL_Int(PixelH))
264 Serious+=2; // too much shrinking compared to the original pixels
265 if (ProfileNum!=PROFILE_WEBM)
266 --Serious; // in Matroska it's tolerated as it's been operating like that for a while
267
268 if (Serious>2)
269 Result |= OutputError(0xE3,T("The output pixels for Video track #%d seem wrong %") TPRId64 T("x%") TPRId64 T("px from %") TPRId64 T("x%") TPRId64,TrackNum,DisplayW,DisplayH,EL_Int(PixelW),EL_Int(PixelH));
270 else if (Serious)
271 OutputWarning(0xE3,T("The output pixels for Video track #%d seem wrong %") TPRId64 T("x%") TPRId64 T("px from %") TPRId64 T("x%") TPRId64,TrackNum,DisplayW,DisplayH,EL_Int(PixelW),EL_Int(PixelH));
272 }
273 }
274
275 if (EL_Int(Unit)==MATROSKA_DISPLAY_UNIT_DAR)
276 {
277 // crop values should never exist
278 Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextPixelCropTop);
279 if (Elt)
280 Result |= OutputError(0xE4,T("Video track #%d is using unconstrained aspect ratio and has top crop at %") TPRId64,TrackNum,EL_Pos(Elt));
281 Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextPixelCropBottom);
282 if (Elt)
283 Result |= OutputError(0xE4,T("Video track #%d is using unconstrained aspect ratio and has bottom crop at %") TPRId64,TrackNum,EL_Pos(Elt));
284 Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextPixelCropLeft);
285 if (Elt)
286 Result |= OutputError(0xE4,T("Video track #%d is using unconstrained aspect ratio and has left crop at %") TPRId64,TrackNum,EL_Pos(Elt));
287 Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextPixelCropRight);
288 if (Elt)
289 Result |= OutputError(0xE4,T("Video track #%d is using unconstrained aspect ratio and has right crop at %") TPRId64,TrackNum,EL_Pos(Elt));
290
291 if (PixelW && DisplayW == EL_Int(PixelW))
292 OutputWarning(0xE7,T("DisplayUnit seems to be pixels not aspect-ratio for Video track #%d %") TPRId64 T("px width from %") TPRId64,TrackNum,DisplayW,EL_Int(PixelW));
293 if (PixelH && DisplayH == EL_Int(PixelH))
294 OutputWarning(0xE7,T("DisplayUnit seems to be pixels not aspect-ratio for Video track #%d %") TPRId64 T("px height from %") TPRId64,TrackNum,DisplayH,EL_Int(PixelH));
295 }
296 else
297 {
298 // crop values should be less than the extended value
299 PixelW = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelCropTop);
300 PixelH = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelCropBottom);
301 if (EL_Int(PixelW) + EL_Int(PixelH) >= DisplayH)
302 Result |= OutputError(0xE5,T("Video track #%d is cropping too many vertical pixels %") TPRId64 T(" vs %") TPRId64 T(" + %") TPRId64,TrackNum, DisplayH, EL_Int(PixelW), EL_Int(PixelH));
303
304 PixelW = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelCropLeft);
305 PixelH = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelCropRight);
306 if (EL_Int(PixelW) + EL_Int(PixelH) >= DisplayW)
307 Result |= OutputError(0xE6,T("Video track #%d is cropping too many horizontal pixels %") TPRId64 T(" vs %") TPRId64 T(" + %") TPRId64,TrackNum, DisplayW, EL_Int(PixelW), EL_Int(PixelH));
308 }
309 }
310 return Result;
311 }
312
CheckTracks(ebml_master * Tracks,int ProfileNum)313 static int CheckTracks(ebml_master *Tracks, int ProfileNum)
314 {
315 ebml_master *Track;
316 ebml_element *TrackType, *TrackNum, *Elt, *Elt2;
317 ebml_string *CodecID;
318 tchar_t CodecName[MAXPATH],String[MAXPATH];
319 int Result = 0;
320 Track = (ebml_master*)EBML_MasterFindChild(Tracks, &MATROSKA_ContextTrackEntry);
321 while (Track)
322 {
323 // check if the codec is valid for the profile
324 TrackNum = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackNumber);
325 if (TrackNum)
326 {
327 TrackType = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackType);
328 CodecID = (ebml_string*)EBML_MasterGetChild(Track, &MATROSKA_ContextCodecID);
329 if (!CodecID)
330 Result |= OutputError(0x300,T("Track #%d has no CodecID defined"),(int)EL_Int(TrackNum));
331 else if (!TrackType)
332 Result |= OutputError(0x301,T("Track #%d has no type defined"),(int)EL_Int(TrackNum));
333 else
334 {
335 EBML_StringGet(CodecID,CodecName,TSIZEOF(CodecName));
336 tcscpy_s(String,TSIZEOF(String),CodecName);
337 if (tcscmp(tcsupr(String),CodecName)!=0)
338 OutputWarning(0x307,T("Track #%d codec '%s' should be uppercase"),(int)EL_Int(TrackNum),CodecName);
339 if (tcslen(String)<3 || String[1]!='_' || (String[0]!='A' && String[0]!='V' && String[0]!='S' && String[0]!='B'))
340 OutputWarning(0x308,T("Track #%d codec '%s' doesn't appear to be valid"),(int)EL_Int(TrackNum),String);
341
342 // check that the audio frequencies are not 0
343 if (EL_Int(TrackType) == TRACK_TYPE_AUDIO)
344 {
345 Elt = EBML_MasterGetChild(Track, &MATROSKA_ContextAudio);
346 if (Elt==NULL)
347 Result |= OutputError(0x309,T("Audio Track #%d has no audio settings"),(int)EL_Int(TrackNum));
348 else
349 {
350 Elt2 = EBML_MasterFindChild(Elt, &MATROSKA_ContextOutputSamplingFrequency);
351 if (Elt2 && EBML_FloatValue((ebml_float*)Elt2)==0)
352 Result |= OutputError(0x30A,T("Audio Track #%d has a null output sampling frequency"),(int)EL_Int(TrackNum));
353 Elt2 = EBML_MasterFindChild(Elt, &MATROSKA_ContextSamplingFrequency);
354 if (Elt2 && EBML_FloatValue((ebml_float*)Elt2)==0)
355 Result |= OutputError(0x30A,T("Audio Track #%d has a null sampling frequency"),(int)EL_Int(TrackNum));
356 }
357 }
358
359 if (ProfileNum==PROFILE_WEBM)
360 {
361 if (EL_Int(TrackType) != TRACK_TYPE_AUDIO && EL_Int(TrackType) != TRACK_TYPE_VIDEO)
362 Result |= OutputError(0x302,T("Track #%d type %d not supported for profile '%s'"),(int)EL_Int(TrackNum),(int)EL_Int(TrackType),GetProfileName(ProfileNum));
363 if (CodecID)
364 {
365 if (EL_Int(TrackType) == TRACK_TYPE_AUDIO)
366 {
367 if (!tcsisame_ascii(CodecName,T("A_VORBIS")))
368 Result |= OutputError(0x303,T("Track #%d codec %s not supported for profile '%s'"),(int)EL_Int(TrackNum),CodecName,GetProfileName(ProfileNum));
369 }
370 else if (EL_Int(TrackType) == TRACK_TYPE_VIDEO)
371 {
372 if (!tcsisame_ascii(CodecName,T("V_VP8")))
373 Result |= OutputError(0x304,T("Track #%d codec %s not supported for profile '%s'"),(int)EL_Int(TrackNum),CodecName,GetProfileName(ProfileNum));
374 }
375 }
376 }
377 }
378 }
379
380 // check if the AttachmentLink values match existing attachments
381 TrackType = EBML_MasterFindChild(Track, &MATROSKA_ContextAttachmentLink);
382 while (TrackType)
383 {
384 if (!RAttachments)
385 {
386 if (TrackNum)
387 Result |= OutputError(0x305,T("Track #%d has attachment links but not attachments in the file"),(int)EL_Int(TrackNum));
388 else
389 Result |= OutputError(0x305,T("Track at %") TPRId64 T(" has attachment links but not attachments in the file"),EL_Pos(Track));
390 break;
391 }
392
393 for (Elt=EBML_MasterChildren(RAttachments);Elt;Elt=EBML_MasterNext(Elt))
394 {
395 if (EL_Type(Elt, &MATROSKA_ContextAttachedFile))
396 {
397 Elt2 = EBML_MasterFindChild((ebml_master*)Elt, &MATROSKA_ContextFileUID);
398 if (Elt2 && EL_Int(Elt2) == EL_Int(TrackType))
399 break;
400 }
401 }
402 if (!Elt)
403 {
404 if (TrackNum)
405 Result |= OutputError(0x306,T("Track #%d attachment link UID 0x%") TPRIx64 T(" not found in attachments"),(int)EL_Int(TrackNum),EL_Int(TrackType));
406 else
407 Result |= OutputError(0x306,T("Track at %") TPRId64 T(" attachment link UID 0x%") TPRIx64 T(" not found in attachments"),EL_Pos(Track),EL_Int(TrackType));
408 }
409
410 TrackType = EBML_MasterFindNextElt(Track, TrackType, 0, 0);
411 }
412
413 Track = (ebml_master*)EBML_MasterFindNextElt(Tracks, (ebml_element*)Track, 0, 0);
414 }
415 return Result;
416 }
417
418 struct profile_check
419 {
420 int *Result;
421 const ebml_element *Parent;
422 const tchar_t *EltName;
423 int ProfileMask;
424 };
425
ProfileCallback(struct profile_check * check,int type,const tchar_t * ClassName,const ebml_element * Elt)426 static bool_t ProfileCallback(struct profile_check *check, int type, const tchar_t *ClassName, const ebml_element* Elt)
427 {
428 if (type==MASTER_CHECK_PROFILE_INVALID)
429 *check->Result |= OutputError(0x201,T("Invalid '%s' for profile '%s' in %s at %") TPRId64,ClassName,GetProfileName(check->ProfileMask),check->EltName,EL_Pos(check->Parent));
430 else if (type==MASTER_CHECK_MISSING_MANDATORY)
431 *check->Result |= OutputError(0x200,T("Missing element '%s' in %s at %") TPRId64, ClassName,check->EltName,EL_Pos(check->Parent));
432 else if (type==MASTER_CHECK_MULTIPLE_UNIQUE)
433 *check->Result |= OutputError(0x202,T("Unique element '%s' in %s at %") TPRId64 T(" found more than once at %") TPRId64, ClassName,check->EltName,EL_Pos(check->Parent),EL_Pos(Elt));
434 return 0; // don't remove anything
435 }
436
CheckProfileViolation(ebml_element * Elt,int ProfileMask)437 static int CheckProfileViolation(ebml_element *Elt, int ProfileMask)
438 {
439 int Result = 0;
440 tchar_t String[MAXPATH];
441 ebml_element *SubElt;
442 struct profile_check Checker;
443
444 if (Node_IsPartOf(Elt,EBML_MASTER_CLASS))
445 {
446 EBML_ElementGetName(Elt,String,TSIZEOF(String));
447 if (!EBML_MasterIsChecksumValid((ebml_master*)Elt))
448 Result |= OutputError(0x203,T("Invalid checksum for element '%s' at %") TPRId64,String,EL_Pos(Elt));
449
450 Checker.EltName = String;
451 Checker.ProfileMask = ProfileMask;
452 Checker.Parent = Elt;
453 Checker.Result = &Result;
454 EBML_MasterCheckContext((ebml_master*)Elt, ProfileMask, ProfileCallback, &Checker);
455
456 for (SubElt = EBML_MasterChildren(Elt); SubElt; SubElt = EBML_MasterNext(SubElt))
457 if (Node_IsPartOf(SubElt,EBML_MASTER_CLASS))
458 Result |= CheckProfileViolation(SubElt,ProfileMask);
459 }
460
461 return Result;
462 }
463
CheckSeekHead(ebml_master * SeekHead)464 static int CheckSeekHead(ebml_master *SeekHead)
465 {
466 int Result = 0;
467 matroska_seekpoint *RLevel1 = (matroska_seekpoint*)EBML_MasterFindChild(SeekHead, &MATROSKA_ContextSeek);
468 bool_t BSegmentInfo = 0, BTrackInfo = 0, BCues = 0, BTags = 0, BChapters = 0, BAttachments = 0, BSecondSeek = 0;
469 while (RLevel1)
470 {
471 filepos_t Pos = MATROSKA_MetaSeekAbsolutePos(RLevel1);
472 fourcc_t SeekId = MATROSKA_MetaSeekID(RLevel1);
473 tchar_t IdString[32];
474
475 EBML_IdToString(IdString,TSIZEOF(IdString),SeekId);
476 if (Pos == INVALID_FILEPOS_T)
477 Result |= OutputError(0x60,T("The SeekPoint at %") TPRId64 T(" has an unknown position (ID %s)"),EL_Pos(RLevel1),IdString);
478 else if (SeekId==0)
479 Result |= OutputError(0x61,T("The SeekPoint at %") TPRId64 T(" has no ID defined (position %") TPRId64 T(")"),EL_Pos(RLevel1),Pos);
480 else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextInfo))
481 {
482 if (!RSegmentInfo)
483 Result |= OutputError(0x62,T("The SeekPoint at %") TPRId64 T(" references an unknown SegmentInfo at %") TPRId64,EL_Pos(RLevel1),Pos);
484 else if (EL_Pos(RSegmentInfo) != Pos)
485 Result |= OutputError(0x63,T("The SeekPoint at %") TPRId64 T(" references a SegmentInfo at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RSegmentInfo));
486 BSegmentInfo = 1;
487 }
488 else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextTracks))
489 {
490 if (!RTrackInfo)
491 Result |= OutputError(0x64,T("The SeekPoint at %") TPRId64 T(" references an unknown TrackInfo at %") TPRId64,EL_Pos(RLevel1),Pos);
492 else if (EL_Pos(RTrackInfo) != Pos)
493 Result |= OutputError(0x65,T("The SeekPoint at %") TPRId64 T(" references a TrackInfo at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RTrackInfo));
494 BTrackInfo = 1;
495 }
496 else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextCues))
497 {
498 if (!RCues)
499 Result |= OutputError(0x66,T("The SeekPoint at %") TPRId64 T(" references an unknown Cues at %") TPRId64,EL_Pos(RLevel1),Pos);
500 else if (EL_Pos(RCues) != Pos)
501 Result |= OutputError(0x67,T("The SeekPoint at %") TPRId64 T(" references a Cues at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RCues));
502 BCues = 1;
503 }
504 else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextTags))
505 {
506 if (!RTags)
507 Result |= OutputError(0x68,T("The SeekPoint at %") TPRId64 T(" references an unknown Tags at %") TPRId64,EL_Pos(RLevel1),Pos);
508 else if (EL_Pos(RTags) != Pos)
509 Result |= OutputError(0x69,T("The SeekPoint at %") TPRId64 T(" references a Tags at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RTags));
510 BTags = 1;
511 }
512 else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextChapters))
513 {
514 if (!RChapters)
515 Result |= OutputError(0x6A,T("The SeekPoint at %") TPRId64 T(" references an unknown Chapters at %") TPRId64,EL_Pos(RLevel1),Pos);
516 else if (EL_Pos(RChapters) != Pos)
517 Result |= OutputError(0x6B,T("The SeekPoint at %") TPRId64 T(" references a Chapters at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RChapters));
518 BChapters = 1;
519 }
520 else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextAttachments))
521 {
522 if (!RAttachments)
523 Result |= OutputError(0x6C,T("The SeekPoint at %") TPRId64 T(" references an unknown Attachments at %") TPRId64,EL_Pos(RLevel1),Pos);
524 else if (EL_Pos(RAttachments) != Pos)
525 Result |= OutputError(0x6D,T("The SeekPoint at %") TPRId64 T(" references a Attachments at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RAttachments));
526 BAttachments = 1;
527 }
528 else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextSeekHead))
529 {
530 if (EL_Pos(SeekHead) == Pos)
531 Result |= OutputError(0x6E,T("The SeekPoint at %") TPRId64 T(" references references its own SeekHead"),EL_Pos(RLevel1));
532 else if (SeekHead == RSeekHead)
533 {
534 if (!RSeekHead2)
535 Result |= OutputError(0x6F,T("The SeekPoint at %") TPRId64 T(" references an unknown secondary SeekHead at %") TPRId64,EL_Pos(RLevel1),Pos);
536 BSecondSeek = 1;
537 }
538 else if (SeekHead == RSeekHead2 && Pos!=EL_Pos(RSeekHead))
539 Result |= OutputError(0x70,T("The SeekPoint at %") TPRId64 T(" references an unknown extra SeekHead at %") TPRId64,EL_Pos(RLevel1),Pos);
540 }
541 else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextCluster))
542 {
543 ebml_element **Cluster;
544 for (Cluster = ARRAYBEGIN(RClusters,ebml_element*);Cluster != ARRAYEND(RClusters,ebml_element*); ++Cluster)
545 {
546 if (EL_Pos(*Cluster) == Pos)
547 break;
548 }
549 if (Cluster == ARRAYEND(RClusters,ebml_element*) && Cluster != ARRAYBEGIN(RClusters,ebml_element*))
550 Result |= OutputError(0x71,T("The SeekPoint at %") TPRId64 T(" references a Cluster not found at %") TPRId64,EL_Pos(RLevel1),Pos);
551 }
552 else
553 OutputWarning(0x860,T("The SeekPoint at %") TPRId64 T(" references an element that is not a known level 1 ID %s at %") TPRId64 T(")"),EL_Pos(RLevel1),IdString,Pos);
554 RLevel1 = (matroska_seekpoint*)EBML_MasterFindNextElt(SeekHead, (ebml_element*)RLevel1, 0, 0);
555 }
556 if (SeekHead == RSeekHead)
557 {
558 if (!BSegmentInfo && RSegmentInfo)
559 OutputWarning(0x861,T("The SegmentInfo is not referenced in the main SeekHead"));
560 if (!BTrackInfo && RTrackInfo)
561 OutputWarning(0x861,T("The TrackInfo is not referenced in the main SeekHead"));
562 if (!BCues && RCues)
563 OutputWarning(0x861,T("The Cues is not referenced in the main SeekHead"));
564 if (!BTags && RTags)
565 OutputWarning(0x861,T("The Tags is not referenced in the main SeekHead"));
566 if (!BChapters && RChapters)
567 OutputWarning(0x861,T("The Chapters is not referenced in the main SeekHead"));
568 if (!BAttachments && RAttachments)
569 OutputWarning(0x861,T("The Attachments is not referenced in the main SeekHead"));
570 if (!BSecondSeek && RSeekHead2)
571 OutputWarning(0x861,T("The secondary SeekHead is not referenced in the main SeekHead"));
572 }
573 return Result;
574 }
575
LinkClusterBlocks(void)576 static void LinkClusterBlocks(void)
577 {
578 matroska_cluster **Cluster;
579 for (Cluster=ARRAYBEGIN(RClusters,matroska_cluster*);Cluster!=ARRAYEND(RClusters,matroska_cluster*);++Cluster)
580 MATROSKA_LinkClusterBlocks(*Cluster, RSegmentInfo, RTrackInfo, 1);
581 }
582
TrackIsLaced(int16_t TrackNum)583 static bool_t TrackIsLaced(int16_t TrackNum)
584 {
585 ebml_element *TrackData;
586 ebml_master *Track = (ebml_master*)EBML_MasterFindChild(RTrackInfo, &MATROSKA_ContextTrackEntry);
587 while (Track)
588 {
589 TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackNumber);
590 if (EL_Int(TrackData) == TrackNum)
591 {
592 TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextFlagLacing);
593 return EL_Int(TrackData) != 0;
594 }
595 Track = (ebml_master*)EBML_MasterFindNextElt(RTrackInfo, (ebml_element*)Track, 0, 0);
596 }
597 return 1;
598 }
599
TrackIsVideo(int16_t TrackNum)600 static bool_t TrackIsVideo(int16_t TrackNum)
601 {
602 ebml_element *TrackData;
603 ebml_master *Track = (ebml_master*)EBML_MasterFindChild(RTrackInfo, &MATROSKA_ContextTrackEntry);
604 while (Track)
605 {
606 TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackNumber);
607 if (EL_Int(TrackData) == TrackNum)
608 {
609 TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackType);
610 return EL_Int(TrackData) == TRACK_TYPE_VIDEO;
611 }
612 // look for TrackNum in the next Track
613 Track = (ebml_master*)EBML_MasterFindNextElt(RTrackInfo, (ebml_element*)Track, 0, 0);
614 }
615 return 0;
616 }
617
TrackNeedsKeyframe(int16_t TrackNum)618 static bool_t TrackNeedsKeyframe(int16_t TrackNum)
619 {
620 ebml_element *TrackData;
621 ebml_master *Track = (ebml_master*)EBML_MasterFindChild(RTrackInfo, &MATROSKA_ContextTrackEntry);
622 while (Track)
623 {
624 TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackNumber);
625 if (EL_Int(TrackData) == TrackNum)
626 {
627 TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackType);
628 switch (EL_Int(TrackData))
629 {
630 case TRACK_TYPE_VIDEO:
631 return 0;
632 case TRACK_TYPE_AUDIO:
633 {
634 tchar_t CodecName[MAXDATA];
635 ebml_string *CodecID = (ebml_string*)EBML_MasterGetChild(Track, &MATROSKA_ContextCodecID);
636 EBML_StringGet(CodecID,CodecName,TSIZEOF(CodecName));
637 return !tcsisame_ascii(CodecName,T("A_TRUEHD"));
638 }
639 default: return 1;
640 }
641 }
642 // look for TrackNum in the next Track
643 Track = (ebml_master*)EBML_MasterFindNextElt(RTrackInfo, (ebml_element*)Track, 0, 0);
644 }
645 return 0;
646 }
647
CheckVideoStart(void)648 static int CheckVideoStart(void)
649 {
650 int Result = 0;
651 ebml_master **Cluster;
652 ebml_element *Block, *GBlock;
653 int16_t BlockNum;
654 timecode_t ClusterTimecode;
655 array TrackKeyframe;
656 array TrackFirstKeyframePos;
657
658 for (Cluster=ARRAYBEGIN(RClusters,ebml_master*);Cluster!=ARRAYEND(RClusters,ebml_master*);++Cluster)
659 {
660 ArrayInit(&TrackKeyframe);
661 ArrayResize(&TrackKeyframe,sizeof(bool_t)*(TrackMax+1),256);
662 ArrayZero(&TrackKeyframe);
663 ArrayInit(&TrackFirstKeyframePos);
664 ArrayResize(&TrackFirstKeyframePos,sizeof(filepos_t)*(TrackMax+1),256);
665 ArrayZero(&TrackFirstKeyframePos);
666
667 ClusterTimecode = MATROSKA_ClusterTimecode((matroska_cluster*)*Cluster);
668 if (ClusterTimecode==INVALID_TIMECODE_T)
669 Result |= OutputError(0xC1,T("The Cluster at %") TPRId64 T(" has no timecode"),EL_Pos(*Cluster));
670 else if (ClusterTime!=INVALID_TIMECODE_T && ClusterTime >= ClusterTimecode)
671 OutputWarning(0xC2,T("The timecode of the Cluster at %") TPRId64 T(" is not incrementing (may be intentional)"),EL_Pos(*Cluster));
672 ClusterTime = ClusterTimecode;
673
674 for (Block = EBML_MasterChildren(*Cluster);Block;Block=EBML_MasterNext(Block))
675 {
676 if (EL_Type(Block, &MATROSKA_ContextBlockGroup))
677 {
678 for (GBlock = EBML_MasterChildren(Block);GBlock;GBlock=EBML_MasterNext(GBlock))
679 {
680 if (EL_Type(GBlock, &MATROSKA_ContextBlock))
681 {
682 BlockNum = MATROSKA_BlockTrackNum((matroska_block*)GBlock);
683 if (BlockNum > ARRAYCOUNT(TrackKeyframe,bool_t))
684 OutputError(0xC3,T("Unknown track #%d in Cluster at %") TPRId64 T(" in Block at %") TPRId64,(int)BlockNum,EL_Pos(*Cluster),EL_Pos(GBlock));
685 else if (TrackIsVideo(BlockNum))
686 {
687 if (!ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && MATROSKA_BlockKeyframe((matroska_block*)GBlock))
688 ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] = 1;
689 if (!ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum]==0)
690 ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum] = EL_Pos(*Cluster);
691 }
692 break;
693 }
694 }
695 }
696 else if (EL_Type(Block, &MATROSKA_ContextSimpleBlock))
697 {
698 BlockNum = MATROSKA_BlockTrackNum((matroska_block*)Block);
699 if (BlockNum > ARRAYCOUNT(TrackKeyframe,bool_t))
700 OutputError(0xC3,T("Unknown track #%d in Cluster at %") TPRId64 T(" in SimpleBlock at %") TPRId64,(int)BlockNum,EL_Pos(*Cluster),EL_Pos(Block));
701 else if (TrackIsVideo(BlockNum))
702 {
703 if (!ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && MATROSKA_BlockKeyframe((matroska_block*)Block))
704 ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] = 1;
705 if (!ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum]==0)
706 ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum] = EL_Pos(*Cluster);
707 }
708 }
709 }
710 for (BlockNum=0;BlockNum<ARRAYCOUNT(TrackKeyframe,bool_t);++BlockNum)
711 {
712 if (ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum]!=0)
713 OutputWarning(0xC0,T("First Block for video track #%d in Cluster at %") TPRId64 T(" is not a keyframe"),(int)BlockNum,ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum]);
714 }
715 ArrayClear(&TrackKeyframe);
716 ArrayClear(&TrackFirstKeyframePos);
717 }
718 return Result;
719 }
720
CheckPosSize(const ebml_element * RSegment)721 static int CheckPosSize(const ebml_element *RSegment)
722 {
723 int Result = 0;
724 ebml_element **Cluster,*PrevCluster=NULL;
725 ebml_element *Elt;
726
727 for (Cluster=ARRAYBEGIN(RClusters,ebml_element*);Cluster!=ARRAYEND(RClusters,ebml_element*);++Cluster)
728 {
729 Elt = EBML_MasterFindChild((ebml_master*)*Cluster,&MATROSKA_ContextPrevSize);
730 if (Elt)
731 {
732 if (PrevCluster==NULL)
733 Result |= OutputError(0xA0,T("The PrevSize %") TPRId64 T(" was set on the first Cluster at %") TPRId64,EL_Int(Elt),EL_Pos(Elt));
734 else if (EL_Int(Elt) != EL_Pos(*Cluster) - EL_Pos(PrevCluster))
735 Result |= OutputError(0xA1,T("The Cluster PrevSize %") TPRId64 T(" at %") TPRId64 T(" should be %") TPRId64,EL_Int(Elt),EL_Pos(Elt),EL_Pos(*Cluster) - EL_Pos(PrevCluster));
736 }
737 Elt = EBML_MasterFindChild((ebml_master*)*Cluster,&MATROSKA_ContextPosition);
738 if (Elt)
739 {
740 if (EL_Int(Elt) != EL_Pos(*Cluster) - EBML_ElementPositionData(RSegment))
741 Result |= OutputError(0xA2,T("The Cluster position %") TPRId64 T(" at %") TPRId64 T(" should be %") TPRId64,EL_Int(Elt),EL_Pos(Elt),EL_Pos(*Cluster) - EBML_ElementPositionData(RSegment));
742 }
743 PrevCluster = *Cluster;
744 }
745 return Result;
746 }
747
CheckLacingKeyframe(void)748 static int CheckLacingKeyframe(void)
749 {
750 int Result = 0;
751 matroska_cluster **Cluster;
752 ebml_element *Block, *GBlock;
753 int16_t BlockNum;
754 timecode_t BlockTime;
755 size_t Frame,TrackIdx;
756
757 for (Cluster=ARRAYBEGIN(RClusters,matroska_cluster*);Cluster!=ARRAYEND(RClusters,matroska_cluster*);++Cluster)
758 {
759 for (Block = EBML_MasterChildren(*Cluster);Block;Block=EBML_MasterNext(Block))
760 {
761 if (EL_Type(Block, &MATROSKA_ContextBlockGroup))
762 {
763 for (GBlock = EBML_MasterChildren(Block);GBlock;GBlock=EBML_MasterNext(GBlock))
764 {
765 if (EL_Type(GBlock, &MATROSKA_ContextBlock))
766 {
767 //MATROSKA_ContextFlagLacing
768 BlockNum = MATROSKA_BlockTrackNum((matroska_block*)GBlock);
769 for (TrackIdx=0; TrackIdx<ARRAYCOUNT(Tracks,track_info); ++TrackIdx)
770 if (ARRAYBEGIN(Tracks,track_info)[TrackIdx].Num == BlockNum)
771 break;
772
773 if (TrackIdx==ARRAYCOUNT(Tracks,track_info))
774 Result |= OutputError(0xB2,T("Block at %") TPRId64 T(" is using an unknown track #%d"),EL_Pos(GBlock),(int)BlockNum);
775 else
776 {
777 if (MATROSKA_BlockLaced((matroska_block*)GBlock) && !TrackIsLaced(BlockNum))
778 Result |= OutputError(0xB0,T("Block at %") TPRId64 T(" track #%d is laced but the track is not"),EL_Pos(GBlock),(int)BlockNum);
779 if (!MATROSKA_BlockKeyframe((matroska_block*)GBlock) && TrackNeedsKeyframe(BlockNum))
780 Result |= OutputError(0xB1,T("Block at %") TPRId64 T(" track #%d is not a keyframe"),EL_Pos(GBlock),(int)BlockNum);
781
782 for (Frame=0; Frame<MATROSKA_BlockGetFrameCount((matroska_block*)GBlock); ++Frame)
783 ARRAYBEGIN(Tracks,track_info)[TrackIdx].DataLength += MATROSKA_BlockGetLength((matroska_block*)GBlock,Frame);
784 if (Details)
785 {
786 BlockTime = MATROSKA_BlockTimecode((matroska_block*)GBlock);
787 if (MinTime==INVALID_TIMECODE_T || MinTime>BlockTime)
788 MinTime = BlockTime;
789 if (MaxTime==INVALID_TIMECODE_T || MaxTime<BlockTime)
790 MaxTime = BlockTime;
791 }
792 }
793 break;
794 }
795 }
796 }
797 else if (EL_Type(Block, &MATROSKA_ContextSimpleBlock))
798 {
799 BlockNum = MATROSKA_BlockTrackNum((matroska_block*)Block);
800 for (TrackIdx=0; TrackIdx<ARRAYCOUNT(Tracks,track_info); ++TrackIdx)
801 if (ARRAYBEGIN(Tracks,track_info)[TrackIdx].Num == BlockNum)
802 break;
803
804 if (TrackIdx==ARRAYCOUNT(Tracks,track_info))
805 Result |= OutputError(0xB2,T("Block at %") TPRId64 T(" is using an unknown track #%d"),EL_Pos(Block),(int)BlockNum);
806 else
807 {
808 if (MATROSKA_BlockLaced((matroska_block*)Block) && !TrackIsLaced(BlockNum))
809 Result |= OutputError(0xB0,T("SimpleBlock at %") TPRId64 T(" track #%d is laced but the track is not"),EL_Pos(Block),(int)BlockNum);
810 if (!MATROSKA_BlockKeyframe((matroska_block*)Block) && TrackNeedsKeyframe(BlockNum))
811 Result |= OutputError(0xB1,T("SimpleBlock at %") TPRId64 T(" track #%d is not a keyframe"),EL_Pos(Block),(int)BlockNum);
812 for (Frame=0; Frame<MATROSKA_BlockGetFrameCount((matroska_block*)Block); ++Frame)
813 ARRAYBEGIN(Tracks,track_info)[TrackIdx].DataLength += MATROSKA_BlockGetLength((matroska_block*)Block,Frame);
814 if (Details)
815 {
816 BlockTime = MATROSKA_BlockTimecode((matroska_block*)Block);
817 if (MinTime==INVALID_TIMECODE_T || MinTime>BlockTime)
818 MinTime = BlockTime;
819 if (MaxTime==INVALID_TIMECODE_T || MaxTime<BlockTime)
820 MaxTime = BlockTime;
821 }
822 }
823 }
824 }
825 }
826 return Result;
827 }
828
CheckCueEntries(ebml_master * Cues)829 static int CheckCueEntries(ebml_master *Cues)
830 {
831 int Result = 0;
832 timecode_t TimecodeEntry, PrevTimecode = INVALID_TIMECODE_T;
833 int16_t TrackNumEntry;
834 matroska_cluster **Cluster;
835 matroska_block *Block;
836 int ClustNum = 0;
837
838 if (!RSegmentInfo)
839 Result |= OutputError(0x310,T("A Cues (index) is defined but no SegmentInfo was found"));
840 else if (ARRAYCOUNT(RClusters,matroska_cluster*))
841 {
842 matroska_cuepoint *CuePoint = (matroska_cuepoint*)EBML_MasterFindChild(Cues, &MATROSKA_ContextCuePoint);
843 while (CuePoint)
844 {
845 if (!Quiet && ClustNum++ % 24 == 0)
846 TextWrite(StdErr,T("."));
847 MATROSKA_LinkCueSegmentInfo(CuePoint,RSegmentInfo);
848 TimecodeEntry = MATROSKA_CueTimecode(CuePoint);
849 TrackNumEntry = MATROSKA_CueTrackNum(CuePoint);
850
851 if (TimecodeEntry < PrevTimecode && PrevTimecode != INVALID_TIMECODE_T)
852 OutputWarning(0x311,T("The Cues entry for timecode %") TPRId64 T(" ms is listed after entry %") TPRId64 T(" ms"),Scale64(TimecodeEntry,1,1000000),Scale64(PrevTimecode,1,1000000));
853
854 // find a matching Block
855 for (Cluster = ARRAYBEGIN(RClusters,matroska_cluster*);Cluster != ARRAYEND(RClusters,matroska_cluster*); ++Cluster)
856 {
857 Block = MATROSKA_GetBlockForTimecode(*Cluster, TimecodeEntry, TrackNumEntry);
858 if (Block)
859 break;
860 }
861 if (Cluster == ARRAYEND(RClusters,matroska_cluster*))
862 Result |= OutputError(0x312,T("CueEntry Track #%d and timecode %") TPRId64 T(" ms not found"),(int)TrackNumEntry,Scale64(TimecodeEntry,1,1000000));
863 PrevTimecode = TimecodeEntry;
864 CuePoint = (matroska_cuepoint*)EBML_MasterFindNextElt(Cues, (ebml_element*)CuePoint, 0, 0);
865 }
866 }
867 return Result;
868 }
869
870 #if defined(TARGET_WIN) && defined(UNICODE)
wmain(int argc,const wchar_t * argv[])871 int wmain(int argc, const wchar_t *argv[])
872 #else
873 int main(int argc, const char *argv[])
874 #endif
875 {
876 int Result = 0;
877 int ShowUsage = 0;
878 int ShowVersion = 0;
879 parsercontext p;
880 textwriter _StdErr;
881 stream *Input = NULL;
882 tchar_t Path[MAXPATHFULL];
883 tchar_t String[MAXLINE];
884 ebml_master *EbmlHead = NULL, *RSegment = NULL, *RLevel1 = NULL, *Prev, *RLevelX, **Cluster;
885 ebml_element *EbmlDocVer, *EbmlReadDocVer;
886 ebml_string *LibName, *AppName;
887 ebml_parser_context RContext;
888 ebml_parser_context RSegmentContext;
889 int i,UpperElement;
890 int MatroskaProfile = 0;
891 bool_t HasVideo = 0;
892 int DotCount;
893 track_info *TI;
894 filepos_t VoidAmount = 0;
895
896 // Core-C init phase
897 ParserContext_Init(&p,NULL,NULL,NULL);
898 StdAfx_Init((nodemodule*)&p);
899 ProjectSettings((nodecontext*)&p);
900
901 // EBML & Matroska Init
902 MATROSKA_Init((nodecontext*)&p);
903
904 ArrayInit(&RClusters);
905 ArrayInit(&Tracks);
906
907 StdErr = &_StdErr;
908 memset(StdErr,0,sizeof(_StdErr));
909 StdErr->Stream = (stream*)NodeSingleton(&p,STDERR_ID);
910 assert(StdErr->Stream!=NULL);
911
912 for (i=1;i<argc;++i)
913 {
914 #if defined(TARGET_WIN) && defined(UNICODE)
915 Node_FromWcs(&p,Path,TSIZEOF(Path),argv[i]);
916 #else
917 Node_FromStr(&p,Path,TSIZEOF(Path),argv[i]);
918 #endif
919 if (tcsisame_ascii(Path,T("--no-warn"))) Warnings = 0;
920 else if (tcsisame_ascii(Path,T("--live"))) Live = 1;
921 else if (tcsisame_ascii(Path,T("--details"))) Details = 1;
922 else if (tcsisame_ascii(Path,T("--divx"))) DivX = 1;
923 else if (tcsisame_ascii(Path,T("--version"))) ShowVersion = 1;
924 else if (tcsisame_ascii(Path,T("--quiet"))) Quiet = 1;
925 else if (tcsisame_ascii(Path,T("--help"))) {ShowVersion = 1; ShowUsage = 1;}
926 else if (i<argc-1) TextPrintf(StdErr,T("Unknown parameter '%s'\r\n"),Path);
927 }
928
929 if (argc < 2 || ShowVersion)
930 {
931 TextWrite(StdErr,T("mkvalidator v") PROJECT_VERSION T(", Copyright (c) 2010-2015 Matroska Foundation\r\n"));
932 if (argc < 2 || ShowUsage)
933 {
934 Result = OutputError(1,T("Usage: mkvalidator [options] <matroska_src>"));
935 TextWrite(StdErr,T("Options:\r\n"));
936 TextWrite(StdErr,T(" --no-warn only output errors, no warnings\r\n"));
937 TextWrite(StdErr,T(" --live only output errors/warnings relevant to live streams\r\n"));
938 TextWrite(StdErr,T(" --details show details for valid files\r\n"));
939 TextWrite(StdErr,T(" --divx assume the file is using DivX specific extensions\r\n"));
940 TextWrite(StdErr,T(" --quiet don't ouput progress and file info\r\n"));
941 TextWrite(StdErr,T(" --version show the version of mkvalidator\r\n"));
942 TextWrite(StdErr,T(" --help show this screen\r\n"));
943 }
944 goto exit;
945 }
946
947 #if defined(TARGET_WIN) && defined(UNICODE)
948 Node_FromWcs(&p,Path,TSIZEOF(Path),argv[argc-1]);
949 #else
950 Node_FromStr(&p,Path,TSIZEOF(Path),argv[argc-1]);
951 #endif
952 Input = StreamOpen(&p,Path,SFLAG_RDONLY/*|SFLAG_BUFFERED*/);
953 if (!Input)
954 {
955 TextPrintf(StdErr,T("Could not open file \"%s\" for reading\r\n"),Path);
956 Result = -2;
957 goto exit;
958 }
959
960 // parse the source file to determine if it's a Matroska file and determine the location of the key parts
961 RContext.Context = &MATROSKA_ContextStream;
962 RContext.EndPosition = INVALID_FILEPOS_T;
963 RContext.UpContext = NULL;
964 RContext.Profile = 0;
965 EbmlHead = (ebml_master*)EBML_FindNextElement(Input, &RContext, &UpperElement, 0);
966 if (!EbmlHead || !EL_Type(EbmlHead, &EBML_ContextHead))
967 {
968 Result = OutputError(3,T("EBML head not found! Are you sure it's a matroska/webm file?"));
969 goto exit;
970 }
971
972 if (!Quiet) TextWrite(StdErr,T("."));
973
974 if (EBML_ElementReadData(EbmlHead,Input,&RContext,0,SCOPE_ALL_DATA, 1)!=ERR_NONE)
975 {
976 Result = OutputError(4,T("Could not read the EBML head"));
977 goto exit;
978 }
979 if (!EBML_MasterIsChecksumValid(EbmlHead))
980 {
981 Result = OutputError(12,T("The EBML header is damaged (invalid CheckSum)"));
982 goto exit;
983 }
984
985 VoidAmount += CheckUnknownElements((ebml_element*)EbmlHead);
986
987 RLevel1 = (ebml_master*)EBML_MasterGetChild(EbmlHead,&EBML_ContextReadVersion);
988 if (EL_Int(RLevel1) > EBML_MAX_VERSION)
989 OutputError(5,T("The EBML read version is not supported: %d"),(int)EL_Int(RLevel1));
990
991 RLevel1 = (ebml_master*)EBML_MasterGetChild(EbmlHead,&EBML_ContextMaxIdLength);
992 if (EL_Int(RLevel1) > EBML_MAX_ID)
993 OutputError(6,T("The EBML max ID length is not supported: %d"),(int)EL_Int(RLevel1));
994
995 RLevel1 = (ebml_master*)EBML_MasterGetChild(EbmlHead,&EBML_ContextMaxSizeLength);
996 if (EL_Int(RLevel1) > EBML_MAX_SIZE)
997 OutputError(7,T("The EBML max size length is not supported: %d"),(int)EL_Int(RLevel1));
998
999 RLevel1 = (ebml_master*)EBML_MasterGetChild(EbmlHead,&EBML_ContextDocType);
1000 EBML_StringGet((ebml_string*)RLevel1,String,TSIZEOF(String));
1001 if (tcscmp(String,T("matroska"))!=0 && tcscmp(String,T("webm"))!=0)
1002 {
1003 Result = OutputError(8,T("The EBML doctype is not supported: %s"),String);
1004 goto exit;
1005 }
1006
1007 EbmlDocVer = EBML_MasterGetChild(EbmlHead,&EBML_ContextDocTypeVersion);
1008 EbmlReadDocVer = EBML_MasterGetChild(EbmlHead,&EBML_ContextDocTypeReadVersion);
1009
1010 if (EL_Int(EbmlReadDocVer) > EL_Int(EbmlDocVer))
1011 OutputError(9,T("The DocType version %d is higher than the read Doctype version %d"),(int)EL_Int(EbmlDocVer),(int)EL_Int(EbmlReadDocVer));
1012
1013 if (tcscmp(String,T("matroska"))==0)
1014 {
1015 if (DivX)
1016 MatroskaProfile = PROFILE_DIVX;
1017 else if (EL_Int(EbmlDocVer)==4)
1018 MatroskaProfile = PROFILE_MATROSKA_V4;
1019 else if (EL_Int(EbmlDocVer)==3)
1020 MatroskaProfile = PROFILE_MATROSKA_V3;
1021 else if (EL_Int(EbmlDocVer)==2)
1022 MatroskaProfile = PROFILE_MATROSKA_V2;
1023 else if (EL_Int(EbmlDocVer)==1)
1024 MatroskaProfile = PROFILE_MATROSKA_V1;
1025 else
1026 OutputError(10,T("Unknown Matroska profile %d/%d"),(int)EL_Int(EbmlDocVer),(int)EL_Int(EbmlReadDocVer));
1027 }
1028 else if (tcscmp(String,T("webm"))==0)
1029 MatroskaProfile = PROFILE_WEBM;
1030
1031 if (!Quiet) TextWrite(StdErr,T("."));
1032
1033 // find the segment
1034 RSegment = (ebml_master*)EBML_FindNextElement(Input, &RContext, &UpperElement, 1);
1035 RSegmentContext.Context = &MATROSKA_ContextSegment;
1036 RSegmentContext.EndPosition = EBML_ElementPositionEnd((ebml_element*)RSegment);
1037 RSegmentContext.UpContext = &RContext;
1038 RSegmentContext.Profile = MatroskaProfile;
1039
1040 RContext.EndPosition = EBML_ElementPositionEnd((ebml_element*)RSegment);
1041
1042 UpperElement = 0;
1043 DotCount = 0;
1044 Prev = NULL;
1045 RLevel1 = (ebml_master*)EBML_FindNextElement(Input, &RSegmentContext, &UpperElement, 1);
1046 while (RLevel1)
1047 {
1048 RLevelX = NULL;
1049 if (EL_Type(RLevel1, &MATROSKA_ContextCluster))
1050 {
1051 if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,0,SCOPE_PARTIAL_DATA,4)==ERR_NONE)
1052 {
1053 ArrayAppend(&RClusters,&RLevel1,sizeof(RLevel1),256);
1054 NodeTree_SetParent(RLevel1, RSegment, NULL);
1055 VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1056 Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1057 RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1058 }
1059 else
1060 {
1061 Result = OutputError(0x180,T("Failed to read the Cluster at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1062 goto exit;
1063 }
1064 }
1065 else if (EL_Type(RLevel1, &MATROSKA_ContextSeekHead))
1066 {
1067 if (Live)
1068 {
1069 OutputWarning(0x170,T("The live stream has a SeekHead at %") TPRId64,EL_Pos(RLevel1));
1070 RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1071 NodeDelete((node*)RLevel1);
1072 RLevel1 = NULL;
1073 }
1074 else if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,2)==ERR_NONE)
1075 {
1076 if (!RSeekHead)
1077 RSeekHead = RLevel1;
1078 else if (!RSeekHead2)
1079 {
1080 OutputWarning(0x103,T("Unnecessary secondary SeekHead was found at %") TPRId64,EL_Pos(RLevel1));
1081 RSeekHead2 = RLevel1;
1082 }
1083 else
1084 OutputWarning(0x101,T("Extra SeekHead found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1085 NodeTree_SetParent(RLevel1, RSegment, NULL);
1086 VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1087 Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1088 }
1089 else
1090 {
1091 Result = OutputError(0x100,T("Failed to read the SeekHead at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1092 goto exit;
1093 }
1094 }
1095 else if (EL_Type(RLevel1, &MATROSKA_ContextInfo))
1096 {
1097 if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,1)==ERR_NONE)
1098 {
1099 if (RSegmentInfo != NULL)
1100 OutputWarning(0x110,T("Extra SegmentInfo found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1101 else
1102 {
1103 RSegmentInfo = RLevel1;
1104 NodeTree_SetParent(RLevel1, RSegment, NULL);
1105 VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1106 Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1107
1108 if (Live)
1109 {
1110 ebml_master *Elt = (ebml_master*)EBML_MasterFindChild(RLevel1,&MATROSKA_ContextDuration);
1111 if (Elt)
1112 OutputWarning(0x112,T("The live Segment has a duration set at %") TPRId64,EL_Pos(Elt));
1113 }
1114 }
1115 }
1116 else
1117 {
1118 Result = OutputError(0x111,T("Failed to read the SegmentInfo at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1119 goto exit;
1120 }
1121 }
1122 else if (EL_Type(RLevel1, &MATROSKA_ContextTracks))
1123 {
1124 if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,4)==ERR_NONE)
1125 {
1126 if (RTrackInfo != NULL)
1127 OutputWarning(0x120,T("Extra TrackInfo found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1128 else
1129 {
1130 size_t TrackCount;
1131 ebml_master *Elt;
1132
1133 RTrackInfo = RLevel1;
1134 NodeTree_SetParent(RLevel1, RSegment, NULL);
1135 VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1136 Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1137
1138 Elt = (ebml_master*)EBML_MasterFindChild(RTrackInfo,&MATROSKA_ContextTrackEntry);
1139 TrackCount = 0;
1140 while (Elt)
1141 {
1142 Elt = (ebml_master*)EBML_MasterNextChild(RTrackInfo,Elt);
1143 ++TrackCount;
1144 }
1145
1146 ArrayResize(&Tracks,TrackCount*sizeof(track_info),256);
1147 ArrayZero(&Tracks);
1148
1149 Elt = (ebml_master*)EBML_MasterFindChild(RTrackInfo,&MATROSKA_ContextTrackEntry);
1150 TrackCount = 0;
1151 while (Elt)
1152 {
1153 EbmlDocVer = EBML_MasterFindChild(Elt,&MATROSKA_ContextTrackNumber);
1154 assert(EbmlDocVer!=NULL);
1155 if (EbmlDocVer)
1156 {
1157 TrackMax = max(TrackMax,(size_t)EL_Int(EbmlDocVer));
1158 ARRAYBEGIN(Tracks,track_info)[TrackCount].Num = (int)EL_Int(EbmlDocVer);
1159 }
1160 EbmlDocVer = EBML_MasterFindChild(Elt,&MATROSKA_ContextTrackType);
1161 assert(EbmlDocVer!=NULL);
1162 if (EbmlDocVer)
1163 {
1164 if (EL_Int(EbmlDocVer)==TRACK_TYPE_VIDEO)
1165 {
1166 Result |= CheckVideoTrack(Elt, ARRAYBEGIN(Tracks,track_info)[TrackCount].Num, MatroskaProfile);
1167 HasVideo = 1;
1168 }
1169 ARRAYBEGIN(Tracks,track_info)[TrackCount].Kind = (int)EL_Int(EbmlDocVer);
1170 }
1171 ARRAYBEGIN(Tracks,track_info)[TrackCount].CodecID = (ebml_string*)EBML_MasterFindChild(Elt,&MATROSKA_ContextCodecID);
1172 Elt = (ebml_master*)EBML_MasterNextChild(RTrackInfo,Elt);
1173 ++TrackCount;
1174 }
1175 EbmlDocVer = NULL;
1176 Elt = NULL;
1177 }
1178 }
1179 else
1180 {
1181 Result = OutputError(0x121,T("Failed to read the TrackInfo at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1182 goto exit;
1183 }
1184 }
1185 else if (EL_Type(RLevel1, &MATROSKA_ContextCues))
1186 {
1187 if (Live)
1188 {
1189 OutputWarning(0x171,T("The live stream has Cues at %") TPRId64,EL_Pos(RLevel1));
1190 RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1191 NodeDelete((node*)RLevel1);
1192 RLevel1 = NULL;
1193 }
1194 else if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,3)==ERR_NONE)
1195 {
1196 if (RCues != NULL)
1197 OutputWarning(0x130,T("Extra Cues found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1198 else
1199 {
1200 RCues = RLevel1;
1201 NodeTree_SetParent(RLevel1, RSegment, NULL);
1202 VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1203 Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1204 }
1205 }
1206 else
1207 {
1208 Result = OutputError(0x131,T("Failed to read the Cues at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1209 goto exit;
1210 }
1211 }
1212 else if (EL_Type(RLevel1, &MATROSKA_ContextChapters))
1213 {
1214 if (Live)
1215 {
1216 Result |= OutputError(0x172,T("The live stream has Chapters at %") TPRId64,EL_Pos(RLevel1));
1217 RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1218 NodeDelete((node*)RLevel1);
1219 RLevel1 = NULL;
1220 }
1221 else if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,16)==ERR_NONE)
1222 {
1223 if (RChapters != NULL)
1224 OutputWarning(0x140,T("Extra Chapters found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1225 else
1226 {
1227 RChapters = RLevel1;
1228 NodeTree_SetParent(RLevel1, RSegment, NULL);
1229 VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1230 Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1231 }
1232 }
1233 else
1234 {
1235 Result = OutputError(0x141,T("Failed to read the Chapters at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1236 goto exit;
1237 }
1238 }
1239 else if (EL_Type(RLevel1, &MATROSKA_ContextTags))
1240 {
1241 if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,4)==ERR_NONE)
1242 {
1243 if (RTags != NULL)
1244 Result |= OutputError(0x150,T("Extra Tags found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1245 else
1246 {
1247 RTags = RLevel1;
1248 NodeTree_SetParent(RLevel1, RSegment, NULL);
1249 VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1250 Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1251 }
1252 }
1253 else
1254 {
1255 Result = OutputError(0x151,T("Failed to read the Tags at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1256 goto exit;
1257 }
1258 }
1259 else if (EL_Type(RLevel1, &MATROSKA_ContextAttachments))
1260 {
1261 if (Live)
1262 {
1263 Result |= OutputError(0x173,T("The live stream has a Attachments at %") TPRId64,EL_Pos(RLevel1));
1264 RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1265 NodeDelete((node*)RLevel1);
1266 RLevel1 = NULL;
1267 }
1268 else if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,3)==ERR_NONE)
1269 {
1270 if (RAttachments != NULL)
1271 Result |= OutputError(0x160,T("Extra Attachments found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1272 else
1273 {
1274 RAttachments = RLevel1;
1275 NodeTree_SetParent(RLevel1, RSegment, NULL);
1276 VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1277 Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1278 }
1279 }
1280 else
1281 {
1282 Result = OutputError(0x161,T("Failed to read the Attachments at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1283 goto exit;
1284 }
1285 }
1286 else
1287 {
1288 if (Node_IsPartOf(RLevel1,EBML_DUMMY_ID))
1289 {
1290 tchar_t Id[32];
1291 EBML_IdToString(Id,TSIZEOF(Id),EBML_ElementClassID((ebml_element*)RLevel1));
1292 OutputWarning(0x80,T("Unknown element %s at %") TPRId64 T(" size %") TPRId64,Id,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1293 }
1294 if (Node_IsPartOf(RLevel1,EBML_VOID_CLASS))
1295 {
1296 VoidAmount += EBML_ElementFullSize((ebml_element*)RLevel1,0);
1297 }
1298 RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1299 NodeDelete((node*)RLevel1);
1300 RLevel1 = NULL;
1301 }
1302 if (!Quiet) {
1303 TextWrite(StdErr,T(".")); ++DotCount;
1304 if (!(DotCount % 60))
1305 TextWrite(StdErr,T("\r \r"));
1306 }
1307
1308 Prev = RLevel1;
1309 if (RLevelX)
1310 RLevel1 = RLevelX;
1311 else
1312 RLevel1 = (ebml_master*)EBML_FindNextElement(Input, &RSegmentContext, &UpperElement, 1);
1313 }
1314
1315 if (!RSegmentInfo)
1316 {
1317 Result = OutputError(0x40,T("The segment is missing a SegmentInfo"));
1318 goto exit;
1319 }
1320
1321 if (Prev)
1322 {
1323 if (EBML_ElementPositionEnd((ebml_element*)RSegment)!=INVALID_FILEPOS_T && EBML_ElementPositionEnd((ebml_element*)RSegment)!=EBML_ElementPositionEnd((ebml_element*)Prev))
1324 Result |= OutputError(0x42,T("The segment's size %") TPRId64 T(" doesn't match the position where it ends %") TPRId64,EBML_ElementPositionEnd((ebml_element*)RSegment),EBML_ElementPositionEnd((ebml_element*)Prev));
1325 }
1326
1327 if (!RSeekHead)
1328 {
1329 if (!Live)
1330 OutputWarning(0x801,T("The segment has no SeekHead section"));
1331 }
1332 else
1333 Result |= CheckSeekHead(RSeekHead);
1334 if (RSeekHead2)
1335 Result |= CheckSeekHead(RSeekHead2);
1336
1337 if (ARRAYCOUNT(RClusters,ebml_element*))
1338 {
1339 if (!Quiet) TextWrite(StdErr,T("."));
1340 LinkClusterBlocks();
1341
1342 if (HasVideo)
1343 Result |= CheckVideoStart();
1344 Result |= CheckLacingKeyframe();
1345 Result |= CheckPosSize((ebml_element*)RSegment);
1346 if (!RCues)
1347 {
1348 if (!Live && ARRAYCOUNT(RClusters,ebml_element*)>1)
1349 OutputWarning(0x800,T("The segment has Clusters but no Cues section (bad for seeking)"));
1350 }
1351 else
1352 Result |= CheckCueEntries(RCues);
1353 if (!RTrackInfo)
1354 {
1355 Result = OutputError(0x41,T("The segment has Clusters but no TrackInfo section"));
1356 goto exit;
1357 }
1358 }
1359
1360 if (!Quiet) TextWrite(StdErr,T("."));
1361 if (RTrackInfo)
1362 CheckTracks(RTrackInfo, MatroskaProfile);
1363
1364 for (TI=ARRAYBEGIN(Tracks,track_info); TI!=ARRAYEND(Tracks,track_info); ++TI)
1365 {
1366 if (TI->DataLength==0)
1367 OutputWarning(0xB8,T("Track #%d is defined but has no frame"),TI->Num);
1368 }
1369
1370 if (VoidAmount > 4*1024)
1371 OutputWarning(0xD0,T("There are %") TPRId64 T(" bytes of void data\r\n"),VoidAmount);
1372
1373 if (!Quiet && Result==0)
1374 {
1375 TextPrintf(StdErr,T("\r%s %s: the file appears to be valid\r\n"),PROJECT_NAME,PROJECT_VERSION);
1376 if (Details)
1377 {
1378 track_info *TI;
1379 for (TI=ARRAYBEGIN(Tracks,track_info); TI!=ARRAYEND(Tracks,track_info); ++TI)
1380 {
1381 EBML_StringGet(TI->CodecID,String,TSIZEOF(String));
1382 TextPrintf(StdErr,T("Track #%d %18s %") TPRId64 T(" bits/s\r\n"),TI->Num,String,Scale64(TI->DataLength,8000000000,MaxTime-MinTime));
1383 }
1384 }
1385 }
1386
1387 exit:
1388 if (!Quiet && RSegmentInfo)
1389 {
1390 tchar_t App[MAXPATH];
1391 App[0] = 0;
1392 LibName = (ebml_string*)EBML_MasterFindChild(RSegmentInfo,&MATROSKA_ContextMuxingApp);
1393 AppName = (ebml_string*)EBML_MasterFindChild(RSegmentInfo,&MATROSKA_ContextWritingApp);
1394 if (LibName)
1395 {
1396 EBML_StringGet(LibName,String,TSIZEOF(String));
1397 tcscat_s(App,TSIZEOF(App),String);
1398 }
1399 if (AppName)
1400 {
1401 EBML_StringGet(AppName,String,TSIZEOF(String));
1402 if (App[0])
1403 tcscat_s(App,TSIZEOF(App),T(" / "));
1404 tcscat_s(App,TSIZEOF(App),String);
1405 }
1406 if (App[0]==0)
1407 tcscat_s(App,TSIZEOF(App),T("<unknown>"));
1408 TextPrintf(StdErr,T("\r\tfile created with %s\r\n"),App);
1409 }
1410
1411 for (Cluster = ARRAYBEGIN(RClusters,ebml_master*);Cluster != ARRAYEND(RClusters,ebml_master*); ++Cluster)
1412 NodeDelete((node*)*Cluster);
1413 ArrayClear(&RClusters);
1414 if (RAttachments)
1415 NodeDelete((node*)RAttachments);
1416 if (RTags)
1417 NodeDelete((node*)RTags);
1418 if (RCues)
1419 NodeDelete((node*)RCues);
1420 if (RChapters)
1421 NodeDelete((node*)RChapters);
1422 if (RTrackInfo)
1423 NodeDelete((node*)RTrackInfo);
1424 if (RSegmentInfo)
1425 NodeDelete((node*)RSegmentInfo);
1426 if (RLevel1)
1427 NodeDelete((node*)RLevel1);
1428 if (RSegment)
1429 NodeDelete((node*)RSegment);
1430 if (EbmlHead)
1431 NodeDelete((node*)EbmlHead);
1432 ArrayClear(&Tracks);
1433 if (Input)
1434 StreamClose(Input);
1435
1436 // EBML & Matroska ending
1437 MATROSKA_Done((nodecontext*)&p);
1438
1439 // Core-C ending
1440 StdAfx_Done((nodemodule*)&p);
1441 ParserContext_Done(&p);
1442
1443 return Result;
1444 }
1445