2 * Copyright (C) 2008, Google Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
14 * See the License for the specific language governing permissions
15 * and limitations under the License.
16 * -------------------------------------------------------------------
19 #include <media/mediascanner.h>
24 #include "pv_id3_parcom.h"
25 #include "oscl_string_containers.h"
26 #include "oscl_file_io.h"
27 #include "oscl_assert.h"
28 #include "oscl_lock_base.h"
29 #include "oscl_snprintf.h"
30 #include "oscl_string_utf8.h"
31 #include "pvmf_return_codes.h"
32 #include "pv_mime_string_utils.h"
33 #include "pv_id3_parcom_constants.h"
34 #include "oscl_utf8conv.h"
36 #include "impeg4file.h"
37 #include "autodetect.h"
39 // Ogg Vorbis includes
40 #include "ivorbiscodec.h"
41 #include "ivorbisfile.h"
44 #include <libsonivox/eas.h>
46 // used for WMA support
47 #include "media/mediametadataretriever.h"
49 #include <media/thread_init.h>
50 #include <utils/string_array.h>
52 #define MAX_BUFF_SIZE 1024
54 #include <sys/types.h>
60 #include "unicode/ucnv.h"
61 #include "unicode/ustring.h"
64 #define LOG_TAG "MediaScanner"
65 #include "utils/Log.h"
67 #define MAX_STR_LEN 1000
73 MediaScanner::MediaScanner()
78 MediaScanner::~MediaScanner()
83 static PVMFStatus parseMP3(const char *filename, MediaScannerClient& client)
85 PVID3ParCom pvId3Param;
90 if (iFs.Connect() != 0)
92 LOGE("iFs.Connect failed\n");
96 oscl_wchar output[MAX_BUFF_SIZE];
97 oscl_UTF8ToUnicode((const char *)filename, oscl_strlen((const char *)filename), (oscl_wchar *)output, MAX_BUFF_SIZE);
98 if (0 != fileHandle.Open((oscl_wchar *)output, Oscl_File::MODE_READ | Oscl_File::MODE_BINARY, iFs) )
100 LOGE("Could not open the input file for reading(Test: parse id3).\n");
104 fileHandle.Seek(0, Oscl_File::SEEKSET);
105 pvId3Param.ParseID3Tag(&fileHandle);
109 //Get the frames information from ID3 library
110 PvmiKvpSharedPtrVector framevector;
111 pvId3Param.GetID3Frames(framevector);
113 uint32 num_frames = framevector.size();
115 for (uint32 i = 0; i < num_frames;i++)
117 const char* key = framevector[i]->key;
119 bool isIso88591 = false;
121 // type should follow first semicolon
122 const char* type = strchr(key, ';') + 1;
123 if (type == 0) continue;
126 const char* value = framevector[i]->value.pChar_value;
128 // KVP_VALTYPE_UTF8_CHAR check must be first, since KVP_VALTYPE_ISO88591_CHAR
129 // is a substring of KVP_VALTYPE_UTF8_CHAR.
130 // Similarly, KVP_VALTYPE_UTF16BE_WCHAR must be checked before KVP_VALTYPE_UTF16_WCHAR
131 if (oscl_strncmp(type, KVP_VALTYPE_UTF8_CHAR, KVP_VALTYPE_UTF8_CHAR_LEN) == 0) {
133 } else if (oscl_strncmp(type, KVP_VALTYPE_ISO88591_CHAR, KVP_VALTYPE_ISO88591_CHAR_LEN) == 0) {
138 // validate to make sure it is legal utf8
140 if (oscl_str_is_valid_utf8((const uint8 *)value, valid_chars)) {
141 // utf8 can be passed through directly
142 if (!client.handleStringTag(key, value)) goto failure;
144 // treat as ISO-8859-1 if UTF-8 fails
149 // treat it as iso-8859-1 and our native encoding detection will try to
150 // figure out what it is
152 // convert ISO-8859-1 to utf8, worse case is 2x inflation
153 const unsigned char* src = (const unsigned char *)value;
154 char* temp = (char *)alloca(strlen(value) * 2 + 1);
158 while ((uch = *src++) != 0) {
160 *dest++ = (uch >> 6) | 0xc0;
161 *dest++ = (uch & 0x3f) | 0x80;
162 } else *dest++ = uch;
165 if (!client.addStringTag(key, temp)) goto failure;
169 // not UTF-8 or ISO-8859-1, try wide char formats
170 if (!isUtf8 && !isIso88591 &&
171 (oscl_strncmp(type, KVP_VALTYPE_UTF16BE_WCHAR, KVP_VALTYPE_UTF16BE_WCHAR_LEN) == 0 ||
172 oscl_strncmp(type, KVP_VALTYPE_UTF16_WCHAR, KVP_VALTYPE_UTF16_WCHAR_LEN) == 0)) {
173 // convert wchar to utf8
174 // the id3parcom library has already taken care of byteswapping
175 const oscl_wchar* src = framevector[i]->value.pWChar_value;
176 int srcLen = oscl_strlen(src);
177 // worse case is 3 bytes per character, plus zero termination
178 int destLen = srcLen * 3 + 1;
179 char* dest = (char *)alloca(destLen);
181 if (oscl_UnicodeToUTF8(src, oscl_strlen(src), dest, destLen) > 0) {
182 if (!client.addStringTag(key, dest)) goto failure;
184 } else if (oscl_strncmp(type, KVP_VALTYPE_UINT32, KVP_VALTYPE_UINT32_LEN) == 0) {
186 snprintf(temp, sizeof(temp), "%d", (int)framevector[i]->value.uint32_value);
187 if (!client.addStringTag(key, temp)) goto failure;
189 //LOGE("unknown tag type %s for key %s\n", type, key);
193 // extract non-ID3 properties below
195 OSCL_wHeapString<OsclMemAllocator> mp3filename(output);
197 IMpeg3File mp3File(mp3filename, err);
198 if (err != MP3_SUCCESS) {
199 LOGE("IMpeg3File constructor returned %d for %s\n", err, filename);
202 err = mp3File.ParseMp3File();
203 if (err != MP3_SUCCESS) {
204 LOGE("IMpeg3File::ParseMp3File returned %d for %s\n", err, filename);
209 duration = mp3File.GetDuration();
210 sprintf(buffer, "%d", duration);
211 if (!client.addStringTag("duration", buffer)) goto failure;
220 static PVMFStatus reportM4ATags(IMpeg4File *mp4Input, MediaScannerClient& client)
223 OSCL_wHeapString<OsclMemAllocator> valuestring=NULL;
224 MP4FFParserOriginalCharEnc charType = ORIGINAL_CHAR_TYPE_UNKNOWN;
232 char buffer[MAX_STR_LEN];
236 for (i = 0; i < mp4Input->getNumTitle(); ++i)
238 mp4Input->getTitle(i,valuestring,iLangCode,charType);
239 if (oscl_UnicodeToUTF8(valuestring.get_cstr(),valuestring.get_size(),
240 buffer,sizeof(buffer)) > 0)
242 if (!client.addStringTag("title", buffer)) goto failure;
248 for (i = 0; i < mp4Input->getNumArtist(); ++i)
250 mp4Input->getArtist(i,valuestring,iLangCode,charType);
251 if (oscl_UnicodeToUTF8(valuestring.get_cstr(),valuestring.get_size(),
252 buffer,sizeof(buffer)) > 0)
254 if (!client.addStringTag("artist", buffer)) goto failure;
260 for (i = 0; i < mp4Input->getNumAlbum(); ++i)
262 mp4Input->getAlbum(i,valuestring,iLangCode,charType);
263 if (oscl_UnicodeToUTF8(valuestring.get_cstr(),valuestring.get_size(),
264 buffer,sizeof(buffer)) > 0)
266 if (!client.addStringTag("album", buffer)) goto failure;
273 for (i = 0; i < mp4Input->getNumYear(); ++i)
275 mp4Input->getYear(i,val);
276 sprintf(buffer, "%d", val);
279 if (!client.addStringTag("year", buffer)) goto failure;
285 if (oscl_UnicodeToUTF8(mp4Input->getITunesWriter().get_cstr(),
286 mp4Input->getITunesWriter().get_size(),buffer,sizeof(buffer)) > 0)
287 if (!client.addStringTag("composer", buffer)) goto failure;
290 trackNum = mp4Input->getITunesThisTrackNo();
291 totalTracks = mp4Input->getITunesTotalTracks();
292 sprintf(buffer, "%d/%d", trackNum, totalTracks);
293 if (!client.addStringTag("tracknumber", buffer)) goto failure;
296 duration = mp4Input->getMovieDuration();
297 timeScale = mp4Input->getMovieTimescale();
298 // adjust duration to milliseconds if necessary
299 if (timeScale != 1000)
300 duration = (duration * 1000) / timeScale;
301 sprintf(buffer, "%lld", duration);
302 if (!client.addStringTag("duration", buffer)) goto failure;
306 for(i=0; i<mp4Input->getNumGenre(); i++)
308 mp4Input->getGenre(i,valuestring,iLangCode,charType);
309 if (oscl_UnicodeToUTF8(valuestring.get_cstr(),valuestring.get_size(), buffer,sizeof(buffer)) > 0)
313 if (!client.addStringTag("genre", buffer)) goto failure;
315 uint16 id = mp4Input->getITunesGnreID();
317 sprintf(buffer, "(%d)", id - 1);
318 if (!client.addStringTag("genre", buffer)) goto failure;
328 static PVMFStatus parseMP4(const char *filename, MediaScannerClient& client)
333 if (iFs.Connect() != 0)
335 LOGE("Connection with the file server for the parse id3 test failed.\n");
339 oscl_wchar output[MAX_BUFF_SIZE];
340 oscl_UTF8ToUnicode((const char *)filename, oscl_strlen((const char *)filename), (oscl_wchar *)output, MAX_BUFF_SIZE);
341 OSCL_wHeapString<OsclMemAllocator> mpegfilename(output);
343 IMpeg4File *mp4Input = IMpeg4File::readMP4File(mpegfilename, NULL, NULL, 1 /* parsing_mode */, &iFs);
346 // check to see if the file contains video
347 int32 count = mp4Input->getNumTracks();
348 uint32* tracks = new uint32[count];
349 bool hasAudio = false;
350 bool hasVideo = false;
352 mp4Input->getTrackIDList(tracks, count);
353 for (int i = 0; i < count; ++i) {
354 uint32 trackType = mp4Input->getTrackMediaType(tracks[i]);
355 OSCL_HeapString<OsclMemAllocator> streamtype;
356 mp4Input->getTrackMIMEType(tracks[i], streamtype);
357 char streamtypeutf8[128];
358 strncpy (streamtypeutf8, streamtype.get_str(), streamtype.get_size());
359 if (streamtypeutf8[0])
361 if (strcmp(streamtypeutf8,"FORMATUNKNOWN") != 0) {
362 if (trackType == MEDIA_TYPE_AUDIO) {
364 } else if (trackType == MEDIA_TYPE_VISUAL) {
368 //LOGI("@@@@@@@@ %100s: %s\n", filename, streamtypeutf8);
377 if (!client.setMimeType("video/mp4")) return PVMFFailure;
378 } else if (hasAudio) {
379 if (!client.setMimeType("audio/mp4")) return PVMFFailure;
382 IMpeg4File::DestroyMP4FileObject(mp4Input);
386 PVMFStatus result = reportM4ATags(mp4Input, client);
388 IMpeg4File::DestroyMP4FileObject(mp4Input);
395 static PVMFStatus parseOgg(const char *filename, MediaScannerClient& client)
399 FILE *file = fopen(filename,"r");
404 if (ov_open(file, &vf, NULL, 0) < 0) {
408 char **ptr=ov_comment(&vf,-1)->user_comments;
410 char *val = strstr(*ptr, "=");
412 int keylen = val++ - *ptr;
413 char key[keylen + 1];
414 strncpy(key, *ptr, keylen);
416 if (!client.addStringTag(key, val)) goto failure;
422 duration = ov_time_total(&vf, -1);
425 sprintf(buffer, "%d", duration);
426 if (!client.addStringTag("duration", buffer)) goto failure;
429 ov_clear(&vf); // this also closes the FILE
433 ov_clear(&vf); // this also closes the FILE
437 static PVMFStatus parseMidi(const char *filename, MediaScannerClient& client) {
439 // get the library configuration and do sanity check
440 const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config();
441 if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
442 LOGE("EAS library/header mismatch\n");
447 // spin up a new EAS engine
448 EAS_DATA_HANDLE easData = NULL;
449 EAS_HANDLE easHandle = NULL;
450 EAS_RESULT result = EAS_Init(&easData);
451 if (result == EAS_SUCCESS) {
453 file.path = filename;
457 result = EAS_OpenFile(easData, &file, &easHandle);
459 if (result == EAS_SUCCESS) {
460 result = EAS_Prepare(easData, easHandle);
462 if (result == EAS_SUCCESS) {
463 result = EAS_ParseMetaData(easData, easHandle, &temp);
466 EAS_CloseFile(easData, easHandle);
469 EAS_Shutdown(easData);
472 if (result != EAS_SUCCESS) {
477 sprintf(buffer, "%ld", temp);
478 if (!client.addStringTag("duration", buffer)) return PVMFFailure;
482 static PVMFStatus parseWMA(const char *filename, MediaScannerClient& client)
484 sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever();
485 retriever->setMode( 1 /*MediaMetadataRetriever.MODE_GET_METADATA_ONLY*/);
486 status_t status = retriever->setDataSource(filename);
487 if (status != NO_ERROR) {
488 LOGE("parseWMA setDataSource failed (%d)", status);
489 retriever->disconnect();
495 value = retriever->extractMetadata(METADATA_KEY_IS_DRM_CRIPPLED);
496 if (value && strcmp(value, "true") == 0) {
497 // we don't support WMDRM currently
498 // setting this invalid mimetype will make the java side ignore this file
499 client.setMimeType("audio/x-wma-drm");
501 value = retriever->extractMetadata(METADATA_KEY_CODEC);
502 if (value && strcmp(value, "Windows Media Audio 10 Professional") == 0) {
503 // we don't support WM 10 Professional currently
504 // setting this invalid mimetype will make the java side ignore this file
505 client.setMimeType("audio/x-wma-10-professional");
508 value = retriever->extractMetadata(METADATA_KEY_ALBUM);
510 client.addStringTag("album", value);
512 // Look for "author" tag first, if it is not found, try "artist" tag
513 value = retriever->extractMetadata(METADATA_KEY_AUTHOR);
515 value = retriever->extractMetadata(METADATA_KEY_ARTIST);
518 client.addStringTag("artist", value);
519 value = retriever->extractMetadata(METADATA_KEY_COMPOSER);
521 client.addStringTag("composer", value);
522 value = retriever->extractMetadata(METADATA_KEY_GENRE);
524 client.addStringTag("genre", value);
525 value = retriever->extractMetadata(METADATA_KEY_TITLE);
527 client.addStringTag("title", value);
528 value = retriever->extractMetadata(METADATA_KEY_YEAR);
530 client.addStringTag("year", value);
531 value = retriever->extractMetadata(METADATA_KEY_CD_TRACK_NUMBER);
533 client.addStringTag("tracknumber", value);
535 retriever->disconnect();
539 status_t MediaScanner::processFile(const char *path, const char* mimeType, MediaScannerClient& client)
542 InitializeForThread();
544 client.setLocale(mLocale);
547 //LOGD("processFile %s mimeType: %s\n", path, mimeType);
548 const char* extension = strrchr(path, '.');
550 if (extension && strcasecmp(extension, ".mp3") == 0) {
551 result = parseMP3(path, client);
552 } else if (extension &&
553 (strcasecmp(extension, ".mp4") == 0 || strcasecmp(extension, ".m4a") == 0 ||
554 strcasecmp(extension, ".3gp") == 0 || strcasecmp(extension, ".3gpp") == 0 ||
555 strcasecmp(extension, ".3g2") == 0 || strcasecmp(extension, ".3gpp2") == 0)) {
556 result = parseMP4(path, client);
557 } else if (extension && strcasecmp(extension, ".ogg") == 0) {
558 result = parseOgg(path, client);
559 } else if (extension &&
560 ( strcasecmp(extension, ".mid") == 0 || strcasecmp(extension, ".smf") == 0
561 || strcasecmp(extension, ".imy") == 0)) {
562 result = parseMidi(path, client);
563 } else if (extension && strcasecmp(extension, ".wma") == 0) {
564 result = parseWMA(path, client);
566 result = PVMFFailure;
574 static bool fileMatchesExtension(const char* path, const char* extensions) {
575 char* extension = strrchr(path, '.');
576 if (!extension) return false;
577 ++extension; // skip the dot
578 if (extension[0] == 0) return false;
580 while (extensions[0]) {
581 char* comma = strchr(extensions, ',');
582 size_t length = (comma ? comma - extensions : strlen(extensions));
583 if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true;
584 extensions += length;
585 if (extensions[0] == ',') ++extensions;
591 status_t MediaScanner::doProcessDirectory(char *path, int pathRemaining, const char* extensions,
592 MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)
594 // place to copy file or directory name
595 char* fileSpot = path + strlen(path);
596 struct dirent* entry;
598 // ignore directories that contain a ".nomedia" file
599 if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
600 strcpy(fileSpot, ".nomedia");
601 if (access(path, F_OK) == 0) {
602 LOGD("found .nomedia, skipping directory\n");
610 DIR* dir = opendir(path);
612 LOGD("opendir %s failed, errno: %d", path, errno);
616 while ((entry = readdir(dir))) {
617 const char* name = entry->d_name;
619 // ignore "." and ".."
620 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
624 int type = entry->d_type;
625 if (type == DT_REG || type == DT_DIR) {
626 int nameLength = strlen(name);
627 bool isDirectory = (type == DT_DIR);
629 if (nameLength > pathRemaining || (isDirectory && nameLength + 1 > pathRemaining)) {
634 strcpy(fileSpot, name);
636 // ignore directories with a name that starts with '.'
637 // for example, the Mac ".Trashes" directory
638 if (name[0] == '.') continue;
640 strcat(fileSpot, "/");
641 int err = doProcessDirectory(path, pathRemaining - nameLength - 1, extensions, client, exceptionCheck, exceptionEnv);
643 LOGE("Error processing '%s' - skipping\n", path);
646 } else if (fileMatchesExtension(path, extensions)) {
648 stat(path, &statbuf);
649 if (statbuf.st_size > 0) {
650 client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
652 if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
664 status_t MediaScanner::processDirectory(const char *path, const char* extensions,
665 MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)
667 InitializeForThread();
669 int pathLength = strlen(path);
670 if (pathLength >= PATH_MAX) {
673 char* pathBuffer = (char *)malloc(PATH_MAX + 1);
678 int pathRemaining = PATH_MAX - pathLength;
679 strcpy(pathBuffer, path);
680 if (pathBuffer[pathLength - 1] != '/') {
681 pathBuffer[pathLength] = '/';
682 pathBuffer[pathLength + 1] = 0;
686 client.setLocale(mLocale);
687 status_t result = doProcessDirectory(pathBuffer, pathRemaining, extensions, client, exceptionCheck, exceptionEnv);
693 void MediaScanner::setLocale(const char* locale)
700 mLocale = strdup(locale);
704 static char* doExtractAlbumArt(PvmfApicStruct* aApic)
706 char *data = (char*)malloc(aApic->iGraphicDataLen + 4);
708 long *len = (long*)data;
709 *len = aApic->iGraphicDataLen;
710 memcpy(data + 4, aApic->iGraphicData, *len);
715 static char* extractMP3AlbumArt(int fd)
717 PVID3ParCom pvId3Param;
719 OsclFileHandle *filehandle;
722 if(iFs.Connect() != 0)
724 LOGE("Connection with the file server for the parse id3 test failed.\n");
728 FILE *f = fdopen(fd, "r");
729 filehandle = new OsclFileHandle(f);
730 file.SetFileHandle(filehandle);
732 if( 0 != file.Open(NULL, Oscl_File::MODE_READ | Oscl_File::MODE_BINARY, iFs) )
734 LOGE("Could not open the input file for reading(Test: parse id3).\n");
738 file.Seek(0, Oscl_File::SEEKSET);
739 pvId3Param.ParseID3Tag(&file);
743 //Get the frames information from ID3 library
744 PvmiKvpSharedPtrVector framevector;
745 pvId3Param.GetID3Frames(framevector);
747 uint32 num_frames = framevector.size();
748 for (uint32 i = 0; i < num_frames; i++)
750 const char* key = framevector[i]->key;
752 // type should follow first semicolon
753 const char* type = strchr(key, ';') + 1;
754 if (type == 0) continue;
755 const char* value = framevector[i]->value.pChar_value;
756 const unsigned char* src = (const unsigned char *)value;
758 if (oscl_strncmp(key,KVP_KEY_ALBUMART,oscl_strlen(KVP_KEY_ALBUMART)) == 0)
760 PvmfApicStruct* aApic = (PvmfApicStruct*)framevector[i]->value.key_specific_value;
762 char* result = doExtractAlbumArt(aApic);
772 static char* extractM4AAlbumArt(int fd)
775 OsclFileHandle *filehandle;
779 if(iFs.Connect() != 0)
781 LOGE("Connection with the file server for the parse id3 test failed.\n");
785 FILE *f = fdopen(fd, "r");
786 filehandle = new OsclFileHandle(f);
787 file.SetFileHandle(filehandle);
789 oscl_wchar output[MAX_BUFF_SIZE];
790 oscl_UTF8ToUnicode("", 0, (oscl_wchar *)output, MAX_BUFF_SIZE);
791 OSCL_wHeapString<OsclMemAllocator> mpegfilename(output);
792 IMpeg4File *mp4Input = IMpeg4File::readMP4File(
793 mpegfilename, /* name */
794 NULL, /* plugin access interface factory */
796 0, /* parsing_mode */
802 PvmfApicStruct* aApic = mp4Input->getITunesImageData();
804 result = doExtractAlbumArt(aApic);
807 IMpeg4File::DestroyMP4FileObject(mp4Input);
812 char* MediaScanner::extractAlbumArt(int fd)
814 InitializeForThread();
817 lseek(fd, 4, SEEK_SET);
818 read(fd, &ident, sizeof(ident));
820 if (ident == 0x70797466) {
821 // some kind of mpeg 4 stream
822 lseek(fd, 0, SEEK_SET);
823 return extractM4AAlbumArt(fd);
826 return extractMP3AlbumArt(fd);
830 MediaScannerClient::MediaScannerClient()
833 mLocaleEncoding(kEncodingNone)
837 MediaScannerClient::~MediaScannerClient()
843 void MediaScannerClient::setLocale(const char* locale)
847 if (!strncmp(locale, "ja", 2))
848 mLocaleEncoding = kEncodingShiftJIS;
849 else if (!strncmp(locale, "ko", 2))
850 mLocaleEncoding = kEncodingEUCKR;
851 else if (!strncmp(locale, "zh", 2)) {
852 if (!strcmp(locale, "zh_CN")) {
853 // simplified chinese for mainland China
854 mLocaleEncoding = kEncodingGBK;
856 // assume traditional for non-mainland Chinese locales (Taiwan, Hong Kong, Singapore)
857 mLocaleEncoding = kEncodingBig5;
862 void MediaScannerClient::beginFile()
864 mNames = new StringArray;
865 mValues = new StringArray;
868 bool MediaScannerClient::addStringTag(const char* name, const char* value)
870 if (mLocaleEncoding != kEncodingNone) {
871 // don't bother caching strings that are all ASCII.
872 // call handleStringTag directly instead.
873 // check to see if value (which should be utf8) has any non-ASCII characters
874 bool nonAscii = false;
875 const char* chp = value;
877 while ((ch = *chp++)) {
885 // save the strings for later so they can be used for native encoding detection
886 mNames->push_back(name);
887 mValues->push_back(value);
893 // autodetection is not necessary, so no need to cache the values
894 // pass directly to the client instead
895 return handleStringTag(name, value);
898 static uint32_t possibleEncodings(const char* s)
900 uint32_t result = kEncodingAll;
901 // if s contains a native encoding, then it was mistakenly encoded in utf8 as if it were latin-1
902 // so we need to reverse the latin-1 -> utf8 conversion to get the native chars back
904 uint8* chp = (uint8 *)s;
906 while ((ch1 = *chp++)) {
909 ch1 = ((ch1 << 6) & 0xC0) | (ch2 & 0x3F);
910 // ch1 is now the first byte of the potential native char
914 ch2 = ((ch2 << 6) & 0xC0) | (*chp++ & 0x3F);
915 // ch2 is now the second byte of the potential native char
916 int ch = (int)ch1 << 8 | (int)ch2;
917 result &= findPossibleEncodings(ch);
919 // else ASCII character, which could be anything
925 void MediaScannerClient::convertValues(uint32_t encoding)
927 const char* enc = NULL;
929 case kEncodingShiftJIS:
944 UErrorCode status = U_ZERO_ERROR;
946 UConverter *conv = ucnv_open(enc, &status);
947 if (U_FAILURE(status)) {
948 LOGE("could not create UConverter for %s\n", enc);
951 UConverter *utf8Conv = ucnv_open("UTF-8", &status);
952 if (U_FAILURE(status)) {
953 LOGE("could not create UConverter for UTF-8\n");
958 // for each value string, convert from native encoding to UTF-8
959 for (int i = 0; i < mNames->size(); i++) {
960 // first we need to untangle the utf8 and convert it back to the original bytes
961 // since we are reducing the length of the string, we can do this in place
962 uint8* src = (uint8 *)mValues->getEntry(i);
963 int len = strlen((char *)src);
967 while ((uch = *src++)) {
969 *dest++ = ((uch << 6) & 0xC0) | (*src++ & 0x3F);
975 // now convert from native encoding to UTF-8
976 const char* source = mValues->getEntry(i);
977 int targetLength = len * 3 + 1;
978 char* buffer = new char[targetLength];
981 char* target = buffer;
983 ucnv_convertEx(utf8Conv, conv, &target, target + targetLength,
984 &source, (const char *)dest, NULL, NULL, NULL, NULL, TRUE, TRUE, &status);
985 if (U_FAILURE(status)) {
986 LOGE("ucnv_convertEx failed: %d\n", status);
987 mValues->setEntry(i, "???");
991 mValues->setEntry(i, buffer);
998 ucnv_close(utf8Conv);
1002 void MediaScannerClient::endFile()
1004 if (mLocaleEncoding != kEncodingNone) {
1005 int size = mNames->size();
1006 uint32_t encoding = kEncodingAll;
1008 // compute a bit mask containing all possible encodings
1009 for (int i = 0; i < mNames->size(); i++)
1010 encoding &= possibleEncodings(mValues->getEntry(i));
1012 // if the locale encoding matches, then assume we have a native encoding.
1013 if (encoding & mLocaleEncoding)
1014 convertValues(mLocaleEncoding);
1016 // finally, push all name/value pairs to the client
1017 for (int i = 0; i < mNames->size(); i++) {
1018 if (!handleStringTag(mNames->getEntry(i), mValues->getEntry(i)))
1022 // else addStringTag() has done all the work so we have nothing to do
1030 }; // namespace android