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;
118 bool validUtf8 = true;
120 // type should follow first semicolon
121 const char* type = strchr(key, ';') + 1;
122 if (type == 0) continue;
125 const char* value = framevector[i]->value.pChar_value;
127 // KVP_VALTYPE_UTF8_CHAR check must be first, since KVP_VALTYPE_ISO88591_CHAR
128 // is a substring of KVP_VALTYPE_UTF8_CHAR.
129 // Similarly, KVP_VALTYPE_UTF16BE_WCHAR must be checked before KVP_VALTYPE_UTF16_WCHAR
130 if (oscl_strncmp(type, KVP_VALTYPE_UTF8_CHAR, KVP_VALTYPE_UTF8_CHAR_LEN) == 0) {
131 // utf8 can be passed through directly
132 // but first validate to make sure it is legal utf8
134 validUtf8 = oscl_str_is_valid_utf8((const uint8 *)value, valid_chars);
135 if (validUtf8 && !client.handleStringTag(key, value)) goto failure;
138 // if the value is not valid utf8, then we will treat it as iso-8859-1
139 // and our native encoding detection will try to figure out what it is
140 if (oscl_strncmp(type, KVP_VALTYPE_ISO88591_CHAR, KVP_VALTYPE_ISO88591_CHAR_LEN) == 0
144 // worse case is 2x inflation
145 const unsigned char* src = (const unsigned char *)value;
146 char* temp = (char *)alloca(strlen(value) * 2 + 1);
150 while ((uch = *src++) != 0) {
152 *dest++ = (uch >> 6) | 0xc0;
153 *dest++ = (uch & 0x3f) | 0x80;
154 } else *dest++ = uch;
157 if (!client.addStringTag(key, temp)) goto failure;
159 } else if (oscl_strncmp(type, KVP_VALTYPE_UTF16BE_WCHAR, KVP_VALTYPE_UTF16BE_WCHAR_LEN) == 0 ||
160 oscl_strncmp(type, KVP_VALTYPE_UTF16_WCHAR, KVP_VALTYPE_UTF16_WCHAR_LEN) == 0) {
161 // convert wchar to utf8
162 // the id3parcom library has already taken care of byteswapping
163 const oscl_wchar* src = framevector[i]->value.pWChar_value;
164 int srcLen = oscl_strlen(src);
165 // worse case is 3 bytes per character, plus zero termination
166 int destLen = srcLen * 3 + 1;
167 char* dest = (char *)alloca(destLen);
169 if (oscl_UnicodeToUTF8(src, oscl_strlen(src), dest, destLen) > 0) {
170 if (!client.addStringTag(key, dest)) goto failure;
172 } else if (oscl_strncmp(type, KVP_VALTYPE_UINT32, KVP_VALTYPE_UINT32_LEN) == 0) {
174 snprintf(temp, sizeof(temp), "%d", (int)framevector[i]->value.uint32_value);
175 if (!client.addStringTag(key, temp)) goto failure;
177 //LOGE("unknown tag type %s for key %s\n", type, key);
181 // extract non-ID3 properties below
183 OSCL_wHeapString<OsclMemAllocator> mp3filename(output);
185 IMpeg3File mp3File(mp3filename, err);
186 if (err != MP3_SUCCESS) {
187 LOGE("IMpeg3File constructor returned %d.\n", err);
190 err = mp3File.ParseMp3File();
191 if (err != MP3_SUCCESS) {
192 LOGE("IMpeg3File::ParseMp3File returned %d.\n", err);
197 duration = mp3File.GetDuration();
198 sprintf(buffer, "%d", duration);
199 if (!client.addStringTag("duration", buffer)) goto failure;
208 static PVMFStatus reportM4ATags(IMpeg4File *mp4Input, MediaScannerClient& client)
211 OSCL_wHeapString<OsclMemAllocator> valuestring=NULL;
212 MP4FFParserOriginalCharEnc charType = ORIGINAL_CHAR_TYPE_UNKNOWN;
220 char buffer[MAX_STR_LEN];
224 for (i = 0; i < mp4Input->getNumTitle(); ++i)
226 mp4Input->getTitle(i,valuestring,iLangCode,charType);
227 if (oscl_UnicodeToUTF8(valuestring.get_cstr(),valuestring.get_size(),
228 buffer,sizeof(buffer)) > 0)
230 if (!client.addStringTag("title", buffer)) goto failure;
236 for (i = 0; i < mp4Input->getNumArtist(); ++i)
238 mp4Input->getArtist(i,valuestring,iLangCode,charType);
239 if (oscl_UnicodeToUTF8(valuestring.get_cstr(),valuestring.get_size(),
240 buffer,sizeof(buffer)) > 0)
242 if (!client.addStringTag("artist", buffer)) goto failure;
248 for (i = 0; i < mp4Input->getNumAlbum(); ++i)
250 mp4Input->getAlbum(i,valuestring,iLangCode,charType);
251 if (oscl_UnicodeToUTF8(valuestring.get_cstr(),valuestring.get_size(),
252 buffer,sizeof(buffer)) > 0)
254 if (!client.addStringTag("album", buffer)) goto failure;
261 for (i = 0; i < mp4Input->getNumYear(); ++i)
263 mp4Input->getYear(i,val);
264 sprintf(buffer, "%d", val);
267 if (!client.addStringTag("year", buffer)) goto failure;
273 if (oscl_UnicodeToUTF8(mp4Input->getITunesWriter().get_cstr(),
274 mp4Input->getITunesWriter().get_size(),buffer,sizeof(buffer)) > 0)
275 if (!client.addStringTag("composer", buffer)) goto failure;
278 trackNum = mp4Input->getITunesThisTrackNo();
279 totalTracks = mp4Input->getITunesTotalTracks();
280 sprintf(buffer, "%d/%d", trackNum, totalTracks);
281 if (!client.addStringTag("tracknumber", buffer)) goto failure;
284 duration = mp4Input->getMovieDuration();
285 timeScale = mp4Input->getMovieTimescale();
286 // adjust duration to milliseconds if necessary
287 if (timeScale != 1000)
288 duration = (duration * 1000) / timeScale;
289 sprintf(buffer, "%lld", duration);
290 if (!client.addStringTag("duration", buffer)) goto failure;
294 for(i=0; i<mp4Input->getNumGenre(); i++)
296 mp4Input->getGenre(i,valuestring,iLangCode,charType);
297 if (oscl_UnicodeToUTF8(valuestring.get_cstr(),valuestring.get_size(), buffer,sizeof(buffer)) > 0)
301 if (!client.addStringTag("genre", buffer)) goto failure;
303 uint16 id = mp4Input->getITunesGnreID();
305 sprintf(buffer, "(%d)", id - 1);
306 if (!client.addStringTag("genre", buffer)) goto failure;
316 static PVMFStatus parseMP4(const char *filename, MediaScannerClient& client)
321 if (iFs.Connect() != 0)
323 LOGE("Connection with the file server for the parse id3 test failed.\n");
327 oscl_wchar output[MAX_BUFF_SIZE];
328 oscl_UTF8ToUnicode((const char *)filename, oscl_strlen((const char *)filename), (oscl_wchar *)output, MAX_BUFF_SIZE);
329 OSCL_wHeapString<OsclMemAllocator> mpegfilename(output);
331 IMpeg4File *mp4Input = IMpeg4File::readMP4File(mpegfilename, NULL, NULL, 1 /* parsing_mode */, &iFs);
334 // check to see if the file contains video
335 int32 count = mp4Input->getNumTracks();
336 uint32* tracks = new uint32[count];
337 bool hasAudio = false;
338 bool hasVideo = false;
340 mp4Input->getTrackIDList(tracks, count);
341 for (int i = 0; i < count; ++i) {
342 uint32 trackType = mp4Input->getTrackMediaType(tracks[i]);
343 OSCL_HeapString<OsclMemAllocator> streamtype;
344 mp4Input->getTrackMIMEType(tracks[i], streamtype);
345 char streamtypeutf8[128];
346 strncpy (streamtypeutf8, streamtype.get_str(), streamtype.get_size());
347 if (streamtypeutf8[0])
349 if (strcmp(streamtypeutf8,"FORMATUNKNOWN") != 0) {
350 if (trackType == MEDIA_TYPE_AUDIO) {
352 } else if (trackType == MEDIA_TYPE_VISUAL) {
356 //LOGI("@@@@@@@@ %100s: %s\n", filename, streamtypeutf8);
365 if (!client.setMimeType("video/mp4")) return PVMFFailure;
366 } else if (hasAudio) {
367 if (!client.setMimeType("audio/mp4")) return PVMFFailure;
370 IMpeg4File::DestroyMP4FileObject(mp4Input);
374 PVMFStatus result = reportM4ATags(mp4Input, client);
376 IMpeg4File::DestroyMP4FileObject(mp4Input);
383 static PVMFStatus parseOgg(const char *filename, MediaScannerClient& client)
387 FILE *file = fopen(filename,"r");
392 if (ov_open(file, &vf, NULL, 0) < 0) {
396 char **ptr=ov_comment(&vf,-1)->user_comments;
398 char *val = strstr(*ptr, "=");
400 int keylen = val++ - *ptr;
401 char key[keylen + 1];
402 strncpy(key, *ptr, keylen);
404 if (!client.addStringTag(key, val)) goto failure;
410 duration = ov_time_total(&vf, -1);
413 sprintf(buffer, "%d", duration);
414 if (!client.addStringTag("duration", buffer)) goto failure;
417 ov_clear(&vf); // this also closes the FILE
421 ov_clear(&vf); // this also closes the FILE
425 static PVMFStatus parseMidi(const char *filename, MediaScannerClient& client) {
427 // get the library configuration and do sanity check
428 const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config();
429 if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
430 LOGE("EAS library/header mismatch\n");
435 // spin up a new EAS engine
436 EAS_DATA_HANDLE easData = NULL;
437 EAS_HANDLE easHandle = NULL;
438 EAS_RESULT result = EAS_Init(&easData);
439 if (result == EAS_SUCCESS) {
441 file.path = filename;
445 result = EAS_OpenFile(easData, &file, &easHandle, NULL);
447 if (result == EAS_SUCCESS) {
448 result = EAS_Prepare(easData, easHandle);
450 if (result == EAS_SUCCESS) {
451 result = EAS_ParseMetaData(easData, easHandle, &temp);
454 EAS_CloseFile(easData, easHandle);
457 EAS_Shutdown(easData);
460 if (result != EAS_SUCCESS) {
465 sprintf(buffer, "%ld", temp);
466 if (!client.addStringTag("duration", buffer)) return PVMFFailure;
470 static PVMFStatus parseWMA(const char *filename, MediaScannerClient& client)
472 sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever();
473 retriever->setMode( 1 /*MediaMetadataRetriever.MODE_GET_METADATA_ONLY*/);
474 status_t status = retriever->setDataSource(filename);
475 if (status != NO_ERROR) {
476 LOGE("parseWMA setDataSource failed (%d)", status);
477 retriever->disconnect();
483 value = retriever->extractMetadata(METADATA_KEY_IS_DRM_CRIPPLED);
484 if (value && strcmp(value, "true") == 0) {
485 // we don't support WMDRM currently
486 // setting this invalid mimetype will make the java side ignore this file
487 client.setMimeType("audio/x-wma-drm");
489 value = retriever->extractMetadata(METADATA_KEY_CODEC);
490 if (value && strcmp(value, "Windows Media Audio 10 Professional") == 0) {
491 // we don't support WM 10 Professional currently
492 // setting this invalid mimetype will make the java side ignore this file
493 client.setMimeType("audio/x-wma-10-professional");
496 value = retriever->extractMetadata(METADATA_KEY_ALBUM);
498 client.addStringTag("album", value);
500 // Look for "author" tag first, if it is not found, try "artist" tag
501 value = retriever->extractMetadata(METADATA_KEY_AUTHOR);
503 value = retriever->extractMetadata(METADATA_KEY_ARTIST);
506 client.addStringTag("artist", value);
507 value = retriever->extractMetadata(METADATA_KEY_COMPOSER);
509 client.addStringTag("composer", value);
510 value = retriever->extractMetadata(METADATA_KEY_GENRE);
512 client.addStringTag("genre", value);
513 value = retriever->extractMetadata(METADATA_KEY_TITLE);
515 client.addStringTag("title", value);
516 value = retriever->extractMetadata(METADATA_KEY_YEAR);
518 client.addStringTag("year", value);
519 value = retriever->extractMetadata(METADATA_KEY_CD_TRACK_NUMBER);
521 client.addStringTag("tracknumber", value);
523 retriever->disconnect();
527 status_t MediaScanner::processFile(const char *path, const char* mimeType, MediaScannerClient& client)
530 InitializeForThread();
532 client.setLocale(mLocale);
535 //LOGD("processFile %s mimeType: %s\n", path, mimeType);
536 const char* extension = strrchr(path, '.');
538 if (extension && strcasecmp(extension, ".mp3") == 0) {
539 result = parseMP3(path, client);
540 } else if (extension &&
541 (strcasecmp(extension, ".mp4") == 0 || strcasecmp(extension, ".m4a") == 0 ||
542 strcasecmp(extension, ".3gp") == 0 || strcasecmp(extension, ".3gpp") == 0 ||
543 strcasecmp(extension, ".3g2") == 0 || strcasecmp(extension, ".3gpp2") == 0)) {
544 result = parseMP4(path, client);
545 } else if (extension && strcasecmp(extension, ".ogg") == 0) {
546 result = parseOgg(path, client);
547 } else if (extension &&
548 ( strcasecmp(extension, ".mid") == 0 || strcasecmp(extension, ".smf") == 0
549 || strcasecmp(extension, ".imy") == 0)) {
550 result = parseMidi(path, client);
551 } else if (extension && strcasecmp(extension, ".wma") == 0) {
552 result = parseWMA(path, client);
554 result = PVMFFailure;
562 static bool fileMatchesExtension(const char* path, const char* extensions) {
563 char* extension = strrchr(path, '.');
564 if (!extension) return false;
565 ++extension; // skip the dot
566 if (extension[0] == 0) return false;
568 while (extensions[0]) {
569 char* comma = strchr(extensions, ',');
570 int length = (comma ? comma - extensions : strlen(extensions));
571 if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true;
572 extensions += length;
573 if (extensions[0] == ',') ++extensions;
579 status_t MediaScanner::doProcessDirectory(char *path, int pathRemaining, const char* extensions,
580 MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)
582 // place to copy file or directory name
583 char* fileSpot = path + strlen(path);
584 struct dirent* entry;
586 // ignore directories that contain a ".nomedia" file
587 if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
588 strcpy(fileSpot, ".nomedia");
589 if (access(path, F_OK) == 0) {
590 LOGD("found .nomedia, skipping directory\n");
598 DIR* dir = opendir(path);
600 LOGD("opendir %s failed, errno: %d", path, errno);
604 while ((entry = readdir(dir))) {
605 const char* name = entry->d_name;
607 // ignore "." and ".."
608 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
612 int type = entry->d_type;
613 if (type == DT_REG || type == DT_DIR) {
614 int nameLength = strlen(name);
615 bool isDirectory = (type == DT_DIR);
617 if (nameLength > pathRemaining || (isDirectory && nameLength + 1 > pathRemaining)) {
622 strcpy(fileSpot, name);
624 // ignore directories with a name that starts with '.'
625 // for example, the Mac ".Trashes" directory
626 if (name[0] == '.') continue;
628 strcat(fileSpot, "/");
629 int err = doProcessDirectory(path, pathRemaining - nameLength - 1, extensions, client, exceptionCheck, exceptionEnv);
630 if (err) goto failure;
631 } else if (fileMatchesExtension(path, extensions)) {
633 stat(path, &statbuf);
634 if (statbuf.st_size > 0) {
635 client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
637 if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
649 status_t MediaScanner::processDirectory(const char *path, const char* extensions,
650 MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)
652 InitializeForThread();
654 int pathLength = strlen(path);
655 if (pathLength >= PATH_MAX) {
658 char* pathBuffer = (char *)malloc(PATH_MAX + 1);
663 int pathRemaining = PATH_MAX - pathLength;
664 strcpy(pathBuffer, path);
665 if (pathBuffer[pathLength - 1] != '/') {
666 pathBuffer[pathLength] = '/';
667 pathBuffer[pathLength + 1] = 0;
671 client.setLocale(mLocale);
672 status_t result = doProcessDirectory(pathBuffer, pathRemaining, extensions, client, exceptionCheck, exceptionEnv);
678 void MediaScanner::setLocale(const char* locale)
685 mLocale = strdup(locale);
689 static char* doExtractAlbumArt(PvmfApicStruct* aApic)
691 char *data = (char*)malloc(aApic->iGraphicDataLen + 4);
693 long *len = (long*)data;
694 *len = aApic->iGraphicDataLen;
695 memcpy(data + 4, aApic->iGraphicData, *len);
700 static char* extractMP3AlbumArt(int fd)
702 PVID3ParCom pvId3Param;
704 OsclFileHandle *filehandle;
707 if(iFs.Connect() != 0)
709 LOGE("Connection with the file server for the parse id3 test failed.\n");
713 FILE *f = fdopen(fd, "r");
714 filehandle = new OsclFileHandle(f);
715 file.SetFileHandle(filehandle);
717 if( 0 != file.Open(NULL, Oscl_File::MODE_READ | Oscl_File::MODE_BINARY, iFs) )
719 LOGE("Could not open the input file for reading(Test: parse id3).\n");
723 file.Seek(0, Oscl_File::SEEKSET);
724 pvId3Param.ParseID3Tag(&file);
728 //Get the frames information from ID3 library
729 PvmiKvpSharedPtrVector framevector;
730 pvId3Param.GetID3Frames(framevector);
732 uint32 num_frames = framevector.size();
733 for (uint32 i = 0; i < num_frames; i++)
735 const char* key = framevector[i]->key;
737 // type should follow first semicolon
738 const char* type = strchr(key, ';') + 1;
739 if (type == 0) continue;
740 const char* value = framevector[i]->value.pChar_value;
741 const unsigned char* src = (const unsigned char *)value;
743 if (oscl_strncmp(key,KVP_KEY_ALBUMART,oscl_strlen(KVP_KEY_ALBUMART)) == 0)
745 PvmfApicStruct* aApic = (PvmfApicStruct*)framevector[i]->value.key_specific_value;
747 char* result = doExtractAlbumArt(aApic);
757 static char* extractM4AAlbumArt(int fd)
760 OsclFileHandle *filehandle;
764 if(iFs.Connect() != 0)
766 LOGE("Connection with the file server for the parse id3 test failed.\n");
770 FILE *f = fdopen(fd, "r");
771 filehandle = new OsclFileHandle(f);
772 file.SetFileHandle(filehandle);
774 oscl_wchar output[MAX_BUFF_SIZE];
775 oscl_UTF8ToUnicode("", 0, (oscl_wchar *)output, MAX_BUFF_SIZE);
776 OSCL_wHeapString<OsclMemAllocator> mpegfilename(output);
777 IMpeg4File *mp4Input = IMpeg4File::readMP4File(
778 mpegfilename, /* name */
779 NULL, /* plugin access interface factory */
781 0, /* parsing_mode */
787 PvmfApicStruct* aApic = mp4Input->getITunesImageData();
789 result = doExtractAlbumArt(aApic);
792 IMpeg4File::DestroyMP4FileObject(mp4Input);
797 char* MediaScanner::extractAlbumArt(int fd)
799 InitializeForThread();
802 lseek(fd, 4, SEEK_SET);
803 read(fd, &ident, sizeof(ident));
805 if (ident == 0x70797466) {
806 // some kind of mpeg 4 stream
807 lseek(fd, 0, SEEK_SET);
808 return extractM4AAlbumArt(fd);
811 return extractMP3AlbumArt(fd);
815 MediaScannerClient::MediaScannerClient()
818 mLocaleEncoding(kEncodingNone)
822 MediaScannerClient::~MediaScannerClient()
828 void MediaScannerClient::setLocale(const char* locale)
832 if (!strncmp(locale, "ja", 2))
833 mLocaleEncoding = kEncodingShiftJIS;
834 else if (!strncmp(locale, "ko", 2))
835 mLocaleEncoding = kEncodingEUCKR;
836 else if (!strncmp(locale, "zh", 2)) {
837 if (!strcmp(locale, "zh_CN")) {
838 // simplified chinese for mainland China
839 mLocaleEncoding = kEncodingGBK;
841 // assume traditional for non-mainland Chinese locales (Taiwan, Hong Kong, Singapore)
842 mLocaleEncoding = kEncodingBig5;
847 void MediaScannerClient::beginFile()
849 mNames = new StringArray;
850 mValues = new StringArray;
853 bool MediaScannerClient::addStringTag(const char* name, const char* value)
855 if (mLocaleEncoding != kEncodingNone) {
856 // don't bother caching strings that are all ASCII.
857 // call handleStringTag directly instead.
858 // check to see if value (which should be utf8) has any non-ASCII characters
859 bool nonAscii = false;
860 const char* chp = value;
862 while ((ch = *chp++)) {
870 // save the strings for later so they can be used for native encoding detection
871 mNames->push_back(name);
872 mValues->push_back(value);
878 // autodetection is not necessary, so no need to cache the values
879 // pass directly to the client instead
880 return handleStringTag(name, value);
883 static uint32_t possibleEncodings(const char* s)
885 uint32_t result = kEncodingAll;
886 // if s contains a native encoding, then it was mistakenly encoded in utf8 as if it were latin-1
887 // so we need to reverse the latin-1 -> utf8 conversion to get the native chars back
889 uint8* chp = (uint8 *)s;
891 while ((ch1 = *chp++)) {
894 ch1 = ((ch1 << 6) & 0xC0) | (ch2 & 0x3F);
895 // ch1 is now the first byte of the potential native char
899 ch2 = ((ch2 << 6) & 0xC0) | (*chp++ & 0x3F);
900 // ch2 is now the second byte of the potential native char
901 int ch = (int)ch1 << 8 | (int)ch2;
902 result &= findPossibleEncodings(ch);
904 // else ASCII character, which could be anything
910 void MediaScannerClient::convertValues(uint32_t encoding)
912 const char* enc = NULL;
914 case kEncodingShiftJIS:
929 UErrorCode status = U_ZERO_ERROR;
931 UConverter *conv = ucnv_open(enc, &status);
932 if (U_FAILURE(status)) {
933 LOGE("could not create UConverter for %s\n", enc);
936 UConverter *utf8Conv = ucnv_open("UTF-8", &status);
937 if (U_FAILURE(status)) {
938 LOGE("could not create UConverter for UTF-8\n");
943 // for each value string, convert from native encoding to UTF-8
944 for (int i = 0; i < mNames->size(); i++) {
945 // first we need to untangle the utf8 and convert it back to the original bytes
946 // since we are reducing the length of the string, we can do this in place
947 uint8* src = (uint8 *)mValues->getEntry(i);
948 int len = strlen((char *)src);
952 while ((uch = *src++)) {
954 *dest++ = ((uch << 6) & 0xC0) | (*src++ & 0x3F);
960 // now convert from native encoding to UTF-8
961 const char* source = mValues->getEntry(i);
962 int targetLength = len * 3 + 1;
963 char* buffer = new char[targetLength];
966 char* target = buffer;
968 ucnv_convertEx(utf8Conv, conv, &target, target + targetLength,
969 &source, (const char *)dest, NULL, NULL, NULL, NULL, TRUE, TRUE, &status);
970 if (U_FAILURE(status)) {
971 LOGE("ucnv_convertEx failed: %d\n", status);
972 mValues->setEntry(i, "???");
976 mValues->setEntry(i, buffer);
983 ucnv_close(utf8Conv);
987 void MediaScannerClient::endFile()
989 if (mLocaleEncoding != kEncodingNone) {
990 int size = mNames->size();
991 uint32_t encoding = kEncodingAll;
993 // compute a bit mask containing all possible encodings
994 for (int i = 0; i < mNames->size(); i++)
995 encoding &= possibleEncodings(mValues->getEntry(i));
997 // if the locale encoding matches, then assume we have a native encoding.
998 if (encoding & mLocaleEncoding)
999 convertValues(mLocaleEncoding);
1001 // finally, push all name/value pairs to the client
1002 for (int i = 0; i < mNames->size(); i++) {
1003 if (!handleStringTag(mNames->getEntry(i), mValues->getEntry(i)))
1007 // else addStringTag() has done all the work so we have nothing to do
1015 }; // namespace android