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