StagefrightMediaScanner.cpp revision 4595be952e3b1d6776a023e3f447c057797f1505
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 <media/mediametadataretriever.h>
24#include <private/media/VideoFrame.h>
25
26// Sonivox includes
27#include <libsonivox/eas.h>
28
29namespace android {
30
31StagefrightMediaScanner::StagefrightMediaScanner()
32    : mRetriever(new MediaMetadataRetriever) {
33}
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"
43    };
44    static const size_t kNumValidExtensions =
45        sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
46
47    for (size_t i = 0; i < kNumValidExtensions; ++i) {
48        if (!strcasecmp(extension, kValidExtensions[i])) {
49            return true;
50        }
51    }
52
53    return false;
54}
55
56static status_t HandleMIDI(
57        const char *filename, MediaScannerClient *client) {
58    // get the library configuration and do sanity check
59    const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config();
60    if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
61        LOGE("EAS library/header mismatch\n");
62        return UNKNOWN_ERROR;
63    }
64    EAS_I32 temp;
65
66    // spin up a new EAS engine
67    EAS_DATA_HANDLE easData = NULL;
68    EAS_HANDLE easHandle = NULL;
69    EAS_RESULT result = EAS_Init(&easData);
70    if (result == EAS_SUCCESS) {
71        EAS_FILE file;
72        file.path = filename;
73        file.fd = 0;
74        file.offset = 0;
75        file.length = 0;
76        result = EAS_OpenFile(easData, &file, &easHandle);
77    }
78    if (result == EAS_SUCCESS) {
79        result = EAS_Prepare(easData, easHandle);
80    }
81    if (result == EAS_SUCCESS) {
82        result = EAS_ParseMetaData(easData, easHandle, &temp);
83    }
84    if (easHandle) {
85        EAS_CloseFile(easData, easHandle);
86    }
87    if (easData) {
88        EAS_Shutdown(easData);
89    }
90
91    if (result != EAS_SUCCESS) {
92        return UNKNOWN_ERROR;
93    }
94
95    char buffer[20];
96    sprintf(buffer, "%ld", temp);
97    if (!client->addStringTag("duration", buffer)) return UNKNOWN_ERROR;
98
99    return OK;
100}
101
102status_t StagefrightMediaScanner::processFile(
103        const char *path, const char *mimeType,
104        MediaScannerClient &client) {
105    LOGV("processFile '%s'.", path);
106
107    client.setLocale(locale());
108    client.beginFile();
109
110    const char *extension = strrchr(path, '.');
111
112    if (!extension) {
113        return UNKNOWN_ERROR;
114    }
115
116    if (!FileHasAcceptableExtension(extension)) {
117        client.endFile();
118
119        return UNKNOWN_ERROR;
120    }
121
122    if (!strcasecmp(extension, ".mid")
123            || !strcasecmp(extension, ".smf")
124            || !strcasecmp(extension, ".imy")
125            || !strcasecmp(extension, ".midi")
126            || !strcasecmp(extension, ".xmf")
127            || !strcasecmp(extension, ".rtttl")
128            || !strcasecmp(extension, ".rtx")
129            || !strcasecmp(extension, ".ota")) {
130        return HandleMIDI(path, &client);
131    }
132
133    if (mRetriever->setDataSource(path) == OK
134            && mRetriever->setMode(
135                METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) {
136        const char *value;
137        if ((value = mRetriever->extractMetadata(
138                        METADATA_KEY_MIMETYPE)) != NULL) {
139            client.setMimeType(value);
140        }
141
142        struct KeyMap {
143            const char *tag;
144            int key;
145        };
146        static const KeyMap kKeyMap[] = {
147            { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER },
148            { "discnumber", METADATA_KEY_DISC_NUMBER },
149            { "album", METADATA_KEY_ALBUM },
150            { "artist", METADATA_KEY_ARTIST },
151            { "albumartist", METADATA_KEY_ALBUMARTIST },
152            { "composer", METADATA_KEY_COMPOSER },
153            { "genre", METADATA_KEY_GENRE },
154            { "title", METADATA_KEY_TITLE },
155            { "year", METADATA_KEY_YEAR },
156            { "duration", METADATA_KEY_DURATION },
157            { "writer", METADATA_KEY_WRITER },
158        };
159        static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]);
160
161        for (size_t i = 0; i < kNumEntries; ++i) {
162            const char *value;
163            if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) {
164                client.addStringTag(kKeyMap[i].tag, value);
165            }
166        }
167    }
168
169    client.endFile();
170
171    return OK;
172}
173
174char *StagefrightMediaScanner::extractAlbumArt(int fd) {
175    LOGV("extractAlbumArt %d", fd);
176
177    off_t size = lseek(fd, 0, SEEK_END);
178    if (size < 0) {
179        return NULL;
180    }
181    lseek(fd, 0, SEEK_SET);
182
183    if (mRetriever->setDataSource(fd, 0, size) == OK
184            && mRetriever->setMode(
185                METADATA_MODE_FRAME_CAPTURE_ONLY) == OK) {
186        sp<IMemory> mem = mRetriever->extractAlbumArt();
187
188        if (mem != NULL) {
189            MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->pointer());
190
191            char *data = (char *)malloc(art->mSize + 4);
192            *(int32_t *)data = art->mSize;
193            memcpy(&data[4], &art[1], art->mSize);
194
195            return data;
196        }
197    }
198
199    return NULL;
200}
201
202}  // namespace android
203