안드로이드 개발 정보
(글 수 1,069)
안드로이드에서 MP3 파일의 id3tag 가 깨지는 이유를 찾아보았습니다.
http://www.androidpub.com/471907
framework의 버그네요.
froyo 도 그대로 버그가 있구요.
그래서 모토로이 확인해 보았더니, 깨지는 파일들 다 잘 나오네요. ㅡ.ㅡ
음 Open Source 라서 가져다 자기들은 고쳐쓰고,
버그 리포팅은 하지 않나 보네요.
우리나라 대기업도 그렇겠지요.
ID3Tag 깨지는 원인은 mediascanner 의 버그 인데요.
void MediaScannerClient::endFile()
{
if (mLocaleEncoding != kEncodingNone) {
int size = mNames->size();
uint32_t encoding = kEncodingAll;
// compute a bit mask containing all possible encodings
for (int i = 0; i < mNames->size(); i++)
{
encoding &= possibleEncodings(mValues->getEntry(i));
}
// if the locale encoding matches, then assume we have a native encoding.
if (encoding & mLocaleEncoding)
convertValues(mLocaleEncoding);
// finally, push all name/value pairs to the client
for (int i = 0; i < mNames->size(); i++) {
if (!handleStringTag(mNames->getEntry(i), mValues->getEntry(i)))
break;
}
}
// else addStringTag() has done all the work so we have nothing to do
delete mNames;
delete mValues;
mNames = NULL;
mValues = NULL;
}
위 소스가 버그가 있는 곳입니다.
MediaScanner 소스를 보면, ID3tag를 파싱하면 UTF-8 이 아닌것들을 mNames가 mValues에 넣어 두었다가
endFile() 함수에서 locale 인코딩이 필요한지를 판별하여 인코딩을 하고 있답니다.
그런데,
uint32_t encoding = kEncodingAll;
// compute a bit mask containing all possible encodings
for (int i = 0; i < mNames->size(); i++)
{
encoding &= possibleEncodings(mValues->getEntry(i));
}
위 코드에서 보면, 우선 전부 인코딩이 가능한 상태로 encoding변수를 넣어두고,
StringArray 에 들어간 각 값들이 인코딩이 가능한 것인지 확인한 후,
인코딩이 가능한 경우라면,
if (encoding & mLocaleEncoding)
convertValues(mLocaleEncoding);
이와 같이 전부 locale 인코딩을 한답니다.
이게 버그인데요.
예들 들어서, ID3Tag 값중에서 Title 과 Artist는 한글이 있고, Album 에는 영문만 있다거나 혹은 인코딩이 필요없는 값만 있다면,
첫번째 for 문을 돌면서, encoding 값이 0로 바뀌게 되고,
그럼, 아래 if 문에서 locale 인코딩을 그냥 건너 뛰게 된답니다.
그러므로, 이 버그의 수정을 위해서는 possibleEncodings() 함수 호출을 mValues 의 각 값에 따라 개별적으로
비교하여 locale 인코딩을 진행하면,
깨지는 것 없이 잘 나오게 된답니다.
수고하세요!
http://www.androidpub.com/471907
framework의 버그네요.
froyo 도 그대로 버그가 있구요.
그래서 모토로이 확인해 보았더니, 깨지는 파일들 다 잘 나오네요. ㅡ.ㅡ
음 Open Source 라서 가져다 자기들은 고쳐쓰고,
버그 리포팅은 하지 않나 보네요.
우리나라 대기업도 그렇겠지요.
ID3Tag 깨지는 원인은 mediascanner 의 버그 인데요.
void MediaScannerClient::endFile()
{
if (mLocaleEncoding != kEncodingNone) {
int size = mNames->size();
uint32_t encoding = kEncodingAll;
// compute a bit mask containing all possible encodings
for (int i = 0; i < mNames->size(); i++)
{
encoding &= possibleEncodings(mValues->getEntry(i));
}
// if the locale encoding matches, then assume we have a native encoding.
if (encoding & mLocaleEncoding)
convertValues(mLocaleEncoding);
// finally, push all name/value pairs to the client
for (int i = 0; i < mNames->size(); i++) {
if (!handleStringTag(mNames->getEntry(i), mValues->getEntry(i)))
break;
}
}
// else addStringTag() has done all the work so we have nothing to do
delete mNames;
delete mValues;
mNames = NULL;
mValues = NULL;
}
위 소스가 버그가 있는 곳입니다.
MediaScanner 소스를 보면, ID3tag를 파싱하면 UTF-8 이 아닌것들을 mNames가 mValues에 넣어 두었다가
endFile() 함수에서 locale 인코딩이 필요한지를 판별하여 인코딩을 하고 있답니다.
그런데,
uint32_t encoding = kEncodingAll;
// compute a bit mask containing all possible encodings
for (int i = 0; i < mNames->size(); i++)
{
encoding &= possibleEncodings(mValues->getEntry(i));
}
위 코드에서 보면, 우선 전부 인코딩이 가능한 상태로 encoding변수를 넣어두고,
StringArray 에 들어간 각 값들이 인코딩이 가능한 것인지 확인한 후,
인코딩이 가능한 경우라면,
if (encoding & mLocaleEncoding)
convertValues(mLocaleEncoding);
이와 같이 전부 locale 인코딩을 한답니다.
이게 버그인데요.
예들 들어서, ID3Tag 값중에서 Title 과 Artist는 한글이 있고, Album 에는 영문만 있다거나 혹은 인코딩이 필요없는 값만 있다면,
첫번째 for 문을 돌면서, encoding 값이 0로 바뀌게 되고,
그럼, 아래 if 문에서 locale 인코딩을 그냥 건너 뛰게 된답니다.
그러므로, 이 버그의 수정을 위해서는 possibleEncodings() 함수 호출을 mValues 의 각 값에 따라 개별적으로
비교하여 locale 인코딩을 진행하면,
깨지는 것 없이 잘 나오게 된답니다.
수고하세요!
2010.06.29 14:00:08
possibleEncodings에서 All Ascii 케이스의 경우 result가 최초값인 kEncodingAll이 되어 encoding값은 계속 유지됩니다. (Froyo 기준)
단순히 convertValues의 버그 아닐까요?
convertValues 수정하신 부분 공개가능하신지요.
2010.06.29 18:00:37
extern uint32_t findPossibleEncodings(int ch)
{
// ASCII matches everything
if (ch < 256) return kEncodingAll;
int result = kEncodingNone;
if (charMatchesEncoding(ch, kShiftJISRanges, ARRAY_SIZE(kShiftJISRanges)))
result |= kEncodingShiftJIS;
if (charMatchesEncoding(ch, kGBKRanges, ARRAY_SIZE(kGBKRanges)))
result |= kEncodingGBK;
if (charMatchesEncoding(ch, kBig5Ranges, ARRAY_SIZE(kBig5Ranges)))
result |= kEncodingBig5;
if (charMatchesEncoding(ch, kEUCKRRanges, ARRAY_SIZE(kEUCKRRanges)))
result |= kEncodingEUCKR;
return result;
}
이 부분 말씀 하신거 같은데요. 이 부분은 이전 소스도 똑같습니다.
전부 ascii 코드 인 경우는 아니겠네요.
한글이 깨지는 경우 보면, possibleEncodings 에서 리턴되는값이 0가 되더라구요.
convertValues 의 버그가 아니라 possibleEncodings의 비교하는 위치의 문제 라고 봐야 할듯합니다.
원래 코드가
전부 변환 할 수 있냐?
if ( 있다 )
변환해라
이렇게 되어 있는 거죠.
그럼 위 경우에서, 일부만 변환할 수 있는 경우는 ? (변환하지 않죠 --> 즉 깨진대로 그냥 보입니다 )
convertValues 내에서 possibleEncodings 를 확인하도록 위치를 옮겼답니다.
원래 위치에 있는 것을 날려 버리구요.
{
// ASCII matches everything
if (ch < 256) return kEncodingAll;
int result = kEncodingNone;
if (charMatchesEncoding(ch, kShiftJISRanges, ARRAY_SIZE(kShiftJISRanges)))
result |= kEncodingShiftJIS;
if (charMatchesEncoding(ch, kGBKRanges, ARRAY_SIZE(kGBKRanges)))
result |= kEncodingGBK;
if (charMatchesEncoding(ch, kBig5Ranges, ARRAY_SIZE(kBig5Ranges)))
result |= kEncodingBig5;
if (charMatchesEncoding(ch, kEUCKRRanges, ARRAY_SIZE(kEUCKRRanges)))
result |= kEncodingEUCKR;
return result;
}
이 부분 말씀 하신거 같은데요. 이 부분은 이전 소스도 똑같습니다.
전부 ascii 코드 인 경우는 아니겠네요.
한글이 깨지는 경우 보면, possibleEncodings 에서 리턴되는값이 0가 되더라구요.
convertValues 의 버그가 아니라 possibleEncodings의 비교하는 위치의 문제 라고 봐야 할듯합니다.
원래 코드가
전부 변환 할 수 있냐?
if ( 있다 )
변환해라
이렇게 되어 있는 거죠.
그럼 위 경우에서, 일부만 변환할 수 있는 경우는 ? (변환하지 않죠 --> 즉 깨진대로 그냥 보입니다 )
convertValues 내에서 possibleEncodings 를 확인하도록 위치를 옮겼답니다.
원래 위치에 있는 것을 날려 버리구요.
2010.06.30 11:12:09
0이 나온다면 CP949에 포함되지 않는 문자가 존재한다는 경우인데(한국어 존재 여부를 체크하는 함수가 Microsoft Windows cp949를 기준으로 데이터를 생성하였습니다.) 정상적인 문자가 처리되지 못한다면 그 부분을 수정해야 할 듯 합니다. autodetect.cpp에서 kEUCKRRanges에 포함되지 않는 문자를 찾아봐야 할듯 합니다.
한국만 본다면(그 기계가 여러 언어권에서 사용될 확률이 낮다면) 저런 수정이 쉽게 문제를 해결 할 수 있겠지만 여러 문자셋이 가능한 상황이라면 기존 접근이 안전한 방법일 수도 있겠습니다.
possibleEncodings의 리턴값이 kEncodingNone이 되는 경우의 Value값을 덤프해 보면 문제를 찾을 수 있지 않을가 하는데요.
참고로 AddStringTag에서 non-ASCII일때만 따로 처리합니다.
2010.12.28 20:06:10
CyanogenMod6.x 에서도 같은 문제가 나오길래 찾아보니 볼래노님의 다 분석해 두셨네요. 감사합니다!
설명하신 대로 (그리고 쬐끔 추가하여) 패치를 만들었습니다.
possibleEncodings는 native encoding의 특성상 한가지 값으로 딱! 하고 나오기 힘듭니다. 본래 소스에는 태그 전체의 possibleEncoding을 and 해서 범위를 좁히려는 시도를 하고 있는데.. 결국에는 mLocaleEncoding 이랑 다시 and를 해 버리기 때문에 의미 없는 행동으로 보이네요.
CyanogenMod 에는 제가 PullRequest 해 놓겠습니다.. 흘러 흘러 메인스트림에도 들어가길 바랍니다. :)



