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