167e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber/*
267e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * Copyright (C) 2009 The Android Open Source Project
367e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber *
467e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * Licensed under the Apache License, Version 2.0 (the "License");
567e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * you may not use this file except in compliance with the License.
667e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * You may obtain a copy of the License at
767e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber *
867e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber *      http://www.apache.org/licenses/LICENSE-2.0
967e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber *
1067e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * Unless required by applicable law or agreed to in writing, software
1167e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS,
1267e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1367e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * See the License for the specific language governing permissions and
1467e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber * limitations under the License.
1567e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber */
1667e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
17fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber//#define LOG_NDEBUG 0
18fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber#define LOG_TAG "StagefrightMediaScanner"
19fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber#include <utils/Log.h>
20fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber
21a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen#include <sys/types.h>
22a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen#include <sys/stat.h>
23a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen#include <fcntl.h>
24a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen
2567e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber#include <media/stagefright/StagefrightMediaScanner.h>
2667e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
271b86fe063badb5f28c467ade39be0f4008688947Andreas Huber#include <media/IMediaHTTPService.h>
282e39c1ca101e01ff13b03129a87939e7982b25bfAndreas Huber#include <media/mediametadataretriever.h>
292e39c1ca101e01ff13b03129a87939e7982b25bfAndreas Huber#include <private/media/VideoFrame.h>
3067e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
3167e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Hubernamespace android {
3267e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
330a095d09464ba18e288a3f529410af0f1257ac2aMike LockwoodStagefrightMediaScanner::StagefrightMediaScanner() {}
3467e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
3567e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas HuberStagefrightMediaScanner::~StagefrightMediaScanner() {}
3667e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
37fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huberstatic bool FileHasAcceptableExtension(const char *extension) {
38fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    static const char *kValidExtensions[] = {
39fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber        ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
40fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber        ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
41093437c388e5dff6903a3d43f2ca9f8a1ba4744aAndreas Huber        ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
428d30cc86d36c5e2d7d8defab8f58faed4139ed7fAndreas Huber        ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
432ae1f524a630b413c5724c42105cc504c60d3ba2Marco Nelissen        ".avi", ".mpeg", ".mpg", ".awb", ".mpga", ".mov",
442ae1f524a630b413c5724c42105cc504c60d3ba2Marco Nelissen        ".m4v", ".oga"
45fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    };
46fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    static const size_t kNumValidExtensions =
47fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber        sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
48fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber
49fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    for (size_t i = 0; i < kNumValidExtensions; ++i) {
50fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber        if (!strcasecmp(extension, kValidExtensions[i])) {
51fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber            return true;
52fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber        }
53fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    }
54fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber
55fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    return false;
56fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber}
57fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber
587188e55f54a43c55fd6b96454720c447f1dc454eJeff BrownMediaScanResult StagefrightMediaScanner::processFile(
5967e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber        const char *path, const char *mimeType,
6067e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber        MediaScannerClient &client) {
613856b090cd04ba5dd4a59a12430ed724d5995909Steve Block    ALOGV("processFile '%s'.", path);
622e39c1ca101e01ff13b03129a87939e7982b25bfAndreas Huber
6367e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber    client.setLocale(locale());
6467e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber    client.beginFile();
657188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    MediaScanResult result = processFileInternal(path, mimeType, client);
662ae1f524a630b413c5724c42105cc504c60d3ba2Marco Nelissen    ALOGV("result: %d", result);
672ae1f524a630b413c5724c42105cc504c60d3ba2Marco Nelissen    if (mimeType == NULL && result != MEDIA_SCAN_RESULT_OK) {
682ae1f524a630b413c5724c42105cc504c60d3ba2Marco Nelissen        ALOGW("media scan failed for %s", path);
692ae1f524a630b413c5724c42105cc504c60d3ba2Marco Nelissen        client.setMimeType("application/octet-stream");
702ae1f524a630b413c5724c42105cc504c60d3ba2Marco Nelissen    }
717188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    client.endFile();
727188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    return result;
737188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown}
7467e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
757188e55f54a43c55fd6b96454720c447f1dc454eJeff BrownMediaScanResult StagefrightMediaScanner::processFileInternal(
7684333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber        const char *path, const char * /* mimeType */,
777188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        MediaScannerClient &client) {
78fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    const char *extension = strrchr(path, '.');
79fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber
80fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    if (!extension) {
817188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        return MEDIA_SCAN_RESULT_SKIPPED;
82fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    }
83fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber
84fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    if (!FileHasAcceptableExtension(extension)) {
857188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        return MEDIA_SCAN_RESULT_SKIPPED;
86fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    }
87fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber
887188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);
897188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown
90a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen    int fd = open(path, O_RDONLY | O_LARGEFILE);
91a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen    status_t status;
92a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen    if (fd < 0) {
93a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen        // couldn't open it locally, maybe the media server can?
9424c15776247c46e34562100213035bb7aaffe013Chong Zhang        sp<IMediaHTTPService> nullService;
9524c15776247c46e34562100213035bb7aaffe013Chong Zhang        status = mRetriever->setDataSource(nullService, path);
96a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen    } else {
97a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen        status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL);
98a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen        close(fd);
99a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen    }
100a28976b04c70cef3c8e79137478b6a7bc05608acMarco Nelissen
1017188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    if (status) {
1027188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        return MEDIA_SCAN_RESULT_ERROR;
1037188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    }
1047188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown
1057188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    const char *value;
1067188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    if ((value = mRetriever->extractMetadata(
1077188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown                    METADATA_KEY_MIMETYPE)) != NULL) {
1087188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        status = client.setMimeType(value);
1097188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        if (status) {
1107188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown            return MEDIA_SCAN_RESULT_ERROR;
1116ed70d2d41f9929d1c3f5179d947766495f9efe5Hiroshi Takekawa        }
1127188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    }
1130a095d09464ba18e288a3f529410af0f1257ac2aMike Lockwood
1147188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    struct KeyMap {
1157188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        const char *tag;
1167188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        int key;
1177188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    };
1187188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    static const KeyMap kKeyMap[] = {
1197188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER },
1207188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "discnumber", METADATA_KEY_DISC_NUMBER },
1217188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "album", METADATA_KEY_ALBUM },
1227188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "artist", METADATA_KEY_ARTIST },
1237188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "albumartist", METADATA_KEY_ALBUMARTIST },
1247188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "composer", METADATA_KEY_COMPOSER },
1257188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "genre", METADATA_KEY_GENRE },
1267188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "title", METADATA_KEY_TITLE },
1277188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "year", METADATA_KEY_YEAR },
1287188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "duration", METADATA_KEY_DURATION },
1297188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "writer", METADATA_KEY_WRITER },
1307188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "compilation", METADATA_KEY_COMPILATION },
1317188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        { "isdrm", METADATA_KEY_IS_DRM },
1321f45624f74b615820fb330faf788ff2e5e6c8e0dBryan Mawhinney        { "date", METADATA_KEY_DATE },
1334762a5164a986bd6a7ca0e7aba43881b64f146ddMarco Nelissen        { "width", METADATA_KEY_VIDEO_WIDTH },
1344762a5164a986bd6a7ca0e7aba43881b64f146ddMarco Nelissen        { "height", METADATA_KEY_VIDEO_HEIGHT },
1357188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    };
1367188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]);
1377188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown
1387188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    for (size_t i = 0; i < kNumEntries; ++i) {
1397188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        const char *value;
1407188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown        if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) {
1417188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown            status = client.addStringTag(kKeyMap[i].tag, value);
142153cefdf4acab25355f590d7760ebf73ef6096a9Marco Nelissen            if (status != OK) {
1437188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown                return MEDIA_SCAN_RESULT_ERROR;
14467e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber            }
14567e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber        }
14667e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber    }
14767e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
1487188e55f54a43c55fd6b96454720c447f1dc454eJeff Brown    return MEDIA_SCAN_RESULT_OK;
14967e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber}
15067e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
151f3e80dddd7376aa9deeb27de25e1d50030a2ad98Elliott HughesMediaAlbumArt *StagefrightMediaScanner::extractAlbumArt(int fd) {
1523856b090cd04ba5dd4a59a12430ed724d5995909Steve Block    ALOGV("extractAlbumArt %d", fd);
1532e39c1ca101e01ff13b03129a87939e7982b25bfAndreas Huber
154c7fc37a3dab9bd1f96713649f351b5990e6316ffJames Dong    off64_t size = lseek64(fd, 0, SEEK_END);
155fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    if (size < 0) {
156fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber        return NULL;
157fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber    }
158c7fc37a3dab9bd1f96713649f351b5990e6316ffJames Dong    lseek64(fd, 0, SEEK_SET);
159fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber
1600a095d09464ba18e288a3f529410af0f1257ac2aMike Lockwood    sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);
1617f7d52ac18dfc3c6d8f6267dad29306613e9bd0eJames Dong    if (mRetriever->setDataSource(fd, 0, size) == OK) {
1622e39c1ca101e01ff13b03129a87939e7982b25bfAndreas Huber        sp<IMemory> mem = mRetriever->extractAlbumArt();
1632e39c1ca101e01ff13b03129a87939e7982b25bfAndreas Huber        if (mem != NULL) {
1642e39c1ca101e01ff13b03129a87939e7982b25bfAndreas Huber            MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->pointer());
165f3e80dddd7376aa9deeb27de25e1d50030a2ad98Elliott Hughes            return art->clone();
166fc9ba09e3bb368f823d473f5e2bb9aa32dba6289Andreas Huber        }
16767e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber    }
16867e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
16967e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber    return NULL;
17067e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber}
17167e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber
17267e5a4f6f6879d512a859e5dba92e9beec7a2f91Andreas Huber}  // namespace android
173