1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "StagefrightMediaScanner" 19#include <utils/Log.h> 20 21#include <sys/types.h> 22#include <sys/stat.h> 23#include <fcntl.h> 24 25#include <media/stagefright/StagefrightMediaScanner.h> 26 27#include <media/IMediaHTTPService.h> 28#include <media/mediametadataretriever.h> 29#include <private/media/VideoFrame.h> 30 31namespace android { 32 33StagefrightMediaScanner::StagefrightMediaScanner() {} 34 35StagefrightMediaScanner::~StagefrightMediaScanner() {} 36 37static bool FileHasAcceptableExtension(const char *extension) { 38 static const char *kValidExtensions[] = { 39 ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", 40 ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", 41 ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", 42 ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf", 43 ".avi", ".mpeg", ".mpg", ".awb", ".mpga" 44 }; 45 static const size_t kNumValidExtensions = 46 sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); 47 48 for (size_t i = 0; i < kNumValidExtensions; ++i) { 49 if (!strcasecmp(extension, kValidExtensions[i])) { 50 return true; 51 } 52 } 53 54 return false; 55} 56 57MediaScanResult StagefrightMediaScanner::processFile( 58 const char *path, const char *mimeType, 59 MediaScannerClient &client) { 60 ALOGV("processFile '%s'.", path); 61 62 client.setLocale(locale()); 63 client.beginFile(); 64 MediaScanResult result = processFileInternal(path, mimeType, client); 65 client.endFile(); 66 return result; 67} 68 69MediaScanResult StagefrightMediaScanner::processFileInternal( 70 const char *path, const char * /* mimeType */, 71 MediaScannerClient &client) { 72 const char *extension = strrchr(path, '.'); 73 74 if (!extension) { 75 return MEDIA_SCAN_RESULT_SKIPPED; 76 } 77 78 if (!FileHasAcceptableExtension(extension)) { 79 return MEDIA_SCAN_RESULT_SKIPPED; 80 } 81 82 sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever); 83 84 int fd = open(path, O_RDONLY | O_LARGEFILE); 85 status_t status; 86 if (fd < 0) { 87 // couldn't open it locally, maybe the media server can? 88 status = mRetriever->setDataSource(NULL /* httpService */, path); 89 } else { 90 status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL); 91 close(fd); 92 } 93 94 if (status) { 95 return MEDIA_SCAN_RESULT_ERROR; 96 } 97 98 const char *value; 99 if ((value = mRetriever->extractMetadata( 100 METADATA_KEY_MIMETYPE)) != NULL) { 101 status = client.setMimeType(value); 102 if (status) { 103 return MEDIA_SCAN_RESULT_ERROR; 104 } 105 } 106 107 struct KeyMap { 108 const char *tag; 109 int key; 110 }; 111 static const KeyMap kKeyMap[] = { 112 { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER }, 113 { "discnumber", METADATA_KEY_DISC_NUMBER }, 114 { "album", METADATA_KEY_ALBUM }, 115 { "artist", METADATA_KEY_ARTIST }, 116 { "albumartist", METADATA_KEY_ALBUMARTIST }, 117 { "composer", METADATA_KEY_COMPOSER }, 118 { "genre", METADATA_KEY_GENRE }, 119 { "title", METADATA_KEY_TITLE }, 120 { "year", METADATA_KEY_YEAR }, 121 { "duration", METADATA_KEY_DURATION }, 122 { "writer", METADATA_KEY_WRITER }, 123 { "compilation", METADATA_KEY_COMPILATION }, 124 { "isdrm", METADATA_KEY_IS_DRM }, 125 { "width", METADATA_KEY_VIDEO_WIDTH }, 126 { "height", METADATA_KEY_VIDEO_HEIGHT }, 127 }; 128 static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]); 129 130 for (size_t i = 0; i < kNumEntries; ++i) { 131 const char *value; 132 if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) { 133 status = client.addStringTag(kKeyMap[i].tag, value); 134 if (status != OK) { 135 return MEDIA_SCAN_RESULT_ERROR; 136 } 137 } 138 } 139 140 return MEDIA_SCAN_RESULT_OK; 141} 142 143MediaAlbumArt *StagefrightMediaScanner::extractAlbumArt(int fd) { 144 ALOGV("extractAlbumArt %d", fd); 145 146 off64_t size = lseek64(fd, 0, SEEK_END); 147 if (size < 0) { 148 return NULL; 149 } 150 lseek64(fd, 0, SEEK_SET); 151 152 sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever); 153 if (mRetriever->setDataSource(fd, 0, size) == OK) { 154 sp<IMemory> mem = mRetriever->extractAlbumArt(); 155 if (mem != NULL) { 156 MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->pointer()); 157 return art->clone(); 158 } 159 } 160 161 return NULL; 162} 163 164} // namespace android 165