static void flac_metadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { MediaScannerClient *client = (MediaScannerClient *)client_data; if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { FLAC__uint64 duration = 1000 * metadata->data.stream_info.total_samples / metadata->data.stream_info.sample_rate; if (duration > 0) { char buffer[20]; sprintf(buffer, "%lld", duration); if (!client->addStringTag("duration", buffer)) return; } } else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { for (uint32_t i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { char *ptr = (char *)metadata->data.vorbis_comment.comments[i].entry; char *val = strchr(ptr, '='); if (val) { int keylen = val++ - ptr; char key[keylen + 1]; strncpy(key, ptr, keylen); key[keylen] = 0; if (!client->addStringTag(key, val)) return; } } } }
MediaScanResult StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { ALOGV("processFile '%s'.", path); client.setLocale(locale()); client.beginFile(); MediaScanResult result = processFileInternal(path, mimeType, client); client.endFile(); return result; }
MediaScanResult StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { #ifndef ANDROID_DEFAULT_CODE LOGD("processFile '%s'.", path); #else LOGV("processFile '%s'.", path); #endif // #ifndef ANDROID_DEFAULT_CODE client.setLocale(locale()); client.beginFile(); MediaScanResult result = processFileInternal(path, mimeType, client); client.endFile(); return result; }
MediaScanResult StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { #ifdef MTK_AOSP_ENHANCEMENT ALOGI("processFile '%s'.", path); #else ALOGV("processFile '%s'.", path); #endif // #ifdef MTK_AOSP_ENHANCEMENT client.setLocale(locale()); client.beginFile(); MediaScanResult result = processFileInternal(path, mimeType, client); client.endFile(); return result; }
MediaScanResult MediaScanner::processDirectory( const char *path, MediaScannerClient &client) { #ifndef ANDROID_DEFAULT_CODE ALOGI("processDirectory: %s \n", path); #endif int pathLength = strlen(path); if (pathLength >= PATH_MAX) { return MEDIA_SCAN_RESULT_SKIPPED; } char* pathBuffer = (char *)malloc(PATH_MAX + 1); if (!pathBuffer) { return MEDIA_SCAN_RESULT_ERROR; } int pathRemaining = PATH_MAX - pathLength; strcpy(pathBuffer, path); if (pathLength > 0 && pathBuffer[pathLength - 1] != '/') { pathBuffer[pathLength] = '/'; pathBuffer[pathLength + 1] = 0; --pathRemaining; } client.setLocale(locale()); MediaScanResult result = doProcessDirectory(pathBuffer, pathRemaining, client, false); free(pathBuffer); return result; }
status_t MediaScanner::processDirectory( const char *path, const char *extensions, MediaScannerClient &client, ExceptionCheck exceptionCheck, void *exceptionEnv) { int pathLength = strlen(path); if (pathLength >= PATH_MAX) { return UNKNOWN_ERROR; } char* pathBuffer = (char *)malloc(PATH_MAX + 1); if (!pathBuffer) { return UNKNOWN_ERROR; } int pathRemaining = PATH_MAX - pathLength; strcpy(pathBuffer, path); if (pathLength > 0 && pathBuffer[pathLength - 1] != '/') { pathBuffer[pathLength] = '/'; pathBuffer[pathLength + 1] = 0; --pathRemaining; } client.setLocale(locale()); status_t result = doProcessDirectory( pathBuffer, pathRemaining, extensions, client, exceptionCheck, exceptionEnv); free(pathBuffer); return result; }
MediaScanResult MediaScanner::processDirectory( const char *path, MediaScannerClient &client) { #ifdef MTK_AOSP_ENHANCEMENT ALOGI("processDirectory: %s \n", path); #endif int pathLength = strlen(path); if (pathLength >= PATH_MAX) { return MEDIA_SCAN_RESULT_SKIPPED; } char* pathBuffer = (char *)malloc(PATH_MAX + 1); if (!pathBuffer) { return MEDIA_SCAN_RESULT_ERROR; } int pathRemaining = PATH_MAX - pathLength; strcpy(pathBuffer, path); if (pathLength > 0 && pathBuffer[pathLength - 1] != '/') { pathBuffer[pathLength] = '/'; pathBuffer[pathLength + 1] = 0; --pathRemaining; } client.setLocale(locale()); /// M: add for the new feature about the new thread pool on JB9.MP/KK/KK.AOSP branch bool flag = false; for (int i = pathLength - 1; i >= 1; i--) { if ((pathBuffer[i] == '.') && (pathBuffer[i - 1] == '/')) { ALOGW("Include nomeida folder."); flag = true; } } MediaScanResult result = doProcessDirectory(pathBuffer, pathRemaining, client, flag); /// @} free(pathBuffer); return result; }
MediaScanResult StagefrightMediaScanner::processFileInternal( const char *path, const char *mimeType, MediaScannerClient &client) { const char *extension = strrchr(path, '.'); if (!extension) { return MEDIA_SCAN_RESULT_SKIPPED; } if (!FileHasAcceptableExtension(extension)) { return MEDIA_SCAN_RESULT_SKIPPED; } if (!strcasecmp(extension, ".mid") || !strcasecmp(extension, ".smf") || !strcasecmp(extension, ".imy") || !strcasecmp(extension, ".midi") || !strcasecmp(extension, ".xmf") || !strcasecmp(extension, ".rtttl") || !strcasecmp(extension, ".rtx") || !strcasecmp(extension, ".ota") || !strcasecmp(extension, ".mxmf")) { return HandleMIDI(path, &client); } sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever); int fd = open(path, O_RDONLY | O_LARGEFILE); status_t status; if (fd < 0) { // couldn't open it locally, maybe the media server can? status = mRetriever->setDataSource(path); } else { status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL); close(fd); } if (status) { return MEDIA_SCAN_RESULT_ERROR; } const char *value; if ((value = mRetriever->extractMetadata( METADATA_KEY_MIMETYPE)) != NULL) { status = client.setMimeType(value); if (status) { return MEDIA_SCAN_RESULT_ERROR; } } struct KeyMap { const char *tag; int key; }; static const KeyMap kKeyMap[] = { { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER }, { "discnumber", METADATA_KEY_DISC_NUMBER }, { "album", METADATA_KEY_ALBUM }, { "artist", METADATA_KEY_ARTIST }, { "albumartist", METADATA_KEY_ALBUMARTIST }, { "composer", METADATA_KEY_COMPOSER }, { "genre", METADATA_KEY_GENRE }, { "title", METADATA_KEY_TITLE }, { "year", METADATA_KEY_YEAR }, { "duration", METADATA_KEY_DURATION }, { "writer", METADATA_KEY_WRITER }, { "compilation", METADATA_KEY_COMPILATION }, { "isdrm", METADATA_KEY_IS_DRM }, { "width", METADATA_KEY_VIDEO_WIDTH }, { "height", METADATA_KEY_VIDEO_HEIGHT }, }; static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]); for (size_t i = 0; i < kNumEntries; ++i) { const char *value; if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) { status = client.addStringTag(kKeyMap[i].tag, value); if (status != OK) { return MEDIA_SCAN_RESULT_ERROR; } } } return MEDIA_SCAN_RESULT_OK; }
MediaScanResult MediaScanner::doProcessDirectoryEntry( char *path, int pathRemaining, MediaScannerClient &client, bool noMedia, struct dirent* entry, char* fileSpot) { struct stat statbuf; const char* name = entry->d_name; #ifndef ANDROID_DEFAULT_CODE ALOGV("doProcessDirectoryEntry: %s \n", path); #endif // ignore "." and ".." if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { return MEDIA_SCAN_RESULT_SKIPPED; } int nameLength = strlen(name); if (nameLength + 1 > pathRemaining) { // path too long! return MEDIA_SCAN_RESULT_SKIPPED; } strcpy(fileSpot, name); int type = entry->d_type; if (type == DT_UNKNOWN) { // If the type is unknown, stat() the file instead. // This is sometimes necessary when accessing NFS mounted filesystems, but // could be needed in other cases well. if (stat(path, &statbuf) == 0) { if (S_ISREG(statbuf.st_mode)) { type = DT_REG; } else if (S_ISDIR(statbuf.st_mode)) { type = DT_DIR; } } else { ALOGD("stat() failed for %s: %s", path, strerror(errno) ); } } if (type == DT_DIR) { bool childNoMedia = noMedia; // set noMedia flag on directories with a name that starts with '.' // for example, the Mac ".Trashes" directory if (name[0] == '.') childNoMedia = true; // report the directory to the client if (stat(path, &statbuf) == 0) { status_t status = client.scanFile(path, statbuf.st_mtime, 0, true /*isDirectory*/, childNoMedia); if (status) { return MEDIA_SCAN_RESULT_ERROR; } } // and now process its contents strcat(fileSpot, "/"); MediaScanResult result = doProcessDirectory(path, pathRemaining - nameLength - 1, client, childNoMedia); if (result == MEDIA_SCAN_RESULT_ERROR) { return MEDIA_SCAN_RESULT_ERROR; } } else if (type == DT_REG) { stat(path, &statbuf); status_t status = client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false /*isDirectory*/, noMedia); if (status) { return MEDIA_SCAN_RESULT_ERROR; } } return MEDIA_SCAN_RESULT_OK; }
status_t StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { LOGV("processFile '%s'.", path); client.setLocale(locale()); client.beginFile(); const char *extension = strrchr(path, '.'); if (!extension) { return UNKNOWN_ERROR; } if (!FileHasAcceptableExtension(extension)) { client.endFile(); return UNKNOWN_ERROR; } if (!strcasecmp(extension, ".mid") || !strcasecmp(extension, ".smf") || !strcasecmp(extension, ".imy") || !strcasecmp(extension, ".midi") || !strcasecmp(extension, ".xmf") || !strcasecmp(extension, ".rtttl") || !strcasecmp(extension, ".rtx") || !strcasecmp(extension, ".ota")) { return HandleMIDI(path, &client); } if (!strcasecmp(extension, ".flac")) { return HandleFLAC(path, &client); } if (mRetriever->setDataSource(path) == OK && mRetriever->setMode( METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) { const char *value; if ((value = mRetriever->extractMetadata( METADATA_KEY_MIMETYPE)) != NULL) { client.setMimeType(value); } struct KeyMap { const char *tag; int key; }; static const KeyMap kKeyMap[] = { { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER }, { "discnumber", METADATA_KEY_DISC_NUMBER }, { "album", METADATA_KEY_ALBUM }, { "artist", METADATA_KEY_ARTIST }, { "albumartist", METADATA_KEY_ALBUMARTIST }, { "composer", METADATA_KEY_COMPOSER }, { "genre", METADATA_KEY_GENRE }, { "title", METADATA_KEY_TITLE }, { "year", METADATA_KEY_YEAR }, { "duration", METADATA_KEY_DURATION }, { "writer", METADATA_KEY_WRITER }, }; static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]); for (size_t i = 0; i < kNumEntries; ++i) { const char *value; if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) { client.addStringTag(kKeyMap[i].tag, value); } } } client.endFile(); return OK; }
MediaScanResult StagefrightMediaScanner::processFileInternal( const char *path, const char *mimeType, MediaScannerClient &client) { const char *extension = strrchr(path, '.'); if (!extension) { return MEDIA_SCAN_RESULT_SKIPPED; } if (!FileHasAcceptableExtension(extension)) { return MEDIA_SCAN_RESULT_SKIPPED; } #ifndef ANDROID_DEFAULT_CODE #ifdef MTK_DRM_APP // add dcf meta data for dcf file // check extension first LOGV("processFileInternal() : the extension: %s", extension); bool isOMADrmDcf = false; if (0 == strcasecmp(extension, ".dcf")) { String8 tmp(path); DrmManagerClient* drmManagerClient = new DrmManagerClient(); DrmMetadata* dcfMetadata = drmManagerClient->getMetadata(&tmp); if (dcfMetadata == NULL) { LOGW("scan: OMA DRM v1: failed to get drm metadata, not scanned into db."); delete drmManagerClient; client.setMimeType("bad mime type"); return MEDIA_SCAN_RESULT_SKIPPED; } struct Map { const char* from; int to; }; static const Map kMap[] = { {DrmMetaKey::META_KEY_IS_DRM, METADATA_KEY_IS_DRM}, // "is_drm" {DrmMetaKey::META_KEY_CONTENT_URI, METADATA_KEY_DRM_CONTENT_URI}, {DrmMetaKey::META_KEY_OFFSET, METADATA_KEY_DRM_OFFSET}, {DrmMetaKey::META_KEY_DATALEN, METADATA_KEY_DRM_DATALEN}, {DrmMetaKey::META_KEY_RIGHTS_ISSUER, METADATA_KEY_DRM_RIGHTS_ISSUER}, {DrmMetaKey::META_KEY_CONTENT_NAME, METADATA_KEY_DRM_CONTENT_NAME}, {DrmMetaKey::META_KEY_CONTENT_DESCRIPTION, METADATA_KEY_DRM_CONTENT_DES}, {DrmMetaKey::META_KEY_CONTENT_VENDOR, METADATA_KEY_DRM_CONTENT_VENDOR}, {DrmMetaKey::META_KEY_ICON_URI, METADATA_KEY_DRM_ICON_URI} , {DrmMetaKey::META_KEY_METHOD, METADATA_KEY_DRM_METHOD}, {DrmMetaKey::META_KEY_MIME, METADATA_KEY_DRM_MIME} }; static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); int action = Action::PLAY; String8 type; for (size_t i = 0; i < kNumMapEntries; ++i) { String8 value = dcfMetadata->get(String8(kMap[i].from)); if (value.length() != 0) { if (kMap[i].to == METADATA_KEY_DRM_MIME) { value = DrmMtkUtil::toCommonMime(value.string()); // not audio/video/image -> not scan into db type.setTo(value.string(), 6); if (0 != strcasecmp(type.string(), "audio/") && 0 != strcasecmp(type.string(), "video/") && 0 != strcasecmp(type.string(), "image/")) { LOGW("scan: OMA DRM v1: invalid drm media file mime type[%s], not added into db.", value.string()); delete dcfMetadata; delete drmManagerClient; client.setMimeType("bad mime type"); return MEDIA_SCAN_RESULT_SKIPPED; } client.setMimeType(value.string()); LOGD("scan: OMA DRM v1: drm original mime type[%s].", value.string()); // determine the Action it shall used. if ((0 == strcasecmp(type.string(), "audio/")) || (0 == strcasecmp(type.string(), "video/"))) { action = Action::PLAY; } else if ((0 == strcasecmp(type.string(), "image/"))) { action = Action::DISPLAY; } } if (kMap[i].to == METADATA_KEY_IS_DRM) { isOMADrmDcf = (value == String8("1")); } client.addStringTag(kMap[i].from, value.string()); LOGD("scan: OMA DRM v1: client.addString tag[%s] value[%s].", kMap[i].from, value.string()); } } // if there's no valid rights for this file currently, just return OK // to make sure it can be scanned into db. if (isOMADrmDcf && RightsStatus::RIGHTS_VALID != drmManagerClient->checkRightsStatus(tmp, action)) { LOGD("scan: OMA DRM v1: current no valid rights, return OK so that it can be added into db."); delete dcfMetadata; delete drmManagerClient; return MEDIA_SCAN_RESULT_OK; } // when there's valid rights, should contine to add extra metadata LOGD("scan: OMA DRM v1: current valid rights, continue to add extra info."); delete dcfMetadata; dcfMetadata = NULL; delete drmManagerClient; drmManagerClient = NULL; // if picture then we need not to scan with extractors. if (isOMADrmDcf && 0 == strcasecmp(type.string(), "image/")) { LOGD("scan: OMA DRM v1: for DRM image we do not sniff with extractors."); return MEDIA_SCAN_RESULT_OK; } } #endif #endif if (!strcasecmp(extension, ".mid") || !strcasecmp(extension, ".smf") || !strcasecmp(extension, ".imy") || !strcasecmp(extension, ".midi") || !strcasecmp(extension, ".xmf") || !strcasecmp(extension, ".rtttl") || !strcasecmp(extension, ".rtx") || !strcasecmp(extension, ".ota") || !strcasecmp(extension, ".mxmf")) { return HandleMIDI(path, &client); } sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever); status_t status = mRetriever->setDataSource(path); if (status) { #ifndef ANDROID_DEFAULT_CODE //if this still need on ICS // set mime type to "bad mime type" for unsupported format, otherwise the file will be included in Gallery/Music LOGE("processFile '%s' - not supported", path); client.setMimeType("bad mime type"); return MEDIA_SCAN_RESULT_SKIPPED; #else return MEDIA_SCAN_RESULT_ERROR; #endif } const char *value; if ((value = mRetriever->extractMetadata( METADATA_KEY_MIMETYPE)) != NULL) { status = client.setMimeType(value); if (status) { return MEDIA_SCAN_RESULT_ERROR; } } struct KeyMap { const char *tag; int key; }; static const KeyMap kKeyMap[] = { { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER }, { "discnumber", METADATA_KEY_DISC_NUMBER }, { "album", METADATA_KEY_ALBUM }, { "artist", METADATA_KEY_ARTIST }, { "albumartist", METADATA_KEY_ALBUMARTIST }, { "composer", METADATA_KEY_COMPOSER }, { "genre", METADATA_KEY_GENRE }, { "title", METADATA_KEY_TITLE }, { "year", METADATA_KEY_YEAR }, { "duration", METADATA_KEY_DURATION }, { "writer", METADATA_KEY_WRITER }, { "compilation", METADATA_KEY_COMPILATION }, { "isdrm", METADATA_KEY_IS_DRM }, // "isdrm" #ifdef MTK_S3D_SUPPORT {"stereotype",METADATA_KEY_STEREO_3D}, //stereo 3d info #endif }; static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]); for (size_t i = 0; i < kNumEntries; ++i) { const char *value; if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) { #ifndef ANDROID_DEFAULT_CODE #ifdef MTK_DRM_APP if (kKeyMap[i].key == METADATA_KEY_IS_DRM) { if (isOMADrmDcf) { LOGD("set METADATA_KEY_IS_DRM to 1 for OMA DRM v1."); value = "1"; } } #endif #endif status = client.addStringTag(kKeyMap[i].tag, value); LOGD("processFileInternal() : client.addString tag[%s] value[%s]", kKeyMap[i].tag, value); if (status != OK) { return MEDIA_SCAN_RESULT_ERROR; } #ifndef ANDROID_DEFAULT_CODE if (kKeyMap[i].key == METADATA_KEY_DURATION) { if (!strcasecmp(extension, ".mp3") || !strcasecmp(extension, ".aac") || !strcasecmp(extension, ".amr") || !strcasecmp(extension, ".awb")) { client.addStringTag("isAccurateDuration", "0"); } else if (!strcasecmp(extension, ".wav") || !strcasecmp(extension, ".ogg") || !strcasecmp(extension, ".oga")) { client.addStringTag("isAccurateDuration", "1"); } } #endif // ANDROID_DEFAULT_CODE } } #ifndef ANDROID_DEFAULT_CODE LOGD("processFileInternal '%s' - return OK", path); #endif return MEDIA_SCAN_RESULT_OK; }
MediaScanResult MediaScanner::doProcessDirectory( char *path, int pathRemaining, MediaScannerClient &client, bool noMedia) { #ifndef ANDROID_DEFAULT_CODE LOGV("doProcessDirectory %s extensions: %s\n", path, extensions); #endif // place to copy file or directory name char* fileSpot = path + strlen(path); struct dirent* entry; if (shouldSkipDirectory(path)) { LOGD("Skipping: %s", path); return MEDIA_SCAN_RESULT_OK; } // Treat all files as non-media in directories that contain a ".nomedia" file if (pathRemaining >= 8 /* strlen(".nomedia") */ ) { strcpy(fileSpot, ".nomedia"); if (access(path, F_OK) == 0) { LOGV("found .nomedia, setting noMedia flag\n"); noMedia = true; } // restore path fileSpot[0] = 0; } DIR* dir = opendir(path); if (!dir) { LOGW("Error opening directory '%s', skipping: %s.", path, strerror(errno)); return MEDIA_SCAN_RESULT_SKIPPED; } #ifndef ANDROID_DEFAULT_CODE //--start--find the best file to set default ringtone. static bool isSetRingtone = false; static bool isSetNotification = false; static bool isSetAlarm = false; bool needFindFile = false; if (isSetRingtone == false) { if (strcmp(path, "/system/media/audio/ringtones/") == 0) { needFindFile = true; isSetRingtone = true; } } if (isSetNotification == false) { if (strcmp(path, "/system/media/audio/notifications/") == 0) { needFindFile = true; isSetNotification = true; } } if (isSetAlarm == false) { if (strcmp(path, "/system/media/audio/alarms/") == 0) { needFindFile = true; isSetAlarm = true; } } if (needFindFile) { char* findFile = 0; while ((entry = readdir(dir))) { const char* name = entry->d_name; // ignore "." and ".." if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { continue; } int type = entry->d_type; if (type == DT_UNKNOWN) { // If the type is unknown, stat() the file instead. // This is sometimes necessary when accessing NFS mounted filesystems, but // could be needed in other cases well. struct stat statbuf; if (stat(path, &statbuf) == 0) { if (S_ISREG(statbuf.st_mode)) { type = DT_REG; } else if (S_ISDIR(statbuf.st_mode)) { type = DT_DIR; } } else { LOGD("stat() failed for %s: %s", path, strerror(errno) ); } } if (type == DT_REG) { int nameLength = strlen(name); if (nameLength > pathRemaining) { // path too long! continue; } int pathLength = strlen(path); strcpy(fileSpot, name); //if (fileMatchesExtension(path, extensions)) { if (findFile == 0) { findFile = new char[pathLength + pathRemaining]; strcpy(findFile, path); } else { if (strcmp(path, findFile) < 0) { strcpy(findFile, path); } } // } } } if (findFile != 0) { struct stat statbuf; stat(findFile, &statbuf); if (statbuf.st_size > 0) { LOGD("find the default audio file = %s", findFile); // client.scanFile(findFile, statbuf.st_mtime, statbuf.st_size); //need audio owner check client.scanFile(findFile, statbuf.st_mtime, statbuf.st_size,false,false); } delete findFile; findFile = 0; // if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure; } else { LOGD("Can not get the best file for ringtone"); } closedir(dir); //restore the path fileSpot[0] = 0; //reopen the dir for android's default deal. dir = opendir(path); if (!dir) { LOGD("opendir %s failed, errno: %d", path, errno); return MEDIA_SCAN_RESULT_ERROR; } } //--end--find the best file to set default ringtone. #endif MediaScanResult result = MEDIA_SCAN_RESULT_OK; while ((entry = readdir(dir))) { if (doProcessDirectoryEntry(path, pathRemaining, client, noMedia, entry, fileSpot) == MEDIA_SCAN_RESULT_ERROR) { result = MEDIA_SCAN_RESULT_ERROR; break; } } closedir(dir); return result; }
status_t MediaScanner::doProcessDirectory( char *path, int pathRemaining, const char *extensions, MediaScannerClient &client, ExceptionCheck exceptionCheck, void *exceptionEnv) { // place to copy file or directory name char* fileSpot = path + strlen(path); struct dirent* entry; char* tempPathBuffer = NULL; char* pathBuffer = NULL; DIR* dir = NULL; int pathLength = strlen(path); if (pathLength >= PATH_MAX) { goto failure; } pathBuffer = (char *)malloc(PATH_MAX + 1); tempPathBuffer = (char *)malloc(PATH_MAX + 1); if (!pathBuffer || !tempPathBuffer) { goto failure; } strcpy(pathBuffer, path); // ignore directories that contain a ".nomedia" file if (pathRemaining >= 8 /* strlen(".nomedia") */ ) { strcpy(fileSpot, ".nomedia"); if (access(path, F_OK) == 0) { LOGD("found .nomedia, skipping directory\n"); fileSpot[0] = 0; client.addNoMediaFolder(path); goto success; } // restore path fileSpot[0] = 0; } dir = opendir(path); if (!dir) { LOGD("opendir %s failed, errno: %d", path, errno); goto failure; } while ((entry = readdir(dir))) { const char* name = entry->d_name; // ignore "." and ".." if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { continue; } int type = entry->d_type; if (type == DT_UNKNOWN) { // If the type is unknown, stat() the file instead. // This is sometimes necessary when accessing NFS mounted filesystems, but // could be needed in other cases well. struct stat statbuf; int tempPathLength = strlen(pathBuffer) + strlen(name); if (tempPathLength >= PATH_MAX) { goto failure; } strcpy(tempPathBuffer, pathBuffer); strcat(tempPathBuffer, name); if (stat(tempPathBuffer, &statbuf) == 0) { if (S_ISREG(statbuf.st_mode)) { type = DT_REG; } else if (S_ISDIR(statbuf.st_mode)) { type = DT_DIR; } } else { LOGD("stat() failed for %s %s: %s", path, name, strerror(errno) ); } } if (type == DT_REG || type == DT_DIR) { int nameLength = strlen(name); bool isDirectory = (type == DT_DIR); if (nameLength > pathRemaining || (isDirectory && nameLength + 1 > pathRemaining)) { // path too long! continue; } strcpy(fileSpot, name); if (isDirectory) { // ignore directories with a name that starts with '.' // for example, the Mac ".Trashes" directory if (name[0] == '.') continue; strcat(fileSpot, "/"); int err = doProcessDirectory(path, pathRemaining - nameLength - 1, extensions, client, exceptionCheck, exceptionEnv); if (err) { // pass exceptions up - ignore other errors if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure; LOGE("Error processing '%s' - skipping\n", path); continue; } } else if (fileMatchesExtension(path, extensions)) { struct stat statbuf; stat(path, &statbuf); if (statbuf.st_size > 0) { client.scanFile(path, statbuf.st_mtime, statbuf.st_size); } if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure; } } } closedir(dir); success: free(pathBuffer); free(tempPathBuffer); return OK; failure: free(pathBuffer); free(tempPathBuffer); closedir(dir); return -1; }
static PVMFStatus parseMP3(const char *filename,MediaScannerClient& client) { PVID3ParCom pvId3Param; PVFile fileHandle; Oscl_FileServer iFs; uint32 duration; //LOGD("c---> filename 好的 = %s", filename); if (iFs.Connect() != 0) { LOGD("=======iFs.Connect failed========>\n"); return PVMFFailure; } oscl_wchar output[MAX_BUFF_SIZE]; oscl_UTF8ToUnicode((const char *) filename, oscl_strlen((const char *) filename), (oscl_wchar *) output, MAX_BUFF_SIZE); if (0 != fileHandle.Open((oscl_wchar *) output, Oscl_File::MODE_READ | Oscl_File::MODE_BINARY, iFs)) { //LOGE("Could not open the input file for reading(Test: parse id3).\n"); return PVMFFailure; } fileHandle.Seek(0, Oscl_File::SEEKSET); pvId3Param.ParseID3Tag(&fileHandle); fileHandle.Close(); iFs.Close(); //Get the frames information from ID3 library PvmiKvpSharedPtrVector framevector; pvId3Param.GetID3Frames(framevector); uint32 num_frames = framevector.size(); for (uint32 i = 0; i < num_frames; i++) { const char *key = framevector[i]->key; bool isUtf8 = false; bool isIso88591 = false; // type should follow first semicolon const char *type = strchr(key, ';'); if (type == NULL) continue; type++; char tracknumkeybuf[100]; if (strncmp(key, "track-info/track-number;", 24) == 0) { // Java expects the track number key to be called "tracknumber", so // construct a temporary one here. snprintf(tracknumkeybuf, sizeof(tracknumkeybuf), "tracknumber;%s", type); key = tracknumkeybuf; } const char *value = framevector[i]->value.pChar_value; // KVP_VALTYPE_UTF8_CHAR check must be first, since KVP_VALTYPE_ISO88591_CHAR // is a substring of KVP_VALTYPE_UTF8_CHAR. // Similarly, KVP_VALTYPE_UTF16BE_WCHAR must be checked before KVP_VALTYPE_UTF16_WCHAR if (oscl_strncmp(type, KVP_VALTYPE_UTF8_CHAR, KVP_VALTYPE_UTF8_CHAR_LEN) == 0) { isUtf8 = true; } else if ( oscl_strncmp(type, KVP_VALTYPE_ISO88591_CHAR, KVP_VALTYPE_ISO88591_CHAR_LEN) == 0) { isIso88591 = true; } if (isUtf8) { // validate to make sure it is legal utf8 uint32 valid_chars; if (oscl_str_is_valid_utf8((const uint8 *) value, valid_chars)) { // utf8 can be passed through directly //LOGD("c---> key(utf8) = %s ; value = %s", key, value); if (!client.handleStringTag(key, value)) goto failure; } else { // treat as ISO-8859-1 if UTF-8 fails isIso88591 = true; } } if (isIso88591) { size_t iInLen = strlen(value); char sOutBuf[100]; size_t iOutLen = 100; memset(sOutBuf, 0x00, 100); ChangeCode("GBK", "UTF-8", value, &iInLen, sOutBuf, &iOutLen); //LOGD("c---> key(gbk) = %s ; value = %s", key, sOutBuf); if (!client.handleStringTag(key, sOutBuf)) goto failure; } // treat it as iso-8859-1 and our native encoding detection will try to // figure out what it is /* if (isIso88591) { // convert ISO-8859-1 to utf8, worse case is 2x inflation const unsigned char *src = (const unsigned char *) value; char *temp = (char *) alloca(strlen(value) * 2 + 1); if (temp) { char *dest = temp; unsigned int uch; while ((uch = *src++) != 0) { if (uch & 0x80) { *dest++ = (uch >> 6) | 0xc0; *dest++ = (uch & 0x3f) | 0x80; } else *dest++ = uch; } *dest = 0; LOGD("c---> key(iso) = %s ; value = %s", key, temp); //if (!client.addStringTag(key, temp)) goto failure; } }*/ // not UTF-8 or ISO-8859-1, try wide char formats if (!isUtf8 && !isIso88591 && (oscl_strncmp(type, KVP_VALTYPE_UTF16BE_WCHAR, KVP_VALTYPE_UTF16BE_WCHAR_LEN) == 0 || oscl_strncmp(type, KVP_VALTYPE_UTF16_WCHAR, KVP_VALTYPE_UTF16_WCHAR_LEN) == 0)) { // convert wchar to utf8 // the id3parcom library has already taken care of byteswapping const oscl_wchar *src = framevector[i]->value.pWChar_value; int srcLen = oscl_strlen(src); // worse case is 3 bytes per character, plus zero termination int destLen = srcLen * 3 + 1; char *dest = (char *) alloca(destLen); if (oscl_UnicodeToUTF8(src, oscl_strlen(src), dest, destLen) > 0) { //LOGD("c---> key(!utf8 && !iso) = %s ; value = %s", key, dest); if (!client.handleStringTag(key, dest)) goto failure; } } else if (oscl_strncmp(type, KVP_VALTYPE_UINT32, KVP_VALTYPE_UINT32_LEN) == 0) { char temp[20]; snprintf(temp, sizeof(temp), "%d", (int) framevector[i]->value.uint32_value); //LOGD("c---> key() = %s ; value = %s", key, temp); if (!client.handleStringTag(key, temp)) goto failure; } else { //LOGE("unknown tag type %s for key %s\n", type, key); } } // extract non-ID3 properties below { OSCL_wHeapString<OsclMemAllocator> mp3filename(output); MP3ErrorType err; IMpeg3File mp3File(mp3filename, err); if (err != MP3_SUCCESS) { //LOGE("IMpeg3File constructor returned %d for %s\n", err, filename); return err; } err = mp3File.ParseMp3File(); if (err != MP3_SUCCESS) { //LOGE("IMpeg3File::ParseMp3File returned %d for %s\n", err, filename); return err; } char buffer[20]; duration = mp3File.GetDuration(); sprintf(buffer, "%d", duration); LOGD("c---> duration = %s", "duration"); //if (!client.addStringTag("duration", buffer)) goto failure; } return PVMFSuccess; failure: return PVMFFailure; }
static PVMFStatus parseMP3(const char *filename, MediaScannerClient& client) { PVID3ParCom pvId3Param; PVFile fileHandle; Oscl_FileServer iFs; uint32 duration; if (iFs.Connect() != 0) { LOGE("iFs.Connect failed\n"); return PVMFFailure; } oscl_wchar output[MAX_BUFF_SIZE]; oscl_UTF8ToUnicode((const char *)filename, oscl_strlen((const char *)filename), (oscl_wchar *)output, MAX_BUFF_SIZE); if (0 != fileHandle.Open((oscl_wchar *)output, Oscl_File::MODE_READ | Oscl_File::MODE_BINARY, iFs) ) { LOGE("Could not open the input file for reading(Test: parse id3).\n"); return PVMFFailure; } fileHandle.Seek(0, Oscl_File::SEEKSET); pvId3Param.ParseID3Tag(&fileHandle); fileHandle.Close(); iFs.Close(); //Get the frames information from ID3 library PvmiKvpSharedPtrVector framevector; pvId3Param.GetID3Frames(framevector); uint32 num_frames = framevector.size(); for (uint32 i = 0; i < num_frames;i++) { const char* key = framevector[i]->key; bool validUtf8 = true; // type should follow first semicolon const char* type = strchr(key, ';') + 1; if (type == 0) continue; const char* value = framevector[i]->value.pChar_value; // KVP_VALTYPE_UTF8_CHAR check must be first, since KVP_VALTYPE_ISO88591_CHAR // is a substring of KVP_VALTYPE_UTF8_CHAR. // Similarly, KVP_VALTYPE_UTF16BE_WCHAR must be checked before KVP_VALTYPE_UTF16_WCHAR if (oscl_strncmp(type, KVP_VALTYPE_UTF8_CHAR, KVP_VALTYPE_UTF8_CHAR_LEN) == 0) { // utf8 can be passed through directly // but first validate to make sure it is legal utf8 uint32 valid_chars; validUtf8 = oscl_str_is_valid_utf8((const uint8 *)value, valid_chars); if (validUtf8 && !client.handleStringTag(key, value)) goto failure; } // if the value is not valid utf8, then we will treat it as iso-8859-1 // and our native encoding detection will try to figure out what it is if (oscl_strncmp(type, KVP_VALTYPE_ISO88591_CHAR, KVP_VALTYPE_ISO88591_CHAR_LEN) == 0 || !validUtf8) { // iso-8859-1 // convert to utf8 // worse case is 2x inflation const unsigned char* src = (const unsigned char *)value; char* temp = (char *)alloca(strlen(value) * 2 + 1); if (temp) { char* dest = temp; unsigned int uch; while ((uch = *src++) != 0) { if (uch & 0x80) { *dest++ = (uch >> 6) | 0xc0; *dest++ = (uch & 0x3f) | 0x80; } else *dest++ = uch; } *dest = 0; if (!client.addStringTag(key, temp)) goto failure; } } else if (oscl_strncmp(type, KVP_VALTYPE_UTF16BE_WCHAR, KVP_VALTYPE_UTF16BE_WCHAR_LEN) == 0 ||