/* * CommonUtils.h * Created by David Conrad on 10/13/06. * * This file is part of Perian. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avcodec.h" #include "CommonUtils.h" #import #import #import #import typedef struct LanguageTriplet { char twoChar[3]; char threeChar[4]; // (ISO 639-2 3 char code) short qtLang; } LanguageTriplet; // don't think there's a function already to do ISO 639-1/2 -> language code // that SetMediaLanguage() accepts static const LanguageTriplet ISO_QTLanguages[] = { { "", "und", langUnspecified }, { "af", "afr", langAfrikaans }, { "sq", "alb", langAlbanian }, { "sq", "sqi", langAlbanian }, { "am", "amh", langAmharic }, { "ar", "ara", langArabic }, { "hy", "arm", langArmenian }, { "hy", "hye", langArmenian }, { "as", "asm", langAssamese }, { "ay", "aym", langAymara }, { "az", "aze", langAzerbaijani }, { "eu", "baq", langBasque }, { "eu", "eus", langBasque }, { "bn", "ben", langBengali }, { "br", "bre", langBreton }, { "bg", "bul", langBulgarian }, { "my", "bur", langBurmese }, { "my", "mya", langBurmese }, { "ca", "cat", langCatalan }, { "zh", "chi", langTradChinese }, { "zh", "zho", langTradChinese }, { "cs", "cze", langCzech }, { "cs", "ces", langCzech }, { "da", "dan", langDanish }, { "nl", "dut", langDutch }, { "nl", "nld", langDutch }, { "dz", "dzo", langDzongkha }, { "en", "eng", langEnglish }, { "eo", "epo", langEsperanto }, { "et", "est", langEstonian }, { "fo", "fao", langFaroese }, { "fi", "fin", langFinnish }, { "fr", "fre", langFrench }, { "fr", "fra", langFrench }, { "ka", "geo", langGeorgian }, { "ka", "kat", langGeorgian }, { "de", "ger", langGerman }, { "de", "deu", langGerman }, { "gl", "glg", langGalician }, { "gd", "gla", langScottishGaelic }, { "ga", "gle", langIrishGaelic }, { "gv", "glv", langManxGaelic }, { "", "grc", langGreekAncient }, { "el", "gre", langGreek }, { "el", "ell", langGreek }, { "gn", "grn", langGuarani }, { "gu", "guj", langGujarati }, { "he", "heb", langHebrew }, { "hi", "hin", langHindi }, { "hu", "hun", langHungarian }, { "is", "ice", langIcelandic }, { "is", "isl", langIcelandic }, { "id", "ind", langIndonesian }, { "it", "ita", langItalian }, { "jv", "jav", langJavaneseRom }, { "ja", "jpn", langJapanese }, { "kl", "kal", langGreenlandic }, { "kn", "kan", langKannada }, { "ks", "kas", langKashmiri }, { "kk", "kaz", langKazakh }, { "km", "khm", langKhmer }, { "rw", "kin", langKinyarwanda }, { "ky", "kir", langKirghiz }, { "ko", "kor", langKorean }, { "ku", "kur", langKurdish }, { "lo", "lao", langLao }, { "la", "lat", langLatin }, { "lv", "lav", langLatvian }, { "lt", "lit", langLithuanian }, { "mk", "mac", langMacedonian }, { "mk", "mkd", langMacedonian }, { "ml", "mal", langMalayalam }, { "mr", "mar", langMarathi }, { "ms", "may", langMalayRoman }, { "ms", "msa", langMalayRoman }, { "mg", "mlg", langMalagasy }, { "mt", "mlt", langMaltese }, { "mo", "mol", langMoldavian }, { "mn", "mon", langMongolian }, { "ne", "nep", langNepali }, { "nb", "nob", langNorwegian }, // Norwegian Bokmal { "no", "nor", langNorwegian }, { "nn", "nno", langNynorsk }, { "ny", "nya", langNyanja }, { "or", "ori", langOriya }, { "om", "orm", langOromo }, { "pa", "pan", langPunjabi }, { "fa", "per", langPersian }, { "fa", "fas", langPersian }, { "pl", "pol", langPolish }, { "pt", "por", langPortuguese }, { "qu", "que", langQuechua }, { "ro", "rum", langRomanian }, { "ro", "ron", langRomanian }, { "rn", "run", langRundi }, { "ru", "rus", langRussian }, { "sa", "san", langSanskrit }, { "sr", "scc", langSerbian }, { "sr", "srp", langSerbian }, { "hr", "scr", langCroatian }, { "hr", "hrv", langCroatian }, { "si", "sin", langSinhalese }, { "", "sit", langTibetan }, // Sino-Tibetan (Other) { "sk", "slo", langSlovak }, { "sk", "slk", langSlovak }, { "sl", "slv", langSlovenian }, { "se", "sme", langSami }, { "", "smi", langSami }, // Sami languages (Other) { "sd", "snd", langSindhi }, { "so", "som", langSomali }, { "es", "spa", langSpanish }, { "su", "sun", langSundaneseRom }, { "sw", "swa", langSwahili }, { "sv", "swe", langSwedish }, { "ta", "tam", langTamil }, { "tt", "tat", langTatar }, { "te", "tel", langTelugu }, { "tg", "tgk", langTajiki }, { "tl", "tgl", langTagalog }, { "th", "tha", langThai }, { "bo", "tib", langTibetan }, { "bo", "bod", langTibetan }, { "ti", "tir", langTigrinya }, { "", "tog", langTongan }, // Tonga (Nyasa, Tonga Islands) { "tr", "tur", langTurkish }, { "tk", "tuk", langTurkmen }, { "ug", "uig", langUighur }, { "uk", "ukr", langUkrainian }, { "ur", "urd", langUrdu }, { "uz", "uzb", langUzbek }, { "vi", "vie", langVietnamese }, { "cy", "wel", langWelsh }, { "cy", "cym", langWelsh }, { "yi", "yid", langYiddish } }; short ISO639_1ToQTLangCode(const char *lang) { int i; if (strlen(lang) != 2) return langUnspecified; for (i = 0; i < sizeof(ISO_QTLanguages) / sizeof(LanguageTriplet); i++) { if (strcasecmp(lang, ISO_QTLanguages[i].twoChar) == 0) return ISO_QTLanguages[i].qtLang; } return langUnspecified; } short ISO639_2ToQTLangCode(const char *lang) { int i; if (strlen(lang) != 3) return langUnspecified; for (i = 0; i < sizeof(ISO_QTLanguages) / sizeof(LanguageTriplet); i++) { if (strcasecmp(lang, ISO_QTLanguages[i].threeChar) == 0) return ISO_QTLanguages[i].qtLang; } return langUnspecified; } /* write the int32_t data to target & then return a pointer which points after that data */ uint8_t *write_int32(uint8_t *target, int32_t data) { return write_data(target, (uint8_t*)&data, sizeof(data)); } /* write_int32() */ /* write the int16_t data to target & then return a pointer which points after that data */ uint8_t *write_int16(uint8_t *target, int16_t data) { return write_data(target, (uint8_t*)&data, sizeof(data)); } /* write_int16() */ /* write the data to the target adress & then return a pointer which points after the written data */ uint8_t *write_data(uint8_t *target, uint8_t* data, int32_t data_size) { if(data_size > 0) memcpy(target, data, data_size); return (target + data_size); } /* write_data() */ #define MP4ESDescrTag 0x03 #define MP4DecConfigDescrTag 0x04 #define MP4DecSpecificDescrTag 0x05 // based off of mov_mp4_read_descr_len from mov.c in ffmpeg's libavformat static int readDescrLen(UInt8 **buffer) { int len = 0; int count = 4; while (count--) { int c = *(*buffer)++; len = (len << 7) | (c & 0x7f); if (!(c & 0x80)) break; } return len; } // based off of mov_mp4_read_descr from mov.c in ffmpeg's libavformat static int readDescr(UInt8 **buffer, int *tag) { *tag = *(*buffer)++; return readDescrLen(buffer); } // based off of mov_read_esds from mov.c in ffmpeg's libavformat ComponentResult ReadESDSDescExt(Handle descExt, UInt8 **buffer, int *size) { UInt8 *esds = (UInt8 *) *descExt; int tag, len; *size = 0; esds += 4; // version + flags len = readDescr(&esds, &tag); esds += 2; // ID if (tag == MP4ESDescrTag) esds++; // priority len = readDescr(&esds, &tag); if (tag == MP4DecConfigDescrTag) { esds++; // object type id esds++; // stream type esds += 3; // buffer size db esds += 4; // max bitrate esds += 4; // average bitrate len = readDescr(&esds, &tag); if (tag == MP4DecSpecificDescrTag) { *buffer = calloc(1, len + FF_INPUT_BUFFER_PADDING_SIZE); if (*buffer) { memcpy(*buffer, esds, len); *size = len; } } } return noErr; } int isImageDescriptionExtensionPresent(ImageDescriptionHandle desc, long type) { ImageDescriptionPtr d = *desc; int offset = sizeof(ImageDescription); uint8_t *p = (uint8_t *)d; //read next description, need 8 bytes for size and type while(offset < d->idSize - 8) { long len = *(p+offset) << 24 | *(p+offset+1) << 16 | *(p+offset+2) << 8 | *(p+offset+3); long rtype = *(p+offset + 4) << 24 | *(p+offset+5) << 16 | *(p+offset+6) << 8 | *(p+offset+7); if(rtype == type && offset + len <= d->idSize) return 1; offset += len; } return 0; } static const CFStringRef defaultFrameDroppingList[] = { CFSTR("Finder"), CFSTR("Front Row"), CFSTR("Movie Time"), CFSTR("Movist"), CFSTR("NicePlayer"), CFSTR("QTKitServer"), CFSTR("QuickTime Player"), CFSTR("Spiral") }; static const CFStringRef defaultTransparentSubtitleList_10_6[] = { CFSTR("CoreMediaAuthoringSessionHelper"), CFSTR("CoreMediaAuthoringSourcePropertyHelper"), CFSTR("Front Row"), CFSTR("QTPlayerHelper") }; static const CFStringRef defaultTransparentSubtitleList_10_5[] = { CFSTR("Front Row") }; static const CFStringRef defaultForcedAppList[] = { CFSTR("iChat") }; static int findNameInList(CFStringRef loadingApp, const CFStringRef *names, int count) { int i; for (i = 0; i < count; i++) { if (CFGetTypeID(names[i]) != CFStringGetTypeID()) continue; if (CFStringCompare(loadingApp, names[i], 0) == kCFCompareEqualTo) return 1; } return 0; } static CFDictionaryRef getMyProcessInformation() { ProcessSerialNumber myProcess; GetCurrentProcess(&myProcess); CFDictionaryRef processInformation; processInformation = ProcessInformationCopyDictionary(&myProcess, kProcessDictionaryIncludeAllInformationMask); return processInformation; } static CFStringRef getProcessName(CFDictionaryRef processInformation) { CFStringRef path = CFDictionaryGetValue(processInformation, kCFBundleExecutableKey); CFRange entireRange = CFRangeMake(0, CFStringGetLength(path)), basename; CFStringFindWithOptions(path, CFSTR("/"), entireRange, kCFCompareBackwards, &basename); basename.location += 1; //advance past "/" basename.length = entireRange.length - basename.location; CFStringRef myProcessName = CFStringCreateWithSubstring(NULL, path, basename); return myProcessName; } static int isApplicationNameInList(CFStringRef prefOverride, const CFStringRef *defaultList, unsigned int defaultListCount) { CFDictionaryRef processInformation = getMyProcessInformation(); if (!processInformation) return FALSE; CFArrayRef list = CopyPreferencesValueTyped(prefOverride, CFArrayGetTypeID()); CFStringRef myProcessName = getProcessName(processInformation); int ret; if (list) { int count = CFArrayGetCount(list); CFStringRef names[count]; CFArrayGetValues(list, CFRangeMake(0, count), (void *)names); ret = findNameInList(myProcessName, names, count); CFRelease(list); } else { ret = findNameInList(myProcessName, defaultList, defaultListCount); } CFRelease(myProcessName); CFRelease(processInformation); return ret; } int IsFrameDroppingEnabled() { static int enabled = -1; if (enabled == -1) enabled = isApplicationNameInList(CFSTR("FrameDroppingWhiteList"), defaultFrameDroppingList, sizeof(defaultFrameDroppingList)/sizeof(defaultFrameDroppingList[0])); return enabled; } int IsForcedDecodeEnabled() { static int forced = -1; if(forced == -1) forced = isApplicationNameInList(CFSTR("ForcePerianAppList"), defaultForcedAppList, sizeof(defaultForcedAppList)/sizeof(defaultForcedAppList[0])); return forced; } static int GetSystemMinorVersion() { long minorVersion; Gestalt(gestaltSystemVersionMinor, &minorVersion); return minorVersion; } int IsTransparentSubtitleHackEnabled() { static int forced = -1; if(forced == -1) { int minorVersion = GetSystemMinorVersion(); if (minorVersion == 5) forced = isApplicationNameInList(CFSTR("TransparentModeSubtitleAppList"), defaultTransparentSubtitleList_10_5, sizeof(defaultTransparentSubtitleList_10_5)/sizeof(defaultTransparentSubtitleList_10_5[0])); else if (minorVersion == 6) forced = isApplicationNameInList(CFSTR("TransparentModeSubtitleAppList"), defaultTransparentSubtitleList_10_6, sizeof(defaultTransparentSubtitleList_10_6)/sizeof(defaultTransparentSubtitleList_10_6[0])); else forced = 0; } return forced; } int IsAltivecSupported() { static int altivec = -1; if (altivec == -1) { long response = 0; int err = Gestalt(gestaltPowerPCProcessorFeatures, &response); altivec = !err && ((response & (1 << gestaltPowerPCHasVectorInstructions)) != 0); } return altivec; } // this could be a defaults setting, but no real call for it yet int ShouldImportFontFileName(const char *filename) { // match DynaFont Labs (1997) fonts, which are in many files // and completely break ATSUI on different OS versions // FIXME: This font works when in ~/Library/Fonts (!). Check it again with CoreText. return !(GetSystemMinorVersion() >= 6 && fnmatch("DF*.ttc", filename, 0) == 0); } CFPropertyListRef CopyPreferencesValueTyped(CFStringRef key, CFTypeID type) { CFPropertyListRef val = CFPreferencesCopyAppValue(key, PERIAN_PREF_DOMAIN); if (val && CFGetTypeID(val) != type) { CFRelease(val); val = NULL; } return val; } static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; int PerianInitEnter(volatile Boolean *inited) { if (*inited) return FALSE; pthread_mutex_lock(&init_mutex); return TRUE; } void PerianInitExit(int unlock) { if (unlock) pthread_mutex_unlock(&init_mutex); } void *fast_realloc_with_padding(void *ptr, unsigned int *size, unsigned int min_size) { void *res = ptr; av_fast_malloc(&res, size, min_size + FF_INPUT_BUFFER_PADDING_SIZE); if (res) memset(res + min_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); return res; } // from Apple Q&A 1396 static CGColorSpaceRef CreateICCColorSpaceFromPathToProfile (const char * iccProfilePath) { CMProfileRef iccProfile = NULL; CGColorSpaceRef iccColorSpace = NULL; CMProfileLocation loc; // Specify that the location of the profile will be a POSIX path to the profile. loc.locType = cmPathBasedProfile; // Make sure the path is not larger then the buffer if(strlen(iccProfilePath) > sizeof(loc.u.pathLoc.path)) return NULL; // Copy the path the profile into the CMProfileLocation structure strcpy (loc.u.pathLoc.path, iccProfilePath); // Open the profile if (CMOpenProfile(&iccProfile, &loc) != noErr) { iccProfile = (CMProfileRef) 0; return NULL; } // Create the ColorSpace with the open profile. iccColorSpace = CGColorSpaceCreateWithPlatformColorSpace( iccProfile ); // Close the profile now that we have what we need from it. CMCloseProfile(iccProfile); return iccColorSpace; } static CGColorSpaceRef CreateColorSpaceFromSystemICCProfileName(CFStringRef profileName) { FSRef pathToProfilesFolder; FSRef pathToProfile; // Find the Systems Color Sync Profiles folder if(FSFindFolder(kOnSystemDisk, kColorSyncProfilesFolderType, kDontCreateFolder, &pathToProfilesFolder) == noErr) { // Make a UniChar string of the profile name UniChar uniBuffer[sizeof(CMPathLocation)]; CFStringGetCharacters (profileName,CFRangeMake(0,CFStringGetLength(profileName)),uniBuffer); // Create a FSRef to the profile in the Systems Color Sync Profile folder if(FSMakeFSRefUnicode (&pathToProfilesFolder,CFStringGetLength(profileName),uniBuffer, kUnicodeUTF8Format,&pathToProfile) == noErr) { unsigned char path[sizeof(CMPathLocation)]; // Write the posix path to the profile into our path buffer from the FSRef if(FSRefMakePath (&pathToProfile,path,sizeof(CMPathLocation)) == noErr) return CreateICCColorSpaceFromPathToProfile((char*)path); } } return NULL; } CGColorSpaceRef GetSRGBColorSpace() { static Boolean loaded = FALSE; static CGColorSpaceRef sRGBColorSpace; int unlock = PerianInitEnter(&loaded); if (!loaded) { loaded = TRUE; if (kCGColorSpaceSRGB) { sRGBColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); // does not exist on 10.4 } else { sRGBColorSpace = CreateColorSpaceFromSystemICCProfileName(CFSTR("sRGB Profile.icc")); } CGColorSpaceRetain(sRGBColorSpace); } PerianInitExit(unlock); return sRGBColorSpace; } // Map 8-bit alpha (graphicsModePreBlackAlpha) to 1-bit alpha (transparent) // Pretty much this is just mapping all opaque black to (1,1,1,255) // Leaves ugly borders where AAing turned into opaque colors, but that's harder to deal with void ConvertImageToQDTransparent(Ptr baseAddr, OSType pixelFormat, int rowBytes, int width, int height) { UInt32 alphaMask = EndianU32_BtoN((pixelFormat == k32ARGBPixelFormat) ? 0xFF000000 : 0xFF), replacement = EndianU32_BtoN((pixelFormat == k32ARGBPixelFormat) ? 0xFF010101 : 0x010101FF); Ptr p = baseAddr; int y, x; for (y = 0; y < height; y++) { UInt32 *p32 = (UInt32*)p; for (x = 0; x < width; x++) { UInt32 px = *p32; // if px is black, and opaque (alpha == 255) if (!(px & ~alphaMask) && (px & alphaMask == alphaMask)) { // then set it to not-quite-black so it'll show up *p32 = replacement; } p32++; } p += rowBytes; } }