1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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 express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "VorbisComment"
19 #include <utils/Log.h>
20
21 #include "media/VorbisComment.h"
22
23 #include <media/stagefright/foundation/base64.h>
24 #include <media/stagefright/foundation/ABuffer.h>
25 #include <media/stagefright/foundation/AString.h>
26 #include <media/stagefright/foundation/ByteUtils.h>
27 #include <media/stagefright/MetaDataBase.h>
28
29 namespace android {
30
extractAlbumArt(MetaDataBase * fileMeta,const void * data,size_t size)31 static void extractAlbumArt(
32 MetaDataBase *fileMeta, const void *data, size_t size) {
33 ALOGV("extractAlbumArt from '%s'", (const char *)data);
34
35 sp<ABuffer> flacBuffer = decodeBase64(AString((const char *)data, size));
36 if (flacBuffer == NULL) {
37 ALOGE("malformed base64 encoded data.");
38 return;
39 }
40
41 size_t flacSize = flacBuffer->size();
42 uint8_t *flac = flacBuffer->data();
43 ALOGV("got flac of size %zu", flacSize);
44
45 uint32_t picType;
46 uint32_t typeLen;
47 uint32_t descLen;
48 uint32_t dataLen;
49 char type[128];
50
51 if (flacSize < 8) {
52 return;
53 }
54
55 picType = U32_AT(flac);
56
57 if (picType != 3) {
58 // This is not a front cover.
59 return;
60 }
61
62 typeLen = U32_AT(&flac[4]);
63 if (typeLen > sizeof(type) - 1) {
64 return;
65 }
66
67 // we've already checked above that flacSize >= 8
68 if (flacSize - 8 < typeLen) {
69 return;
70 }
71
72 memcpy(type, &flac[8], typeLen);
73 type[typeLen] = '\0';
74
75 ALOGV("picType = %d, type = '%s'", picType, type);
76
77 if (!strcmp(type, "-->")) {
78 // This is not inline cover art, but an external url instead.
79 return;
80 }
81
82 if (flacSize < 32 || flacSize - 32 < typeLen) {
83 return;
84 }
85
86 descLen = U32_AT(&flac[8 + typeLen]);
87 if (flacSize - 32 - typeLen < descLen) {
88 return;
89 }
90
91 dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
92
93 // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0
94 if (flacSize - 32 - typeLen - descLen < dataLen) {
95 return;
96 }
97
98 ALOGV("got image data, %zu trailing bytes",
99 flacSize - 32 - typeLen - descLen - dataLen);
100
101 fileMeta->setData(
102 kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
103
104 fileMeta->setCString(kKeyAlbumArtMIME, type);
105 }
106
parseVorbisComment(MetaDataBase * fileMeta,const char * comment,size_t commentLength)107 void parseVorbisComment(
108 MetaDataBase *fileMeta, const char *comment, size_t commentLength)
109 {
110 struct {
111 const char *const mTag;
112 uint32_t mKey;
113 } kMap[] = {
114 { "TITLE", kKeyTitle },
115 { "ARTIST", kKeyArtist },
116 { "ALBUMARTIST", kKeyAlbumArtist },
117 { "ALBUM ARTIST", kKeyAlbumArtist },
118 { "COMPILATION", kKeyCompilation },
119 { "ALBUM", kKeyAlbum },
120 { "COMPOSER", kKeyComposer },
121 { "GENRE", kKeyGenre },
122 { "AUTHOR", kKeyAuthor },
123 { "TRACKNUMBER", kKeyCDTrackNumber },
124 { "DISCNUMBER", kKeyDiscNumber },
125 { "DATE", kKeyDate },
126 { "YEAR", kKeyYear },
127 { "LYRICIST", kKeyWriter },
128 { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
129 { "ANDROID_LOOP", kKeyAutoLoop },
130 };
131
132 for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
133 size_t tagLen = strlen(kMap[j].mTag);
134 if (!strncasecmp(kMap[j].mTag, comment, tagLen)
135 && comment[tagLen] == '=') {
136 if (kMap[j].mKey == kKeyAlbumArt) {
137 extractAlbumArt(
138 fileMeta,
139 &comment[tagLen + 1],
140 commentLength - tagLen - 1);
141 } else if (kMap[j].mKey == kKeyAutoLoop) {
142 if (!strcasecmp(&comment[tagLen + 1], "true")) {
143 fileMeta->setInt32(kKeyAutoLoop, true);
144 }
145 } else {
146 fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
147 }
148 }
149 }
150
151 }
152
153 } // namespace android
154