source: trunk/CommonUtils.c @ 1194

Revision 1194, 16.3 KB checked in by astrange, 4 years ago (diff)

Fix frame-dropping not being enabled in QT Player 10.

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("QTKitDecodeServer"),
311        CFSTR("QuickTime Player"),
312        CFSTR("Spiral")
313};
314
315static const CFStringRef defaultTransparentSubtitleList_10_6[] = {
316        CFSTR("CoreMediaAuthoringSourcePropertyHelper"),
317        CFSTR("Front Row"),
318        CFSTR("QTPlayerHelper")
319};
320
321static const CFStringRef defaultTransparentSubtitleList_10_5[] = {
322        CFSTR("Front Row")
323};
324
325static const CFStringRef defaultForcedAppList[] = {
326        CFSTR("iChat")
327};
328
329static int findNameInList(CFStringRef loadingApp, const CFStringRef *names, int count)
330{
331        int i;
332
333        for (i = 0; i < count; i++) {
334                if (CFGetTypeID(names[i]) != CFStringGetTypeID())
335                        continue;
336                if (CFStringCompare(loadingApp, names[i], 0) == kCFCompareEqualTo) return 1;
337        }
338
339        return 0;
340}
341
342static CFDictionaryRef getMyProcessInformation()
343{
344        ProcessSerialNumber myProcess;
345        GetCurrentProcess(&myProcess);
346        CFDictionaryRef processInformation;
347       
348        processInformation = ProcessInformationCopyDictionary(&myProcess, kProcessDictionaryIncludeAllInformationMask);
349        return processInformation;
350}
351
352static CFStringRef getProcessName(CFDictionaryRef processInformation)
353{
354        CFStringRef path = CFDictionaryGetValue(processInformation, kCFBundleExecutableKey);
355        CFRange entireRange = CFRangeMake(0, CFStringGetLength(path)), basename;
356       
357        CFStringFindWithOptions(path, CFSTR("/"), entireRange, kCFCompareBackwards, &basename);
358       
359        basename.location += 1; //advance past "/"
360        basename.length = entireRange.length - basename.location;
361       
362        CFStringRef myProcessName = CFStringCreateWithSubstring(NULL, path, basename);
363        return myProcessName;
364}
365
366static int isApplicationNameInList(CFStringRef prefOverride, const CFStringRef *defaultList, unsigned int defaultListCount)
367{
368        CFDictionaryRef processInformation = getMyProcessInformation();
369       
370        if (!processInformation)
371                return FALSE;
372       
373        CFArrayRef list = CopyPreferencesValueTyped(prefOverride, CFArrayGetTypeID());
374        CFStringRef myProcessName = getProcessName(processInformation);
375        int ret;
376       
377        if (list) {
378                int count = CFArrayGetCount(list);
379                CFStringRef names[count];
380               
381                CFArrayGetValues(list, CFRangeMake(0, count), (void *)names);
382                ret = findNameInList(myProcessName, names, count);
383                CFRelease(list);
384        } else {
385                ret = findNameInList(myProcessName, defaultList, defaultListCount);
386        }
387        CFRelease(myProcessName);
388        CFRelease(processInformation);
389       
390        return ret;
391}
392
393int IsFrameDroppingEnabled()
394{
395        static int enabled = -1;
396       
397        if (enabled == -1)
398                enabled = isApplicationNameInList(CFSTR("FrameDroppingWhiteList"),
399                                                                                  defaultFrameDroppingList,
400                                                                                  sizeof(defaultFrameDroppingList)/sizeof(defaultFrameDroppingList[0]));
401        return enabled;
402}
403
404int IsForcedDecodeEnabled()
405{
406        static int forced = -1;
407       
408        if(forced == -1)
409                forced = isApplicationNameInList(CFSTR("ForcePerianAppList"),
410                                                                                 defaultForcedAppList,
411                                                                                 sizeof(defaultForcedAppList)/sizeof(defaultForcedAppList[0]));
412        return forced;
413}
414
415static int GetSystemMinorVersion()
416{
417        long minorVersion;
418        Gestalt(gestaltSystemVersionMinor, &minorVersion);
419       
420        return minorVersion;
421}
422
423int IsTransparentSubtitleHackEnabled()
424{
425        static int forced = -1;
426       
427        if(forced == -1)
428        {
429                int minorVersion = GetSystemMinorVersion();
430               
431                if (minorVersion == 5)
432                        forced = isApplicationNameInList(CFSTR("TransparentModeSubtitleAppList"),
433                                                                                         defaultTransparentSubtitleList_10_5,
434                                                                                         sizeof(defaultTransparentSubtitleList_10_5)/sizeof(defaultTransparentSubtitleList_10_5[0]));
435                else if (minorVersion == 6)
436                        forced = isApplicationNameInList(CFSTR("TransparentModeSubtitleAppList"),
437                                                                                         defaultTransparentSubtitleList_10_6,
438                                                                                         sizeof(defaultTransparentSubtitleList_10_6)/sizeof(defaultTransparentSubtitleList_10_6[0]));
439                else
440                        forced = 0;
441        }
442
443        return forced;
444}
445
446int IsAltivecSupported()
447{
448        static int altivec = -1;
449       
450        if (altivec == -1) {
451                long response = 0;
452                int err = Gestalt(gestaltPowerPCProcessorFeatures, &response);
453               
454                altivec = !err && ((response & (1 << gestaltPowerPCHasVectorInstructions)) != 0);
455        }
456       
457        return altivec;
458}
459
460
461// this could be a defaults setting, but no real call for it yet
462int ShouldImportFontFileName(const char *filename)
463{
464        // match DynaFont Labs (1997) fonts, which are in many files
465        // and completely break ATSUI on different OS versions
466        // FIXME: This font works when in ~/Library/Fonts (!). Check it again with CoreText.
467        return !(GetSystemMinorVersion() >= 6 && fnmatch("DF*.ttc", filename, 0) == 0);
468}
469
470
471CFPropertyListRef CopyPreferencesValueTyped(CFStringRef key, CFTypeID type)
472{
473        CFPropertyListRef val = CFPreferencesCopyAppValue(key, PERIAN_PREF_DOMAIN);
474       
475        if (val && CFGetTypeID(val) != type) {
476                CFRelease(val);
477                val = NULL;
478        }
479       
480        return val;
481}
482
483static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
484
485int PerianInitEnter(volatile Boolean *inited)
486{
487        if (*inited)
488                return FALSE;
489       
490        pthread_mutex_lock(&init_mutex);
491        return TRUE;
492}
493
494void PerianInitExit(int unlock)
495{
496        if (unlock)
497                pthread_mutex_unlock(&init_mutex);
498}
499
500void *fast_realloc_with_padding(void *ptr, unsigned int *size, unsigned int min_size)
501{
502        void *res = ptr;
503        av_fast_malloc(&res, size, min_size + FF_INPUT_BUFFER_PADDING_SIZE);
504        if (res) memset(res + min_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
505        return res;
506}
507
508// from Apple Q&A 1396
509static CGColorSpaceRef CreateICCColorSpaceFromPathToProfile (const char * iccProfilePath)
510{
511        CMProfileRef    iccProfile = NULL;
512        CGColorSpaceRef iccColorSpace = NULL;
513        CMProfileLocation loc;
514       
515        // Specify that the location of the profile will be a POSIX path to the profile.
516        loc.locType = cmPathBasedProfile;
517       
518        // Make sure the path is not larger then the buffer
519        if(strlen(iccProfilePath) > sizeof(loc.u.pathLoc.path))
520                return NULL;
521       
522        // Copy the path the profile into the CMProfileLocation structure
523        strcpy (loc.u.pathLoc.path, iccProfilePath);
524       
525        // Open the profile
526        if (CMOpenProfile(&iccProfile, &loc) != noErr)
527        {
528                iccProfile = (CMProfileRef) 0;
529                return NULL;
530        }
531       
532        // Create the ColorSpace with the open profile.
533        iccColorSpace = CGColorSpaceCreateWithPlatformColorSpace( iccProfile );
534       
535        // Close the profile now that we have what we need from it.
536        CMCloseProfile(iccProfile);
537       
538        return iccColorSpace;
539}
540
541static CGColorSpaceRef CreateColorSpaceFromSystemICCProfileName(CFStringRef profileName)
542{
543        FSRef pathToProfilesFolder;
544    FSRef pathToProfile;
545       
546        // Find the Systems Color Sync Profiles folder
547        if(FSFindFolder(kOnSystemDisk, kColorSyncProfilesFolderType,
548                                        kDontCreateFolder, &pathToProfilesFolder) == noErr) {
549               
550                // Make a UniChar string of the profile name
551                UniChar uniBuffer[sizeof(CMPathLocation)];
552                CFStringGetCharacters (profileName,CFRangeMake(0,CFStringGetLength(profileName)),uniBuffer);
553               
554                // Create a FSRef to the profile in the Systems Color Sync Profile folder
555                if(FSMakeFSRefUnicode (&pathToProfilesFolder,CFStringGetLength(profileName),uniBuffer,
556                                                           kUnicodeUTF8Format,&pathToProfile) == noErr) {
557                        unsigned char path[sizeof(CMPathLocation)];
558                       
559                        // Write the posix path to the profile into our path buffer from the FSRef
560                        if(FSRefMakePath (&pathToProfile,path,sizeof(CMPathLocation)) == noErr)
561                                return CreateICCColorSpaceFromPathToProfile((char*)path);
562                }
563        }
564       
565        return NULL;
566}
567
568CGColorSpaceRef GetSRGBColorSpace()
569{
570        static Boolean loaded = FALSE;
571        static CGColorSpaceRef sRGBColorSpace;
572        int unlock = PerianInitEnter(&loaded);
573        CFStringRef *srgb;
574
575        if (!loaded) {
576                loaded = TRUE;
577
578                if ((srgb = dlsym(RTLD_NEXT, "kCGColorSpaceSRGB"))) {
579                        sRGBColorSpace = CGColorSpaceCreateWithName(*srgb); // does not exist on 10.4
580                } else {
581                        sRGBColorSpace = CreateColorSpaceFromSystemICCProfileName(CFSTR("sRGB Profile.icc"));
582                }
583               
584                CGColorSpaceRetain(sRGBColorSpace);
585        }
586       
587        PerianInitExit(unlock);
588       
589        return sRGBColorSpace;
590}
Note: See TracBrowser for help on using the repository browser.