StagefrightMediaScanner.cpp revision 66ac4df65516ebfd0e500bfca75dc4b5ef8d674e
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 <media/stagefright/StagefrightMediaScanner.h> 22 23#include "include/StagefrightMetadataRetriever.h" 24 25// Sonivox includes 26#include <libsonivox/eas.h> 27 28// Ogg Vorbis includes 29#include "ivorbiscodec.h" 30#include "ivorbisfile.h" 31 32namespace android { 33 34StagefrightMediaScanner::StagefrightMediaScanner() 35 : mRetriever(new StagefrightMetadataRetriever) { 36} 37 38StagefrightMediaScanner::~StagefrightMediaScanner() {} 39 40static bool FileHasAcceptableExtension(const char *extension) { 41 static const char *kValidExtensions[] = { 42 ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", 43 ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", 44 ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota" 45 }; 46 static const size_t kNumValidExtensions = 47 sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); 48 49 for (size_t i = 0; i < kNumValidExtensions; ++i) { 50 if (!strcasecmp(extension, kValidExtensions[i])) { 51 return true; 52 } 53 } 54 55 return false; 56} 57 58static status_t HandleMIDI( 59 const char *filename, MediaScannerClient *client) { 60 // get the library configuration and do sanity check 61 const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config(); 62 if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) { 63 LOGE("EAS library/header mismatch\n"); 64 return UNKNOWN_ERROR; 65 } 66 EAS_I32 temp; 67 68 // spin up a new EAS engine 69 EAS_DATA_HANDLE easData = NULL; 70 EAS_HANDLE easHandle = NULL; 71 EAS_RESULT result = EAS_Init(&easData); 72 if (result == EAS_SUCCESS) { 73 EAS_FILE file; 74 file.path = filename; 75 file.fd = 0; 76 file.offset = 0; 77 file.length = 0; 78 result = EAS_OpenFile(easData, &file, &easHandle); 79 } 80 if (result == EAS_SUCCESS) { 81 result = EAS_Prepare(easData, easHandle); 82 } 83 if (result == EAS_SUCCESS) { 84 result = EAS_ParseMetaData(easData, easHandle, &temp); 85 } 86 if (easHandle) { 87 EAS_CloseFile(easData, easHandle); 88 } 89 if (easData) { 90 EAS_Shutdown(easData); 91 } 92 93 if (result != EAS_SUCCESS) { 94 return UNKNOWN_ERROR; 95 } 96 97 char buffer[20]; 98 sprintf(buffer, "%ld", temp); 99 if (!client->addStringTag("duration", buffer)) return UNKNOWN_ERROR; 100 101 return OK; 102} 103 104static status_t HandleOGG( 105 const char *filename, MediaScannerClient *client) { 106 int duration; 107 108 FILE *file = fopen(filename,"r"); 109 if (!file) 110 return UNKNOWN_ERROR; 111 112 OggVorbis_File vf; 113 if (ov_open(file, &vf, NULL, 0) < 0) { 114 return UNKNOWN_ERROR; 115 } 116 117 char **ptr=ov_comment(&vf,-1)->user_comments; 118 while(*ptr){ 119 char *val = strstr(*ptr, "="); 120 if (val) { 121 int keylen = val++ - *ptr; 122 char key[keylen + 1]; 123 strncpy(key, *ptr, keylen); 124 key[keylen] = 0; 125 if (!client->addStringTag(key, val)) goto failure; 126 } 127 ++ptr; 128 } 129 130 // Duration 131 duration = ov_time_total(&vf, -1); 132 if (duration > 0) { 133 char buffer[20]; 134 sprintf(buffer, "%d", duration); 135 if (!client->addStringTag("duration", buffer)) goto failure; 136 } 137 138 ov_clear(&vf); // this also closes the FILE 139 return OK; 140 141failure: 142 ov_clear(&vf); // this also closes the FILE 143 return UNKNOWN_ERROR; 144} 145 146status_t StagefrightMediaScanner::processFile( 147 const char *path, const char *mimeType, 148 MediaScannerClient &client) { 149 client.setLocale(locale()); 150 client.beginFile(); 151 152 const char *extension = strrchr(path, '.'); 153 154 if (!extension) { 155 return UNKNOWN_ERROR; 156 } 157 158 if (!FileHasAcceptableExtension(extension)) { 159 client.endFile(); 160 161 return UNKNOWN_ERROR; 162 } 163 164 if (!strcasecmp(extension, ".mid") 165 || !strcasecmp(extension, ".smf") 166 || !strcasecmp(extension, ".imy") 167 || !strcasecmp(extension, ".midi") 168 || !strcasecmp(extension, ".xmf") 169 || !strcasecmp(extension, ".rtttl") 170 || !strcasecmp(extension, ".rtx") 171 || !strcasecmp(extension, ".ota")) { 172 return HandleMIDI(path, &client); 173 } 174 175 if (!strcasecmp(extension, ".ogg")) { 176 return HandleOGG(path, &client); 177 } 178 179 if (mRetriever->setDataSource(path) == OK 180 && mRetriever->setMode( 181 METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) { 182 const char *value; 183 if ((value = mRetriever->extractMetadata( 184 METADATA_KEY_MIMETYPE)) != NULL) { 185 client.setMimeType(value); 186 } 187 188 struct KeyMap { 189 const char *tag; 190 int key; 191 }; 192 static const KeyMap kKeyMap[] = { 193 { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER }, 194 { "discnumber", METADATA_KEY_DISC_NUMBER }, 195 { "album", METADATA_KEY_ALBUM }, 196 { "artist", METADATA_KEY_ARTIST }, 197 { "albumartist", METADATA_KEY_ALBUMARTIST }, 198 { "composer", METADATA_KEY_COMPOSER }, 199 { "genre", METADATA_KEY_GENRE }, 200 { "title", METADATA_KEY_TITLE }, 201 { "year", METADATA_KEY_YEAR }, 202 { "duration", METADATA_KEY_DURATION }, 203 { "writer", METADATA_KEY_WRITER }, 204 }; 205 static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]); 206 207 for (size_t i = 0; i < kNumEntries; ++i) { 208 const char *value; 209 if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) { 210 client.addStringTag(kKeyMap[i].tag, value); 211 } 212 } 213 } 214 215 client.endFile(); 216 217 return OK; 218} 219 220char *StagefrightMediaScanner::extractAlbumArt(int fd) { 221 off_t size = lseek(fd, 0, SEEK_END); 222 if (size < 0) { 223 return NULL; 224 } 225 lseek(fd, 0, SEEK_SET); 226 227 if (mRetriever->setDataSource(fd, 0, size) == OK 228 && mRetriever->setMode( 229 METADATA_MODE_FRAME_CAPTURE_ONLY) == OK) { 230 MediaAlbumArt *art = mRetriever->extractAlbumArt(); 231 232 if (art != NULL) { 233 char *data = (char *)malloc(art->mSize + 4); 234 *(int32_t *)data = art->mSize; 235 memcpy(&data[4], art->mData, art->mSize); 236 237 delete art; 238 art = NULL; 239 240 return data; 241 } 242 } 243 244 return NULL; 245} 246 247} // namespace android 248