| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "VorbisComment" |
| #include <utils/Log.h> |
| |
| #include "media/VorbisComment.h" |
| |
| #include <media/stagefright/foundation/base64.h> |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/AString.h> |
| #include <media/stagefright/foundation/ByteUtils.h> |
| #include <media/stagefright/MetaDataBase.h> |
| |
| namespace android { |
| |
| static void extractAlbumArt( |
| MetaDataBase *fileMeta, const void *data, size_t size) { |
| ALOGV("extractAlbumArt from '%s'", (const char *)data); |
| |
| sp<ABuffer> flacBuffer = decodeBase64(AString((const char *)data, size)); |
| if (flacBuffer == NULL) { |
| ALOGE("malformed base64 encoded data."); |
| return; |
| } |
| |
| size_t flacSize = flacBuffer->size(); |
| uint8_t *flac = flacBuffer->data(); |
| ALOGV("got flac of size %zu", flacSize); |
| |
| uint32_t picType; |
| uint32_t typeLen; |
| uint32_t descLen; |
| uint32_t dataLen; |
| char type[128]; |
| |
| if (flacSize < 8) { |
| return; |
| } |
| |
| picType = U32_AT(flac); |
| |
| if (picType != 3) { |
| // This is not a front cover. |
| return; |
| } |
| |
| typeLen = U32_AT(&flac[4]); |
| if (typeLen > sizeof(type) - 1) { |
| return; |
| } |
| |
| // we've already checked above that flacSize >= 8 |
| if (flacSize - 8 < typeLen) { |
| return; |
| } |
| |
| memcpy(type, &flac[8], typeLen); |
| type[typeLen] = '\0'; |
| |
| ALOGV("picType = %d, type = '%s'", picType, type); |
| |
| if (!strcmp(type, "-->")) { |
| // This is not inline cover art, but an external url instead. |
| return; |
| } |
| |
| if (flacSize < 32 || flacSize - 32 < typeLen) { |
| return; |
| } |
| |
| descLen = U32_AT(&flac[8 + typeLen]); |
| if (flacSize - 32 - typeLen < descLen) { |
| return; |
| } |
| |
| dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]); |
| |
| // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0 |
| if (flacSize - 32 - typeLen - descLen < dataLen) { |
| return; |
| } |
| |
| ALOGV("got image data, %zu trailing bytes", |
| flacSize - 32 - typeLen - descLen - dataLen); |
| |
| fileMeta->setData( |
| kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen); |
| } |
| |
| void parseVorbisComment( |
| MetaDataBase *fileMeta, const char *comment, size_t commentLength) |
| { |
| struct { |
| const char *const mTag; |
| uint32_t mKey; |
| } kMap[] = { |
| { "TITLE", kKeyTitle }, |
| { "ARTIST", kKeyArtist }, |
| { "ALBUMARTIST", kKeyAlbumArtist }, |
| { "ALBUM ARTIST", kKeyAlbumArtist }, |
| { "COMPILATION", kKeyCompilation }, |
| { "ALBUM", kKeyAlbum }, |
| { "COMPOSER", kKeyComposer }, |
| { "GENRE", kKeyGenre }, |
| { "AUTHOR", kKeyAuthor }, |
| { "TRACKNUMBER", kKeyCDTrackNumber }, |
| { "DISCNUMBER", kKeyDiscNumber }, |
| { "DATE", kKeyDate }, |
| { "YEAR", kKeyYear }, |
| { "LYRICIST", kKeyWriter }, |
| { "METADATA_BLOCK_PICTURE", kKeyAlbumArt }, |
| { "ANDROID_LOOP", kKeyAutoLoop }, |
| }; |
| |
| for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) { |
| size_t tagLen = strlen(kMap[j].mTag); |
| if (!strncasecmp(kMap[j].mTag, comment, tagLen) |
| && comment[tagLen] == '=') { |
| if (kMap[j].mKey == kKeyAlbumArt) { |
| extractAlbumArt( |
| fileMeta, |
| &comment[tagLen + 1], |
| commentLength - tagLen - 1); |
| } else if (kMap[j].mKey == kKeyAutoLoop) { |
| if (!strcasecmp(&comment[tagLen + 1], "true")) { |
| fileMeta->setInt32(kKeyAutoLoop, true); |
| } |
| } else { |
| fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]); |
| } |
| } |
| } |
| |
| } |
| |
| } // namespace android |