Home | History | Annotate | Download | only in libstagefright
      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 
     31 // Sonivox includes
     32 #include <libsonivox/eas.h>
     33 
     34 namespace android {
     35 
     36 StagefrightMediaScanner::StagefrightMediaScanner() {}
     37 
     38 StagefrightMediaScanner::~StagefrightMediaScanner() {}
     39 
     40 static 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         ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
     46         ".avi", ".mpeg", ".mpg", ".awb", ".mpga"
     47     };
     48     static const size_t kNumValidExtensions =
     49         sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
     50 
     51     for (size_t i = 0; i < kNumValidExtensions; ++i) {
     52         if (!strcasecmp(extension, kValidExtensions[i])) {
     53             return true;
     54         }
     55     }
     56 
     57     return false;
     58 }
     59 
     60 static MediaScanResult HandleMIDI(
     61         const char *filename, MediaScannerClient *client) {
     62     // get the library configuration and do sanity check
     63     const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config();
     64     if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
     65         ALOGE("EAS library/header mismatch\n");
     66         return MEDIA_SCAN_RESULT_ERROR;
     67     }
     68     EAS_I32 temp;
     69 
     70     // spin up a new EAS engine
     71     EAS_DATA_HANDLE easData = NULL;
     72     EAS_HANDLE easHandle = NULL;
     73     EAS_RESULT result = EAS_Init(&easData);
     74     if (result == EAS_SUCCESS) {
     75         EAS_FILE file;
     76         file.path = filename;
     77         file.fd = 0;
     78         file.offset = 0;
     79         file.length = 0;
     80         result = EAS_OpenFile(easData, &file, &easHandle);
     81     }
     82     if (result == EAS_SUCCESS) {
     83         result = EAS_Prepare(easData, easHandle);
     84     }
     85     if (result == EAS_SUCCESS) {
     86         result = EAS_ParseMetaData(easData, easHandle, &temp);
     87     }
     88     if (easHandle) {
     89         EAS_CloseFile(easData, easHandle);
     90     }
     91     if (easData) {
     92         EAS_Shutdown(easData);
     93     }
     94 
     95     if (result != EAS_SUCCESS) {
     96         return MEDIA_SCAN_RESULT_SKIPPED;
     97     }
     98 
     99     char buffer[20];
    100     sprintf(buffer, "%ld", temp);
    101     status_t status = client->addStringTag("duration", buffer);
    102     if (status != OK) {
    103         return MEDIA_SCAN_RESULT_ERROR;
    104     }
    105     return MEDIA_SCAN_RESULT_OK;
    106 }
    107 
    108 MediaScanResult StagefrightMediaScanner::processFile(
    109         const char *path, const char *mimeType,
    110         MediaScannerClient &client) {
    111     ALOGV("processFile '%s'.", path);
    112 
    113     client.setLocale(locale());
    114     client.beginFile();
    115     MediaScanResult result = processFileInternal(path, mimeType, client);
    116     client.endFile();
    117     return result;
    118 }
    119 
    120 MediaScanResult StagefrightMediaScanner::processFileInternal(
    121         const char *path, const char * /* mimeType */,
    122         MediaScannerClient &client) {
    123     const char *extension = strrchr(path, '.');
    124 
    125     if (!extension) {
    126         return MEDIA_SCAN_RESULT_SKIPPED;
    127     }
    128 
    129     if (!FileHasAcceptableExtension(extension)) {
    130         return MEDIA_SCAN_RESULT_SKIPPED;
    131     }
    132 
    133     if (!strcasecmp(extension, ".mid")
    134             || !strcasecmp(extension, ".smf")
    135             || !strcasecmp(extension, ".imy")
    136             || !strcasecmp(extension, ".midi")
    137             || !strcasecmp(extension, ".xmf")
    138             || !strcasecmp(extension, ".rtttl")
    139             || !strcasecmp(extension, ".rtx")
    140             || !strcasecmp(extension, ".ota")
    141             || !strcasecmp(extension, ".mxmf")) {
    142         return HandleMIDI(path, &client);
    143     }
    144 
    145     sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);
    146 
    147     int fd = open(path, O_RDONLY | O_LARGEFILE);
    148     status_t status;
    149     if (fd < 0) {
    150         // couldn't open it locally, maybe the media server can?
    151         status = mRetriever->setDataSource(NULL /* httpService */, path);
    152     } else {
    153         status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL);
    154         close(fd);
    155     }
    156 
    157     if (status) {
    158         return MEDIA_SCAN_RESULT_ERROR;
    159     }
    160 
    161     const char *value;
    162     if ((value = mRetriever->extractMetadata(
    163                     METADATA_KEY_MIMETYPE)) != NULL) {
    164         status = client.setMimeType(value);
    165         if (status) {
    166             return MEDIA_SCAN_RESULT_ERROR;
    167         }
    168     }
    169 
    170     struct KeyMap {
    171         const char *tag;
    172         int key;
    173     };
    174     static const KeyMap kKeyMap[] = {
    175         { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER },
    176         { "discnumber", METADATA_KEY_DISC_NUMBER },
    177         { "album", METADATA_KEY_ALBUM },
    178         { "artist", METADATA_KEY_ARTIST },
    179         { "albumartist", METADATA_KEY_ALBUMARTIST },
    180         { "composer", METADATA_KEY_COMPOSER },
    181         { "genre", METADATA_KEY_GENRE },
    182         { "title", METADATA_KEY_TITLE },
    183         { "year", METADATA_KEY_YEAR },
    184         { "duration", METADATA_KEY_DURATION },
    185         { "writer", METADATA_KEY_WRITER },
    186         { "compilation", METADATA_KEY_COMPILATION },
    187         { "isdrm", METADATA_KEY_IS_DRM },
    188         { "width", METADATA_KEY_VIDEO_WIDTH },
    189         { "height", METADATA_KEY_VIDEO_HEIGHT },
    190     };
    191     static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]);
    192 
    193     for (size_t i = 0; i < kNumEntries; ++i) {
    194         const char *value;
    195         if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) {
    196             status = client.addStringTag(kKeyMap[i].tag, value);
    197             if (status != OK) {
    198                 return MEDIA_SCAN_RESULT_ERROR;
    199             }
    200         }
    201     }
    202 
    203     return MEDIA_SCAN_RESULT_OK;
    204 }
    205 
    206 MediaAlbumArt *StagefrightMediaScanner::extractAlbumArt(int fd) {
    207     ALOGV("extractAlbumArt %d", fd);
    208 
    209     off64_t size = lseek64(fd, 0, SEEK_END);
    210     if (size < 0) {
    211         return NULL;
    212     }
    213     lseek64(fd, 0, SEEK_SET);
    214 
    215     sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);
    216     if (mRetriever->setDataSource(fd, 0, size) == OK) {
    217         sp<IMemory> mem = mRetriever->extractAlbumArt();
    218         if (mem != NULL) {
    219             MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->pointer());
    220             return art->clone();
    221         }
    222     }
    223 
    224     return NULL;
    225 }
    226 
    227 }  // namespace android
    228