├── src ├── EndianMP4.h ├── ValidateMP4.r ├── ValidateAtoms.c ├── ValidateFileIO.c ├── ValidateAtomList.c ├── ValidateBits.c ├── ValidateMP4.c ├── ValidateMP4.h └── ValidateHints.c ├── CONTRIBUTING.md ├── PULL_REQUEST_TEMPLATE.md ├── linux └── Makefile ├── macosx └── makefile └── LICENSE.txt /src/EndianMP4.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macosforge/ValidateMP4/HEAD/src/EndianMP4.h -------------------------------------------------------------------------------- /src/ValidateMP4.r: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macosforge/ValidateMP4/HEAD/src/ValidateMP4.r -------------------------------------------------------------------------------- /src/ValidateAtoms.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macosforge/ValidateMP4/HEAD/src/ValidateAtoms.c -------------------------------------------------------------------------------- /src/ValidateFileIO.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macosforge/ValidateMP4/HEAD/src/ValidateFileIO.c -------------------------------------------------------------------------------- /src/ValidateAtomList.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macosforge/ValidateMP4/HEAD/src/ValidateAtomList.c -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | By submitting a request, you represent that you have the right to license 2 | your contribution to the community, and agree that your contributions are 3 | licensed under the [Apple Public Source License Version 2.0](LICENSE.txt). 4 | 5 | For existing files modified by your request, you represent that you have 6 | retained any existing copyright notices and licensing terms. For each new 7 | file in your request, you represent that you have added to the file a 8 | copyright notice (including the year and the copyright owner's name) and 9 | ValidateMP4's licensing terms. 10 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | By submitting a request, you represent that you have the right to license 2 | your contribution to the community, and agree that your contributions are 3 | licensed under the [Apple Public Source License Version 2.0](LICENSE.txt). 4 | 5 | For existing files modified by your request, you represent that you have 6 | retained any existing copyright notices and licensing terms. For each new 7 | file in your request, you represent that you have added to the file a 8 | copyright notice (including the year and the copyright owner's name) and 9 | ValidateMP4's licensing terms. 10 | -------------------------------------------------------------------------------- /linux/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This file contains Original Code and/or Modifications of Original Code 3 | # as defined in and that are subject to the Apple Public Source License 4 | # Version 2.0 (the 'License'). You may not use this file except in 5 | # compliance with the License. Please obtain a copy of the License at 6 | # http://www.opensource.apple.com/apsl/ and read it before using this 7 | # file. 8 | # 9 | # The Original Code and all software distributed under the License are 10 | # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 11 | # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 12 | # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 13 | # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 14 | # Please see the License for the specific language governing rights and 15 | # limitations under the License. 16 | # 17 | 18 | VPATH = . ../src 19 | 20 | CFLAGS = -g -DLITTLEENDIAN -Wno-multichar 21 | 22 | CC = gcc 23 | 24 | HEADERS = \ 25 | ValidateMP4.h \ 26 | EndianMP4.h 27 | 28 | SOURCES = \ 29 | ValidateAtomList.c \ 30 | ValidateAtoms.c \ 31 | ValidateBitStreams.c \ 32 | ValidateBits.c \ 33 | ValidateFileIO.c \ 34 | ValidateHints.c \ 35 | ValidateMP4.c 36 | 37 | OBJECTS := $(patsubst %.c,%.o,$(SOURCES)) 38 | 39 | ValidateMP4: $(OBJECTS) $(HEADERS) 40 | $(CC) -g -o $@ $(CFLAGS) $(OBJECTS) 41 | 42 | clean: 43 | -rm $(OBJECTS) $(SOURCES:.c=.d) ValidateMP4 44 | 45 | 46 | %.d: %.c 47 | $(SHELL) -ec '$(CC) -M $(CFLAGS) $< | sed '\''s/$*.o/& $@/g'\'' > $@' 48 | 49 | TAGS: $(SOURCES) 50 | etags $^ 51 | 52 | # 53 | # Include all dependency files 54 | # 55 | -include $(SOURCES:.c=.d) 56 | -------------------------------------------------------------------------------- /macosx/makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This file contains Original Code and/or Modifications of Original Code 3 | # as defined in and that are subject to the Apple Public Source License 4 | # Version 2.0 (the 'License'). You may not use this file except in 5 | # compliance with the License. Please obtain a copy of the License at 6 | # http://www.opensource.apple.com/apsl/ and read it before using this 7 | # file. 8 | # 9 | # The Original Code and all software distributed under the License are 10 | # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 11 | # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 12 | # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 13 | # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 14 | # Please see the License for the specific language governing rights and 15 | # limitations under the License. 16 | # 17 | 18 | #CFLAGS = -g -DLITTLEENDIAN -Wno-multichar -arch i386 19 | CFLAGS = -gdwarf-2 -Wno-multichar -DUSE_STRCASECMP 20 | PreprocessOptions = "-d forPublicRelease -d no3GP" 21 | 22 | SRCDIR = ../src 23 | PUBDIR = ../public/ 24 | #OBJDIR = ./obj 25 | OBJDIR = . 26 | 27 | VPATH = ../src:./obj 28 | 29 | HEADERS = \ 30 | $(SRCDIR)/ValidateMP4.h \ 31 | $(SRCDIR)/EndianMP4.h 32 | 33 | 34 | 35 | SOURCES = \ 36 | ValidateAtomList.c \ 37 | ValidateAtoms.c \ 38 | ValidateBitStreams.c \ 39 | ValidateBits.c \ 40 | ValidateFileIO.c \ 41 | ValidateHints.c \ 42 | ValidateMP4.c 43 | 44 | OBJS := $(patsubst %.c,%.o,$(SOURCES)) 45 | 46 | 47 | ValidateMP4: ValidateObjDir $(OBJS) $(HEADERS) 48 | $(CC) -o $@ $(CFLAGS) $(OBJS) 49 | 50 | ValidateObjDir: 51 | # mkdir -p $(OBJDIR) 52 | 53 | clean: 54 | -rm $(OBJS) $(SOURCES:.c=.d) ValidateMP4 55 | 56 | 57 | %.d: %.c 58 | $(SHELL) -ec '$(CC) -M $(CFLAGS) $< | sed '\''s/$*.o/& $@/g'\'' > $@' 59 | 60 | TAGS: $(SOURCES) 61 | etags $^ 62 | 63 | 64 | # 65 | # Include all dependency files 66 | # 67 | -include $(SOURCES:.c=.d) 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/ValidateBits.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file contains Original Code and/or Modifications of Original Code 4 | as defined in and that are subject to the Apple Public Source License 5 | Version 2.0 (the 'License'). You may not use this file except in 6 | compliance with the License. Please obtain a copy of the License at 7 | http://www.opensource.apple.com/apsl/ and read it before using this 8 | file. 9 | 10 | The Original Code and all software distributed under the License are 11 | distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 12 | EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 13 | INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 15 | Please see the License for the specific language governing rights and 16 | limitations under the License. 17 | 18 | */ 19 | 20 | #include "ValidateMP4.h" 21 | 22 | 23 | 24 | OSErr BitBuffer_Init(BitBuffer *bb, UInt8 *p, UInt32 length) 25 | { 26 | int err = noErr; 27 | 28 | if (length > 0x0fffffff) { 29 | err = paramErr; 30 | goto bail; 31 | } 32 | 33 | bb->ptr = (void*)p; 34 | bb->length = length; 35 | 36 | bb->cptr = (void*)p; 37 | bb->cbyte = *bb->cptr; 38 | bb->curbits = 8; 39 | 40 | bb->bits_left = length * 8; 41 | 42 | bb->prevent_emulation = 0; 43 | bb->emulation_position = (bb->cbyte == 0 ? 1 : 0); 44 | 45 | bail: 46 | return err; 47 | } 48 | 49 | 50 | 51 | OSErr GetBytes(BitBuffer *bb, UInt32 nBytes, UInt8 *p) 52 | { 53 | OSErr err = noErr; 54 | unsigned int i; 55 | 56 | for (i = 0; i < nBytes; i++) { 57 | *p++ = (UInt8)GetBits(bb, 8, &err); 58 | if (err) break; 59 | } 60 | 61 | return err; 62 | } 63 | 64 | OSErr SkipBytes(BitBuffer *bb, UInt32 nBytes) 65 | { 66 | OSErr err = noErr; 67 | unsigned int i; 68 | 69 | for (i = 0; i < nBytes; i++) { 70 | GetBits(bb, 8, &err); 71 | if (err) break; 72 | } 73 | 74 | return err; 75 | } 76 | 77 | 78 | UInt32 NumBytesLeft(BitBuffer *bb) 79 | { 80 | UInt32 numBytes; 81 | 82 | numBytes = ((bb->bits_left + 7) / 8); 83 | return numBytes; 84 | } 85 | 86 | UInt32 GetBits(BitBuffer *bb, UInt32 nBits, OSErr *errout) 87 | { 88 | OSErr err = noErr; 89 | int myBits; 90 | int myValue = 0; 91 | int myResidualBits; 92 | int leftToRead; 93 | 94 | if (nBits==0) goto bail; 95 | 96 | if (nBits > bb->bits_left || 0 == bb->bits_left) { 97 | err = outOfDataErr; 98 | goto bail; 99 | } 100 | 101 | if (bb->curbits <= 0) { 102 | bb->cbyte = *++bb->cptr; 103 | bb->curbits = 8; 104 | 105 | if (bb->prevent_emulation != 0) { 106 | if ((bb->emulation_position >= 2) && (bb->cbyte == 3)) { 107 | bb->cbyte = *++bb->cptr; 108 | bb->bits_left -= 8; 109 | bb->emulation_position = 0; 110 | if (nBits>bb->bits_left) { 111 | err = outOfDataErr; 112 | goto bail; 113 | } 114 | } 115 | else if (bb->cbyte == 0) bb->emulation_position += 1; 116 | else bb->emulation_position = 0; 117 | } 118 | } 119 | 120 | if (nBits > bb->curbits) 121 | myBits = bb->curbits; 122 | else 123 | myBits = nBits; 124 | 125 | myValue = (bb->cbyte>>(8-myBits)); 126 | myResidualBits = bb->curbits - myBits; 127 | leftToRead = nBits - myBits; 128 | bb->bits_left -= myBits; 129 | 130 | bb->curbits = myResidualBits; 131 | bb->cbyte = ((bb->cbyte) << myBits) & 0xff; 132 | 133 | if (leftToRead > 0) { 134 | UInt32 newBits; 135 | newBits = GetBits(bb, leftToRead, &err); 136 | myValue = (myValue<bb->bits_left) { 157 | err = outOfDataErr; 158 | goto bail; 159 | } 160 | 161 | if (bb->curbits <= 0) { 162 | bb->cbyte = *++bb->cptr; 163 | bb->curbits = 8; 164 | } 165 | 166 | if (nBits > bb->curbits) 167 | myBits = bb->curbits; 168 | else 169 | myBits = nBits; 170 | 171 | myValue = (bb->cbyte>>(8-myBits)); 172 | myResidualBits = bb->curbits - myBits; 173 | leftToRead = nBits - myBits; 174 | 175 | bb->curbits = myResidualBits; 176 | bb->cbyte = ((bb->cbyte) << myBits) & 0xff; 177 | 178 | if (leftToRead > 0) { 179 | UInt32 newBits; 180 | newBits = PeekBits(bb, leftToRead, &err); 181 | myValue = (myValue< 0) { 271 | value = GetBits( bb, nbits, &err); if (err) goto bail; 272 | } 273 | 274 | bail: 275 | if (errout) *errout = err; 276 | return (power - 1 + value); 277 | } 278 | 279 | SInt32 read_golomb_sev(BitBuffer *bb, OSErr *errout) 280 | { 281 | OSErr err = noErr; 282 | UInt32 uev; 283 | SInt32 val; 284 | 285 | uev = read_golomb_uev( bb, &err ); if (err) goto bail; 286 | if (uev & 1) 287 | val = (uev + 1)/2; 288 | else 289 | val = -1 * (uev/2); 290 | bail: 291 | if (errout) *errout = err; 292 | return val; 293 | } 294 | 295 | UInt32 strip_trailing_zero_bits(BitBuffer *bb, OSErr *errout) 296 | { 297 | OSErr err = noErr; 298 | UInt8 bit_check = 1; 299 | UInt8* byte_ptr; 300 | UInt32 trailing = 0, bits; 301 | 302 | bits = bb->bits_left; 303 | byte_ptr = bb->cptr; 304 | 305 | bits -= bb->curbits; 306 | byte_ptr++; 307 | 308 | byte_ptr += (bits / 8); 309 | bits = bits % 8; 310 | if (bits == 0) { bits = 8; byte_ptr--; } 311 | 312 | bit_check = 1 << (8- bits); 313 | 314 | while (( *byte_ptr & bit_check ) == 0) { 315 | trailing++; 316 | if (bit_check == 0x80) { 317 | if ((--byte_ptr) < bb->cptr) { 318 | err = outOfDataErr; 319 | goto bail; 320 | } 321 | bit_check = 1; 322 | } else bit_check = bit_check << 1; 323 | bb->bits_left -= 1; 324 | } 325 | bail: 326 | if (errout != NULL) *errout = err; 327 | return trailing; 328 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | APPLE PUBLIC SOURCE LICENSE 2 | Version 2.0 - August 6, 2003 3 | 4 | Please read this License carefully before downloading this software. By downloading or using this software, you are agreeing to be bound by the terms of this License. If you do not or cannot agree to the terms of this License, please do not download or use the software. 5 | 6 | Apple Note: In January 2007, Apple changed its corporate name from "Apple Computer, Inc." to "Apple Inc." This change has been reflected below and copyright years updated, but no other changes have been made to the APSL 2.0. 7 | 8 | 1. General; Definitions. This License applies to any program or other work which Apple Inc. ("Apple") makes publicly available and which contains a notice placed by Apple identifying such program or work as "Original Code" and stating that it is subject to the terms of this Apple Public Source License version 2.0 ("License"). As used in this License: 9 | 10 | 1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to Apple and (ii) that cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce and/or distribute the Original Code without infringement; and (b) in the case where You are the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to You and (ii) that cover subject matter in Your Modifications, taken alone or in combination with Original Code. 11 | 12 | 1.2 "Contributor" means any person or entity that creates or contributes to the creation of Modifications. 13 | 14 | 1.3 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof. 15 | 16 | 1.4 "Externally Deploy" means: (a) to sublicense, distribute or otherwise make Covered Code available, directly or indirectly, to anyone other than You; and/or (b) to use Covered Code, alone or as part of a Larger Work, in any way to provide a service, including but not limited to delivery of content, through electronic communication with a client other than You. 17 | 18 | 1.5 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 19 | 20 | 1.6 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of the Original Code, any previous Modifications, the combination of Original Code and any previous Modifications, and/or any respective portions thereof. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code. 21 | 22 | 1.7 "Original Code" means (a) the Source Code of a program or other work as originally made available by Apple under this License, including the Source Code of any updates or upgrades to such programs or works made available by Apple under this License, and that has been expressly identified by Apple as such in the header file(s) of such work; and (b) the object code compiled from such Source Code and originally made available by Apple under this License 23 | 24 | 1.8 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code). 25 | 26 | 1.9 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. 27 | 28 | 2. Permitted Uses; Conditions & Restrictions. Subject to the terms and conditions of this License, Apple hereby grants You, effective on the date You accept this License and download the Original Code, a world-wide, royalty-free, non-exclusive license, to the extent of Apple's Applicable Patent Rights and copyrights covering the Original Code, to do the following: 29 | 30 | 2.1 Unmodified Code. You may use, reproduce, display, perform, internally distribute within Your organization, and Externally Deploy verbatim, unmodified copies of the Original Code, for commercial or non-commercial purposes, provided that in each instance: 31 | 32 | (a) You must retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Apple as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License; and 33 | 34 | (b) You must include a copy of this License with every copy of Source Code of Covered Code and documentation You distribute or Externally Deploy, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6. 35 | 36 | 2.2 Modified Code. You may modify Covered Code and use, reproduce, display, perform, internally distribute within Your organization, and Externally Deploy Your Modifications and Covered Code, for commercial or non-commercial purposes, provided that in each instance You also meet all of these conditions: 37 | 38 | (a) You must satisfy all the conditions of Section 2.1 with respect to the Source Code of the Covered Code; 39 | 40 | (b) You must duplicate, to the extent it does not already exist, the notice in Exhibit A in each file of the Source Code of all Your Modifications, and cause the modified files to carry prominent notices stating that You changed the files and the date of any change; and 41 | 42 | (c) If You Externally Deploy Your Modifications, You must make Source Code of all Your Externally Deployed Modifications either available to those to whom You have Externally Deployed Your Modifications, or publicly available. Source Code of Your Externally Deployed Modifications must be released under the terms set forth in this License, including the license grants set forth in Section 3 below, for as long as you Externally Deploy the Covered Code or twelve (12) months from the date of initial External Deployment, whichever is longer. You should preferably distribute the Source Code of Your Externally Deployed Modifications electronically (e.g. download from a web site). 43 | 44 | 2.3 Distribution of Executable Versions. In addition, if You Externally Deploy Covered Code (Original Code and/or Modifications) in object code, executable form only, You must include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code. 45 | 46 | 2.4 Third Party Rights. You expressly acknowledge and agree that although Apple and each Contributor grants the licenses to their respective portions of the Covered Code set forth herein, no assurances are provided by Apple or any Contributor that the Covered Code does not infringe the patent or other intellectual property rights of any other entity. Apple and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to distribute the Covered Code, it is Your responsibility to acquire that license before distributing the Covered Code. 47 | 48 | 3. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License, You hereby grant to any person or entity receiving or distributing Covered Code under this License a non-exclusive, royalty-free, perpetual, irrevocable license, under Your Applicable Patent Rights and other intellectual property rights (other than patent) owned or controlled by You, to use, reproduce, display, perform, modify, sublicense, distribute and Externally Deploy Your Modifications of the same scope and extent as Apple's licenses under Sections 2.1 and 2.2 above. 49 | 50 | 4. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof. 51 | 52 | 5. Limitations on Patent License. Except as expressly stated in Section 2, no other patent rights, express or implied, are granted by Apple herein. Modifications and/or Larger Works may require additional patent licenses from Apple which Apple may grant in its sole discretion. 53 | 54 | 6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the scope of the license granted herein ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Apple or any Contributor. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Apple and every Contributor harmless for any liability incurred by or claims asserted against Apple or such Contributor by reason of any such Additional Terms. 55 | 56 | 7. Versions of the License. Apple may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Apple. No one other than Apple has the right to modify the terms applicable to Covered Code created under this License. 57 | 58 | 8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part pre-release, untested, or not fully tested works. The Covered Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Covered Code, or any portion thereof, is at Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge that the Covered Code is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Covered Code could lead to death, personal injury, or severe physical or environmental damage. 59 | 60 | 9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall Apple's total liability to You for all damages (other than as may be required by applicable law) under this License exceed the amount of fifty dollars ($50.00). 61 | 62 | 10. Trademarks. This License does not grant any rights to use the trademarks or trade names "Apple", "Mac", "Mac OS", "QuickTime", "QuickTime Streaming Server" or any other trademarks, service marks, logos or trade names belonging to Apple (collectively "Apple Marks") or to any trademark, service mark, logo or trade name belonging to any Contributor. You agree not to use any Apple Marks in or as part of the name of products derived from the Original Code or to endorse or promote products derived from the Original Code other than as expressly permitted by and in strict compliance at all times with Apple's third party trademark usage guidelines which are posted at http://www.apple.com/legal/guidelinesfor3rdparties.html. 63 | 64 | 11. Ownership. Subject to the licenses granted under this License, each Contributor retains all rights, title and interest in and to any Modifications made by such Contributor. Apple retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Apple ("Apple Modifications"), and such Apple Modifications will not be automatically subject to this License. Apple may, at its sole discretion, choose to license such Apple Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all. 65 | 66 | 12. Termination. 67 | 68 | 12.1 Termination. This License and the rights granted hereunder will terminate: 69 | 70 | (a) automatically without notice from Apple if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach; 71 | (b) immediately in the event of the circumstances described in Section 13.5(b); or 72 | (c) automatically without notice from Apple if You, at any time during the term of this License, commence an action for patent infringement against Apple; provided that Apple did not first commence an action for patent infringement against You in that instance. 73 | 74 | 12.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification, sublicensing and distribution of the Covered Code. All sublicenses to the Covered Code which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of any party. 75 | 76 | 13. Miscellaneous. 77 | 78 | 13.1 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein. 79 | 80 | 13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between or among You, Apple or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise. 81 | 82 | 13.3 Independent Development. Nothing in this License will impair Apple's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Larger Works, technology or products that You may develop, produce, market or distribute. 83 | 84 | 13.4 Waiver; Construction. Failure by Apple or any Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License. 85 | 86 | 13.5 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control. 87 | 88 | 13.6 Dispute Resolution. Any litigation or other dispute resolution between You and Apple relating to this License shall take place in the Northern District of California, and You and Apple hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. 89 | 90 | 13.7 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law. 91 | 92 | Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exigé que le présent contrat et tous les documents connexes soient rédigés en anglais. 93 | 94 | EXHIBIT A. 95 | 96 | "Portions Copyright (c) 1999-2007 Apple Inc. All Rights Reserved. 97 | 98 | This file contains Original Code and/or Modifications of Original Code as defined in and that are subject to the Apple Public Source License Version 2.0 (the 'License'). You may not use this file except in compliance with the License. Please obtain a copy of the License at http://www.opensource.apple.com/apsl/ and read it before using this file. 99 | 100 | The Original Code and all software distributed under the License are distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the specific language governing rights and limitations under the License." -------------------------------------------------------------------------------- /src/ValidateMP4.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file contains Original Code and/or Modifications of Original Code 4 | as defined in and that are subject to the Apple Public Source License 5 | Version 2.0 (the 'License'). You may not use this file except in 6 | compliance with the License. Please obtain a copy of the License at 7 | http://www.opensource.apple.com/apsl/ and read it before using this 8 | file. 9 | 10 | The Original Code and all software distributed under the License are 11 | distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 12 | EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 13 | INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 15 | Please see the License for the specific language governing rights and 16 | limitations under the License. 17 | 18 | */ 19 | 20 | 21 | 22 | #include "ValidateMP4.h" 23 | #if STAND_ALONE_APP 24 | #include "console.h" 25 | #endif 26 | 27 | #if 1 28 | #define myTAB " " 29 | #else 30 | #define myTAB "\t" 31 | #endif 32 | 33 | ValidateGlobals vg = {0}; 34 | 35 | 36 | static int keymatch (const char * arg, const char * keyword, int minchars); 37 | 38 | //#define STAND_ALONE_APP 1 // #define this if you're using a source level debugger (i.e. Visual C++ in Windows) 39 | // also, near the beginning of main(), hard-code your arguments (e.g. your test file) 40 | 41 | //========================================================================================== 42 | 43 | /* 44 | * Case-insensitive matching of possibly-abbreviated keyword switches. 45 | * keyword is the constant keyword (must be lower case already), 46 | * minchars is length of minimum legal abbreviation. 47 | */ 48 | 49 | static int keymatch (const char * arg, const char * keyword, int minchars) 50 | { 51 | register int ca, ck; 52 | register int nmatched = 0; 53 | 54 | while ((ca = *arg++) != '\0') { 55 | if ((ck = *keyword++) == '\0') 56 | return false; /* arg longer than keyword, no good */ 57 | if (isupper(ca)) /* force arg to lcase (assume ck is already) */ 58 | ca = tolower(ca); 59 | if (ca != ck) 60 | return false; /* no good */ 61 | nmatched++; /* count matched characters */ 62 | } 63 | /* reached end of argument; fail if it's too short for unique abbrev */ 64 | if (nmatched < minchars) 65 | return false; 66 | return true; /* A-OK */ 67 | } 68 | 69 | //========================================================================================== 70 | //_MSL_IMP_EXP_C extern int ccommand(char ***); 71 | 72 | #define getNextArgStr( _str_, _str_err_str_ ) \ 73 | argn++; \ 74 | arg = argv[argn]; \ 75 | if( nil == arg ) \ 76 | { \ 77 | fprintf( stderr, "Expected " _str_err_str_ " got end of args\n" ); \ 78 | err = -1; \ 79 | goto usageError; \ 80 | } \ 81 | if( arg[0] == '-' ) \ 82 | { \ 83 | fprintf( stderr, "Expected " _str_err_str_ " next arg\n" ); \ 84 | err = -1; \ 85 | goto usageError; \ 86 | } \ 87 | strcpy(*(_str_), arg); 88 | 89 | 90 | #if !STAND_ALONE_APP 91 | int main(int argc, char *argv[]); 92 | int main(int argc, char *argv[]) 93 | { 94 | #else 95 | int main(void); 96 | int main(void) 97 | { 98 | char *argv[] = { 99 | "ValidateMP4", 100 | "" 101 | }; 102 | int argc = sizeof(argv)/sizeof(char*); 103 | #endif 104 | int argn; 105 | int gotInputFile = false; 106 | int err; 107 | char gInputFileFullPath[1024]; 108 | int usedefaultfiletype = true; 109 | 110 | FILE *infile = nil; 111 | atomOffsetEntry aoe = {0}; 112 | 113 | vg.warnings = true; 114 | // vg.qtwarnings = true; 115 | // vg.print_atompath = true; 116 | // strcpy( vg.atompath, "moov-1:trak-1:mdia-1:minf-1:stbl-1:stsd-1" ); 117 | // vg.print_atom = true; 118 | // vg.print_fulltable = true; 119 | // vg.print_sample = true; 120 | // vg.print_sampleraw = true; 121 | // vg.print_hintpayload = true; 122 | vg.visualProfileLevelIndication = 255; 123 | // this is simply the wrong place for this; it's not a program parameter, it's the mpeg-4 124 | // profile/level indication as found in the video stream. 125 | // But neither movie info nor track info are available at the right points. Ugh [dws] 126 | 127 | 128 | 129 | 130 | // Check the parameters 131 | for( argn = 1; argn < argc; argn++ ) 132 | { 133 | const char *arg = argv[argn]; 134 | 135 | if( '-' != arg[0] ) 136 | { 137 | char *extensionstartp = nil; 138 | 139 | if (gotInputFile) { 140 | fprintf( stderr, "Unexpected argument \"%s\"\n", arg ); 141 | err = -1; 142 | goto usageError; 143 | } 144 | strcpy(gInputFileFullPath, arg); 145 | gotInputFile = true; 146 | 147 | #ifdef USE_STRCASECMP 148 | #define rStrCaseCmp(a,b) strcasecmp(a,b) 149 | #else 150 | #define rStrCaseCmp(a,b) my_stricmp(a,b) 151 | #endif 152 | extensionstartp = strrchr(gInputFileFullPath,'.'); 153 | if (extensionstartp) { 154 | if (rStrCaseCmp(extensionstartp,".mp4") == 0) { 155 | vg.filetype = filetype_mp4; 156 | usedefaultfiletype = false; 157 | } 158 | } 159 | 160 | continue; 161 | } 162 | 163 | arg++; // skip '-' 164 | 165 | if( keymatch( arg, "help", 1 ) ) { 166 | goto usageError; 167 | } else if( keymatch( arg, "warnings", 1 ) ) { 168 | vg.warnings = true; 169 | } else if ( keymatch( arg, "filetype", 1 ) ) { 170 | getNextArgStr( &vg.filetypestr, "filetype" ); 171 | } else if ( keymatch( arg, "atompath", 1 ) ) { 172 | getNextArgStr( &vg.atompath, "atompath" ); 173 | } else if ( keymatch( arg, "checklevel", 1 ) ) { 174 | getNextArgStr( &vg.checklevelstr, "checklevel" ); 175 | } else if ( keymatch( arg, "printtype", 1 ) ) { 176 | getNextArgStr( &vg.printtypestr, "printtype" ); 177 | } else if ( keymatch( arg, "samplenumber", 1 ) ) { 178 | getNextArgStr( &vg.samplenumberstr, "samplenumber" ); 179 | 180 | 181 | 182 | } else { 183 | fprintf( stderr, "Unexpected option \"%s\"\n", arg ); 184 | err = -1; 185 | goto usageError; 186 | } 187 | } 188 | 189 | 190 | //===================== 191 | // Process input parameters 192 | 193 | if ((usedefaultfiletype && (vg.filetypestr[0] == 0)) || // default to mp4 194 | (strcmp(vg.filetypestr, "mp4") == 0)) { 195 | vg.filetype = filetype_mp4; 196 | } else if (strcmp(vg.filetypestr, "mp4v") == 0) { 197 | vg.filetype = filetype_mp4v; 198 | } else if (vg.filetype == 0) { 199 | fprintf( stderr, "Invalid filetype\n" ); 200 | err = -1; 201 | goto usageError; 202 | } 203 | 204 | if (vg.checklevelstr[0] == 0) { 205 | vg.checklevel = 1; // default 206 | } else { 207 | vg.checklevel = atoi(vg.checklevelstr); 208 | if (vg.checklevel < 1) { 209 | fprintf( stderr, "Invalid check level\n" ); 210 | goto usageError; 211 | } 212 | } 213 | 214 | if (vg.printtypestr[0] == 0) { 215 | // default is not to print anything 216 | } else { 217 | char instr[256]; 218 | char *tokstr; 219 | 220 | strcpy(instr, vg.printtypestr); 221 | tokstr = strtok(instr,"+"); 222 | while (tokstr) { 223 | if (keymatch(tokstr, "atompath", 5)) { 224 | vg.print_atompath = true; 225 | } else if (keymatch(tokstr, "atom", 4)) { 226 | vg.print_atom = true; 227 | } else if (keymatch(tokstr, "fulltable", 1)) { 228 | vg.print_fulltable = true; 229 | } else if (keymatch(tokstr, "sampleraw", 7)) { 230 | vg.print_sampleraw = true; 231 | } else if (keymatch(tokstr, "sample", 6)) { 232 | vg.print_sample = true; 233 | } else if (keymatch(tokstr, "hintpayload", 1)) { 234 | vg.print_hintpayload = true; 235 | } else { 236 | fprintf( stderr, "Invalid print type option\n" ); 237 | goto usageError; 238 | } 239 | tokstr = strtok(nil,"+"); 240 | } 241 | } 242 | 243 | if (vg.samplenumberstr[0] == 0) { 244 | vg.samplenumber = 0; // zero means print them all if you print any 245 | } else { 246 | vg.samplenumber = atoi(vg.samplenumberstr); 247 | if (vg.samplenumber < 1) goto usageError; 248 | } 249 | 250 | //===================== 251 | 252 | if (!gotInputFile) { 253 | err = -1; 254 | fprintf( stderr, "No input file specified\n" ); 255 | goto usageError; 256 | } 257 | 258 | infile = fopen(gInputFileFullPath, "rb"); 259 | if (!infile) { 260 | err = -1; 261 | fprintf( stderr, "Could not open input file \"%s\"\n", gInputFileFullPath ); 262 | goto usageError; 263 | } 264 | 265 | fprintf(stdout,"\n\n\n\n", gInputFileFullPath); 266 | 267 | vg.inFile = infile; 268 | vg.inOffset = 0; 269 | err = fseek(infile, 0, SEEK_END); 270 | if (err) goto bail; 271 | vg.inMaxOffset = ftell( infile ); 272 | if (vg.inMaxOffset < 0) { 273 | err = vg.inMaxOffset; 274 | goto bail; 275 | } 276 | 277 | aoe.type = 'file'; 278 | aoe.size = vg.inMaxOffset; 279 | aoe.offset = 0; 280 | aoe.atomStartSize = 0; 281 | aoe.maxOffset = aoe.size; 282 | 283 | vg.fileaoe = &aoe; // used when you need to read file & size from the file 284 | 285 | if (vg.filetype == filetype_mp4v) { 286 | err = ValidateElementaryVideoStream( &aoe, nil ); 287 | } else { 288 | err = ValidateFileAtoms( &aoe, nil ); 289 | fprintf(stdout,"\n", gInputFileFullPath); 290 | } 291 | 292 | goto bail; 293 | 294 | //===================== 295 | 296 | usageError: 297 | fprintf( stderr, "Usage: %s [-filetype ] " 298 | "[-printtype ] [-checklevel ]\n", "ValidateMP4" ); 299 | fprintf( stderr, " [-samplenumber ] [-verbose [-help] inputfile\n" ); 300 | fprintf( stderr, " -a[tompath] - limit certain operations to (e.g. moov-1:trak-2)\n" ); 301 | fprintf( stderr, " this effects -checklevel and -printtype (default is everything) \n" ); 302 | fprintf( stderr, " -p[rinttype] - controls output (combine options with +) \n" ); 303 | fprintf( stderr, " atompath - output the atompath for each atom \n" ); 304 | fprintf( stderr, " atom - output the contents of each atom \n" ); 305 | fprintf( stderr, " fulltable - output those long tables (e.g. samplesize tables) \n" ); 306 | fprintf( stderr, " sample - output the samples as well \n" ); 307 | fprintf( stderr, " (depending on the track type, this is the same as sampleraw) \n" ); 308 | fprintf( stderr, " sampleraw - output the samples in raw form \n" ); 309 | fprintf( stderr, " hintpayload - output payload for hint tracks \n" ); 310 | fprintf( stderr, " -c[hecklevel] - increase the amount of checking performed \n" ); 311 | fprintf( stderr, " 1: check the moov container (default -atompath is ignored) \n" ); 312 | fprintf( stderr, " 2: check the samples \n" ); 313 | fprintf( stderr, " 3: check the payload of hint track samples \n" ); 314 | fprintf( stderr, " -s[amplenumber] - limit sample checking or printing operations to sample \n" ); 315 | fprintf( stderr, " most effective in combination with -atompath (default is all samples) \n" ); 316 | 317 | fprintf( stderr, " -h[elp] - print this usage message \n" ); 318 | 319 | 320 | //===================== 321 | 322 | bail: 323 | if (infile) { 324 | fclose(infile); 325 | } 326 | 327 | return err; 328 | } 329 | 330 | 331 | //========================================================================================== 332 | 333 | #include 334 | // change here if you want to send both types of output to stdout to get interleaved output 335 | #if 1 336 | #define _stdout stdout 337 | #define _stderr stderr 338 | #else 339 | #define _stdout stdout 340 | #define _stderr stdout 341 | #endif 342 | 343 | void toggleprintatom( Boolean onOff ) 344 | { 345 | 346 | // need hackaround to print certain things - can't figure out arg scheme 347 | // true is on, false is off 348 | vg.printatom = onOff; 349 | // if( vg.printatom ) 350 | // fprintf( _stdout, "--> turning ON vg.printatom \n"); 351 | // else 352 | // fprintf( _stdout, "--> turning OFF vg.printatom \n" ); 353 | 354 | } 355 | 356 | void toggleprintatomdetailed( Boolean onOff ) 357 | { 358 | 359 | // need hackaround to print certain things - can't figure out arg scheme 360 | // true is on, false is off 361 | vg.printatom = onOff; 362 | vg.print_fulltable = onOff; 363 | // if( vg.printatom ) 364 | // fprintf( _stdout, "--> turning ON vg.printatom and vg.print_fulltable \n" ); 365 | // else 366 | // fprintf( _stdout, "--> turning OFF vg.printatom and vg.print_fulltable \n" ); 367 | 368 | } 369 | 370 | void toggleprintsample( Boolean onOff ) 371 | { 372 | 373 | // need hackaround to print certain things - can't figure out arg scheme 374 | // true is on, false is off 375 | vg.printsample = onOff; 376 | 377 | } 378 | 379 | 380 | void atomprintnotab(const char *formatStr, ...) 381 | { 382 | va_list ap; 383 | va_start(ap, formatStr); 384 | 385 | if (vg.printatom) 386 | { 387 | vfprintf( _stdout, formatStr, (void *)ap ); 388 | } 389 | 390 | va_end(ap); 391 | } 392 | 393 | void atomprint(const char *formatStr, ...) 394 | { 395 | va_list ap; 396 | va_start(ap, formatStr); 397 | 398 | if (vg.printatom) { 399 | long tabcnt = vg.tabcnt; 400 | while (tabcnt--) { 401 | fprintf(_stdout,myTAB); 402 | } 403 | vfprintf( _stdout, formatStr, (void *)ap ); 404 | } 405 | 406 | va_end(ap); 407 | } 408 | 409 | void atomprinthexdata(char *dataP, UInt32 size) 410 | { 411 | char hexstr[4] = "12 "; 412 | int widthCnt = 0; 413 | char c; 414 | static char hc[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 415 | 416 | while (size) { 417 | c = *dataP++; 418 | hexstr[0] = hc[(c>>4)&0x0F]; 419 | hexstr[1] = hc[(c )&0x0F]; 420 | if (widthCnt == 0) { 421 | atomprint(hexstr); 422 | } else { 423 | atomprintnotab(hexstr); 424 | } 425 | if (++widthCnt >= 16) { 426 | widthCnt = 0; 427 | atomprint("\n"); 428 | } 429 | size--; 430 | } 431 | if (widthCnt != 0) 432 | atomprint("\n"); 433 | } 434 | 435 | 436 | 437 | void atomprintdetailed(const char *formatStr, ...) 438 | { 439 | va_list ap; 440 | va_start(ap, formatStr); 441 | 442 | if (vg.printatom && vg.print_fulltable) { 443 | long tabcnt = vg.tabcnt; 444 | while (tabcnt--) { 445 | fprintf(_stdout,myTAB); 446 | } 447 | vfprintf( _stdout, formatStr, (void *)ap ); 448 | } 449 | 450 | va_end(ap); 451 | } 452 | 453 | void sampleprint(const char *formatStr, ...) 454 | { 455 | va_list ap; 456 | va_start(ap, formatStr); 457 | 458 | if (vg.printsample) { 459 | long tabcnt = vg.tabcnt; 460 | while (tabcnt--) { 461 | fprintf(_stdout,myTAB); 462 | } 463 | vfprintf( _stdout, formatStr, (void *)ap ); 464 | } 465 | 466 | va_end(ap); 467 | } 468 | 469 | void sampleprintnotab(const char *formatStr, ...) 470 | { 471 | va_list ap; 472 | va_start(ap, formatStr); 473 | 474 | if (vg.printsample) { 475 | vfprintf( _stdout, formatStr, (void *)ap ); 476 | } 477 | 478 | va_end(ap); 479 | } 480 | 481 | void sampleprinthexdata(char *dataP, UInt32 size) 482 | { 483 | char hexstr[4] = "12 "; 484 | int widthCnt = 0; 485 | char c; 486 | static char hc[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 487 | 488 | while (size) { 489 | c = *dataP++; 490 | hexstr[0] = hc[(c>>4)&0x0F]; 491 | hexstr[1] = hc[(c )&0x0F]; 492 | if (widthCnt == 0) { 493 | sampleprint(hexstr); 494 | } else { 495 | sampleprintnotab(hexstr); 496 | } 497 | if (++widthCnt >= 16) { 498 | widthCnt = 0; 499 | sampleprint("\n"); 500 | } 501 | size--; 502 | } 503 | if (widthCnt != 0) 504 | sampleprint("\n"); 505 | } 506 | 507 | 508 | void sampleprinthexandasciidata(char *dataP, UInt32 size) 509 | { 510 | char hexstr[4] = "12 "; 511 | char asciiStr[17]; 512 | int widthCnt = 0; 513 | char threeSpaces[4] = " "; 514 | char c; 515 | static char hc[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 516 | 517 | 518 | // similar to sampleprinthexdata() but also prints ascii characters to the right of hex dump 519 | // (ala Mac OS X's HexDump or 9's MacsBug; if the character is not ascii, it will print a '.' ) 520 | // sampleprintnotab("\n"); 521 | 522 | asciiStr[16] = 0; 523 | while (size) { 524 | c = *dataP++; 525 | hexstr[0] = hc[(c>>4)&0x0F]; 526 | hexstr[1] = hc[(c )&0x0F]; 527 | 528 | if( isprint( c ) && c != 0 ) 529 | asciiStr[ widthCnt ] = c ; 530 | else 531 | asciiStr[ widthCnt ] = '.'; 532 | 533 | if (widthCnt == 0) { 534 | sampleprint(hexstr); 535 | } else { 536 | sampleprintnotab(hexstr); 537 | } 538 | if (++widthCnt >= 16) { 539 | sampleprintnotab( threeSpaces ); // some space between hex chars and ascii chars 540 | sampleprintnotab( asciiStr ); // prints the ascii characters to the right of hex dump 541 | 542 | widthCnt = 0; 543 | sampleprintnotab("\n"); 544 | } 545 | 546 | size--; 547 | } 548 | if (widthCnt != 0){ 549 | 550 | // for the last line, fill out the rest of the hex row with blanks 551 | // and fill the unused right end of asciiStr with blanks 552 | while( widthCnt < 16 ){ 553 | sampleprintnotab( threeSpaces ); 554 | asciiStr[ widthCnt++ ] = ' '; 555 | } 556 | 557 | 558 | sampleprintnotab( threeSpaces ); 559 | sampleprintnotab( asciiStr ); 560 | sampleprintnotab("\n"); 561 | } 562 | 563 | // sampleprintnotab("\n"); 564 | 565 | } 566 | 567 | 568 | void warnprint(const char *formatStr, ...) 569 | { 570 | va_list ap; 571 | va_start(ap, formatStr); 572 | 573 | if (vg.warnings) 574 | vfprintf( _stderr, formatStr, (void *)ap ); 575 | 576 | va_end(ap); 577 | } 578 | 579 | 580 | void errprint(const char *formatStr, ...) 581 | { 582 | va_list ap; 583 | va_start(ap, formatStr); 584 | 585 | fprintf( _stderr, "### error: %s \n### ",vg.curatompath); 586 | vfprintf( _stderr, formatStr, (void *)ap ); 587 | 588 | va_end(ap); 589 | } 590 | 591 | int my_stricmp(const char* p, const char* q) 592 | { 593 | while (tolower(*p) == tolower(*q) && *p && *q) 594 | { 595 | p++; 596 | q++; 597 | } 598 | 599 | return tolower(*p) - tolower(*q); 600 | } 601 | 602 | char *ostypetostr(UInt32 num) 603 | { 604 | static char str[sizeof(num)+1] = {0}; 605 | 606 | str[0] = (num >> 24) & 0xff; 607 | str[1] = (num >> 16) & 0xff; 608 | str[2] = (num >> 8) & 0xff; 609 | str[3] = (num >> 0) & 0xff; 610 | 611 | return str; 612 | } 613 | 614 | char *ostypetostr_r(UInt32 num, char * buffer) 615 | { 616 | buffer[0] = (num >> 24) & 0xff; 617 | buffer[1] = (num >> 16) & 0xff; 618 | buffer[2] = (num >> 8) & 0xff; 619 | buffer[3] = (num >> 0) & 0xff; 620 | buffer[4] = 0; 621 | 622 | return buffer; 623 | } 624 | 625 | // careful about using more than one call to this in the same print statement, they end up all being the same 626 | // for cases where you need it more than once in the same print statment, use int64toxstr_r() instead 627 | char *int64toxstr(UInt64 num) 628 | { 629 | static char str[20]; 630 | UInt32 hi,lo; 631 | 632 | hi = num>>32; 633 | lo = num&(0xffffffff); 634 | if (hi) { 635 | sprintf(str,"0x%lx%8.8lx",(unsigned long) hi,(unsigned long) lo); 636 | } else { 637 | sprintf(str,"0x%lx",(unsigned long) lo); 638 | } 639 | return str; 640 | } 641 | 642 | char *int64toxstr_r(UInt64 num, char * str) 643 | { 644 | UInt32 hi,lo; 645 | 646 | hi = num>>32; 647 | lo = num&(0xffffffff); 648 | if (hi) { 649 | sprintf(str,"0x%lx%8.8lx",(unsigned long) hi,(unsigned long) lo); 650 | } else { 651 | sprintf(str,"0x%lx",(unsigned long) lo); 652 | } 653 | return str; 654 | } 655 | 656 | // careful about using more than one call to this in the same print statement, they end up all being the same 657 | // for cases where you need it more than once in the same print statment, use int64toxstr_r() instead 658 | char *int64todstr(UInt64 num) 659 | { 660 | static char str[40]; 661 | sprintf(str,"%lld",(long long) num); 662 | return str; 663 | } 664 | 665 | 666 | char *int64todstr_r(UInt64 num, char * str) 667 | { 668 | sprintf(str,"%lld",(long long) num); 669 | return str; 670 | } 671 | 672 | // careful about using more than one call to this in the same print statement, they end up all being the same 673 | char *langtodstr(UInt16 num) 674 | { 675 | static char str[4]; 676 | 677 | str[3] = 0; 678 | 679 | if (num==0) { 680 | str[0] = str[1] = str[2] = ' '; 681 | } 682 | else { 683 | str[0] = ((num >> 10) & 0x1F) + 0x60; 684 | str[1] = ((num >> 5 ) & 0x1F) + 0x60; 685 | str[2] = ( num & 0x1F) + 0x60; 686 | } 687 | 688 | return str; 689 | } 690 | 691 | 692 | // careful about using more than one call to this in the same print statement, they end up all being the same 693 | // for cases where you need it more than once in the same print statment, use fixed16str_r() instead 694 | char *fixed16str(SInt16 num) 695 | { 696 | static char str[40]; 697 | float f; 698 | 699 | f = num; 700 | f /= 0x100; 701 | 702 | sprintf(str,"%f",f); 703 | 704 | return str; 705 | } 706 | 707 | char *fixed16str_r(SInt16 num, char * str) 708 | { 709 | float f; 710 | 711 | f = num; 712 | f /= 0x100; 713 | 714 | sprintf(str,"%f",f); 715 | 716 | return str; 717 | } 718 | 719 | 720 | // careful about using more than one call to this in the same print statement, they end up all being the same 721 | // for cases where you need it more than once in the same print statment, use fixed32str_r() instead 722 | char *fixed32str(SInt32 num) 723 | { 724 | static char str[40]; 725 | double f; 726 | 727 | f = num; 728 | f /= 0x10000; 729 | 730 | sprintf(str,"%lf",f); 731 | 732 | return str; 733 | } 734 | 735 | char *fixed32str_r(SInt32 num, char * str) 736 | { 737 | double f; 738 | 739 | f = num; 740 | f /= 0x10000; 741 | 742 | sprintf(str,"%lf",f); 743 | 744 | return str; 745 | } 746 | 747 | 748 | 749 | // careful about using more than one call to this in the same print statement, they end up all being the same 750 | // for cases where you need it more than once in the same print statment, use fixedU32str_r() instead 751 | char *fixedU32str(UInt32 num) 752 | { 753 | static char str[40]; 754 | double f; 755 | 756 | f = num; 757 | f /= 0x10000; 758 | 759 | sprintf(str,"%lf",f); 760 | 761 | return str; 762 | } 763 | 764 | char *fixedU32str_r(UInt32 num, char * str) 765 | { 766 | double f; 767 | 768 | f = num; 769 | f /= 0x10000; 770 | 771 | sprintf(str,"%lf",f); 772 | 773 | return str; 774 | } 775 | 776 | // copy non-terminated C string (chars) to terminated C string (str) 777 | void copyCharsToStr( char *chars, char *str, UInt16 count ){ 778 | SInt16 i; 779 | 780 | for( i = 0; i < count; ++i ) 781 | str[i] = chars[i]; 782 | 783 | str[ count ] = 0; 784 | 785 | } 786 | 787 | //========================================================================================== 788 | 789 | void addEscapedChar( char *str, char c ); 790 | void addEscapedChar( char *str, char c ) 791 | { 792 | char addc[4] = {0}; 793 | 794 | if ((('a' <= c) && (c <= 'z')) 795 | || (('A' <= c) && (c <= 'Z')) 796 | || (('0' <= c) && (c <= '9')) 797 | // add extra chars here 798 | // we want to escape - & . for now 799 | ) { 800 | addc[0] = c; 801 | } else { 802 | char n; 803 | 804 | addc[0] = '%'; 805 | 806 | n = ((c >> 4) & 0x0F); 807 | if (n < 10) 808 | n = n + '0'; 809 | else 810 | n = (n - 10) + 'a'; 811 | addc[1] = n; 812 | 813 | n = ((c) & 0x0F); 814 | if (n < 10) 815 | n = n + '0'; 816 | else 817 | n = (n - 10) + 'a'; 818 | addc[2] = n; 819 | } 820 | 821 | strcat(str, addc); 822 | } 823 | 824 | void addAtomToPath( atompathType workingpath, OSType atomId, long atomIndex, atompathType curpath ) 825 | { 826 | strcpy( curpath, workingpath ); 827 | if (workingpath[0]) 828 | strcat( workingpath, ":"); 829 | addEscapedChar(workingpath, (atomId>>24) & 0xff); 830 | addEscapedChar(workingpath, (atomId>>16) & 0xff); 831 | addEscapedChar(workingpath, (atomId>> 8) & 0xff); 832 | addEscapedChar(workingpath, (atomId>> 0) & 0xff); 833 | strcat( workingpath, "-"); 834 | sprintf(&workingpath[strlen(workingpath)],"%ld",atomIndex); 835 | } 836 | 837 | void restoreAtomPath( atompathType workingpath, atompathType curpath ) 838 | { 839 | strcpy( workingpath, curpath ); 840 | } 841 | 842 | //=========================================================== 843 | // Validate a Video Elementary Stream 844 | //=========================================================== 845 | 846 | OSErr ValidateElementaryVideoStream( atomOffsetEntry *aoe, void *refcon ) 847 | { 848 | #pragma unused(refcon) 849 | TrackInfoRec tir = {0}; 850 | OSErr err = noErr; 851 | UInt32 startCode; 852 | UInt32 prevStartCode; 853 | UInt64 offset1 = aoe->offset; 854 | UInt64 offset2, offset3; 855 | UInt32 sampleNum = 0; 856 | BitBuffer bb; 857 | Ptr dataP; 858 | UInt32 dataSize; 859 | OSErr valerr; 860 | UInt32 refcons[2]; 861 | 862 | if (vg.checklevel < checklevel_samples) 863 | vg.checklevel = checklevel_samples; 864 | 865 | tir.sampleDescriptionCnt = 1; 866 | tir.validatedSampleDescriptionRefCons = &refcons[0]; 867 | 868 | err = GetFileStartCode( aoe, &prevStartCode, offset1, &offset2 ); 869 | if (err) { 870 | fprintf(stderr,"### did NOT find ANY start codes\n"); 871 | goto bail; 872 | } 873 | 874 | do { 875 | err = GetFileStartCode( aoe, &startCode, offset2, &offset3 ); 876 | 877 | if (err) { 878 | offset3 = aoe->maxOffset; 879 | } 880 | 881 | if (err || (startCode == 0x000001B6) || (startCode == 0x000001B3)) { 882 | if (!err && prevStartCode == 0x000001B3) { 883 | goto nextone; 884 | } 885 | 886 | dataSize = offset3 - offset1; 887 | BAILIFNIL( dataP = malloc(dataSize), allocFailedErr ); 888 | err = GetFileData( vg.fileaoe, dataP, offset1, dataSize, nil ); 889 | 890 | err = BitBuffer_Init(&bb, (void *)dataP, dataSize); 891 | 892 | if (sampleNum == 0) { 893 | atomprint("\n"); 897 | } else { 898 | atomprint("\n"); 902 | } 903 | 904 | sampleNum++; 905 | offset1 = offset2 = offset3; 906 | } 907 | nextone: 908 | prevStartCode = startCode; 909 | offset2 = offset3 + 4; 910 | } while (!err); 911 | 912 | 913 | 914 | bail: 915 | return err; 916 | } 917 | 918 | 919 | -------------------------------------------------------------------------------- /src/ValidateMP4.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file contains Original Code and/or Modifications of Original Code 4 | as defined in and that are subject to the Apple Public Source License 5 | Version 2.0 (the 'License'). You may not use this file except in 6 | compliance with the License. Please obtain a copy of the License at 7 | http://www.opensource.apple.com/apsl/ and read it before using this 8 | file. 9 | 10 | The Original Code and all software distributed under the License are 11 | distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 12 | EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 13 | INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 15 | Please see the License for the specific language governing rights and 16 | limitations under the License. 17 | 18 | */ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #if defined(__GNUC__) && ( defined(__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__MACOS_CLASSIC__) ) 28 | #if defined(__i386__) || defined(__x86_64__) 29 | #define LITTLEENDIAN 1 30 | #endif 31 | #endif 32 | 33 | #if defined(_MSC_VER) || (LITTLEENDIAN) 34 | #define TARGET_RT_LITTLE_ENDIAN 1 35 | #define TARGET_RT_BIG_ENDIAN 0 36 | #else 37 | #define TARGET_RT_LITTLE_ENDIAN 0 38 | #define TARGET_RT_BIG_ENDIAN 1 39 | #endif 40 | 41 | #define true 1 42 | #define false 0 43 | 44 | #define TYPE_LONGLONG 1 45 | 46 | 47 | #if defined(_MSC_VER) 48 | #pragma warning (disable: 4068) // ignore unknown pragmas 49 | #pragma warning (disable: 4102) // don't tell me about unreferenced labels (I like to put bail: everywhere) 50 | #endif 51 | 52 | typedef char *Ptr; 53 | typedef uint32_t OSType; 54 | typedef unsigned char Boolean; 55 | typedef short OSErr; 56 | 57 | #define nil 0L 58 | 59 | #ifndef fieldOffset 60 | #define fieldOffset(type, field) ((short) &((type *) 0)->field) 61 | #endif 62 | 63 | enum { 64 | kSkipUnknownAtoms = 1L<<0 65 | }; 66 | 67 | typedef uint8_t UInt8; 68 | typedef int8_t SInt8; 69 | typedef int32_t SInt32; 70 | typedef uint32_t UInt32; 71 | typedef int16_t SInt16; 72 | typedef uint16_t UInt16; 73 | typedef UInt32 UnsignedFixed; 74 | 75 | typedef unsigned char uuidType[16]; // 128-bit uuid (guid) 76 | #ifdef _MSC_VER 77 | typedef unsigned __int64 UInt64; 78 | typedef __int64 SInt64; 79 | #else 80 | typedef uint64_t UInt64; 81 | typedef int64_t SInt64; 82 | #endif 83 | typedef UInt32 TimeValue; 84 | typedef UInt32 PriorityType; 85 | typedef SInt32 Fixed; 86 | typedef SInt32 Fract; 87 | 88 | 89 | 90 | typedef Fixed MatrixRecord[3][3]; 91 | 92 | enum { 93 | kAtomValidated = 1L<<0, 94 | kAtomSkipThisAtom = 1L<<1 95 | }; 96 | 97 | typedef struct startAtomType { 98 | UInt32 size; 99 | UInt32 type; 100 | } startAtomType; 101 | 102 | 103 | // Limitation: we don't handle atom's with sizes or file offsets that need more than 32-bits 104 | typedef struct atomOffsetEntry { 105 | OSType type; // if atomId == 'uuid', use uuid field 106 | uuidType uuid; 107 | UInt64 size; // if atomSize == 1, use longSize field 108 | 109 | UInt64 offset; // offset into file of atom's start 110 | UInt64 maxOffset; // max offset into file of atom's start 111 | 112 | UInt32 atomStartSize; // size of id & size info, so it is easy to skip 113 | 114 | UInt32 aoeflags; // used for processing 115 | void* refconOverride; // used for processing 116 | } atomOffsetEntry; 117 | 118 | enum { 119 | getAtomDispOnlyOne = 1 << 0, 120 | getAtomDispIsLeaf = 1 << 1, 121 | getAtomDispMustHave = 1 << 2, 122 | getAtomDispGotOne = 1 << 3 123 | }; 124 | 125 | enum { 126 | noErr = 0, 127 | paramErr = -50, 128 | allocFailedErr = -2019, 129 | outOfDataErr = -2020, 130 | tooMuchDataErr = -2021, 131 | noCanDoErr = -2022, 132 | badAtomSize = -2023, 133 | badAtomErr = -2024, 134 | atomOverRunErr = -2025, 135 | badPublicMovieAtom = -2026, 136 | dataSharedErr = -2027, 137 | programErr = -2028 138 | }; 139 | 140 | typedef struct { 141 | UInt8 *ptr; 142 | UInt32 length; 143 | UInt8 *cptr; 144 | UInt8 cbyte; 145 | SInt32 curbits; 146 | UInt32 bits_left; 147 | 148 | UInt8 prevent_emulation; // true or false 149 | UInt8 emulation_position; // 0 usually, 1 after 1 zero byte, 2 after 2 zero bytes, 3 150 | // after 00 00 03, and the 3 gets stripped 151 | 152 | } BitBuffer; 153 | 154 | OSErr BitBuffer_Init(BitBuffer *bb, UInt8 *p, UInt32 length); 155 | UInt32 GetBits(BitBuffer *bb, UInt32 nBits, OSErr *err); 156 | 157 | 158 | OSErr GetBytes(BitBuffer *bb, UInt32 nBytes, UInt8 *p); 159 | UInt32 NumBytesLeft(BitBuffer *bb); 160 | UInt32 PeekBits(BitBuffer *bb, UInt32 nBits, OSErr *err); 161 | OSErr SkipBytes(BitBuffer *bb, UInt32 nBytes); 162 | 163 | // ===== bit buffer video support 164 | Boolean BitBuffer_IsVideoStartCode(BitBuffer *bb); 165 | OSErr BitBuffer_GetVideoStartCode(BitBuffer *bb, unsigned char *outStartCode); 166 | UInt32 read_golomb_uev(BitBuffer *bb, OSErr *errout); 167 | SInt32 read_golomb_sev(BitBuffer *bb, OSErr *errout); 168 | UInt32 strip_trailing_zero_bits(BitBuffer *bb, OSErr *errout); 169 | 170 | enum { 171 | kMPEG4StartCode_VOS = 0xB0, 172 | kMPEG4StartCode_VO = 0xB5, 173 | 174 | kMPEG4StartCode_VOLMin = 0x20, 175 | kMPEG4StartCode_VOLMax = 0x2F, 176 | 177 | kMPEG4StartCode_GOV = 0xB3, 178 | kMPEG4StartCode_VOP = 0xB6 179 | 180 | }; 181 | 182 | enum { 183 | nal_unspec = 0, 184 | nal_slice_layer_without_partitioning = 1, 185 | nal_slice_data_partition_a_layer = 2, 186 | nal_slice_data_partition_b_layer = 3, 187 | nal_slice_data_partition_c_layer = 4, 188 | nal_slice_layer_without_partitioning_IDR = 5, 189 | nal_SEI = 6, 190 | nal_SPS = 7, 191 | nal_PPS = 8, 192 | nal_access_unit_delimiter = 9, 193 | nal_end_of_seq = 10, 194 | nal_end_of_stream = 11, 195 | nal_filler_data = 12, 196 | nal_SPS_Ext = 13, 197 | nal_aux_slice = 19 198 | }; 199 | 200 | typedef char atompathType[100]; 201 | typedef char argstr[100]; 202 | 203 | void addAtomToPath( atompathType workingpath, OSType atomId, long atomIndex, atompathType curpath ); 204 | void restoreAtomPath( atompathType workingpath, atompathType curpath ); 205 | 206 | 207 | //===== Sample Table typedefs 208 | 209 | typedef struct SampleToChunk { 210 | UInt32 firstChunk; 211 | UInt32 samplesPerChunk; 212 | UInt32 sampleDescriptionIndex; 213 | } SampleToChunk; 214 | 215 | typedef struct TimeToSampleNum { 216 | UInt32 sampleCount; 217 | TimeValue sampleDuration; // duration for a single sample, not total duration 218 | } TimeToSampleNum; 219 | 220 | typedef struct ChunkOffsetRecord { 221 | UInt32 chunkOffset; 222 | } ChunkOffsetRecord; 223 | 224 | typedef struct ChunkOffset64Record { 225 | UInt64 chunkOffset; 226 | } ChunkOffset64Record; 227 | 228 | typedef struct SampleSizeRecord { 229 | UInt32 sampleSize; 230 | } SampleSizeRecord; 231 | 232 | typedef struct SampleDescriptionHead { 233 | UInt32 size; 234 | UInt32 sdType; 235 | UInt32 resvd1; 236 | UInt16 resvdA; 237 | SInt16 dataRefIndex; 238 | } SampleDescriptionHead; 239 | 240 | 241 | typedef struct SampleDescriptionRecord { 242 | SampleDescriptionHead head; 243 | char data[1]; 244 | } SampleDescriptionRecord, *SampleDescriptionPtr; 245 | 246 | 247 | 248 | //=========================== 249 | 250 | typedef struct VideoSampleDescriptionInfo { 251 | SInt16 version; /* which version is this data */ 252 | SInt16 revisionLevel; /* what version of that codec did this */ 253 | UInt32 vendor; /* whose codec compressed this data */ 254 | UInt32 temporalQuality; /* what was the temporal quality factor */ 255 | UInt32 spatialQuality; /* what was the spatial quality factor */ 256 | SInt16 width; /* how many pixels wide is this data */ 257 | SInt16 height; /* how many pixels high is this data */ 258 | Fixed hRes; /* horizontal resolution */ 259 | Fixed vRes; /* vertical resolution */ 260 | UInt32 dataSize; /* if known, the size of data for this image descriptor */ 261 | SInt16 frameCount; /* number of frames this description applies to */ 262 | char name[32]; /* Str31 -- name of codec ( in case not installed ) */ 263 | SInt16 depth; /* what depth is this data (1-32) or ( 33-40 grayscale ) */ 264 | SInt16 clutID; /* clut id or if 0 clut follows or -1 if no clut */ 265 | char extensions[1]; // having trouble - sizeof this guy gives me a bad value 266 | } VideoSampleDescriptionInfo; 267 | 268 | 269 | //=========================== 270 | 271 | typedef struct { 272 | OSType mediaType; 273 | SInt16 trackVolume; 274 | Fixed trackWidth; 275 | Fixed trackHeight; 276 | Fixed sampleDescWidth, sampleDescHeight; 277 | UInt32 trackID; 278 | UInt32 hintRefTrackID; 279 | 280 | UInt32 mediaTimeScale; 281 | UInt64 mediaDuration; 282 | 283 | //==== enough sample table information to read through the data sequentially 284 | UInt32 currentSampleDescriptionIndex; 285 | 286 | UInt32 sampleDescriptionCnt; // number of sampleDescriptions 287 | SampleDescriptionPtr *sampleDescriptions; // 1 based array of sample description pointers 288 | UInt32 *validatedSampleDescriptionRefCons; // available for SampleDescriptionValidator to stash info for SampleValidator 289 | 290 | UInt32 sampleSizeEntryCnt; // number of sample size entries 291 | UInt32 singleSampleSize; // set if there is a constant sample size 292 | SampleSizeRecord *sampleSize; // 1 based array of sample sizes 293 | 294 | UInt32 chunkOffsetEntryCnt; // number of chunk offset entries 295 | ChunkOffset64Record *chunkOffset; // 1 based array of chunk offsets 296 | 297 | UInt32 sampleToChunkEntryCnt; // number of sampleToChunk entries 298 | SampleToChunk *sampleToChunk; // 1-based array of sampleToChunk entries 299 | UInt32 sampleToChunkSampleSubTotal; // total accounted for all but last entry 300 | 301 | UInt32 timeToSampleEntryCnt; // number of timeToSample entries 302 | TimeToSampleNum *timeToSample; // 1-based array of TimeToSampleNum entries 303 | 304 | UInt32 timeToSampleSampleCnt; // number of samples described in the timeToSampleAtom 305 | UInt64 timeToSampleDuration; // duration described by timeToSampleAtom (this is Total duration of all samples, 306 | // not a single sample's duration) 307 | } TrackInfoRec; 308 | 309 | int GetSampleOffsetSize( TrackInfoRec *tir, UInt32 sampleNum, UInt64 *offsetOut, UInt32 *sizeOut, UInt32 *sampleDescriptionIndexOut ); 310 | int GetChunkOffsetSize( TrackInfoRec *tir, UInt32 chunkNum, UInt64 *offsetOut, UInt32 *sizeOut, UInt32 *sampleDescriptionIndexOut ); 311 | 312 | // movie Globals 313 | typedef struct { 314 | 315 | long numTIRs; 316 | long maxTIRs; 317 | TrackInfoRec tirList[1]; 318 | } MovieInfoRec; 319 | 320 | 321 | // enums for fileType 322 | enum { 323 | filetype_mp4 = 'mp4 ', 324 | filetype_mp4v = 'mp4v', 325 | brandtype_mp41 = 'mp41', 326 | brandtype_isom = 'isom' 327 | }; 328 | 329 | 330 | 331 | enum { 332 | checklevel_samples = 2, 333 | checklevel_payload = 3 334 | }; 335 | 336 | 337 | 338 | // to validate VideoSpecificInfo and VideoProfileLevelIndication 339 | typedef struct{ 340 | UInt32 maxMBsec; 341 | UInt32 profileLevelInd; 342 | UInt32 vopTimeIncResolution; 343 | UInt32 volWidth; 344 | UInt32 volHeight; 345 | } PartialVideoSC; 346 | 347 | 348 | // Validate Globals 349 | typedef struct { 350 | FILE *inFile; 351 | long inOffset; 352 | long inMaxOffset; 353 | 354 | atompathType curatompath; 355 | Boolean printatom; 356 | Boolean printsample; 357 | long tabcnt; 358 | 359 | atomOffsetEntry *fileaoe; // used when you need to read file & size from the file 360 | 361 | Boolean warnings; 362 | 363 | MovieInfoRec *mir; 364 | 365 | // ----- 366 | atompathType atompath; 367 | 368 | argstr filetypestr; 369 | argstr checklevelstr; 370 | argstr samplenumberstr; 371 | argstr printtypestr; 372 | 373 | long filetype; 374 | long checklevel; 375 | long samplenumber; 376 | 377 | long majorBrand; 378 | 379 | Boolean print_atompath; 380 | Boolean print_atom; 381 | Boolean print_fulltable; 382 | Boolean print_sample; 383 | Boolean print_sampleraw; 384 | Boolean print_hintpayload; 385 | 386 | UInt32 visualProfileLevelIndication;// to validate if IOD corresponds to VSC 387 | 388 | 389 | } ValidateGlobals; 390 | 391 | extern ValidateGlobals vg; 392 | 393 | typedef struct AtomSizeType { 394 | UInt32 atomSize; 395 | OSType atomType; 396 | } AtomSizeType; 397 | 398 | typedef struct AtomStartRecord { 399 | AtomSizeType atom; 400 | UInt32 versFlags; 401 | } AtomStartRecord; 402 | 403 | typedef OSErr (*ValidateAtomProcPtr)(OSType atomId, unsigned long atomSize, void *atomRec); 404 | #define CallValidateAtomProc(userRoutine, atomId, atomSize, atomRec) \ 405 | (*(userRoutine))((atomId), (atomSize), (atomRec)) 406 | 407 | typedef struct ValidateAtomDispatch { 408 | OSType atomType; 409 | short flags; 410 | ValidateAtomProcPtr atomProc; 411 | void *getAtomRec; 412 | short cnt; 413 | } ValidateAtomDispatch; 414 | 415 | void warnprint(const char *formatStr, ...); 416 | void errprint(const char *formatStr, ...); 417 | void atomprint(const char *formatStr, ...); 418 | void atomprintnotab(const char *formatStr, ...); 419 | void atomprintdetailed(const char *formatStr, ...); 420 | void atomprinthexdata(char *dataP, UInt32 size); 421 | void sampleprint(const char *formatStr, ...); 422 | void sampleprintnotab(const char *formatStr, ...); 423 | void sampleprinthexdata(char *dataP, UInt32 size); 424 | void sampleprinthexandasciidata(char *dataP, UInt32 size); 425 | void toggleprintatom( Boolean onOff ); 426 | void toggleprintatomdetailed( Boolean onOff ); 427 | void toggleprintsample( Boolean onOff ); 428 | void copyCharsToStr( char *chars, char *str, UInt16 count ); 429 | 430 | 431 | 432 | //========================================================================================== 433 | 434 | 435 | 436 | typedef struct { 437 | UInt64 chunkStart; 438 | UInt64 chunkStop; 439 | UInt32 trackID; 440 | OSType mediaType; 441 | 442 | } chunkOverlapRec; 443 | 444 | 445 | 446 | typedef struct AvcConfigInfo { 447 | AtomSizeType start; 448 | UInt8 config_ver; 449 | UInt8 profile; 450 | UInt8 compatibility; 451 | UInt8 level; 452 | UInt8 lengthsize; 453 | UInt8 sps_count; 454 | UInt8 pps_count; 455 | UInt8 chroma_format; 456 | UInt8 bit_depth_luma_minus8; 457 | UInt8 bit_depth_chroma_minus8; 458 | UInt8 sps_ext_count; 459 | } AvcConfigInfo; 460 | 461 | typedef struct AvcBtrtInfo { 462 | AtomSizeType start; 463 | UInt32 buffersizeDB; 464 | UInt32 maxBitrate; 465 | UInt32 avgBitrate; 466 | } AvcBtrtInfo; 467 | 468 | typedef struct ColrInfo { 469 | AtomSizeType start; 470 | UInt32 colrtype; 471 | UInt16 primaries; 472 | UInt16 function; 473 | UInt16 matrix; 474 | } ColrInfo; 475 | 476 | 477 | OSErr GetFullAtomVersionFlags( atomOffsetEntry *aoe, UInt32 *version, UInt32 *flags, UInt64 *offsetOut); 478 | 479 | #define FOUR_CHAR_CODE(a) a 480 | 481 | enum { 482 | MovieAID = FOUR_CHAR_CODE('moov'), 483 | MovieHeaderAID = FOUR_CHAR_CODE('mvhd'), 484 | ClipAID = FOUR_CHAR_CODE('clip'), 485 | RgnClipAID = FOUR_CHAR_CODE('crgn'), 486 | MatteAID = FOUR_CHAR_CODE('matt'), 487 | MatteCompAID = FOUR_CHAR_CODE('kmat'), 488 | TrackAID = FOUR_CHAR_CODE('trak'), 489 | UserDataAID = FOUR_CHAR_CODE('udta'), 490 | TrackHeaderAID = FOUR_CHAR_CODE('tkhd'), 491 | EditsAID = FOUR_CHAR_CODE('edts'), 492 | EditListAID = FOUR_CHAR_CODE('elst'), 493 | MediaAID = FOUR_CHAR_CODE('mdia'), 494 | MediaHeaderAID = FOUR_CHAR_CODE('mdhd'), 495 | MediaInfoAID = FOUR_CHAR_CODE('minf'), 496 | VideoMediaInfoHeaderAID = FOUR_CHAR_CODE('vmhd'), 497 | SoundMediaInfoHeaderAID = FOUR_CHAR_CODE('smhd'), 498 | GenericMediaInfoHeaderAID = FOUR_CHAR_CODE('gmhd'), 499 | GenericMediaInfoAID = FOUR_CHAR_CODE('gmin'), 500 | DataInfoAID = FOUR_CHAR_CODE('dinf'), 501 | DataRefAID = FOUR_CHAR_CODE('dref'), 502 | SampleTableAID = FOUR_CHAR_CODE('stbl'), 503 | STSampleDescAID = FOUR_CHAR_CODE('stsd'), 504 | STTimeToSampAID = FOUR_CHAR_CODE('stts'), 505 | STSyncSampleAID = FOUR_CHAR_CODE('stss'), 506 | STSampleToChunkAID = FOUR_CHAR_CODE('stsc'), 507 | STShadowSyncAID = FOUR_CHAR_CODE('stsh'), 508 | HandlerAID = FOUR_CHAR_CODE('hdlr'), 509 | STSampleSizeAID = FOUR_CHAR_CODE('stsz'), 510 | STSampleSize2AID = FOUR_CHAR_CODE('stz2'), 511 | STChunkOffsetAID = FOUR_CHAR_CODE('stco'), 512 | STChunkOffset64AID = FOUR_CHAR_CODE('co64'), 513 | STSamplePadAID = FOUR_CHAR_CODE('padb'), 514 | STSampleIDAID = FOUR_CHAR_CODE('stid'), 515 | DataRefContainerAID = FOUR_CHAR_CODE('drfc'), 516 | TrackReferenceAID = FOUR_CHAR_CODE('tref'), 517 | ColorTableAID = FOUR_CHAR_CODE('ctab'), 518 | LoadSettingsAID = FOUR_CHAR_CODE('load'), 519 | PropertyAtomAID = FOUR_CHAR_CODE('code'), 520 | InputMapAID = FOUR_CHAR_CODE('imap'), 521 | MovieBufferHintsAID = FOUR_CHAR_CODE('mbfh'), 522 | MovieDataRefAliasAID = FOUR_CHAR_CODE('mdra'), 523 | SoundLocalizationAID = FOUR_CHAR_CODE('sloc'), 524 | CompressedMovieAID = FOUR_CHAR_CODE('cmov'), 525 | CompressedMovieDataAID = FOUR_CHAR_CODE('cmvd'), 526 | DataCompressionAtomAID = FOUR_CHAR_CODE('dcom'), 527 | ReferenceMovieRecordAID = FOUR_CHAR_CODE('rmra'), 528 | ReferenceMovieDescriptorAID = FOUR_CHAR_CODE('rmda'), 529 | ReferenceMovieDataRefAID = FOUR_CHAR_CODE('rdrf'), 530 | ReferenceMovieVersionCheckAID = FOUR_CHAR_CODE('rmvc'), 531 | ReferenceMovieDataRateAID = FOUR_CHAR_CODE('rmdr'), 532 | ReferenceMovieComponentCheckAID = FOUR_CHAR_CODE('rmcd'), 533 | ReferenceMovieQualityAID = FOUR_CHAR_CODE('rmqu'), 534 | ReferenceMovieLanguageAID = FOUR_CHAR_CODE('rmla'), 535 | ReferenceMovieContentRatingAID = FOUR_CHAR_CODE('rmcr'), 536 | ReferenceMovieCPURatingAID = FOUR_CHAR_CODE('rmcs'), 537 | ReferenceMovieAlternateGroupAID = FOUR_CHAR_CODE('rmag'), 538 | ReferenceMovieNetworkStatusAID = FOUR_CHAR_CODE('rnet'), 539 | CloneMediaAID = FOUR_CHAR_CODE('clon'), 540 | TrackRefAID = FOUR_CHAR_CODE('tref'), 541 | HintMediaInfoHeaderAID = FOUR_CHAR_CODE('hmhd'), 542 | MpegMediaInfoHeaderAID = FOUR_CHAR_CODE('mp4s'), 543 | STCompTimeToSampAID = FOUR_CHAR_CODE('ctts'), 544 | STDegradationPriorityAID = FOUR_CHAR_CODE('stdp'), 545 | STSampleDependdencyAID = FOUR_CHAR_CODE('sdtp'), 546 | UuidAID = FOUR_CHAR_CODE('uuid'), 547 | 548 | 549 | 550 | IODSAID = FOUR_CHAR_CODE('iods') 551 | 552 | }; 553 | 554 | enum { 555 | Class_ForbiddenTag = 0x00, 556 | Class_ObjectDescrTag = 0x01, 557 | Class_InitialObjectDescTag = 0x02, 558 | Class_ES_DescrTag = 0x03, 559 | Class_DecoderConfigDescTag = 0x04, 560 | Class_DecSpecificInfoTag = 0x05, 561 | Class_SLConfigDescrTag = 0x06, 562 | Class_ContentIdentDescTag = 0x07, 563 | Class_SupplContentIdentDescTag = 0x08, 564 | Class_IPI_DescPointerTag = 0x09, 565 | Class_IPMP_DescrPointerTag = 0x0a, 566 | Class_IPMP_DescTag = 0x0b, 567 | Class_QoS_DescrTag = 0x0c, 568 | Class_RegbistrationDescrTag = 0x0d, 569 | Class_ES_ID_IncTag = 0x0e, 570 | Class_ES_ID_RefTag = 0x0f, 571 | Class_MP4_IOD_Tag = 0x10, 572 | Class_MP4_OD_Tag = 0x11, 573 | Class_IPL_DescPointerRefTag = 0x12, 574 | Class_ExtendedProvileLevelDescrTag = 0x13, 575 | Class_profileLevelIndicationIndexDescrTag = 0x14, 576 | Class_Reserved1_min = 0x15, 577 | Class_Reserved1_max = 0x3f, 578 | Class_ContentClassificationDescrTag = 0x40, 579 | Class_KeyWordDescrTag = 0x41, 580 | Class_RatingDescrTag = 0x42, 581 | Class_LanguageDescrTag = 0x43, 582 | Class_ShortTextualDescrTag = 0x44, 583 | Class_ExpandedTextualDescrTag = 0x45, 584 | Class_ContentCreatorNameDescrTag = 0x46, 585 | Class_ContentCreationDateDescrTag = 0x47, 586 | Class_OCICreatorNameDescrTag = 0x48, 587 | Class_OCICreationDateDescrTag = 0x49, 588 | Class_SmpteCameraPositionTag = 0x4a, 589 | Class_Reserved2_min = 0x4b, 590 | Class_Reserved2_max = 0x5f, 591 | Class_Reserved3_min = 0x60, 592 | Class_Reserved3_max = 0xbf, 593 | Class_UserPrivate_min = 0xc0, 594 | Class_UserPrivate_max = 0xfe, 595 | Class_ForbiddenTag2 = 0xff 596 | }; 597 | 598 | enum { 599 | ExtDescrTagStartRange = 0x80, 600 | ExtDescrTagEndRange = 0xfe, 601 | OCIDescrTagStartRange = 0x40, 602 | OCIDescrTagEndRange = 0x5f 603 | }; 604 | 605 | enum { 606 | Object_Systems_1 = 0x01, 607 | Object_Systems_2 = 0x02, 608 | Object_Visual_14496 = 0x20, 609 | Object_Visual_AVC = 0x21, 610 | Object_Audio_14496 = 0x40, 611 | Object_Unspecified = 0xFF 612 | }; 613 | 614 | enum { 615 | Stream_OD = 0x01, 616 | Stream_ClockRef = 0x02, 617 | Stream_BIFS = 0x03, 618 | Stream_Visual = 0x04, 619 | Stream_Audio = 0x05, 620 | Stream_MPEG7 = 0x06, 621 | Stream_IPMP = 0x07, 622 | Stream_OCI = 0x08, 623 | Stream_MPEGJ = 0x09 624 | }; 625 | 626 | enum { 627 | VSC_VO_Sequence = 0x1B0, 628 | VSC_VO_Sequence_end = 0x1B1, 629 | VSC_UserData = 0x1B2, 630 | VSC_GroupVOP = 0x1B3, 631 | VSC_ErrorCode = 0x1B4, 632 | VSC_VO = 0x1B5, 633 | VSC_VOP = 0x1B6 634 | }; 635 | 636 | #define bitParsingSlop 4 // number of bytes extra to allocate for the bit parsing code so we never run dry 637 | 638 | OSErr PeekDescriptorTag(BitBuffer *bb, UInt32 *tag, UInt32 *size); 639 | OSErr GetDescriptorTagAndSize(BitBuffer *bb, UInt32 *tag, UInt32 *size); 640 | 641 | OSErr Validate_iods_OD_Bits( Ptr dataP, unsigned long dataSize, Boolean fileForm ); 642 | 643 | 644 | int FindAtomOffsets( atomOffsetEntry *aoe, UInt64 startOffset, UInt64 maxOffset, 645 | long *atomCountOut, atomOffsetEntry **atomOffsetsOut ); 646 | int GetFileDataN64( atomOffsetEntry *aoe, void *dataP, UInt64 offset64, UInt64 *newoffset64 ); 647 | int GetFileDataN32( atomOffsetEntry *aoe, void *dataP, UInt64 offset64, UInt64 *newoffset64 ); 648 | int GetFileDataN16( atomOffsetEntry *aoe, void *dataP, UInt64 offset64, UInt64 *newoffset64 ); 649 | int GetFileData( atomOffsetEntry *aoe, void *dataP, UInt64 offset64, UInt64 size64, UInt64 *newoffset64 ); 650 | int GetFileCString( atomOffsetEntry *aoe, char **strP, UInt64 offset64, UInt64 maxSize64, UInt64 *newoffset64 ); 651 | int GetFileUTFString( atomOffsetEntry *aoe, char **strP, UInt64 offset64, UInt64 maxSize64, UInt64 *newoffset64 ); 652 | int GetFileBitStreamData( atomOffsetEntry *aoe, Ptr bsDataP, UInt32 bsSize, UInt64 offset64, UInt64 *newoffset64 ); 653 | int GetFileBitStreamDataToEndOfAtom( atomOffsetEntry *aoe, Ptr *bsDataPout, UInt32 *bsSizeout, UInt64 offset64, UInt64 *newoffset64 ); 654 | int GetFileStartCode( atomOffsetEntry *aoe, UInt32 *startCode, UInt64 offset64, UInt64 *newoffset64 ); 655 | 656 | OSErr Base64DecodeToBuffer(const char *inData, UInt32 *ioEncodedLength, char *outDecodedData, UInt32 *ioDecodedDataLength); 657 | 658 | 659 | #define BAILIFERR( statement ) do { err = statement; if (err) goto bail; } while (false) 660 | #define BAILIFERRSET( statement ) do { statement; if (err) goto bail; } while (false) 661 | #define BAILIF( condition, errvalue ) do { if (condition) {err = errvalue; goto bail; } } while (false) 662 | #define BAILIFNULL( statement, errvalue ) do {if ((statement) == nil) {err = errvalue; goto bail;}} while (false) 663 | #define BAILIFNIL( statement, errvalue ) do {if ((statement) == nil) {err = errvalue; goto bail;}} while (false) 664 | 665 | 666 | 667 | OSErr Validate_mvhd_Atom( atomOffsetEntry *aoe, void *refcon ); 668 | OSErr Validate_trak_Atom( atomOffsetEntry *aoe, void *refcon ); 669 | OSErr Validate_iods_Atom( atomOffsetEntry *aoe, void *refcon ); 670 | OSErr Validate_moov_Atom( atomOffsetEntry *aoe, void *refcon ); 671 | OSErr Validate_dinf_Atom( atomOffsetEntry *aoe, void *refcon ); 672 | OSErr Validate_minf_Atom( atomOffsetEntry *aoe, void *refcon ); 673 | OSErr Validate_mdia_Atom( atomOffsetEntry *aoe, void *refcon ); 674 | OSErr Validate_stbl_Atom( atomOffsetEntry *aoe, void *refcon ); 675 | OSErr Validate_cprt_Atom( atomOffsetEntry *aoe, void *refcon ); 676 | OSErr Validate_loci_Atom( atomOffsetEntry *aoe, void *refcon ); 677 | 678 | OSErr Validate_moovhnti_Atom( atomOffsetEntry *aoe, void *refcon ); 679 | OSErr Validate_Movie_SDP( char *inSDP ); 680 | 681 | OSErr Validate_url_Entry( atomOffsetEntry *aoe, void *refcon ); 682 | OSErr Validate_urn_Entry( atomOffsetEntry *aoe, void *refcon ); 683 | OSErr Validate_dref_Atom( atomOffsetEntry *aoe, void *refcon ); 684 | OSErr Validate_vmhd_Atom( atomOffsetEntry *aoe, void *refcon ); 685 | OSErr Validate_smhd_Atom( atomOffsetEntry *aoe, void *refcon ); 686 | OSErr Validate_hmhd_Atom( atomOffsetEntry *aoe, void *refcon ); 687 | OSErr Validate_mp4s_Atom( atomOffsetEntry *aoe, void *refcon ); 688 | OSErr Validate_mdhd_Atom( atomOffsetEntry *aoe, void *refcon ); 689 | OSErr Validate_mdia_hdlr_Atom( atomOffsetEntry *aoe, void *refcon ); 690 | OSErr Validate_hdlr_Atom( atomOffsetEntry *aoe, void *refcon ); 691 | 692 | OSErr Validate_tkhd_Atom( atomOffsetEntry *aoe, void *refcon ); 693 | OSErr Validate_tref_Atom( atomOffsetEntry *aoe, void *refcon ); 694 | OSErr Validate_edts_Atom( atomOffsetEntry *aoe, void *refcon ); 695 | OSErr Validate_mdia_Atom( atomOffsetEntry *aoe, void *refcon ); 696 | 697 | OSErr Validate_stsd_Atom( atomOffsetEntry *aoe, void *refcon ); 698 | OSErr Validate_stts_Atom( atomOffsetEntry *aoe, void *refcon ); 699 | OSErr Validate_ctts_Atom( atomOffsetEntry *aoe, void *refcon ); 700 | OSErr Validate_stss_Atom( atomOffsetEntry *aoe, void *refcon ); 701 | OSErr Validate_stsc_Atom( atomOffsetEntry *aoe, void *refcon ); 702 | OSErr Validate_stsz_Atom( atomOffsetEntry *aoe, void *refcon ); 703 | OSErr Validate_stz2_Atom( atomOffsetEntry *aoe, void *refcon ); 704 | OSErr Validate_stco_Atom( atomOffsetEntry *aoe, void *refcon ); 705 | OSErr Validate_padb_Atom( atomOffsetEntry *aoe, void *refcon ); 706 | 707 | OSErr Validate_edts_Atom( atomOffsetEntry *aoe, void *refcon ); 708 | OSErr Validate_elst_Atom( atomOffsetEntry *aoe, void *refcon ); 709 | 710 | OSErr Validate_udta_Atom( atomOffsetEntry *aoe, void *refcon ); 711 | 712 | OSErr Validate_stsh_Atom( atomOffsetEntry *aoe, void *refcon ); 713 | OSErr Validate_stdp_Atom( atomOffsetEntry *aoe, void *refcon ); 714 | OSErr Validate_sdtp_Atom( atomOffsetEntry *aoe, void *refcon ); 715 | 716 | OSErr ValidateFileAtoms( atomOffsetEntry *aoe, void *refcon ); 717 | OSErr Validate_co64_Atom( atomOffsetEntry *aoe, void *refcon ); 718 | OSErr Validate_nmhd_Atom( atomOffsetEntry *aoe, void *refcon ); 719 | 720 | OSErr Validate_tref_type_Atom( atomOffsetEntry *aoe, void *refcon, OSType trefType, UInt32 *firstRefTrackID ); 721 | OSErr Validate_tref_hint_Atom( atomOffsetEntry *aoe, void *refcon ); 722 | OSErr Validate_tref_dpnd_Atom( atomOffsetEntry *aoe, void *refcon ); 723 | OSErr Validate_tref_ipir_Atom( atomOffsetEntry *aoe, void *refcon ); 724 | OSErr Validate_tref_mpod_Atom( atomOffsetEntry *aoe, void *refcon ); 725 | OSErr Validate_tref_sync_Atom( atomOffsetEntry *aoe, void *refcon ); 726 | 727 | 728 | OSErr Validate_vide_SD_Entry( atomOffsetEntry *aoe, void *refcon ); 729 | OSErr Validate_soun_SD_Entry( atomOffsetEntry *aoe, void *refcon ); 730 | OSErr Validate_hint_SD_Entry( atomOffsetEntry *aoe, void *refcon ); 731 | 732 | 733 | OSErr ValidateElementaryVideoStream( atomOffsetEntry *aoe, void *refcon ); 734 | 735 | OSErr Validate_Hint_Track( atomOffsetEntry *aoe, TrackInfoRec *tir ); 736 | 737 | OSErr Validate_Random_Descriptor(BitBuffer *bb, char* dname); 738 | 739 | OSErr Validate_uuid_Atom( atomOffsetEntry *aoe, void *refcon ); 740 | 741 | OSErr Validate_colr_Atom( atomOffsetEntry *aoe, void *refcon ); 742 | 743 | enum { 744 | kTypeAtomFlagMustHaveOne = 1<<0, 745 | kTypeAtomFlagCanHaveAtMostOne = 1<<1, 746 | kTypeAtomFlagMustBeFirst = 1<<2 747 | }; 748 | 749 | typedef OSErr (*ValidateAtomTypeProcPtr)( atomOffsetEntry *aoe, void *refcon ); 750 | #define CallValidateAtomTypeProc(userRoutine, aoe, refcon) \ 751 | (*(userRoutine))((aoe),(refcon)) 752 | 753 | OSErr ValidateAtomOfType( OSType theType, long flags, ValidateAtomTypeProcPtr validateProc, 754 | long cnt, atomOffsetEntry *list, void *refcon ); 755 | 756 | #define FieldMustBe( num, value, errstr ) \ 757 | do { if ((num) != (value)) { err = badAtomErr; errprint(errstr "\n", (value), num); }} while (false) 758 | 759 | #define FieldCheck( _condition_, errstr ) \ 760 | do { if (!(_condition_)) { err = badAtomErr; errprint(errstr "\n"); }} while (false) 761 | 762 | #define FieldList2(t1,t2) {t1,t2}; 763 | #define FieldList3(t1,t2,t3) {t1,t2,t3}; 764 | #define FieldList4(t1,t2,t3,t4) {t1,t2,t3,t4}; 765 | #define FieldList10(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10) {t1,t2,t3,t4,t5,t6,t7,t8,t9,t10}; 766 | #define FieldList11(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11) {t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11}; 767 | 768 | #define FieldOneOfBegin( _value_, _valtype_, _errstr_, _list_ ) \ 769 | do { _valtype_ _test_array_[] = 770 | #define FieldOneOfEnd( _value_, _valtype_, _errstr_, _list_ ) \ 771 | int num_entries = sizeof(_test_array_)/sizeof(_valtype_); \ 772 | int i = 0; \ 773 | while (i <= num_entries) { \ 774 | if (i == num_entries) { \ 775 | errprint(_errstr_ #_list_ "\n"); \ 776 | i++; \ 777 | } else { \ 778 | if (_test_array_[i++] == _value_) break; \ 779 | } \ 780 | } \ 781 | } while (false) 782 | 783 | #define FieldMustBeOneOf2( _value_, _valtype_, _errstr_, _list_ ) \ 784 | FieldOneOfBegin( _value_, _valtype_, _errstr_, _list_ ) \ 785 | FieldList2 _list_ \ 786 | FieldOneOfEnd( _value_, _valtype_, _errstr_, _list_ ) 787 | #define FieldMustBeOneOf3( _value_, _valtype_, _errstr_, _list_ ) \ 788 | FieldOneOfBegin( _value_, _valtype_, _errstr_, _list_ ) \ 789 | FieldList3 _list_ \ 790 | FieldOneOfEnd( _value_, _valtype_, _errstr_, _list_ ) 791 | #define FieldMustBeOneOf4( _value_, _valtype_, _errstr_, _list_ ) \ 792 | FieldOneOfBegin( _value_, _valtype_, _errstr_, _list_ ) \ 793 | FieldList4 _list_ \ 794 | FieldOneOfEnd( _value_, _valtype_, _errstr_, _list_ ) 795 | #define FieldMustBeOneOf10( _value_, _valtype_, _errstr_, _list_ ) \ 796 | FieldOneOfBegin( _value_, _valtype_, _errstr_, _list_ ) \ 797 | FieldList10 _list_ \ 798 | FieldOneOfEnd( _value_, _valtype_, _errstr_, _list_ ) 799 | #define FieldMustBeOneOf11( _value_, _valtype_, _errstr_, _list_ ) \ 800 | FieldOneOfBegin( _value_, _valtype_, _errstr_, _list_ ) \ 801 | FieldList11 _list_ \ 802 | FieldOneOfEnd( _value_, _valtype_, _errstr_, _list_ ) 803 | 804 | char *int64toxstr(UInt64 num); 805 | char *int64toxstr_r(UInt64 num, char * str); 806 | char *int64todstr(UInt64 num); 807 | char *int64todstr_r(UInt64 num, char * str); 808 | char *fixed16str(SInt16 num); 809 | char *fixed16str_r(SInt16 num, char * str); 810 | char *fixed32str(SInt32 num); 811 | char *fixed32str_r(SInt32 num, char * str); 812 | char *fixedU32str(UInt32 num); 813 | char *fixedU32str_r(UInt32 num, char * str); 814 | char *ostypetostr(UInt32 num); 815 | char *ostypetostr_r(UInt32 num, char * buffer); 816 | char *langtodstr(UInt16 num); 817 | int my_stricmp(const char* p, const char* q); 818 | 819 | OSErr CheckMatrixForUnity( MatrixRecord mr ); 820 | 821 | OSErr Validate_vide_sample_Bitstream( BitBuffer *bb, void *refcon ); 822 | OSErr Validate_soun_sample_Bitstream( BitBuffer *bb, void *refcon ); 823 | 824 | OSErr Validate_vide_ES_Bitstream( BitBuffer *bb, void *refcon ); 825 | OSErr Validate_soun_ES_Bitstream( BitBuffer *bb, void *refcon ); 826 | OSErr Validate_mp4s_ES_Bitstream( BitBuffer *bb, void *refcon ); 827 | OSErr Validate_odsm_ES_Bitstream( BitBuffer *bb, void *refcon ); 828 | OSErr Validate_sdsm_ES_Bitstream( BitBuffer *bb, void *refcon ); 829 | 830 | OSErr Validate_odsm_sample_Bitstream( BitBuffer *bb, void *refcon ); 831 | OSErr Validate_sdsm_sample_Bitstream( BitBuffer *bb, void *refcon ); 832 | 833 | typedef OSErr (*ValidateBitstreamProcPtr)( BitBuffer *bb, void *refcon ); 834 | #define CallValidateBitstreamProc(userRoutine, bb, refcon) \ 835 | (*(userRoutine))((bb),(refcon)) 836 | 837 | 838 | OSErr Validate_ESDAtom( atomOffsetEntry *aoe, void *refcon, ValidateBitstreamProcPtr validateBitstreamProc, char *esname ); 839 | OSErr Validate_mp4_SD_Entry( atomOffsetEntry *aoe, void *refcon, ValidateBitstreamProcPtr validateBitstreamProc, char *esname ); 840 | 841 | OSErr Validate_avcC_Atom( atomOffsetEntry *aoe, void *refcon, char *esname ); 842 | OSErr Validate_btrt_Atom( atomOffsetEntry *aoe, void *refcon, char *esname ); 843 | OSErr Validate_m4ds_Atom( atomOffsetEntry *aoe, void *refcon, char *esname ); 844 | 845 | OSErr Validate_ftyp_Atom( atomOffsetEntry *aoe, void *refcon ); 846 | 847 | OSErr Validate_sinf_Atom( atomOffsetEntry *aoe, void *refcon, UInt32 flags ); 848 | OSErr Validate_frma_Atom( atomOffsetEntry *aoe, void *refcon ); 849 | OSErr Validate_schm_Atom( atomOffsetEntry *aoe, void *refcon ); 850 | OSErr Validate_schi_Atom( atomOffsetEntry *aoe, void *refcon ); 851 | 852 | OSErr Validate_meta_Atom( atomOffsetEntry *aoe, void *refcon ); 853 | OSErr Validate_xml_Atom ( atomOffsetEntry *aoe, void *refcon ); 854 | OSErr Validate_iloc_Atom( atomOffsetEntry *aoe, void *refcon ); 855 | OSErr Validate_pitm_Atom( atomOffsetEntry *aoe, void *refcon ); 856 | OSErr Validate_ipro_Atom( atomOffsetEntry *aoe, void *refcon ); 857 | OSErr Validate_infe_Atom( atomOffsetEntry *aoe, void *refcon ); 858 | OSErr Validate_iinf_Atom( atomOffsetEntry *aoe, void *refcon ); 859 | 860 | 861 | 862 | OSErr Validate_NAL_Unit( BitBuffer *bb, UInt8 expect_type, UInt32 nal_length ); 863 | OSErr Validate_AVCConfigRecord( BitBuffer *bb, void *refcon ); 864 | 865 | #include "EndianMP4.h" 866 | 867 | void EndianMatrix_BtoN( MatrixRecord *matrix ); 868 | void EndianSampleDescriptionHead_BtoN( SampleDescriptionHead *sdhP ); 869 | OSErr Get_trak_Type( atomOffsetEntry *aoe, TrackInfoRec *tir ); 870 | OSErr Get_mdia_hdlr_mediaType( atomOffsetEntry *aoe, TrackInfoRec *tir ); 871 | 872 | 873 | void dispose_mir( MovieInfoRec *mir ); 874 | 875 | -------------------------------------------------------------------------------- /src/ValidateHints.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file contains Original Code and/or Modifications of Original Code 4 | as defined in and that are subject to the Apple Public Source License 5 | Version 2.0 (the 'License'). You may not use this file except in 6 | compliance with the License. Please obtain a copy of the License at 7 | http://www.opensource.apple.com/apsl/ and read it before using this 8 | file. 9 | 10 | The Original Code and all software distributed under the License are 11 | distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 12 | EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 13 | INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 15 | Please see the License for the specific language governing rights and 16 | limitations under the License. 17 | 18 | */ 19 | 20 | /* 21 | To Do: 22 | 23 | - validate user data atom 'hinf' 24 | 25 | - validate sample description 26 | 27 | - print sample time of each sample 28 | - validate sampletimes of audio based on number of au's in packets 29 | 30 | - if any packet > stated max packet size, warning 31 | 32 | - warn if there's no track ref but we need it 33 | 34 | - sdp 35 | - other generic sdp validation (o= etc) 36 | 37 | - validate esd in track matches what's in iod 38 | - validate esd in sdp matches original track's esd 39 | 40 | - audio 41 | - get params based on what's in sdp 42 | - match params to what's in the spec - warning if no match 43 | 44 | - print sdp with nice wrapping 45 | 46 | - log to a file? window overflows in standalone mode 47 | 48 | 49 | */ 50 | 51 | #include "ValidateMP4.h" 52 | 53 | // --------------------------------------------------------------------------- 54 | // D E F I N I T I O N S 55 | // --------------------------------------------------------------------------- 56 | 57 | #define kHintDataTableEntrySize 16 58 | 59 | #define kSelfTrackRefIndex 0xFF 60 | 61 | #define kBitsPerByte 8 62 | 63 | #define kSpaceChar ' ' 64 | 65 | 66 | #define kMaxSDPPayloadNameLength 255 67 | #define kMaxSDPModeNameLength 31 68 | 69 | typedef OSErr (*ValidatePayloadTypeProcPtr)( char *inPayload, UInt32 inLength, void *refcon ); 70 | #define CallValidatePayloadTypeProc(userRoutine, inPayload, inLength, inRefCon) \ 71 | (*(userRoutine))((inPayload),(inLength),(inRefCon)) 72 | 73 | 74 | typedef struct { 75 | UInt8 payloadNum; 76 | char payloadName[kMaxSDPPayloadNameLength+1]; 77 | char modeName[kMaxSDPModeNameLength+1]; 78 | ValidatePayloadTypeProcPtr payloadValidateProc; 79 | 80 | 81 | } SDPInfoRec; 82 | 83 | 84 | typedef struct { 85 | atomOffsetEntry *aoe; 86 | TrackInfoRec *tir; 87 | 88 | TrackInfoRec *originalMediaTIR; 89 | 90 | // ----- settings 91 | UInt32 validateLevel; 92 | Boolean validatePayload; 93 | 94 | UInt32 verboseLevel; 95 | Boolean printSamples; 96 | Boolean printPayloadContents; 97 | 98 | UInt32 startingSampleNum; 99 | UInt32 numSamplesToValidate; 100 | 101 | SDPInfoRec sdpInfo; 102 | 103 | 104 | // ----- 105 | UInt32 hintSampleNum; 106 | Ptr hintSampleData; 107 | UInt32 hintSampleLength; 108 | 109 | Boolean constructPacket; 110 | Boolean packetConstructedOK; 111 | Ptr packetData; 112 | char *packetDataCurrent; 113 | UInt32 packetDataMaxLength; 114 | 115 | // ----- params for audio payload 116 | Boolean genericPayloadParamsOK; 117 | SInt32 genericPayloadMode; 118 | UInt16 numLengthBits; 119 | UInt16 numIndexBits; 120 | UInt16 numIndexDeltaBits; 121 | UInt16 bytesPerHeader; 122 | UInt16 indexMask; 123 | UInt16 maxFrameLength; 124 | UInt16 constantSize; 125 | 126 | } HintInfoRec; 127 | 128 | // --------------------------------------------------------------------------- 129 | // P R O T O T Y P E S 130 | // --------------------------------------------------------------------------- 131 | 132 | #define H_ATOM_PRINT(_x) { if (doPrinting) {atomprint _x ;}} 133 | #define H_ATOM_PRINT_INCR(_x) { if (doPrinting) {atomprint _x ; vg.tabcnt++;}} 134 | #define H_ATOM_PRINT_DECR(_x) { if (doPrinting) {vg.tabcnt--; atomprint _x ;}} 135 | #define H_ATOM_PRINT_HEXDATA(_x, _l) { if (doPrinting) {atomprinthexdata((_x), (_l)) ;}} 136 | 137 | 138 | static OSErr Validate_Hint_Sample( HintInfoRec *hir, char *inSampleData, UInt32 inLength ); 139 | static OSErr Validate_Packet_Entry( HintInfoRec *hir, char *inPacketEntry, UInt32 inMaxLength, char **outNextEntryPtr ); 140 | static OSErr Validate_Data_Entry( HintInfoRec *hir, char *inEntry ); 141 | 142 | static OSErr Validate_rfc3016_Payload( char *inPayload, UInt32 inLength, void *refcon ); 143 | static OSErr Validate_Generic_MPEG4_Audio_Payload( char *inPayload, UInt32 inLength, void *refcon ); 144 | static OSErr Validate_H264_Payload( char *inPayload, UInt32 inLength, void *refcon ); 145 | 146 | static OSErr Validate_hint_udta_Atom( atomOffsetEntry *aoe, void *refcon ); 147 | static OSErr Validate_hinf_Atom( atomOffsetEntry *aoe, void *refcon ); 148 | static OSErr Validate_hnti_Atom( atomOffsetEntry *aoe, void *refcon ); 149 | 150 | static OSErr Validate_Track_SDP( HintInfoRec *hir, char *inSDP ); 151 | static OSErr Validate_SDP_Media_Line( HintInfoRec *hir, char *inLine ); 152 | static OSErr Validate_SDP_Attribute_Line( HintInfoRec *hir, char *inLine ); 153 | 154 | static OSErr Validate_fmtp_attribute( HintInfoRec *hir, char *inValue); 155 | static OSErr Validate_rtpmap_attribute( HintInfoRec *hir, char *inValue); 156 | static OSErr Validate_iod_attribute( HintInfoRec *hir, char *inValue); 157 | static OSErr Validate_isma_attribute( HintInfoRec *hir, char *inValue); 158 | 159 | 160 | static OSErr SDP_Get_Tag( char **ioCurrent, char *outTag ); 161 | static char *SDP_Find_Line_End( char *inCurrent ); 162 | static void Validate_SDP_Line_Ending( HintInfoRec *hir, char *inEndOfLine ); 163 | static char *SDP_Skip_Line_Ending_Chars( char *inLineEnd ); 164 | 165 | static SInt32 Chars_To_Num( char *inCharsStart, char *inCharsEnd, Boolean *outFoundNum ); 166 | static SInt32 Chars_To_hexNum( char *inCharsStart, char *inCharsEnd, Boolean *outFoundNum ); 167 | static Boolean is_num( char *inCharsStart, char *inCharsEnd); 168 | static Boolean is_hexnum( char *inCharsStart, char *inCharsEnd); 169 | static Boolean is_in_range( SInt32 inNum, SInt32 inMin, SInt32 inMax); 170 | static Boolean compare_nocase(const char *s1, const char *s2); 171 | static Boolean get_next_fmtp_param(char **inLine, char **outTagString, char **outParamValue); 172 | 173 | static OSErr get_original_track_info(UInt32 inRefTrackID, TrackInfoRec **outTIR); 174 | static OSErr get_track_sample(TrackInfoRec *tir, UInt32 inSampleNum, Ptr *dataOut, UInt32 *sizeOut, UInt32 *sampleDescriptionIndexOut); 175 | 176 | 177 | // use hex equivalents instead of '\r' and '\n' since some compilers (MPW) are different 178 | #define CR 0x0d 179 | #define LF 0x0a 180 | 181 | 182 | //========================================================================================== 183 | 184 | #define kMPEG43016_PayloadName "mp4v-es" 185 | 186 | 187 | //========================================================================================== 188 | 189 | #define kMPEG4Generic_PayloadName "mpeg4-generic" 190 | 191 | 192 | #define kMPEG4GenericParam_Mode "mode" 193 | #define kMPEG4GenericParam_ConstantSize "constantsize" 194 | #define kMPEG4GenericParam_SizeLength "sizelength" 195 | #define kMPEG4GenericParam_IndexLength "indexlength" 196 | #define kMPEG4GenericParam_IndexDeltaLength "indexdeltalength" 197 | 198 | 199 | #define kMPEG4GenericModeName_CELPCBR "CELP-cbr" 200 | #define kMPEGGeneric_CELPCBR_OverheadBytesPerFrame 0 201 | 202 | 203 | #define kMPEG4GenericModeName_CELPVBR "CELP-vbr" 204 | #define kMPEG4Generic_CELPVBR_SizeLengthDefault 6 205 | #define kMPEG4Generic_CELPVBR_IndexLengthDefault 2 206 | #define kMPEG4Generic_CELPVBR_IndexDeltaLengthDefault 2 207 | #define kMPEGGeneric_CELPVBR_OverheadBytesPerFrame 1 208 | #define kMPEGGeneric_CELPVBR_IndexMask 0x03 209 | 210 | #define kMPEG4Generic_CELPVBR_MaxFrameLength 63 211 | 212 | 213 | #define kMPEG4GenericModeName_AACLowBitRate "AAC-lbr" 214 | #define kMPEG4Generic_AACLBR_SizeLengthDefault 6 215 | #define kMPEG4Generic_AACLBR_IndexLengthDefault 2 216 | #define kMPEG4Generic_AACLBR_IndexDeltaLengthDefault 2 217 | 218 | #define kMPEGGeneric_AACLBR_OverheadBytesPerFrame 1 219 | #define kMPEGGeneric_AACLBR_IndexMask 0x03 220 | 221 | #define kMPEG4Generic_AACLBR_MaxFrameLength 63 222 | 223 | 224 | #define kMPEG4GenericModeName_AACHighBitRate "AAC-hbr" 225 | #define kMPEG4Generic_AACHBR_SizeLengthDefault 13 226 | #define kMPEG4Generic_AACHBR_IndexLengthDefault 3 227 | #define kMPEG4Generic_AACHBR_IndexDeltaLengthDefault 3 228 | #define kMPEGGeneric_AACHBR_OverheadBytesPerFrame 2 229 | 230 | #define kMPEGGeneric_AACHBR_IndexMask 0x0007 231 | 232 | #define kMPEG4Generic_AACHBR_MaxFrameLength 8191 233 | 234 | 235 | // these numbers aren't in the spec - we just made them up so we can 236 | // keep track of what mode we're using 237 | enum { 238 | 239 | kMPEG4GenericMode_CELPCBR = 1, 240 | kMPEG4GenericMode_CELPVBR = 2, 241 | kMPEG4GenericMode_AACLowBitRate = 3, 242 | kMPEG4GenericMode_AACHighBitRate = 4 243 | }; 244 | 245 | 246 | //========================================================================================== 247 | 248 | #define kH264_PayloadName "H264" 249 | #define kNAL_STAP_A 24 250 | #define kNAL_STAP_B 25 251 | #define kNAL_MTAP16 26 252 | #define kNAL_MTAP24 27 253 | #define kNAL_FU_A 28 254 | #define kNAL_FU_B 29 255 | 256 | 257 | //========================================================================================== 258 | 259 | OSErr Validate_Hint_Track( atomOffsetEntry *aoe, TrackInfoRec *tir ) 260 | { 261 | OSErr err = noErr; 262 | UInt64 sampleOffset; 263 | UInt32 sampleSize; 264 | UInt32 sampleDescriptionIndex; 265 | Ptr dataP = nil; 266 | UInt32 i; 267 | UInt32 startSampleNum; 268 | UInt32 endSampleNum; 269 | Boolean doPrinting = false; 270 | HintInfoRec hir = {0}; 271 | 272 | UInt64 minOffset, maxOffset; 273 | long cnt; 274 | atomOffsetEntry *list; 275 | OSErr tempErr; 276 | 277 | // ------------------------------------------------------- 278 | hir.aoe = aoe; 279 | hir.tir = tir; 280 | hir.hintSampleNum = 0; 281 | hir.hintSampleData = NULL; 282 | 283 | hir.constructPacket = true; 284 | hir.validatePayload = true; 285 | 286 | if (vg.checklevel >= checklevel_samples) { 287 | hir.printSamples = true; 288 | } 289 | if (vg.checklevel >= checklevel_payload) { 290 | hir.printPayloadContents = true; 291 | } 292 | 293 | //hir.numSamplesToValidate = 1; 294 | 295 | //vg.printatom = true; 296 | 297 | // ------------------------------------------------------- 298 | 299 | 300 | if (hir.printSamples) { 301 | doPrinting = true; 302 | } 303 | 304 | if (hir.constructPacket) { 305 | hir.packetDataMaxLength = 10*1024; 306 | BAILIFNIL( hir.packetData = malloc(hir.packetDataMaxLength), allocFailedErr ); 307 | hir.packetDataCurrent = 0; 308 | } 309 | 310 | if (tir->hintRefTrackID != 0) { 311 | get_original_track_info(tir->hintRefTrackID, &(hir.originalMediaTIR)); 312 | } 313 | 314 | // Process 'udta' atoms 315 | minOffset = aoe->offset + aoe->atomStartSize; 316 | maxOffset = aoe->offset + aoe->size - aoe->atomStartSize; 317 | 318 | BAILIFERR( FindAtomOffsets( aoe, minOffset, maxOffset, &cnt, &list ) ); 319 | 320 | tempErr = ValidateAtomOfType( 'udta', kTypeAtomFlagMustHaveOne | kTypeAtomFlagCanHaveAtMostOne, 321 | Validate_hint_udta_Atom, cnt, list, &hir ); 322 | if (!err) err = tempErr; 323 | 324 | H_ATOM_PRINT(("\n", hir.sdpInfo.payloadNum, hir.sdpInfo.payloadName)); 325 | if (hir.originalMediaTIR != NULL) { 326 | H_ATOM_PRINT(("\n", &hir.originalMediaTIR->mediaType)); 327 | //@@@ remove this restriction for other file type 328 | if (hir.originalMediaTIR->mediaType == 'soun') { 329 | if (!compare_nocase(hir.sdpInfo.payloadName, kMPEG4Generic_PayloadName)) { 330 | errprint("audio payload name should be '%s'", kMPEG4Generic_PayloadName); 331 | } 332 | if (!hir.genericPayloadParamsOK) { 333 | errprint("wrong or missing params - cannot validate payload\n"); 334 | } else { 335 | H_ATOM_PRINT(("", hir.sdpInfo.modeName)); 336 | } 337 | } else if (hir.originalMediaTIR->mediaType == 'vide') { 338 | if ((!compare_nocase(hir.sdpInfo.payloadName, kMPEG43016_PayloadName)) && 339 | (!compare_nocase(hir.sdpInfo.payloadName, kH264_PayloadName)) ) { 340 | errprint("video payload name should be '%s' or '%s'", kMPEG43016_PayloadName, kH264_PayloadName); 341 | } 342 | } 343 | } 344 | 345 | 346 | // TODO: vaidate the payload matches the original track type (e.g. video payload 347 | // for a hinted video track) 348 | if (hir.startingSampleNum > 0) { 349 | startSampleNum = hir.startingSampleNum; 350 | } else { 351 | startSampleNum = 1; 352 | } 353 | if (hir.numSamplesToValidate!= 0) { 354 | endSampleNum = startSampleNum + hir.numSamplesToValidate-1; 355 | } else { 356 | endSampleNum = tir->sampleSizeEntryCnt; 357 | } 358 | 359 | H_ATOM_PRINT_INCR(("\n")); 360 | for (i = startSampleNum; i <= endSampleNum; i++) { 361 | if ((vg.samplenumber==0) || (vg.samplenumber==i)) { 362 | err = GetSampleOffsetSize( tir, i, &sampleOffset, &sampleSize, &sampleDescriptionIndex ); 363 | if (err != noErr) { 364 | errprint("couldn't GetSampleOffsetSize for sample %ld (err %ld)\n", i, err); 365 | continue; 366 | } 367 | H_ATOM_PRINT_INCR(( "\n")) 383 | } 384 | } 385 | H_ATOM_PRINT_DECR(("\n")); 386 | 387 | bail: 388 | if (hir.packetData != NULL) { 389 | free(hir.packetData); 390 | } 391 | return err; 392 | } 393 | 394 | //========================================================================================== 395 | static OSErr Validate_Hint_Sample( HintInfoRec *hir, char *inSampleData, UInt32 inLength ) 396 | { 397 | OSErr err = noErr; 398 | UInt16 numPackets; 399 | UInt16 i; 400 | char *packetEntryPtr; 401 | UInt16 temp16; 402 | char *next; 403 | Boolean doPrinting = hir->printSamples; 404 | 405 | /* 406 | 2 entry count 407 | 2 reserved (0) 408 | var packet entry table 409 | var additional data 410 | */ 411 | 412 | numPackets = EndianU16_BtoN(*((UInt16*)inSampleData)); 413 | temp16 = EndianU16_BtoN(*((UInt16*)(inSampleData+2))); 414 | if ((temp16 != 0) && (temp16 != 65535)) { 415 | // alas the spec forgets to say what the reserved value is, and mpeg people always think 416 | // -1, whereas RTP people think 0 [dws] 417 | warnprint("WARNING - reserved in sample data %ld should be 0 or -1\n", temp16); 418 | } 419 | H_ATOM_PRINT(("numPackets=\"%ld\"\n", numPackets)); 420 | H_ATOM_PRINT(("reserved=\"%ld\"\n", temp16)); 421 | 422 | if (numPackets == 0) { 423 | warnprint("WARNING - numPackets = 0\n"); 424 | } 425 | 426 | packetEntryPtr = inSampleData + sizeof(UInt16) + sizeof(UInt16); 427 | for (i=0; i\n", i)) 429 | BAILIFERR(Validate_Packet_Entry(hir, packetEntryPtr, inLength - (UInt16)(packetEntryPtr-inSampleData), &next )); 430 | packetEntryPtr = next; 431 | H_ATOM_PRINT_DECR(("\n")) 432 | if (packetEntryPtr > (inSampleData + inLength)) { 433 | errprint("ERROR - hint sample packet entries %ld overflowed\n", i); 434 | err = outOfDataErr; 435 | goto bail; 436 | } 437 | } 438 | 439 | bail: 440 | return err; 441 | } 442 | 443 | //========================================================================================== 444 | static OSErr Validate_Packet_Entry( HintInfoRec *hir, char *inPacketEntry, UInt32 inMaxLength, char **outNextEntryPtr ) 445 | { 446 | OSErr err = noErr; 447 | UInt32 temp32; 448 | UInt16 temp16; 449 | char *current = inPacketEntry; 450 | Boolean hasExtraInfoTLVs = false; 451 | UInt16 entryCount; 452 | UInt16 i; 453 | OSErr tempErr; 454 | Boolean doPrinting = hir->printSamples; 455 | 456 | /* 457 | 4 relative packet transmission 458 | 2 rtp header info 459 | 2 rtp sequence number 460 | 2 flags 461 | 2 entry count 462 | 0 or var extra info TLVs 463 | var data table 464 | */ 465 | 466 | #define kRTPHeader_PBit 0x2000 467 | #define kRTPHeader_MBit 0x0080 468 | #define kRTPHeader_PayloadMask 0x007F 469 | 470 | temp32 = EndianU32_BtoN(*((UInt32*)current)); 471 | current += sizeof(temp32); 472 | H_ATOM_PRINT(("relativeTransmissionTime=\"%ld\"\n", temp32)); 473 | temp16 = EndianU16_BtoN(*((UInt16*)current)); 474 | current += sizeof(temp16); 475 | H_ATOM_PRINT_INCR(("\n")); 476 | //@@@ check reserved fields of rtp header 477 | H_ATOM_PRINT(("P=\"%d\"\n", ((temp16 & kRTPHeader_PBit) != 0))); 478 | H_ATOM_PRINT(("M=\"%d\"\n", ((temp16 & kRTPHeader_MBit) != 0))); 479 | H_ATOM_PRINT(("payloadType=\"%d\"\n", temp16 & kRTPHeader_PayloadMask)); 480 | H_ATOM_PRINT_DECR(("\n")); 481 | 482 | temp16 = EndianU16_BtoN(*((UInt16*)current)); 483 | current += sizeof(temp16); 484 | H_ATOM_PRINT(("sequenceNumber=\"%ld\"\n", temp16)) 485 | 486 | #define kPacketEntry_XBit 0x0004 487 | #define kPacketEntry_BBit 0x0002 488 | #define kPacketEntry_RBit 0x0001 489 | 490 | 491 | temp16 = EndianU16_BtoN(*((UInt16*)current)); 492 | current += sizeof(temp16); 493 | H_ATOM_PRINT(("flags=\"%.4x\"\n", temp16)); 494 | //@@@ check reserved fields of flags 495 | hasExtraInfoTLVs = ((temp16 & kPacketEntry_XBit) != 0); 496 | H_ATOM_PRINT(("BFrame=\"%d\"\n", ((temp16 & kPacketEntry_BBit) != 0))); 497 | H_ATOM_PRINT(("repeatPacket=\"%d\"\n", ((temp16 & kPacketEntry_RBit) != 0))); 498 | 499 | entryCount = EndianU16_BtoN(*((UInt16*)current)); 500 | current += sizeof(temp16); 501 | H_ATOM_PRINT(("numTableEntries=\"%ld\"\n", entryCount)); 502 | 503 | if (hasExtraInfoTLVs) { 504 | char *tlv; 505 | temp32 = EndianU32_BtoN(*((UInt32*)current)); 506 | tlv = current + sizeof(temp32); 507 | current += temp32; 508 | while (tlv < current) { 509 | UInt32 boxlen, boxtype; 510 | char *tlvdata; 511 | tlvdata = tlv; 512 | boxlen = EndianU32_BtoN(*((UInt32*)tlvdata)); tlvdata += sizeof(temp32); 513 | boxtype = EndianU32_BtoN(*((UInt32*)tlvdata)); tlvdata += sizeof(temp32); 514 | if (boxtype == 'rtpo') { 515 | temp32 = EndianU32_BtoN(*((UInt32*)tlvdata)); 516 | H_ATOM_PRINT(("RTP timestamp offset=\"%ld\"\n", temp32)); 517 | } 518 | else warnprint("Unknown packet extra info TLV %s\n",ostypetostr(boxtype)); 519 | tlv += (boxlen + 3) & (0xFFFFFFFc); // rounded up to a 4-byte boundary 520 | } 521 | } 522 | 523 | 524 | if (current + (entryCount * kHintDataTableEntrySize) > inPacketEntry + inMaxLength) { 525 | errprint("entrycount %ld is too big for data size\n", entryCount); 526 | err = outOfDataErr; 527 | goto bail; 528 | } 529 | 530 | hir->packetConstructedOK = true; 531 | hir->packetDataCurrent = hir->packetData; 532 | for (i=0; i\n", i)); 534 | tempErr = Validate_Data_Entry(hir, current); 535 | current += kHintDataTableEntrySize; 536 | H_ATOM_PRINT_DECR(("\n")); 537 | } 538 | *outNextEntryPtr = current; 539 | 540 | 541 | if (hir->constructPacket && hir->validatePayload && hir->packetData && hir->packetConstructedOK) { 542 | if (hir->sdpInfo.payloadValidateProc != NULL) { 543 | CallValidatePayloadTypeProc(hir->sdpInfo.payloadValidateProc, 544 | (char*)hir->packetData, (UInt32)(hir->packetDataCurrent - hir->packetData),(void*)hir); 545 | } 546 | } 547 | 548 | 549 | bail: 550 | return err; 551 | } 552 | 553 | //========================================================================================== 554 | static OSErr Validate_Data_Entry( HintInfoRec *hir, char *inEntry ) 555 | { 556 | OSErr err = noErr; 557 | char dataSource; 558 | SInt8 trackRefIndex; 559 | UInt32 sampleNum; 560 | UInt32 offset; 561 | UInt16 length; 562 | UInt16 temp16; 563 | UInt32 temp32; 564 | char *current; 565 | Ptr sampleData = NULL; 566 | UInt32 sampleDataLength = 0; 567 | Boolean doPrinting = hir->printSamples; 568 | 569 | /* 570 | 1 data source 571 | 15 data (depends on value in dataSource) 572 | */ 573 | 574 | 575 | enum { 576 | kHintTrackDataSource_None = 0, 577 | kHintTrackDataSource_Immediate = 1, 578 | kHintTrackDataSource_Sample = 2, 579 | kHintTrackDataSource_SampleDescription = 3 580 | }; 581 | 582 | dataSource = inEntry[0]; 583 | current = inEntry + 1; 584 | switch (dataSource) { 585 | case kHintTrackDataSource_None: 586 | H_ATOM_PRINT(("dataSource=none\n")); 587 | break; 588 | case kHintTrackDataSource_Immediate: 589 | H_ATOM_PRINT(("dataSource=immediate\n")); 590 | H_ATOM_PRINT(("length=\"%d\"\n", inEntry[1])); 591 | H_ATOM_PRINT_HEXDATA((char*)inEntry+2, kHintDataTableEntrySize-2); 592 | if (inEntry[1] > kHintDataTableEntrySize-2) { 593 | errprint("data entry immediate length (%d) > 14", inEntry[1]); 594 | err = paramErr; 595 | goto bail; 596 | } 597 | if (hir->constructPacket) { 598 | if (hir->packetDataCurrent-hir->packetData + inEntry[1] > hir->packetDataMaxLength) { 599 | errprint("data entry - immed data length too big %ld", inEntry[1]); 600 | err = paramErr; 601 | goto bail; 602 | } 603 | memcpy(hir->packetDataCurrent, inEntry+2, inEntry[1]); 604 | hir->packetDataCurrent += inEntry[1]; 605 | } 606 | break; 607 | 608 | case kHintTrackDataSource_Sample: 609 | H_ATOM_PRINT(("dataSource=sample\n")); 610 | trackRefIndex = (SInt8)inEntry[1]; 611 | H_ATOM_PRINT(("trackRefIndex=\"%d\"\n", trackRefIndex)); 612 | length = EndianU16_BtoN(*((UInt16*)(inEntry+2))); 613 | H_ATOM_PRINT(("length=\"%d\"\n", length)); 614 | sampleNum = EndianU32_BtoN(*((UInt32*)(inEntry+4))); 615 | H_ATOM_PRINT(("sampleNum=\"%ld\"\n", sampleNum)); 616 | offset = EndianU32_BtoN(*((UInt32*)(inEntry+8))); 617 | H_ATOM_PRINT(("offset=\"%ld\"\n", offset)); 618 | temp16 = EndianU16_BtoN(*((UInt16*)(inEntry+12))); 619 | H_ATOM_PRINT(("blockSize=\"%d\"\n", temp16)); 620 | //@@@ don't check this for .mov files 621 | if ((temp16 != 0) && (temp16 != 1)) { 622 | warnprint("data entry - blocksize should be 0 or 1"); 623 | } 624 | temp16 = EndianU16_BtoN(*((UInt16*)(inEntry+14))); 625 | H_ATOM_PRINT(("blockSamples=\"%d\"\n", temp16)); 626 | //@@@ don't check this for .mov files 627 | if ((temp16 != 0) && (temp16 != 1)) { 628 | warnprint("data entry - blockSamples should be 0 or 1"); 629 | } 630 | 631 | //@@@ handle blocksize, blocksamples != 0 632 | if ((trackRefIndex == (SInt8)kSelfTrackRefIndex) && (sampleNum == hir->hintSampleNum)) { 633 | if (offset+length > hir->hintSampleLength) { 634 | errprint("[1] data entry - offset(%d) + length(%d) > samplelength (%d)\n", offset, length, hir->hintSampleLength); 635 | err = paramErr; 636 | goto bail; 637 | } 638 | 639 | if (hir->constructPacket) { 640 | if (hir->packetDataCurrent-hir->packetData + length > hir->packetDataMaxLength) { 641 | errprint("data entry - packet data too big %ld\n", hir->packetDataCurrent-hir->packetData + length); 642 | err = paramErr; 643 | goto bail; 644 | } 645 | memcpy(hir->packetDataCurrent, hir->hintSampleData + offset, length); 646 | hir->packetDataCurrent += length; 647 | } 648 | } else { 649 | TrackInfoRec *thisTIR = ( (trackRefIndex == (SInt8)kSelfTrackRefIndex) ? hir->tir : hir->originalMediaTIR ); 650 | 651 | if (trackRefIndex != (SInt8)kSelfTrackRefIndex) { 652 | if (trackRefIndex != 0) { 653 | errprint("data entry - trackRefIndex (%ld) should be 0", trackRefIndex); 654 | err = paramErr; 655 | goto bail; 656 | 657 | } 658 | //@@@ warning here about missing tir? 659 | } 660 | 661 | if (thisTIR == NULL) { 662 | errprint("data entry -can't find trackinfo for referenced trackid %ld\n", hir->tir->hintRefTrackID); 663 | err = paramErr; 664 | goto bail; 665 | } 666 | 667 | BAILIFERR( get_track_sample(thisTIR, sampleNum, &sampleData, &sampleDataLength, NULL) ); 668 | if (offset+length >sampleDataLength) { 669 | errprint("[2] data entry - offset(%d) + length(%d) > samplelength (%d)\n", offset, length, sampleDataLength); 670 | err = paramErr; 671 | goto bail; 672 | } 673 | if (hir->constructPacket) { 674 | if (hir->packetDataCurrent-hir->packetData + length > hir->packetDataMaxLength) { 675 | errprint("data entry - packet data too big %ld\n", hir->packetDataCurrent-hir->packetData + length); 676 | err = paramErr; 677 | goto bail; 678 | } 679 | memcpy(hir->packetDataCurrent, sampleData + offset, length); 680 | hir->packetDataCurrent += length; 681 | } 682 | } 683 | break; 684 | 685 | case kHintTrackDataSource_SampleDescription: 686 | H_ATOM_PRINT(("dataSource=sampleDescription\n")); 687 | trackRefIndex = (SInt8)inEntry[1]; 688 | H_ATOM_PRINT(("trackRefIndex=\"%d\"\n", trackRefIndex)); 689 | temp16 = EndianU16_BtoN(*((UInt16*)(inEntry+2))); 690 | H_ATOM_PRINT(("length=\"%d\"\n", temp16)); 691 | temp32 = EndianU32_BtoN(*((UInt32*)(inEntry+4))); 692 | H_ATOM_PRINT(("sampleDescriptionIndex=\"%ld\"\n", temp32)); 693 | temp32 = EndianU32_BtoN(*((UInt32*)(inEntry+8))); 694 | H_ATOM_PRINT(("offset=\"%ld\"\n", temp32)); 695 | temp32 = EndianU32_BtoN(*((UInt32*)(inEntry+12))); 696 | H_ATOM_PRINT(("reserved=\"%ld\"\n", temp32)); 697 | if (temp32 != 0) { 698 | warnprint("reserved in sample desc data entry %ld != 0\n", temp32); 699 | } 700 | if (hir->constructPacket) { 701 | errprint("can't get sample descriptions yet\n"); 702 | err = paramErr; 703 | goto bail; 704 | //@@@ retreive sample description and print it 705 | } 706 | break; 707 | default: 708 | H_ATOM_PRINT(("dataSource=unknown=\"%ld\"\n", dataSource)); 709 | H_ATOM_PRINT_HEXDATA((char*)inEntry+1, kHintDataTableEntrySize-1); 710 | warnprint("unknown datasource in data entry\n"); 711 | break; 712 | 713 | } 714 | 715 | bail: 716 | if (sampleData != NULL) { 717 | free( sampleData ); 718 | } 719 | if (err != noErr) { 720 | hir->packetConstructedOK = false; 721 | } 722 | return err; 723 | } 724 | 725 | #pragma mark - 726 | 727 | //========================================================================================== 728 | static OSErr Validate_rfc3016_Payload( char *inPayload, UInt32 inLength, void *inRefCon ) 729 | { 730 | OSErr err = noErr; 731 | HintInfoRec *hir = (HintInfoRec*)inRefCon; 732 | unsigned char startCodeTag; 733 | Boolean doPrinting = (hir->printSamples && hir->printPayloadContents); 734 | BitBuffer bb; 735 | 736 | BitBuffer_Init(&bb, (void *)inPayload, inLength); 737 | if (BitBuffer_IsVideoStartCode(&bb)) { 738 | BAILIFERR( BitBuffer_GetVideoStartCode(&bb, &startCodeTag) ); 739 | switch (startCodeTag) { 740 | case kMPEG4StartCode_VOS: 741 | case kMPEG4StartCode_GOV: 742 | case kMPEG4StartCode_VOP: 743 | //case kMPEG4StartCode_VP: 744 | // these are valid starting headers for the payload 745 | break; 746 | 747 | default: 748 | errprint("UnknownStartCode=%ld\n", startCodeTag); 749 | break; 750 | } 751 | if (hir->printPayloadContents) { 752 | H_ATOM_PRINT_INCR(("\n")); 753 | switch (startCodeTag) { 754 | case kMPEG4StartCode_VOS: 755 | H_ATOM_PRINT(("VisualObjectSequenceHeader\n")); 756 | break; 757 | case kMPEG4StartCode_GOV: 758 | H_ATOM_PRINT(("GroupOfVOPHeader\n")); 759 | break; 760 | case kMPEG4StartCode_VOP: 761 | H_ATOM_PRINT(("VideoObjectPlaneHeader\n")); 762 | break; 763 | //case kMPEG4StartCode_VP: 764 | // these are valid starting headers for the payload 765 | break; 766 | } 767 | 768 | 769 | atomprinthexdata(inPayload+4, inLength-4); 770 | 771 | H_ATOM_PRINT_DECR(("\n")); 772 | 773 | } 774 | 775 | } else { 776 | if (hir->printPayloadContents) { 777 | H_ATOM_PRINT_INCR(("\n")); 778 | H_ATOM_PRINT_HEXDATA(inPayload, inLength); 779 | H_ATOM_PRINT_DECR(("\n")); 780 | } 781 | } 782 | 783 | //@@@ check sdp params 784 | 785 | bail: 786 | return err; 787 | } 788 | 789 | 790 | //========================================================================================== 791 | static OSErr Validate_Generic_MPEG4_Audio_Payload( char *inPayload, UInt32 inLength, void *inRefCon ) 792 | { 793 | OSErr err = noErr; 794 | HintInfoRec *hir = (HintInfoRec*)inRefCon; 795 | UInt32 temp32; 796 | UInt32 numHeaders; 797 | UInt16 auLength; 798 | char *headerCurrent; 799 | char *auCurrent; 800 | char *auMax; 801 | UInt16 i; 802 | UInt16 index; 803 | Boolean doPrinting = (hir->printSamples && hir->printPayloadContents); 804 | 805 | 806 | if (!hir->genericPayloadParamsOK) { 807 | goto bail; 808 | } 809 | 810 | 811 | auMax = inPayload + inLength; 812 | headerCurrent = inPayload; 813 | temp32 = EndianU16_BtoN(*((UInt16*)headerCurrent)); 814 | numHeaders = temp32 / hir->bytesPerHeader; 815 | 816 | if ((numHeaders % kBitsPerByte) != 0) { 817 | errprint("audio payload-au header length not a multiple of 8: %ld\n", temp32); 818 | err = noCanDoErr; 819 | goto bail; 820 | } 821 | 822 | numHeaders /= kBitsPerByte; 823 | headerCurrent += sizeof(UInt16); 824 | 825 | auCurrent = headerCurrent + (hir->bytesPerHeader * numHeaders); 826 | if (auCurrent > inPayload + inLength) { 827 | errprint("num au headers %ld is too long for pkt length\n", temp32); 828 | err = outOfDataErr; 829 | goto bail; 830 | } 831 | 832 | if ((hir->bytesPerHeader < 1) || (hir->bytesPerHeader > 2)) { 833 | errprint("bytesPerHeader=%ld unsupported\n", hir->bytesPerHeader); 834 | err = noCanDoErr; 835 | goto bail; 836 | } 837 | 838 | if (hir->printPayloadContents) { 839 | H_ATOM_PRINT_INCR(("\n")); 840 | H_ATOM_PRINT(("numHeaders=\"%ld\"\n", numHeaders)); 841 | } 842 | 843 | for (i=0; ibytesPerHeader == 1) { 845 | auLength = (UInt8) headerCurrent[0]; 846 | ++headerCurrent; 847 | } else if (hir->bytesPerHeader == 2) { 848 | auLength = EndianU16_BtoN(*((UInt16*)headerCurrent)); 849 | headerCurrent += sizeof(UInt16); 850 | } 851 | index = auLength & hir->indexMask; 852 | auLength = auLength >> hir->numIndexBits; 853 | 854 | if ((auCurrent + auLength > auMax) && (numHeaders > 1)) { 855 | errprint("aulength %ld too big-overflows payload\n", auLength); 856 | err = outOfDataErr; 857 | break; 858 | } 859 | if (hir->printPayloadContents) { 860 | H_ATOM_PRINT_INCR(("\n", i, auLength, index)); 861 | H_ATOM_PRINT_HEXDATA(auCurrent, auLength); 862 | H_ATOM_PRINT_DECR(("\n")); 863 | auCurrent += auLength; 864 | } 865 | } 866 | 867 | if (hir->printPayloadContents) { 868 | H_ATOM_PRINT_DECR(("\n")); 869 | } 870 | 871 | 872 | bail: 873 | return err; 874 | } 875 | 876 | //========================================================================================== 877 | 878 | static char* nalNames[] = { 879 | "Unspec", "Non-IDR Slice", "DataPtn A Slice", "DataPtn B Slice", 880 | "DataPtn C Slice", "IDR Slice", "SEI", "SPS", 881 | "PPS", "AUDelim", "EOSeq", "EOStrm", 882 | "Filler", "SPS Extn", NULL, NULL, 883 | NULL, NULL, NULL, "Aux Slice", 884 | NULL, NULL, NULL, NULL, 885 | "STAP_A", "STAP_B", "MTAP16", "MTAP24", 886 | "FU_A", "FU_B", NULL, NULL }; 887 | 888 | static void Validate_NAL_Byte( UInt8 nalByte, UInt32 nalSize, Boolean doPrinting ) 889 | { 890 | char* nalName; 891 | 892 | nalName = nalNames[nalByte & 0x1F]; 893 | if (nalName==NULL) { 894 | errprint("Reserved NAL Type %d used\n",nalByte & 0x1f); 895 | nalName = "Resvd"; 896 | } 897 | 898 | H_ATOM_PRINT_INCR(("\n", nalName, nalByte & 0x1F, (nalByte>>5) & 3, nalSize)); 899 | 900 | if (nalByte & 0x80) { 901 | errprint("NAL Unit F bit is not zero, NAL Byte 0x%x (NAL Type %d -- %s)\n", nalByte, nalByte & 0x1F, nalName); 902 | } 903 | 904 | } 905 | 906 | static OSErr Validate_H264_Payload( char *inPayload, UInt32 inLength, void *inRefCon ) 907 | { 908 | OSErr err = noErr; 909 | HintInfoRec *hir = (HintInfoRec*)inRefCon; 910 | UInt8 nalByte, fuHdr; 911 | UInt16 nalSize, don, donB, donD; 912 | Boolean doPrinting = (hir->printSamples && hir->printPayloadContents); 913 | UInt8* headerCurrent; 914 | UInt8* headerLimit; 915 | UInt32 tsOffset; 916 | char* nalName; 917 | 918 | nalByte = inPayload[0]; 919 | Validate_NAL_Byte( nalByte, inLength, doPrinting ); 920 | nalByte = nalByte & 0x1F; 921 | 922 | headerCurrent = (UInt8*) inPayload; 923 | headerLimit = headerCurrent + inLength; 924 | headerCurrent++; 925 | nalSize = inLength; 926 | 927 | switch (nalByte) { 928 | case kNAL_STAP_A: 929 | case kNAL_STAP_B: 930 | if (nalByte==kNAL_STAP_B) { 931 | don = EndianU16_BtoN(*((UInt16*)headerCurrent)); 932 | H_ATOM_PRINT(("DON %d",don)); 933 | headerCurrent += 2; 934 | } 935 | while (headerCurrent < headerLimit) { 936 | nalSize = EndianU16_BtoN(*((UInt16*)headerCurrent)); 937 | headerCurrent += 2; 938 | Validate_NAL_Byte( headerCurrent[0], nalSize, doPrinting ); 939 | if (doPrinting) atomprinthexdata((char*) headerCurrent, nalSize); 940 | H_ATOM_PRINT_DECR(("\n")); 941 | headerCurrent += nalSize; 942 | } 943 | if (headerCurrent > headerLimit) errprint("NAL Unit overflowed %d\n",headerCurrent-headerLimit); 944 | break; 945 | 946 | case kNAL_MTAP16: 947 | case kNAL_MTAP24: 948 | donB = EndianU16_BtoN(*((UInt16*)headerCurrent)); 949 | H_ATOM_PRINT(("DONB %d",donB)); 950 | headerCurrent += 2; 951 | while (headerCurrent < headerLimit) { 952 | nalSize = EndianU16_BtoN(*((UInt16*)headerCurrent)); 953 | headerCurrent += 2; 954 | 955 | donD = *((UInt8*)headerCurrent); 956 | H_ATOM_PRINT(("DOND %d",donD)); 957 | headerCurrent += 1; 958 | 959 | if (nalByte==kNAL_MTAP16) { 960 | tsOffset = EndianU16_BtoN(*((UInt16*)headerCurrent)); 961 | headerCurrent += 2; 962 | } else { 963 | tsOffset = EndianU24_BtoN(*((UInt32*)headerCurrent)); 964 | headerCurrent += 3; 965 | } 966 | H_ATOM_PRINT(("TSOffset %d",nalSize)); 967 | 968 | Validate_NAL_Byte( headerCurrent[0], nalSize, doPrinting ); 969 | if (doPrinting) atomprinthexdata((char*) headerCurrent, nalSize); 970 | H_ATOM_PRINT_DECR(("\n")); 971 | headerCurrent += nalSize; 972 | } 973 | if (headerCurrent > headerLimit) errprint("NAL Unit overflowed %d\n",headerCurrent-headerLimit); 974 | break; 975 | 976 | case kNAL_FU_A: 977 | case kNAL_FU_B: 978 | fuHdr = *((UInt8*)headerCurrent); 979 | headerCurrent += 1; 980 | nalName = nalNames[fuHdr & 0x1F]; 981 | if (nalName==NULL) { 982 | errprint("Reserved NAL Type %d used\n",nalByte & 0x1f); 983 | nalName = "Resvd"; 984 | } 985 | 986 | H_ATOM_PRINT(("\n", 987 | (fuHdr>>7)&1,(fuHdr>>6)&1,(fuHdr>>5)&1,nalName,fuHdr & 0x1F)); 988 | nalSize = inLength - 2; 989 | 990 | if (nalByte == kNAL_FU_B) { 991 | don = EndianU16_BtoN(*((UInt16*)headerCurrent)); 992 | H_ATOM_PRINT(("DON %d",don)); 993 | headerCurrent += 2; 994 | nalSize -= 2; 995 | 996 | if (((fuHdr>>7)&1) != 1) errprint("FU_B should always have S=1\n"); 997 | } 998 | H_ATOM_PRINT_INCR(("\n")); 999 | if (doPrinting) atomprinthexdata((char*) headerCurrent, nalSize); 1000 | H_ATOM_PRINT_DECR(("\n")); 1001 | break; 1002 | 1003 | default: 1004 | --headerCurrent; 1005 | if (doPrinting) atomprinthexdata((char*) headerCurrent, nalSize); 1006 | break; 1007 | } 1008 | H_ATOM_PRINT_DECR(("\n")); 1009 | 1010 | //@@@ check sdp params 1011 | 1012 | bail: 1013 | return err; 1014 | } 1015 | 1016 | #pragma mark - 1017 | //========================================================================================== 1018 | #define kMaxShortSDPLineLength 255 1019 | 1020 | //========================================================================================== 1021 | 1022 | OSErr Validate_Movie_SDP( char *inSDP ) 1023 | { 1024 | 1025 | 1026 | OSErr err = noErr; 1027 | char tag; 1028 | char *current; 1029 | char *lineEnd; 1030 | char *sdpLine = NULL; 1031 | char shortSDPLine[kMaxShortSDPLineLength+1]; 1032 | Ptr longSDPLineP = NULL; 1033 | 1034 | current = inSDP; 1035 | 1036 | do { 1037 | if (current[0] == '\0') { 1038 | break; 1039 | } 1040 | lineEnd = SDP_Find_Line_End(current); 1041 | // check that the line ends in CRLF 1042 | Validate_SDP_Line_Ending(NULL, lineEnd); 1043 | if (lineEnd > current) { 1044 | tag = 0; 1045 | SDP_Get_Tag(¤t, &tag); 1046 | 1047 | // copy the line and null terminate it 1048 | // if it's too long, we have to allocate a ptr instead 1049 | if (lineEnd - current < kMaxShortSDPLineLength) { 1050 | sdpLine = shortSDPLine; 1051 | } else { 1052 | BAILIFNIL( longSDPLineP = malloc(lineEnd-current+1), allocFailedErr ); 1053 | sdpLine = longSDPLineP; 1054 | } 1055 | memcpy(sdpLine, current, lineEnd-current); 1056 | sdpLine[lineEnd-current] = '\0'; 1057 | 1058 | switch (tag) { 1059 | case 'a': 1060 | Validate_SDP_Attribute_Line(NULL, sdpLine); 1061 | break; 1062 | 1063 | case 'b': 1064 | break; 1065 | 1066 | case 0: 1067 | // we didn't find a tag-warning msg already printed in SDP_Get_Tag 1068 | break; 1069 | default: 1070 | warnprint("unknown sdp tag '%c'\n", tag); 1071 | break; 1072 | } 1073 | } 1074 | current = SDP_Skip_Line_Ending_Chars(lineEnd); 1075 | 1076 | } while (current[0] != '\0'); 1077 | 1078 | // ----- do any ending validation here 1079 | 1080 | bail: 1081 | return err; 1082 | } 1083 | 1084 | #pragma mark - 1085 | //========================================================================================== 1086 | 1087 | static OSErr Validate_hint_udta_Atom( atomOffsetEntry *aoe, void *refcon ) 1088 | { 1089 | OSErr err = noErr; 1090 | OSErr atomerr = noErr; 1091 | UInt64 minOffset, maxOffset; 1092 | long cnt; 1093 | atomOffsetEntry *list; 1094 | 1095 | minOffset = aoe->offset + aoe->atomStartSize; 1096 | maxOffset = aoe->offset + aoe->size - aoe->atomStartSize; 1097 | 1098 | BAILIFERR( FindAtomOffsets( aoe, minOffset, maxOffset, &cnt, &list ) ); 1099 | 1100 | // Process 'hnti' atoms 1101 | atomerr = ValidateAtomOfType( 'hnti', kTypeAtomFlagCanHaveAtMostOne, 1102 | Validate_hnti_Atom, cnt, list, refcon ); 1103 | if (!err) err = atomerr; 1104 | 1105 | // Process 'hinf' atoms 1106 | atomerr = ValidateAtomOfType( 'hinf', kTypeAtomFlagCanHaveAtMostOne, 1107 | Validate_hinf_Atom, cnt, list, refcon ); 1108 | if (!err) err = atomerr; 1109 | 1110 | bail: 1111 | return err; 1112 | } 1113 | 1114 | //========================================================================================== 1115 | 1116 | static OSErr Validate_hinf_Atom( atomOffsetEntry *aoe, void *refcon ) 1117 | { 1118 | #pragma unused(aoe) 1119 | OSErr err = noErr; 1120 | HintInfoRec *hir = (HintInfoRec*)refcon; 1121 | 1122 | atomprintnotab(">\n"); 1123 | 1124 | bail: 1125 | return err; 1126 | } 1127 | 1128 | //========================================================================================== 1129 | static OSErr Validate_hnti_Atom( atomOffsetEntry *aoe, void *refcon ) 1130 | { 1131 | OSErr err = noErr; 1132 | HintInfoRec *hir = (HintInfoRec*)refcon; 1133 | char *current; 1134 | 1135 | OSType atomType; 1136 | UInt32 atomLength; 1137 | 1138 | Ptr hntiDataP = NULL; 1139 | Ptr sdpDataP = NULL; 1140 | UInt64 temp64; 1141 | 1142 | Boolean doPrinting = hir->printSamples; 1143 | 1144 | atomprintnotab(">\n"); 1145 | 1146 | BAILIFNIL( hntiDataP = malloc((UInt32)aoe->size), allocFailedErr ); 1147 | 1148 | BAILIFERR( GetFileData(aoe, hntiDataP, aoe->offset + aoe->atomStartSize, aoe->size - aoe->atomStartSize, &temp64) ); 1149 | 1150 | 1151 | //@@@ this only processes the first atom 1152 | // we should cycle around 1153 | current = hntiDataP; 1154 | atomLength = EndianU32_BtoN(*((UInt32*)current)); 1155 | current += sizeof(UInt32); 1156 | atomType = EndianU32_BtoN(*((UInt32*)current)); 1157 | current += sizeof(UInt32); 1158 | 1159 | if (atomLength > aoe->size - aoe->atomStartSize) { 1160 | errprint("atomlength %ld in 'hnti' user data too big\n", atomLength); 1161 | err = outOfDataErr; 1162 | goto bail; 1163 | } 1164 | 1165 | if (atomType == 'sdp ') { 1166 | // we found the sdp data 1167 | // make a copy and null terminate it 1168 | atomLength -= 8; // subtract the atomlength/type fields from the length 1169 | BAILIFNIL( sdpDataP = malloc(atomLength+1), allocFailedErr ); 1170 | memcpy(sdpDataP, current, atomLength); 1171 | sdpDataP[atomLength] = '\0'; 1172 | H_ATOM_PRINT_INCR(("\n")); 1173 | //@@@ fix printing of sdp 1174 | // want it like hint dump 1175 | // H_ATOM_PRINT(("%s", sdpDataP)); 1176 | H_ATOM_PRINT(("%.100s\n", sdpDataP)); 1177 | H_ATOM_PRINT_DECR(("\n")); 1178 | 1179 | //@@@ check that the sdp doesn't contain anything but chars and CRLF (no '\0' either) 1180 | BAILIFERR( Validate_Track_SDP(hir, sdpDataP) ); 1181 | } else { 1182 | errprint("no sdp atom in hnti user data\n"); 1183 | err = outOfDataErr; 1184 | goto bail; 1185 | } 1186 | 1187 | 1188 | bail: 1189 | if (hntiDataP != NULL) { 1190 | free(hntiDataP); 1191 | } 1192 | if (sdpDataP != NULL) { 1193 | free(sdpDataP); 1194 | } 1195 | return err; 1196 | } 1197 | 1198 | //========================================================================================== 1199 | static OSErr Validate_Track_SDP( HintInfoRec *hir, char *inSDP ) 1200 | { 1201 | OSErr err = noErr; 1202 | char tag; 1203 | char *current; 1204 | char *lineEnd; 1205 | char *sdpLine = NULL; 1206 | char shortSDPLine[kMaxShortSDPLineLength+1]; 1207 | Ptr longSDPLineP = NULL; 1208 | 1209 | current = inSDP; 1210 | 1211 | do { 1212 | if (current[0] == '\0') { 1213 | break; 1214 | } 1215 | lineEnd = SDP_Find_Line_End(current); 1216 | // check that the line ends in CRLF 1217 | Validate_SDP_Line_Ending(hir, lineEnd); 1218 | if (lineEnd > current) { 1219 | tag = 0; 1220 | SDP_Get_Tag(¤t, &tag); 1221 | 1222 | // copy the line and null terminate it 1223 | // if it's too long, we have to allocate a ptr instead 1224 | if (lineEnd - current < kMaxShortSDPLineLength) { 1225 | sdpLine = shortSDPLine; 1226 | } else { 1227 | BAILIFNIL( longSDPLineP = malloc(lineEnd-current+1), allocFailedErr ); 1228 | sdpLine = longSDPLineP; 1229 | } 1230 | memcpy(sdpLine, current, lineEnd-current); 1231 | sdpLine[lineEnd-current] = '\0'; 1232 | 1233 | switch (tag) { 1234 | case 'm': 1235 | Validate_SDP_Media_Line(hir, sdpLine); 1236 | break; 1237 | 1238 | case 'a': 1239 | Validate_SDP_Attribute_Line(hir, sdpLine); 1240 | break; 1241 | 1242 | case 'b': 1243 | break; 1244 | 1245 | case 0: 1246 | // we didn't find a tag-warning msg already printed in SDP_Get_Tag 1247 | break; 1248 | default: 1249 | warnprint("unknown sdp tag '%c'", tag); 1250 | break; 1251 | } 1252 | } 1253 | current = SDP_Skip_Line_Ending_Chars(lineEnd); 1254 | 1255 | } while (current[0] != '\0'); 1256 | 1257 | // ----- do ending validation here 1258 | // must have a payload name 1259 | if (hir->sdpInfo.payloadNum != 0) { 1260 | if (compare_nocase(kMPEG4Generic_PayloadName, hir->sdpInfo.payloadName)) { 1261 | hir->sdpInfo.payloadValidateProc = Validate_Generic_MPEG4_Audio_Payload; 1262 | } else if (compare_nocase(kMPEG43016_PayloadName, hir->sdpInfo.payloadName)) { 1263 | hir->sdpInfo.payloadValidateProc = Validate_rfc3016_Payload; 1264 | } else if (compare_nocase(kH264_PayloadName, hir->sdpInfo.payloadName)) { 1265 | hir->sdpInfo.payloadValidateProc = Validate_H264_Payload; 1266 | } else { 1267 | if (hir->sdpInfo.payloadName[0] == '\0') { 1268 | errprint("missing rtpmap line for payload num %d", hir->sdpInfo.payloadNum); 1269 | } else { 1270 | warnprint("can't handle this payload '%s'", hir->sdpInfo.payloadName); 1271 | } 1272 | } 1273 | } 1274 | 1275 | bail: 1276 | return err; 1277 | } 1278 | 1279 | //========================================================================================== 1280 | static OSErr Validate_SDP_Media_Line( HintInfoRec *hir, char *inLine ) 1281 | { 1282 | //@@@ check mediatype is ok 1283 | //@@@ check port=0 1284 | OSErr err = noErr; 1285 | char *current = inLine; 1286 | char *next; 1287 | SInt32 temp32; 1288 | Boolean foundNum; 1289 | 1290 | // m= RTP/AVP 1291 | // m=audio 0 RTP/AVP 96 1292 | 1293 | // ----- media type 1294 | next = strchr(current, kSpaceChar); 1295 | if (next == NULL) { 1296 | // line contains only the media type? 1297 | warnprint("sdp media line contains only 1 word"); 1298 | goto bail; 1299 | } 1300 | current = next+1; // skip over the space char 1301 | 1302 | // ----- port 1303 | next = strchr(current, kSpaceChar); 1304 | if (next == NULL) { 1305 | // line contains only the media type? 1306 | warnprint("sdp media line contains only 1 word"); 1307 | goto bail; 1308 | } 1309 | 1310 | temp32 = Chars_To_Num(current, next, &foundNum); 1311 | if (!foundNum) { 1312 | //@@@ err here 1313 | } 1314 | if (!is_in_range(temp32, 0, 0x0000ffff)) { 1315 | warnprint("port out of range %ld\n",temp32); 1316 | } 1317 | if (temp32 != 0) { 1318 | warnprint("port should be 0 but is %ld\n", temp32); 1319 | } 1320 | current = next+1; 1321 | 1322 | // ----- RTP/AVP 1323 | next = strchr(current, kSpaceChar); 1324 | if (next == NULL) { 1325 | // line contains only the media type? 1326 | warnprint("sdp media line is too short"); 1327 | goto bail; 1328 | } 1329 | //@@@ check this is RTP/AVP 1330 | 1331 | current = next+1; 1332 | 1333 | 1334 | // ----- payload number 1335 | next = current + strlen(current); 1336 | temp32 = Chars_To_Num(current, next, &foundNum); 1337 | if (!foundNum) { 1338 | errprint("payloadnum slot is not a number"); 1339 | err = paramErr; 1340 | goto bail; 1341 | } 1342 | if (!is_in_range(temp32, 0, 0x000000ff)) { 1343 | errprint("payloadnum out of range %ld\n",temp32); 1344 | err = paramErr; 1345 | goto bail; 1346 | } 1347 | hir->sdpInfo.payloadNum = (unsigned char)temp32; 1348 | 1349 | 1350 | 1351 | bail: 1352 | return err; 1353 | } 1354 | 1355 | //========================================================================================== 1356 | static OSErr Validate_SDP_Attribute_Line( HintInfoRec *hir, char *inLine ) 1357 | { 1358 | #define kMaxSDPTagLength 63 1359 | OSErr err = noErr; 1360 | char tagString[kMaxSDPTagLength+1]; 1361 | char *value; 1362 | 1363 | 1364 | // a=: 1365 | value = strchr(inLine, ':'); 1366 | if (value == 0) { 1367 | errprint("attribute line - can't find tag"); 1368 | BAILIFERRSET( err = paramErr ); 1369 | } 1370 | memcpy(tagString, inLine, value-inLine); 1371 | tagString[value-inLine] = '\0'; 1372 | value++; // skip over the : 1373 | 1374 | if (strcmp("fmtp", tagString) == 0) { 1375 | err = Validate_fmtp_attribute(hir, value); 1376 | } else if (strcmp("rtpmap", tagString) == 0) { 1377 | err = Validate_rtpmap_attribute(hir, value); 1378 | } else if (strcmp("mpeg4-iod", tagString) == 0) { 1379 | err = Validate_iod_attribute(hir, value); 1380 | } else if (strcmp("isma-compliance", tagString) == 0) { 1381 | err = Validate_isma_attribute(hir, value); 1382 | } else { 1383 | //@@@ warning 1384 | } 1385 | 1386 | 1387 | 1388 | 1389 | bail: 1390 | return err; 1391 | } 1392 | 1393 | //========================================================================================== 1394 | static OSErr Validate_fmtp_attribute( HintInfoRec *hir, char *inValue) 1395 | { 1396 | OSErr err = noErr; 1397 | char *current = inValue; 1398 | char *next; 1399 | SInt32 temp32; 1400 | Boolean foundNum; 1401 | Ptr param = NULL; 1402 | Ptr paramValue = NULL; 1403 | Boolean doPrinting = hir->printSamples; 1404 | 1405 | // a=fmtp: =[;=sdpInfo.payloadNum) { 1427 | goto bail; 1428 | } 1429 | 1430 | #define kMPEG4GenericParam_ConstantSize "constantsize" 1431 | #define kMPEG4GenericParam_SizeLength "sizelength" 1432 | #define kMPEG4GenericParam_IndexLength "indexlength" 1433 | #define kMPEG4GenericParam_IndexDeltaLength "indexdeltalength" 1434 | 1435 | // ----- pick off params 1436 | while (get_next_fmtp_param(¤t, ¶m, ¶mValue)) { 1437 | if (compare_nocase("config", param)) { 1438 | 1439 | } else if (compare_nocase(kMPEG4GenericParam_Mode, param)) { 1440 | if (compare_nocase(kMPEG4GenericModeName_CELPCBR, paramValue)) { 1441 | hir->genericPayloadMode = kMPEG4GenericMode_CELPCBR; 1442 | } else if (compare_nocase(kMPEG4GenericModeName_CELPVBR, paramValue)) { 1443 | hir->genericPayloadMode = kMPEG4GenericMode_CELPVBR; 1444 | } else if (compare_nocase(kMPEG4GenericModeName_AACLowBitRate, paramValue)) { 1445 | hir->genericPayloadMode = kMPEG4GenericMode_AACLowBitRate; 1446 | } else if (compare_nocase(kMPEG4GenericModeName_AACHighBitRate, paramValue)) { 1447 | hir->genericPayloadMode = kMPEG4GenericMode_AACHighBitRate; 1448 | } else { 1449 | errprint("fmtp mode unknown '%s'", paramValue); 1450 | } 1451 | if (hir->genericPayloadMode != 0) { 1452 | strcpy(hir->sdpInfo.modeName, paramValue); 1453 | } 1454 | } else if (compare_nocase(kMPEG4GenericParam_ConstantSize, param)) { 1455 | temp32 = Chars_To_Num(paramValue, paramValue + strlen(paramValue), &foundNum); 1456 | if (foundNum) { 1457 | hir->constantSize = temp32; 1458 | } else { 1459 | errprint("fmtp constantsize param not a number '%s'", paramValue); 1460 | } 1461 | } else if (compare_nocase(kMPEG4GenericParam_SizeLength, param)) { 1462 | temp32 = Chars_To_Num(paramValue, paramValue + strlen(paramValue), &foundNum); 1463 | if (foundNum) { 1464 | hir->numLengthBits = temp32; 1465 | } else { 1466 | errprint("fmtp sizelength param not a number '%s'", paramValue); 1467 | } 1468 | } else if (compare_nocase(kMPEG4GenericParam_IndexLength, param)) { 1469 | temp32 = Chars_To_Num(paramValue, paramValue + strlen(paramValue), &foundNum); 1470 | if (foundNum) { 1471 | hir->numIndexBits = temp32; 1472 | } else { 1473 | errprint("fmtp indexlength param not a number '%s'", paramValue); 1474 | } 1475 | 1476 | } else if (compare_nocase(kMPEG4GenericParam_IndexDeltaLength, param)) { 1477 | temp32 = Chars_To_Num(paramValue, paramValue + strlen(paramValue), &foundNum); 1478 | if (foundNum) { 1479 | hir->numIndexDeltaBits = temp32; 1480 | } else { 1481 | errprint("fmtp indexdeltalength param not a number '%s'", paramValue); 1482 | } 1483 | } else if (compare_nocase("sprop-parameter-sets", param)) { 1484 | /* parameter sets, each base64, separated by commas */ 1485 | UInt8 next = 0; 1486 | char* begin; 1487 | char* paramEnd; 1488 | char* paramnext; 1489 | UInt32 nalSize, base64Size; 1490 | char* nalDataP; 1491 | BitBuffer bb; 1492 | 1493 | paramnext = paramValue; 1494 | 1495 | while (paramnext) { 1496 | begin = paramnext; 1497 | 1498 | paramEnd = strchr(begin, ','); 1499 | if (paramEnd == NULL) { 1500 | paramEnd = begin + strlen(begin); 1501 | paramnext = NULL; 1502 | } else paramnext = paramEnd + 1; 1503 | 1504 | base64Size = paramEnd - begin; 1505 | nalSize = base64Size; // more than enough, will be adjusted down 1506 | BAILIFNIL( nalDataP = malloc(nalSize), allocFailedErr ); 1507 | err = Base64DecodeToBuffer(begin, &base64Size, nalDataP, &nalSize); 1508 | if (err) { 1509 | errprint("bad parameter set-bad base64 encoding"); 1510 | goto bail; 1511 | } 1512 | BitBuffer_Init(&bb, (UInt8*) nalDataP, nalSize ); 1513 | 1514 | /* need to fiddle the printing flags here? */ 1515 | Validate_NAL_Unit( &bb, 0, nalSize ); 1516 | free(nalDataP); 1517 | } 1518 | } else if ((compare_nocase(kH264_PayloadName, hir->sdpInfo.payloadName)) && 1519 | compare_nocase("profile-level-id", param)) { 1520 | /* for AVC, a three byte value, hex representation */ 1521 | UInt8 profile, flags, level; 1522 | temp32 = Chars_To_hexNum(paramValue, paramValue + strlen(paramValue), &foundNum); 1523 | if (foundNum) { 1524 | profile = (temp32 >> 16) & 0xFF; 1525 | flags = (temp32 >> 8 ) & 0xFF; 1526 | level = (temp32 ) & 0xFF; 1527 | if ((profile != 66) && (profile != 77) && (profile != 88)) 1528 | errprint("AVC Profile %d in SDP profile-level-ID is not baseline(66) main(77), or extended(88)\n",profile); 1529 | if (flags & 0x1F) 1530 | errprint("AVC flags in SDP profile-level-ID other than constraint_set, set 0x%x\n",flags); 1531 | 1532 | H_ATOM_PRINT(("AVC Profile in SDP profile-level-ID %d, flags 0x%x, level %d\n",profile,flags,level)); 1533 | } else { 1534 | errprint("fmtp profile-level-id param not a hex number '%s'", paramValue); 1535 | } 1536 | } 1537 | 1538 | if (param != NULL) { 1539 | free( param ); 1540 | param = NULL; 1541 | } 1542 | if (paramValue != NULL) { 1543 | free( paramValue ); 1544 | paramValue = NULL; 1545 | } 1546 | } 1547 | 1548 | 1549 | // check that the params match the mode 1550 | if (compare_nocase(kMPEG4Generic_PayloadName, hir->sdpInfo.payloadName)) { 1551 | switch (hir->genericPayloadMode) { 1552 | case kMPEG4GenericMode_CELPCBR: 1553 | if (hir->constantSize <=0) { 1554 | errprint("constantsize (%ld) param out of range", hir->constantSize); 1555 | err = paramErr; 1556 | } 1557 | break; 1558 | 1559 | case kMPEG4GenericMode_CELPVBR: 1560 | if (hir->numLengthBits != kMPEG4Generic_CELPVBR_SizeLengthDefault) { 1561 | errprint("sizelength (%ld) != default (%ld)", hir->numLengthBits, kMPEG4Generic_CELPVBR_SizeLengthDefault); 1562 | err = paramErr; 1563 | } 1564 | if (hir->numIndexBits != kMPEG4Generic_CELPVBR_IndexLengthDefault) { 1565 | errprint("indexlength (%ld) != default (%ld)", hir->numIndexBits, kMPEG4Generic_CELPVBR_IndexLengthDefault); 1566 | err = paramErr; 1567 | } 1568 | if (hir->numIndexBits != kMPEG4Generic_CELPVBR_IndexDeltaLengthDefault) { 1569 | errprint("indexdeltalength (%ld) != default (%ld)", hir->numIndexBits, kMPEG4Generic_CELPVBR_IndexDeltaLengthDefault); 1570 | err = paramErr; 1571 | } 1572 | hir->bytesPerHeader = kMPEGGeneric_CELPVBR_OverheadBytesPerFrame; 1573 | hir->indexMask = kMPEGGeneric_CELPVBR_IndexMask; 1574 | hir->maxFrameLength = kMPEG4Generic_CELPVBR_MaxFrameLength; 1575 | if (err == noErr) { 1576 | hir->genericPayloadParamsOK = true; 1577 | } 1578 | break; 1579 | 1580 | case kMPEG4GenericMode_AACLowBitRate: 1581 | if (hir->numLengthBits != kMPEG4Generic_AACLBR_SizeLengthDefault) { 1582 | errprint("sizelength (%ld) != default (%ld)", hir->numLengthBits, kMPEG4Generic_AACLBR_SizeLengthDefault); 1583 | err = paramErr; 1584 | } 1585 | if (hir->numIndexBits != kMPEG4Generic_AACLBR_IndexLengthDefault) { 1586 | errprint("indexlength (%ld) != default (%ld)", hir->numIndexBits, kMPEG4Generic_AACLBR_IndexLengthDefault); 1587 | err = paramErr; 1588 | } 1589 | if (hir->numIndexBits != kMPEG4Generic_AACLBR_IndexDeltaLengthDefault) { 1590 | errprint("indexdeltalength (%ld) != default (%ld)", hir->numIndexBits, kMPEG4Generic_AACLBR_IndexDeltaLengthDefault); 1591 | err = paramErr; 1592 | } 1593 | hir->bytesPerHeader = kMPEGGeneric_AACLBR_OverheadBytesPerFrame; 1594 | hir->indexMask = kMPEGGeneric_AACLBR_IndexMask; 1595 | hir->maxFrameLength = kMPEG4Generic_AACLBR_MaxFrameLength; 1596 | if (err == noErr) { 1597 | hir->genericPayloadParamsOK = true; 1598 | } 1599 | break; 1600 | 1601 | case kMPEG4GenericMode_AACHighBitRate: 1602 | if (hir->numLengthBits != kMPEG4Generic_AACHBR_SizeLengthDefault) { 1603 | errprint("sizelength (%ld) != default (%ld)", hir->numLengthBits, kMPEG4Generic_AACHBR_SizeLengthDefault); 1604 | err = paramErr; 1605 | } 1606 | if (hir->numIndexBits != kMPEG4Generic_AACHBR_IndexLengthDefault) { 1607 | errprint("indexlength (%ld) != default (%ld)", hir->numIndexBits, kMPEG4Generic_AACHBR_IndexLengthDefault); 1608 | err = paramErr; 1609 | } 1610 | if (hir->numIndexBits != kMPEG4Generic_AACHBR_IndexDeltaLengthDefault) { 1611 | errprint("indexdeltalength (%ld) != default (%ld)", hir->numIndexBits, kMPEG4Generic_AACHBR_IndexDeltaLengthDefault); 1612 | err = paramErr; 1613 | } 1614 | hir->bytesPerHeader = kMPEGGeneric_AACHBR_OverheadBytesPerFrame; 1615 | hir->indexMask = kMPEGGeneric_AACHBR_IndexMask; 1616 | hir->maxFrameLength = kMPEG4Generic_AACHBR_MaxFrameLength; 1617 | if (err == noErr) { 1618 | hir->genericPayloadParamsOK = true; 1619 | } 1620 | break; 1621 | 1622 | default: 1623 | errprint("fmtp - missing or unknown mode param\n"); 1624 | break; 1625 | 1626 | } 1627 | } 1628 | 1629 | bail: 1630 | if (param != NULL) { 1631 | free( param ); 1632 | param = NULL; 1633 | } 1634 | if (paramValue != NULL) { 1635 | free( paramValue ); 1636 | paramValue = NULL; 1637 | } 1638 | return err; 1639 | } 1640 | 1641 | //========================================================================================== 1642 | static OSErr Validate_rtpmap_attribute( HintInfoRec *hir, char *inValue) 1643 | { 1644 | OSErr err = noErr; 1645 | char *current = inValue; 1646 | char *next; 1647 | SInt32 temp32; 1648 | Boolean foundNum; 1649 | 1650 | // a=rtpmap: [/[/]] 1651 | // a=rtpmap:96 mpeg4-generic/44100/1 1652 | // a=rtpmap:96 mp4v 1653 | // a=rtpmap:96 H264/90000 1654 | 1655 | // ----- payload num 1656 | next = strchr(current, kSpaceChar); 1657 | if (next == NULL) { 1658 | errprint("bad rtpmap attribute"); 1659 | BAILIFERRSET( err = paramErr ); 1660 | } 1661 | temp32 = Chars_To_Num(current, next, &foundNum); 1662 | if (!foundNum) { 1663 | errprint("rtpmap attribute-payloadnum not a num"); 1664 | BAILIFERRSET( err = paramErr ); 1665 | } 1666 | if (!is_in_range(temp32, 96, 255)) { 1667 | //@@@ diff err msg for static numbers 1668 | errprint("rtpmap attribute-payloadnum out of range"); 1669 | BAILIFERRSET( err = paramErr ); 1670 | } 1671 | current = next+1; 1672 | 1673 | // ----- payload name 1674 | next = strchr(current, '/'); 1675 | if (next == 0) { // the rest is optional 1676 | next = current + strlen(current); 1677 | } 1678 | if (next-current > kMaxSDPPayloadNameLength) { 1679 | warnprint("payload name is unusually long"); 1680 | } 1681 | 1682 | if (temp32 == hir->sdpInfo.payloadNum) { 1683 | // this is the payload name we're using 1684 | temp32 = next-current; 1685 | if (temp32 > kMaxSDPPayloadNameLength) { 1686 | temp32 = kMaxSDPPayloadNameLength ; 1687 | } 1688 | memcpy(hir->sdpInfo.payloadName, current, temp32); 1689 | hir->sdpInfo.payloadName[temp32] = '\0'; 1690 | 1691 | 1692 | } 1693 | 1694 | bail: 1695 | return err; 1696 | } 1697 | 1698 | static OSErr Validate_iod_attribute( HintInfoRec *hir, char *inValue) 1699 | { 1700 | #pragma unused(hir) 1701 | OSErr err = noErr; 1702 | char *current = inValue; 1703 | char *next; 1704 | char *end; 1705 | const char* urlStart = "data:application/mpeg4-iod;base64,"; 1706 | UInt32 base64Size; 1707 | Ptr iodDataP = NULL; 1708 | UInt32 iodSize; 1709 | 1710 | // a=mpeg4-iod: "data:application/mpeg4-iod;base64,..." 1711 | 1712 | end = &inValue[strlen(inValue)]; 1713 | next = current; 1714 | while (*next == ' ') { 1715 | next++; 1716 | } 1717 | if (*next == '\0') { 1718 | errprint("bad iod attribute-no url\n"); 1719 | BAILIFERRSET( err = paramErr ); 1720 | } 1721 | if (*next != '\"' || *(end - 1) != '\"') { 1722 | errprint("bad iod attribute-double quotes missing\n"); 1723 | BAILIFERRSET( err = paramErr ); 1724 | } 1725 | next++; 1726 | if (strncmp(next, urlStart, strlen(urlStart)) != 0) { 1727 | errprint("bad iod attribute-bad url\n"); 1728 | BAILIFERRSET( err = paramErr ); 1729 | } 1730 | next += strlen(urlStart); 1731 | 1732 | base64Size = end - next - 1; 1733 | iodSize = base64Size; // more than enough, will be adjusted down 1734 | BAILIFNIL( iodDataP = malloc(iodSize), allocFailedErr ); 1735 | err = Base64DecodeToBuffer(next, &base64Size, iodDataP, &iodSize); 1736 | if (err) { 1737 | errprint("bad iod attribute-bad base64 encoding"); 1738 | goto bail; 1739 | } 1740 | 1741 | BAILIFERR( Validate_iods_OD_Bits( iodDataP, iodSize, false ) ); 1742 | 1743 | bail: 1744 | return err; 1745 | } 1746 | 1747 | static OSErr Validate_isma_attribute( HintInfoRec *hir, char *inValue) 1748 | { 1749 | #pragma unused(hir) 1750 | OSErr err = noErr; 1751 | int profile, i; 1752 | float lowest, authored; 1753 | 1754 | i = sscanf(inValue,"%d,%f,%f",&profile,&lowest,&authored); 1755 | if (i<3) errprint("Bad ISMA compliance attribute %s\n",inValue); 1756 | else { 1757 | if ((profile<0) || (profile>4)) errprint("Bad ISMA compliance profile value %s\n",inValue); 1758 | if ((lowest != 1.0) && (lowest != 2.0)) errprint("Bad ISMA compliance lowest spec value %s\n",inValue); 1759 | if ((authored != 1.0) && (authored != 2.0)) errprint("Bad ISMA compliance authored spec value %s\n",inValue); 1760 | } 1761 | 1762 | // a=isma-compliance:1,1.0,1 1763 | 1764 | //if (strcmp(inValue, "1,1.0,1") != 0) { 1765 | // errprint("bad isma compliance attribute"); 1766 | // BAILIFERRSET( err = paramErr ); 1767 | //} 1768 | 1769 | bail: 1770 | return err; 1771 | } 1772 | 1773 | #pragma mark 1774 | 1775 | #define kBase64BadLookupChar 64 1776 | #define kBase64PadLookupChar 65 1777 | 1778 | static const char sBase64DecodingTable [256] = { 1779 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1780 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1781 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 1782 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 65, 64, 64, 1783 | 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1784 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 1785 | 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 1786 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 1787 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1788 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1789 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1790 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1791 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1792 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1793 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 1794 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 1795 | }; 1796 | 1797 | #define kBase64PadChar '=' 1798 | #define kBase64LineBreakChar '\n' 1799 | 1800 | #define kBase64DecodedCharsPerGroup 3 1801 | #define kBase64EncodedCharsPerGroup 4 1802 | 1803 | // --------------------------------------------------------------------------- 1804 | // Base64DecodeToBuffer 1805 | // --------------------------------------------------------------------------- 1806 | 1807 | OSErr Base64DecodeToBuffer(const char *inData, UInt32 *ioEncodedLength, char *outDecodedData, UInt32 *ioDecodedDataLength) 1808 | { 1809 | OSErr err = noErr; 1810 | 1811 | long encodedDataProcessed = 0; 1812 | const char *current; 1813 | const char *end; 1814 | char lookupChar; 1815 | char *decodedCurrent; 1816 | char tempBuffer[4]; 1817 | int countInTempBuffer = 0; 1818 | int tempNumToDecode; 1819 | int foundPadChar = 0; 1820 | 1821 | if ((ioEncodedLength == NULL) || (ioDecodedDataLength == NULL)) { 1822 | BAILIFERRSET( err = paramErr ); 1823 | } 1824 | 1825 | // we will always decode 4 bytes into 3 characters 1826 | current = inData; 1827 | end = inData + *ioEncodedLength; 1828 | decodedCurrent = outDecodedData; 1829 | 1830 | // process encoded bytes - notice that if the encoded chars 1831 | // (illegal base64 chars are not counted in this - e.g. '\n') 1832 | // are not a mulple of 4, we will only process in multiples of 4 1833 | // and leave any leftovers behind 1834 | while (current < end) { 1835 | 1836 | if ((lookupChar = sBase64DecodingTable[(unsigned char)(*current)]) != kBase64BadLookupChar) { 1837 | tempBuffer[countInTempBuffer] = lookupChar; 1838 | ++countInTempBuffer; 1839 | if ((lookupChar == kBase64PadLookupChar) && (foundPadChar == 0)) { 1840 | foundPadChar = countInTempBuffer; 1841 | } 1842 | if (countInTempBuffer == kBase64EncodedCharsPerGroup) { 1843 | // we have 4 encoded characters now - decode it to 3 1844 | // we have to account for padding characters 1845 | switch (foundPadChar) { 1846 | case 1: 1847 | // the whole thing is padding characters?? 1848 | // shouldn't happen but you never know 1849 | tempNumToDecode = 0; 1850 | break; 1851 | case 2: 1852 | case 3: 1853 | tempNumToDecode = 1; 1854 | break; 1855 | case 4: 1856 | tempNumToDecode = 2; 1857 | break; 1858 | default: 1859 | tempNumToDecode = 3; 1860 | break; 1861 | } 1862 | 1863 | 1864 | if ((UInt32)(decodedCurrent - outDecodedData + tempNumToDecode) > *ioDecodedDataLength) { 1865 | // done processing because we ran out of room in the decode buffer 1866 | break; 1867 | } 1868 | 1869 | if (tempNumToDecode > 0) { 1870 | decodedCurrent[0] = (tempBuffer [0] << 2) | ((tempBuffer [1] & 0x30) >> 4); 1871 | } 1872 | if (tempNumToDecode > 1) { 1873 | decodedCurrent [1] = ((tempBuffer [1] & 0x0F) << 4) | ((tempBuffer [2] & 0x3C) >> 2); 1874 | } 1875 | if (tempNumToDecode > 2) { 1876 | decodedCurrent [2] = ((tempBuffer [2] & 0x03) << 6) | (tempBuffer [3] & 0x3F); 1877 | } 1878 | countInTempBuffer = 0; 1879 | decodedCurrent += tempNumToDecode; 1880 | encodedDataProcessed = (current+1 - inData); 1881 | foundPadChar = 0; 1882 | } 1883 | } else { 1884 | // if the buffer is full of just illegal characters (e.g. \n), we will deal with it 1885 | if (countInTempBuffer == 0) { 1886 | encodedDataProcessed = (current+1 - inData); 1887 | } 1888 | } 1889 | ++current; 1890 | } 1891 | *ioEncodedLength = encodedDataProcessed; 1892 | *ioDecodedDataLength = (decodedCurrent - outDecodedData); 1893 | 1894 | bail: 1895 | return err; 1896 | } 1897 | 1898 | #pragma mark - 1899 | 1900 | //========================================================================================== 1901 | static OSErr SDP_Get_Tag( char **ioCurrent, char *outTag ) 1902 | { 1903 | #define kSDPTagSeparatorChar '=' 1904 | #define kSDPTagLength 2 1905 | 1906 | OSErr err = noErr; 1907 | char *current = *ioCurrent; 1908 | 1909 | if ((current[0] != '\0') && (current[1] != '\0')) { 1910 | if (current[1] != kSDPTagSeparatorChar) { 1911 | errprint("sdp line doesn't start with = '%.20s'", current); 1912 | err = paramErr; 1913 | } else { 1914 | *outTag = current[0]; 1915 | *ioCurrent += kSDPTagLength; 1916 | } 1917 | } else { 1918 | errprint("sdp line doesn't start with = '%.20s'", current); 1919 | err = paramErr; 1920 | } 1921 | return err; 1922 | } 1923 | 1924 | //========================================================================================== 1925 | static char *SDP_Find_Line_End( char *inCurrent ) 1926 | { 1927 | char *current = inCurrent; 1928 | char *lineEnd = 0; 1929 | 1930 | while ( (current[0] != '\0') && 1931 | (current[0] != CR) && 1932 | (current[0] != LF) ) { 1933 | ++current; 1934 | } 1935 | lineEnd = current; 1936 | return lineEnd; 1937 | } 1938 | 1939 | //========================================================================================== 1940 | static char *SDP_Skip_Line_Ending_Chars( char *inLineEnd ) 1941 | { 1942 | char *current = inLineEnd; 1943 | char *nextLineStart = 0; 1944 | 1945 | // go past one CRLF, or just one CR or LF 1946 | if ((inLineEnd[0] == CR) && (inLineEnd[1] == LF)) { 1947 | nextLineStart = inLineEnd + 2; 1948 | } else { 1949 | if ((inLineEnd[0] == CR) || (inLineEnd[0] == LF)) { 1950 | nextLineStart = inLineEnd + 1; 1951 | } else if (inLineEnd[0] == '\0') { 1952 | nextLineStart = inLineEnd; 1953 | } 1954 | } 1955 | return nextLineStart; 1956 | } 1957 | 1958 | //========================================================================================== 1959 | static void Validate_SDP_Line_Ending( HintInfoRec *hir, char *inEndOfLine ) 1960 | { 1961 | #pragma unused(hir) 1962 | // it should be CRLF 1963 | if ((inEndOfLine[0] == CR) && 1964 | (inEndOfLine[1] == LF)) { 1965 | // this is the correct line ending 1966 | } else { 1967 | errprint("sdp line doesn't end in CRLF"); 1968 | } 1969 | } 1970 | 1971 | #pragma mark - 1972 | 1973 | //========================================================================================== 1974 | static SInt32 Chars_To_Num( char *inCharsStart, char *inCharsEnd, Boolean *outFoundNum ) 1975 | { 1976 | #define kMaxCharsToNumLength 11 1977 | char tempString[kMaxCharsToNumLength+1]; 1978 | int length; 1979 | SInt32 value = 0; 1980 | Boolean found = false; 1981 | 1982 | if (!is_num(inCharsStart, inCharsEnd)) { 1983 | errprint("Chars_To_Num - not a number"); 1984 | goto bail; 1985 | } 1986 | 1987 | // the number can't be > a long 1988 | length = inCharsEnd - inCharsStart; 1989 | if (length > kMaxCharsToNumLength) { 1990 | errprint("Chars_To_Num - too many chars"); 1991 | goto bail; 1992 | } 1993 | 1994 | memcpy(tempString, inCharsStart, length); 1995 | tempString[length] = '\0'; 1996 | 1997 | sscanf(tempString, "%ld", &value); 1998 | found = true; 1999 | 2000 | bail: 2001 | if (outFoundNum != NULL) { 2002 | *outFoundNum = found; 2003 | } 2004 | return value; 2005 | } 2006 | 2007 | static SInt32 Chars_To_hexNum( char *inCharsStart, char *inCharsEnd, Boolean *outFoundNum ) 2008 | { 2009 | char tempString[kMaxCharsToNumLength+1]; 2010 | int length; 2011 | SInt32 value = 0; 2012 | Boolean found = false; 2013 | 2014 | if (!is_hexnum(inCharsStart, inCharsEnd)) { 2015 | errprint("Chars_To_hexNum - not a hex number"); 2016 | goto bail; 2017 | } 2018 | 2019 | // the number can't be > a long 2020 | length = inCharsEnd - inCharsStart; 2021 | if (length > kMaxCharsToNumLength) { 2022 | errprint("Chars_To_Num - too many chars"); 2023 | goto bail; 2024 | } 2025 | 2026 | memcpy(tempString, inCharsStart, length); 2027 | tempString[length] = '\0'; 2028 | 2029 | sscanf(tempString, "%lx", &value); 2030 | found = true; 2031 | 2032 | bail: 2033 | if (outFoundNum != NULL) { 2034 | *outFoundNum = found; 2035 | } 2036 | return value; 2037 | } 2038 | 2039 | 2040 | //========================================================================================== 2041 | static Boolean is_num( char *inCharsStart, char *inCharsEnd) 2042 | { 2043 | char *current = inCharsStart; 2044 | Boolean isNum = true; 2045 | 2046 | if (inCharsEnd <= inCharsStart) { 2047 | isNum = false; 2048 | goto bail; 2049 | } 2050 | 2051 | while (current < inCharsEnd) { 2052 | if ((current[0] < '0') || (current[0] > '9')) { 2053 | isNum = false; 2054 | break; 2055 | } 2056 | ++current; 2057 | } 2058 | bail: 2059 | return isNum; 2060 | } 2061 | 2062 | static Boolean is_hexnum( char *inCharsStart, char *inCharsEnd) 2063 | { 2064 | char *current = inCharsStart; 2065 | Boolean isNum = true; 2066 | 2067 | if (inCharsEnd <= inCharsStart) { 2068 | isNum = false; 2069 | goto bail; 2070 | } 2071 | 2072 | while (current < inCharsEnd) { 2073 | if ( ((current[0] < '0') || (current[0] > '9')) && 2074 | ((current[0] < 'a') || (current[0] > 'f')) && 2075 | ((current[0] < 'A') || (current[0] > 'F')) ) { 2076 | isNum = false; 2077 | break; 2078 | } 2079 | ++current; 2080 | } 2081 | bail: 2082 | return isNum; 2083 | } 2084 | 2085 | 2086 | //========================================================================================== 2087 | static Boolean is_in_range( SInt32 inNum, SInt32 inMin, SInt32 inMax) 2088 | { 2089 | Boolean inRange; 2090 | 2091 | if ((inNum < inMin) || (inNum > inMax)) { 2092 | inRange = false; 2093 | } else { 2094 | inRange = true; 2095 | } 2096 | return inRange; 2097 | } 2098 | 2099 | //========================================================================================== 2100 | static Boolean compare_nocase(const char *s1, const char *s2) 2101 | { 2102 | Boolean matches = false; 2103 | const char *c1 = s1; 2104 | const char *c2 = s2; 2105 | 2106 | while ((c1[0] != '\0') && (c2[0] != '\0')) { 2107 | if ( tolower(*c1) != tolower(*c2)) { 2108 | matches = false; 2109 | goto bail; 2110 | } 2111 | ++c1; 2112 | ++c2; 2113 | } 2114 | if ((c1[0] == '\0') && (c2[0] == '\0')) { 2115 | matches = true; 2116 | } 2117 | bail: 2118 | return matches; 2119 | } 2120 | 2121 | //========================================================================================== 2122 | static Boolean get_next_fmtp_param(char **inLine, char **outTagString, char **outValueString) 2123 | { 2124 | OSErr err = noErr; 2125 | char *begin = *inLine; 2126 | char *paramEnd; 2127 | char *tagEnd; 2128 | Boolean found = false; 2129 | Ptr tag = NULL; 2130 | Ptr value = NULL; 2131 | long length; 2132 | 2133 | 2134 | // strip off leading spaces? 2135 | // cisco puts spaces between params 2136 | while (begin[0] == ' ') { 2137 | ++begin; 2138 | } 2139 | 2140 | if (begin[0] == '\0') { 2141 | goto bail; 2142 | } 2143 | 2144 | paramEnd = strchr(begin, ';'); 2145 | if (paramEnd == NULL) { 2146 | paramEnd = begin + strlen(begin); 2147 | } 2148 | 2149 | tagEnd = strchr(begin, '='); 2150 | if (tagEnd == NULL) { 2151 | tagEnd = begin + strlen(begin); 2152 | } 2153 | if (tagEnd > paramEnd) { 2154 | tagEnd = paramEnd; 2155 | } 2156 | 2157 | length = tagEnd - begin; 2158 | BAILIFNIL( tag = malloc(length + 1), allocFailedErr ); 2159 | memcpy(tag, begin, length); 2160 | tag[length] = '\0'; 2161 | 2162 | if (paramEnd != tagEnd) { 2163 | length = paramEnd - tagEnd - 1; // subtract the = 2164 | BAILIFNIL( value = malloc(length + 1), allocFailedErr ); 2165 | memcpy(value, tagEnd+1, length); 2166 | value[length] = '\0'; 2167 | } else { 2168 | BAILIFNIL( value = malloc(1), allocFailedErr ); 2169 | value[0] = '\0'; 2170 | } 2171 | found = true; 2172 | *inLine = paramEnd+1; 2173 | 2174 | bail: 2175 | if (err != noErr) { 2176 | if (tag != NULL) { 2177 | free(tag); 2178 | } 2179 | if (value != NULL) { 2180 | free(value); 2181 | } 2182 | if (outTagString != NULL) { 2183 | *outTagString = NULL; 2184 | } 2185 | if (outValueString != NULL) { 2186 | *outValueString = NULL; 2187 | } 2188 | } else { 2189 | if (outTagString != NULL) { 2190 | *outTagString = tag; 2191 | } 2192 | if (outValueString != NULL) { 2193 | *outValueString = value; 2194 | } 2195 | } 2196 | return found; 2197 | } 2198 | 2199 | #pragma mark - 2200 | 2201 | //========================================================================================== 2202 | static OSErr get_original_track_info(UInt32 inRefTrackID, TrackInfoRec **outTIR) 2203 | { 2204 | OSErr err = noErr; 2205 | long i; 2206 | MovieInfoRec *mir = vg.mir; 2207 | 2208 | if (mir == NULL) { 2209 | err = paramErr; 2210 | goto bail; 2211 | } 2212 | 2213 | for (i=0; inumTIRs; ++i) { 2214 | if (mir->tirList[i].trackID == inRefTrackID) { 2215 | *outTIR = &(mir->tirList[i]); 2216 | break; 2217 | } 2218 | } 2219 | if (!(*outTIR)) { 2220 | errprint("Can't find media track ID %d from hint track\n",inRefTrackID); 2221 | /* for (i=0; inumTIRs; ++i) warnprint(" Candidate track ID %d\n",mir->tirList[i].trackID); */ 2222 | } 2223 | 2224 | bail: 2225 | return err; 2226 | } 2227 | 2228 | //========================================================================================== 2229 | static OSErr get_track_sample(TrackInfoRec *tir, UInt32 inSampleNum, Ptr *dataOut, UInt32 *sizeOut, UInt32 *sampleDescriptionIndexOut) 2230 | { 2231 | OSErr err = noErr; 2232 | UInt64 sampleOffset; 2233 | 2234 | if (tir != NULL) { 2235 | BAILIFERR( GetSampleOffsetSize( tir, inSampleNum, &sampleOffset, sizeOut, sampleDescriptionIndexOut ) ); 2236 | BAILIFNIL( *dataOut = malloc(*sizeOut), allocFailedErr ); 2237 | BAILIFERR( GetFileData( vg.fileaoe, *dataOut, sampleOffset, *sizeOut, nil ) ); 2238 | } 2239 | bail: 2240 | return err; 2241 | } 2242 | 2243 | 2244 | --------------------------------------------------------------------------------