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