source: trunk/CommonUtils.c @ 1426

Revision 1426, 17.6 KB checked in by astrange, 3 years ago (diff)

Fix complete nonsense call to Gestalt() which broke compat checks

Fixes #566

Line 
1/*
2 * CommonUtils.h
3 * Created by David Conrad on 10/13/06.
4 *
5 * This file is part of Perian.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "avcodec.h"
23#include "CommonUtils.h"
24#import <Carbon/Carbon.h>
25#import <pthread.h>
26#import <dlfcn.h>
27#import <fnmatch.h>
28
29typedef struct LanguageTriplet {
30        char twoChar[3];
31        char threeChar[4];      // (ISO 639-2 3 char code)
32        short qtLang;
33} LanguageTriplet;
34
35// don't think there's a function already to do ISO 639-1/2 -> language code
36// that SetMediaLanguage() accepts
37static const LanguageTriplet ISO_QTLanguages[] = {
38        { "",   "und", langUnspecified },
39        { "af", "afr", langAfrikaans },
40        { "sq", "alb", langAlbanian },
41        { "sq", "sqi", langAlbanian },
42        { "am", "amh", langAmharic },
43        { "ar", "ara", langArabic },
44        { "hy", "arm", langArmenian },
45        { "hy", "hye", langArmenian },
46        { "as", "asm", langAssamese }, 
47        { "ay", "aym", langAymara },
48        { "az", "aze", langAzerbaijani },
49        { "eu", "baq", langBasque },
50        { "eu", "eus", langBasque },
51        { "bn", "ben", langBengali },
52        { "br", "bre", langBreton },
53        { "bg", "bul", langBulgarian },
54        { "my", "bur", langBurmese },
55        { "my", "mya", langBurmese },
56        { "ca", "cat", langCatalan },
57        { "zh", "chi", langTradChinese },
58        { "zh", "zho", langTradChinese },
59        { "cs", "cze", langCzech },
60        { "cs", "ces", langCzech },
61        { "da", "dan", langDanish },
62        { "nl", "dut", langDutch },
63        { "nl", "nld", langDutch },
64        { "dz", "dzo", langDzongkha },
65        { "en", "eng", langEnglish },
66        { "eo", "epo", langEsperanto },
67        { "et", "est", langEstonian },
68        { "fo", "fao", langFaroese },
69        { "fi", "fin", langFinnish },
70        { "fr", "fre", langFrench },
71        { "fr", "fra", langFrench },
72        { "ka", "geo", langGeorgian },
73        { "ka", "kat", langGeorgian },
74        { "de", "ger", langGerman },
75        { "de", "deu", langGerman },
76        { "gl", "glg", langGalician },
77        { "gd", "gla", langScottishGaelic },
78        { "ga", "gle", langIrishGaelic },
79        { "gv", "glv", langManxGaelic },
80        { "",   "grc", langGreekAncient },
81        { "el", "gre", langGreek },
82        { "el", "ell", langGreek },
83        { "gn", "grn", langGuarani },
84        { "gu", "guj", langGujarati },
85        { "he", "heb", langHebrew },
86        { "hi", "hin", langHindi },
87        { "hu", "hun", langHungarian },
88        { "is", "ice", langIcelandic },
89        { "is", "isl", langIcelandic },
90        { "id", "ind", langIndonesian },
91        { "it", "ita", langItalian },
92        { "jv", "jav", langJavaneseRom },
93        { "ja", "jpn", langJapanese },
94        { "kl", "kal", langGreenlandic },
95        { "kn", "kan", langKannada },
96        { "ks", "kas", langKashmiri },
97        { "kk", "kaz", langKazakh },
98        { "km", "khm", langKhmer },
99        { "rw", "kin", langKinyarwanda },
100        { "ky", "kir", langKirghiz },
101        { "ko", "kor", langKorean },
102        { "ku", "kur", langKurdish },
103        { "lo", "lao", langLao },
104        { "la", "lat", langLatin },
105        { "lv", "lav", langLatvian },
106        { "lt", "lit", langLithuanian },
107        { "mk", "mac", langMacedonian },
108        { "mk", "mkd", langMacedonian },
109        { "ml", "mal", langMalayalam },
110        { "mr", "mar", langMarathi },
111        { "ms", "may", langMalayRoman },
112        { "ms", "msa", langMalayRoman },
113        { "mg", "mlg", langMalagasy },
114        { "mt", "mlt", langMaltese },
115        { "mo", "mol", langMoldavian },
116        { "mn", "mon", langMongolian },
117        { "ne", "nep", langNepali },
118        { "nb", "nob", langNorwegian },         // Norwegian Bokmal
119        { "no", "nor", langNorwegian },
120        { "nn", "nno", langNynorsk },
121        { "ny", "nya", langNyanja },
122        { "or", "ori", langOriya },
123        { "om", "orm", langOromo },
124        { "pa", "pan", langPunjabi },
125        { "fa", "per", langPersian },
126        { "fa", "fas", langPersian },
127        { "pl", "pol", langPolish },
128        { "pt", "por", langPortuguese },
129        { "qu", "que", langQuechua },
130        { "ro", "rum", langRomanian },
131        { "ro", "ron", langRomanian },
132        { "rn", "run", langRundi },
133        { "ru", "rus", langRussian },
134        { "sa", "san", langSanskrit },
135        { "sr", "scc", langSerbian },
136        { "sr", "srp", langSerbian },
137        { "hr", "scr", langCroatian },
138        { "hr", "hrv", langCroatian },
139        { "si", "sin", langSinhalese },
140        { "",   "sit", langTibetan },           // Sino-Tibetan (Other)
141        { "sk", "slo", langSlovak },
142        { "sk", "slk", langSlovak },
143        { "sl", "slv", langSlovenian },
144        { "se", "sme", langSami },
145        { "",   "smi", langSami },                      // Sami languages (Other)
146        { "sd", "snd", langSindhi },
147        { "so", "som", langSomali },
148        { "es", "spa", langSpanish },
149        { "su", "sun", langSundaneseRom },
150        { "sw", "swa", langSwahili },
151        { "sv", "swe", langSwedish },
152        { "ta", "tam", langTamil },
153        { "tt", "tat", langTatar },
154        { "te", "tel", langTelugu },
155        { "tg", "tgk", langTajiki },
156        { "tl", "tgl", langTagalog },
157        { "th", "tha", langThai },
158        { "bo", "tib", langTibetan },
159        { "bo", "bod", langTibetan },
160        { "ti", "tir", langTigrinya },
161        { "",   "tog", langTongan },            // Tonga (Nyasa, Tonga Islands)
162        { "tr", "tur", langTurkish },
163        { "tk", "tuk", langTurkmen },
164        { "ug", "uig", langUighur },
165        { "uk", "ukr", langUkrainian },
166        { "ur", "urd", langUrdu },
167        { "uz", "uzb", langUzbek },
168        { "vi", "vie", langVietnamese },
169        { "cy", "wel", langWelsh },
170        { "cy", "cym", langWelsh },
171        { "yi", "yid", langYiddish }
172};
173
174short ISO639_1ToQTLangCode(const char *lang)
175{
176        int i;
177       
178        if (strlen(lang) != 2)
179                return langUnspecified;
180       
181        for (i = 0; i < sizeof(ISO_QTLanguages) / sizeof(LanguageTriplet); i++) {
182                if (strcasecmp(lang, ISO_QTLanguages[i].twoChar) == 0)
183                        return ISO_QTLanguages[i].qtLang;
184        }
185       
186        return langUnspecified;
187}
188
189short ISO639_2ToQTLangCode(const char *lang)
190{
191        int i;
192       
193        if (strlen(lang) != 3)
194                return langUnspecified;
195       
196        for (i = 0; i < sizeof(ISO_QTLanguages) / sizeof(LanguageTriplet); i++) {
197                if (strcasecmp(lang, ISO_QTLanguages[i].threeChar) == 0)
198                        return ISO_QTLanguages[i].qtLang;
199        }
200       
201        return langUnspecified;
202}
203
204/* write the int32_t data to target & then return a pointer which points after that data */
205uint8_t *write_int32(uint8_t *target, int32_t data)
206{
207        return write_data(target, (uint8_t*)&data, sizeof(data));
208} /* write_int32() */
209
210/* write the int16_t data to target & then return a pointer which points after that data */
211uint8_t *write_int16(uint8_t *target, int16_t data)
212{
213        return write_data(target, (uint8_t*)&data, sizeof(data));
214} /* write_int16() */
215
216/* write the data to the target adress & then return a pointer which points after the written data */
217uint8_t *write_data(uint8_t *target, uint8_t* data, int32_t data_size)
218{
219        if(data_size > 0)
220                memcpy(target, data, data_size);
221        return (target + data_size);
222} /* write_data() */
223
224
225
226#define MP4ESDescrTag                   0x03
227#define MP4DecConfigDescrTag            0x04
228#define MP4DecSpecificDescrTag          0x05
229
230// based off of mov_mp4_read_descr_len from mov.c in ffmpeg's libavformat
231static int readDescrLen(UInt8 **buffer)
232{
233        int len = 0;
234        int count = 4;
235        while (count--) {
236                int c = *(*buffer)++;
237                len = (len << 7) | (c & 0x7f);
238                if (!(c & 0x80))
239                        break;
240        }
241        return len;
242}
243
244// based off of mov_mp4_read_descr from mov.c in ffmpeg's libavformat
245static int readDescr(UInt8 **buffer, int *tag)
246{
247        *tag = *(*buffer)++;
248        return readDescrLen(buffer);
249}
250
251// based off of mov_read_esds from mov.c in ffmpeg's libavformat
252ComponentResult ReadESDSDescExt(Handle descExt, UInt8 **buffer, int *size)
253{
254        UInt8 *esds = (UInt8 *) *descExt;
255        int tag, len;
256       
257        *size = 0;
258       
259        esds += 4;              // version + flags
260        len = readDescr(&esds, &tag);
261        esds += 2;              // ID
262        if (tag == MP4ESDescrTag)
263                esds++;         // priority
264       
265        len = readDescr(&esds, &tag);
266        if (tag == MP4DecConfigDescrTag) {
267                esds++;         // object type id
268                esds++;         // stream type
269                esds += 3;      // buffer size db
270                esds += 4;      // max bitrate
271                esds += 4;      // average bitrate
272               
273                len = readDescr(&esds, &tag);
274                if (tag == MP4DecSpecificDescrTag) {
275                        *buffer = calloc(1, len + FF_INPUT_BUFFER_PADDING_SIZE);
276                        if (*buffer) {
277                                memcpy(*buffer, esds, len);
278                                *size = len;
279                        }
280                }
281        }
282       
283        return noErr;
284}
285
286int isImageDescriptionExtensionPresent(ImageDescriptionHandle desc, long type)
287{
288        ImageDescriptionPtr d = *desc;
289        int offset = sizeof(ImageDescription);
290        uint8_t *p = (uint8_t *)d;
291       
292        //read next description, need 8 bytes for size and type
293        while(offset < d->idSize - 8)
294        {
295                long len = *(p+offset) << 24 | *(p+offset+1) << 16 | *(p+offset+2) << 8 | *(p+offset+3);
296                long rtype = *(p+offset + 4) << 24 | *(p+offset+5) << 16 | *(p+offset+6) << 8 | *(p+offset+7);
297                if(rtype == type && offset + len <= d->idSize)
298                        return 1;
299                offset += len;
300        }
301        return 0;
302}
303
304static const CFStringRef defaultFrameDroppingList[] = {
305        CFSTR("Finder"),
306        CFSTR("Front Row"),
307        CFSTR("Movie Time"),
308        CFSTR("Movist"),
309        CFSTR("NicePlayer"),
310        CFSTR("QTKitServer"),
311        CFSTR("QuickTime Player"),
312        CFSTR("Spiral")
313};
314
315static const CFStringRef defaultTransparentSubtitleList_10_6[] = {
316        CFSTR("CoreMediaAuthoringSessionHelper"),
317        CFSTR("CoreMediaAuthoringSourcePropertyHelper"),
318        CFSTR("Front Row"),
319        CFSTR("QTPlayerHelper")
320};
321
322static const CFStringRef defaultTransparentSubtitleList_10_5[] = {
323        CFSTR("Front Row")
324};
325
326static const CFStringRef defaultForcedAppList[] = {
327        CFSTR("iChat")
328};
329
330static int findNameInList(CFStringRef loadingApp, const CFStringRef *names, int count)
331{
332        int i;
333
334        for (i = 0; i < count; i++) {
335                if (CFGetTypeID(names[i]) != CFStringGetTypeID())
336                        continue;
337                if (CFStringCompare(loadingApp, names[i], 0) == kCFCompareEqualTo) return 1;
338        }
339
340        return 0;
341}
342
343static CFDictionaryRef getMyProcessInformation()
344{
345        ProcessSerialNumber myProcess;
346        GetCurrentProcess(&myProcess);
347        CFDictionaryRef processInformation;
348       
349        processInformation = ProcessInformationCopyDictionary(&myProcess, kProcessDictionaryIncludeAllInformationMask);
350        return processInformation;
351}
352
353static CFStringRef getProcessName(CFDictionaryRef processInformation)
354{
355        CFStringRef path = CFDictionaryGetValue(processInformation, kCFBundleExecutableKey);
356        CFRange entireRange = CFRangeMake(0, CFStringGetLength(path)), basename;
357       
358        CFStringFindWithOptions(path, CFSTR("/"), entireRange, kCFCompareBackwards, &basename);
359       
360        basename.location += 1; //advance past "/"
361        basename.length = entireRange.length - basename.location;
362       
363        CFStringRef myProcessName = CFStringCreateWithSubstring(NULL, path, basename);
364        return myProcessName;
365}
366
367static int isApplicationNameInList(CFStringRef prefOverride, const CFStringRef *defaultList, unsigned int defaultListCount)
368{
369        CFDictionaryRef processInformation = getMyProcessInformation();
370       
371        if (!processInformation)
372                return FALSE;
373       
374        CFArrayRef list = CopyPreferencesValueTyped(prefOverride, CFArrayGetTypeID());
375        CFStringRef myProcessName = getProcessName(processInformation);
376        int ret;
377       
378        if (list) {
379                int count = CFArrayGetCount(list);
380                CFStringRef names[count];
381               
382                CFArrayGetValues(list, CFRangeMake(0, count), (void *)names);
383                ret = findNameInList(myProcessName, names, count);
384                CFRelease(list);
385        } else {
386                ret = findNameInList(myProcessName, defaultList, defaultListCount);
387        }
388        CFRelease(myProcessName);
389        CFRelease(processInformation);
390       
391        return ret;
392}
393
394int IsFrameDroppingEnabled()
395{
396        static int enabled = -1;
397       
398        if (enabled == -1)
399                enabled = isApplicationNameInList(CFSTR("FrameDroppingWhiteList"),
400                                                                                  defaultFrameDroppingList,
401                                                                                  sizeof(defaultFrameDroppingList)/sizeof(defaultFrameDroppingList[0]));
402        return enabled;
403}
404
405int IsForcedDecodeEnabled()
406{
407        static int forced = -1;
408       
409        if(forced == -1)
410                forced = isApplicationNameInList(CFSTR("ForcePerianAppList"),
411                                                                                 defaultForcedAppList,
412                                                                                 sizeof(defaultForcedAppList)/sizeof(defaultForcedAppList[0]));
413        return forced;
414}
415
416static int GetSystemMinorVersion()
417{
418        static SInt32 minorVersion = -1;
419        if (minorVersion == -1)
420                Gestalt(gestaltSystemVersionMinor, &minorVersion);
421       
422        return minorVersion;
423}
424
425static int GetSystemMicroVersion()
426{
427        static SInt32 microVersion = -1;
428        if (microVersion == -1)
429                Gestalt(gestaltSystemVersionBugFix, &microVersion);
430       
431        return microVersion;
432}
433
434int IsTransparentSubtitleHackEnabled()
435{
436        static int forced = -1;
437       
438        if(forced == -1)
439        {
440                int minorVersion = GetSystemMinorVersion();
441                               
442                if (minorVersion == 5)
443                        forced = isApplicationNameInList(CFSTR("TransparentModeSubtitleAppList"),
444                                                                                         defaultTransparentSubtitleList_10_5,
445                                                                                         sizeof(defaultTransparentSubtitleList_10_5)/sizeof(defaultTransparentSubtitleList_10_5[0]));
446                else if (minorVersion == 6)
447                        forced = isApplicationNameInList(CFSTR("TransparentModeSubtitleAppList"),
448                                                                                         defaultTransparentSubtitleList_10_6,
449                                                                                         sizeof(defaultTransparentSubtitleList_10_6)/sizeof(defaultTransparentSubtitleList_10_6[0]));
450                else
451                        forced = 0;
452        }
453       
454        return forced;
455}
456
457int IsAltivecSupported()
458{
459        static int altivec = -1;
460       
461        if (altivec == -1) {
462                long response = 0;
463                int err = Gestalt(gestaltPowerPCProcessorFeatures, &response);
464               
465                altivec = !err && ((response & (1 << gestaltPowerPCHasVectorInstructions)) != 0);
466        }
467       
468        return altivec;
469}
470
471
472// this could be a defaults setting, but no real call for it yet
473int ShouldImportFontFileName(const char *filename)
474{
475        // match DynaFont Labs (1997) fonts, which are in many files
476        // and completely break ATSUI on different OS versions
477        // FIXME: This font works when in ~/Library/Fonts (!). Check it again with CoreText.
478        return !(GetSystemMinorVersion() >= 6 && fnmatch("DF*.ttc", filename, 0) == 0);
479}
480
481// does the system support HE-AAC with a base frequency over 32khz?
482// 10.6.3 does, nothing else does. this may be conflated with some encoder bugs.
483int ShouldPlayHighFreqSBR()
484{
485        return 0;
486}
487
488
489CFPropertyListRef CopyPreferencesValueTyped(CFStringRef key, CFTypeID type)
490{
491        CFPropertyListRef val = CFPreferencesCopyAppValue(key, PERIAN_PREF_DOMAIN);
492       
493        if (val && CFGetTypeID(val) != type) {
494                CFRelease(val);
495                val = NULL;
496        }
497       
498        return val;
499}
500
501static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
502
503int PerianInitEnter(volatile Boolean *inited)
504{
505        if (*inited)
506                return FALSE;
507       
508        pthread_mutex_lock(&init_mutex);
509        return TRUE;
510}
511
512void PerianInitExit(int unlock)
513{
514        if (unlock)
515                pthread_mutex_unlock(&init_mutex);
516}
517
518void *fast_realloc_with_padding(void *ptr, unsigned int *size, unsigned int min_size)
519{
520        void *res = ptr;
521        av_fast_malloc(&res, size, min_size + FF_INPUT_BUFFER_PADDING_SIZE);
522        if (res) memset(res + min_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
523        return res;
524}
525
526// from Apple Q&A 1396
527static CGColorSpaceRef CreateICCColorSpaceFromPathToProfile (const char * iccProfilePath)
528{
529        CMProfileRef    iccProfile = NULL;
530        CGColorSpaceRef iccColorSpace = NULL;
531        CMProfileLocation loc;
532       
533        // Specify that the location of the profile will be a POSIX path to the profile.
534        loc.locType = cmPathBasedProfile;
535       
536        // Make sure the path is not larger then the buffer
537        if(strlen(iccProfilePath) > sizeof(loc.u.pathLoc.path))
538                return NULL;
539       
540        // Copy the path the profile into the CMProfileLocation structure
541        strcpy (loc.u.pathLoc.path, iccProfilePath);
542       
543        // Open the profile
544        if (CMOpenProfile(&iccProfile, &loc) != noErr)
545        {
546                iccProfile = (CMProfileRef) 0;
547                return NULL;
548        }
549       
550        // Create the ColorSpace with the open profile.
551        iccColorSpace = CGColorSpaceCreateWithPlatformColorSpace( iccProfile );
552       
553        // Close the profile now that we have what we need from it.
554        CMCloseProfile(iccProfile);
555       
556        return iccColorSpace;
557}
558
559static CGColorSpaceRef CreateColorSpaceFromSystemICCProfileName(CFStringRef profileName)
560{
561        FSRef pathToProfilesFolder;
562    FSRef pathToProfile;
563       
564        // Find the Systems Color Sync Profiles folder
565        if(FSFindFolder(kOnSystemDisk, kColorSyncProfilesFolderType,
566                                        kDontCreateFolder, &pathToProfilesFolder) == noErr) {
567               
568                // Make a UniChar string of the profile name
569                UniChar uniBuffer[sizeof(CMPathLocation)];
570                CFStringGetCharacters (profileName,CFRangeMake(0,CFStringGetLength(profileName)),uniBuffer);
571               
572                // Create a FSRef to the profile in the Systems Color Sync Profile folder
573                if(FSMakeFSRefUnicode (&pathToProfilesFolder,CFStringGetLength(profileName),uniBuffer,
574                                                           kUnicodeUTF8Format,&pathToProfile) == noErr) {
575                        unsigned char path[sizeof(CMPathLocation)];
576                       
577                        // Write the posix path to the profile into our path buffer from the FSRef
578                        if(FSRefMakePath (&pathToProfile,path,sizeof(CMPathLocation)) == noErr)
579                                return CreateICCColorSpaceFromPathToProfile((char*)path);
580                }
581        }
582       
583        return NULL;
584}
585
586CGColorSpaceRef GetSRGBColorSpace()
587{
588        static Boolean loaded = FALSE;
589        static CGColorSpaceRef sRGBColorSpace;
590        int unlock = PerianInitEnter(&loaded);
591
592        if (!loaded) {
593                loaded = TRUE;
594
595                if (&kCGColorSpaceSRGB) {
596                        sRGBColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); // does not exist on 10.4
597                } else {
598                        sRGBColorSpace = CreateColorSpaceFromSystemICCProfileName(CFSTR("sRGB Profile.icc"));
599                }
600               
601                CGColorSpaceRetain(sRGBColorSpace);
602        }
603       
604        PerianInitExit(unlock);
605       
606        return sRGBColorSpace;
607}
608
609// Map 8-bit alpha (graphicsModePreBlackAlpha) to 1-bit alpha (transparent)
610// Pretty much this is just mapping all opaque black to (1,1,1,255)
611// Leaves ugly borders where AAing turned into opaque colors, but that's harder to deal with
612void ConvertImageToQDTransparent(Ptr baseAddr, OSType pixelFormat, int rowBytes, int width, int height)
613{
614        UInt32 alphaMask = EndianU32_BtoN((pixelFormat == k32ARGBPixelFormat) ? 0xFF000000 : 0xFF),
615                 replacement = EndianU32_BtoN((pixelFormat == k32ARGBPixelFormat) ? 0xFF010101 : 0x010101FF);
616        Ptr p = baseAddr;
617        int y, x;
618       
619        for (y = 0; y < height; y++) {
620                UInt32 *p32 = (UInt32*)p;
621                for (x = 0; x < width; x++) {
622                        UInt32 px = *p32;
623                       
624                        // if px is black, and opaque (alpha == 255)
625                        if (!(px & ~alphaMask) && ((px & alphaMask) == alphaMask)) {
626                                // then set it to not-quite-black so it'll show up
627                                *p32 = replacement;
628                        }
629                       
630                        p32++;
631                }
632               
633                p += rowBytes;
634        }
635}
Note: See TracBrowser for help on using the repository browser.