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::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; }
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; }
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 ||