1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker * Copyright 2018 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker *
4*ec779b8eSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker *
8*ec779b8eSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker *
10*ec779b8eSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker */
16*ec779b8eSAndroid Build Coastguard Worker
17*ec779b8eSAndroid Build Coastguard Worker //#define LOG_NDEBUG 0
18*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "MetaDataUtils"
19*ec779b8eSAndroid Build Coastguard Worker #include <utils/Log.h>
20*ec779b8eSAndroid Build Coastguard Worker
21*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/foundation/avc_utils.h>
22*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/foundation/base64.h>
23*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/foundation/ABitReader.h>
24*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/foundation/ABuffer.h>
25*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/foundation/ByteUtils.h>
26*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/MediaDefs.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/MetaDataUtils.h>
28*ec779b8eSAndroid Build Coastguard Worker #include <media/NdkMediaFormat.h>
29*ec779b8eSAndroid Build Coastguard Worker
30*ec779b8eSAndroid Build Coastguard Worker namespace android {
31*ec779b8eSAndroid Build Coastguard Worker
MakeAVCCodecSpecificData(MetaDataBase & meta,const uint8_t * data,size_t size)32*ec779b8eSAndroid Build Coastguard Worker bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
33*ec779b8eSAndroid Build Coastguard Worker if (data == nullptr || size == 0) {
34*ec779b8eSAndroid Build Coastguard Worker return false;
35*ec779b8eSAndroid Build Coastguard Worker }
36*ec779b8eSAndroid Build Coastguard Worker
37*ec779b8eSAndroid Build Coastguard Worker int32_t width;
38*ec779b8eSAndroid Build Coastguard Worker int32_t height;
39*ec779b8eSAndroid Build Coastguard Worker int32_t sarWidth;
40*ec779b8eSAndroid Build Coastguard Worker int32_t sarHeight;
41*ec779b8eSAndroid Build Coastguard Worker sp<ABuffer> accessUnit = new ABuffer((void*)data, size);
42*ec779b8eSAndroid Build Coastguard Worker sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
43*ec779b8eSAndroid Build Coastguard Worker if (csd == nullptr) {
44*ec779b8eSAndroid Build Coastguard Worker return false;
45*ec779b8eSAndroid Build Coastguard Worker }
46*ec779b8eSAndroid Build Coastguard Worker meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
47*ec779b8eSAndroid Build Coastguard Worker
48*ec779b8eSAndroid Build Coastguard Worker meta.setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
49*ec779b8eSAndroid Build Coastguard Worker meta.setInt32(kKeyWidth, width);
50*ec779b8eSAndroid Build Coastguard Worker meta.setInt32(kKeyHeight, height);
51*ec779b8eSAndroid Build Coastguard Worker if (sarWidth > 0 && sarHeight > 0) {
52*ec779b8eSAndroid Build Coastguard Worker meta.setInt32(kKeySARWidth, sarWidth);
53*ec779b8eSAndroid Build Coastguard Worker meta.setInt32(kKeySARHeight, sarHeight);
54*ec779b8eSAndroid Build Coastguard Worker }
55*ec779b8eSAndroid Build Coastguard Worker return true;
56*ec779b8eSAndroid Build Coastguard Worker }
57*ec779b8eSAndroid Build Coastguard Worker
MakeAVCCodecSpecificData(AMediaFormat * meta,const uint8_t * data,size_t size)58*ec779b8eSAndroid Build Coastguard Worker bool MakeAVCCodecSpecificData(AMediaFormat *meta, const uint8_t *data, size_t size) {
59*ec779b8eSAndroid Build Coastguard Worker if (meta == nullptr || data == nullptr || size == 0) {
60*ec779b8eSAndroid Build Coastguard Worker return false;
61*ec779b8eSAndroid Build Coastguard Worker }
62*ec779b8eSAndroid Build Coastguard Worker
63*ec779b8eSAndroid Build Coastguard Worker int32_t width;
64*ec779b8eSAndroid Build Coastguard Worker int32_t height;
65*ec779b8eSAndroid Build Coastguard Worker int32_t sarWidth;
66*ec779b8eSAndroid Build Coastguard Worker int32_t sarHeight;
67*ec779b8eSAndroid Build Coastguard Worker sp<ABuffer> accessUnit = new ABuffer((void*)data, size);
68*ec779b8eSAndroid Build Coastguard Worker sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
69*ec779b8eSAndroid Build Coastguard Worker if (csd == nullptr) {
70*ec779b8eSAndroid Build Coastguard Worker return false;
71*ec779b8eSAndroid Build Coastguard Worker }
72*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AVC);
73*ec779b8eSAndroid Build Coastguard Worker
74*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_AVC, csd->data(), csd->size());
75*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, width);
76*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, height);
77*ec779b8eSAndroid Build Coastguard Worker if (sarWidth > 0 && sarHeight > 0) {
78*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
79*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
80*ec779b8eSAndroid Build Coastguard Worker }
81*ec779b8eSAndroid Build Coastguard Worker return true;
82*ec779b8eSAndroid Build Coastguard Worker }
83*ec779b8eSAndroid Build Coastguard Worker
84*ec779b8eSAndroid Build Coastguard Worker // Check if the next 24 bits are VP9 SYNC_CODE
isVp9SyncCode(ABitReader & bits)85*ec779b8eSAndroid Build Coastguard Worker static bool isVp9SyncCode(ABitReader &bits) {
86*ec779b8eSAndroid Build Coastguard Worker if (bits.numBitsLeft() < 24) {
87*ec779b8eSAndroid Build Coastguard Worker return false;
88*ec779b8eSAndroid Build Coastguard Worker }
89*ec779b8eSAndroid Build Coastguard Worker return bits.getBits(24) == 0x498342;
90*ec779b8eSAndroid Build Coastguard Worker }
91*ec779b8eSAndroid Build Coastguard Worker
92*ec779b8eSAndroid Build Coastguard Worker // This parses bitdepth and subsampling in a VP9 uncompressed header
93*ec779b8eSAndroid Build Coastguard Worker // (refer section bitdepth_colorspace_sampling in 6.2 of the VP9 bitstream spec)
getVp9BitdepthChromaSubSampling(ABitReader & bits,int32_t profile,int32_t * bitDepth,int32_t * chromaSubsampling)94*ec779b8eSAndroid Build Coastguard Worker static bool getVp9BitdepthChromaSubSampling(ABitReader &bits,
95*ec779b8eSAndroid Build Coastguard Worker int32_t profile,
96*ec779b8eSAndroid Build Coastguard Worker int32_t *bitDepth,
97*ec779b8eSAndroid Build Coastguard Worker int32_t *chromaSubsampling) {
98*ec779b8eSAndroid Build Coastguard Worker if (profile >= 2) {
99*ec779b8eSAndroid Build Coastguard Worker if (bits.numBitsLeft() < 1) {
100*ec779b8eSAndroid Build Coastguard Worker return false;
101*ec779b8eSAndroid Build Coastguard Worker }
102*ec779b8eSAndroid Build Coastguard Worker *bitDepth = bits.getBits(1) ? 12 : 10;
103*ec779b8eSAndroid Build Coastguard Worker } else {
104*ec779b8eSAndroid Build Coastguard Worker *bitDepth = 8;
105*ec779b8eSAndroid Build Coastguard Worker }
106*ec779b8eSAndroid Build Coastguard Worker
107*ec779b8eSAndroid Build Coastguard Worker uint32_t colorspace;
108*ec779b8eSAndroid Build Coastguard Worker if (!bits.getBitsGraceful(3, &colorspace)) {
109*ec779b8eSAndroid Build Coastguard Worker return false;
110*ec779b8eSAndroid Build Coastguard Worker }
111*ec779b8eSAndroid Build Coastguard Worker
112*ec779b8eSAndroid Build Coastguard Worker *chromaSubsampling = -1;
113*ec779b8eSAndroid Build Coastguard Worker if (colorspace != 7 /*SRGB*/) {
114*ec779b8eSAndroid Build Coastguard Worker // Skip yuv_range_flag
115*ec779b8eSAndroid Build Coastguard Worker if (!bits.skipBits(1)) {
116*ec779b8eSAndroid Build Coastguard Worker return false;
117*ec779b8eSAndroid Build Coastguard Worker }
118*ec779b8eSAndroid Build Coastguard Worker // Check for subsampling only for profiles 1 and 3.
119*ec779b8eSAndroid Build Coastguard Worker if (profile == 1 || profile == 3) {
120*ec779b8eSAndroid Build Coastguard Worker uint32_t ss_x;
121*ec779b8eSAndroid Build Coastguard Worker uint32_t ss_y;
122*ec779b8eSAndroid Build Coastguard Worker if (bits.getBitsGraceful(1, &ss_x) && bits.getBitsGraceful(1, &ss_y)) {
123*ec779b8eSAndroid Build Coastguard Worker *chromaSubsampling = ss_x << 1 & ss_y;
124*ec779b8eSAndroid Build Coastguard Worker } else {
125*ec779b8eSAndroid Build Coastguard Worker return false;
126*ec779b8eSAndroid Build Coastguard Worker }
127*ec779b8eSAndroid Build Coastguard Worker } else {
128*ec779b8eSAndroid Build Coastguard Worker *chromaSubsampling = 3;
129*ec779b8eSAndroid Build Coastguard Worker }
130*ec779b8eSAndroid Build Coastguard Worker } else {
131*ec779b8eSAndroid Build Coastguard Worker if (profile == 1 || profile == 3) {
132*ec779b8eSAndroid Build Coastguard Worker *chromaSubsampling = 0;
133*ec779b8eSAndroid Build Coastguard Worker }
134*ec779b8eSAndroid Build Coastguard Worker }
135*ec779b8eSAndroid Build Coastguard Worker return true;
136*ec779b8eSAndroid Build Coastguard Worker }
137*ec779b8eSAndroid Build Coastguard Worker
138*ec779b8eSAndroid Build Coastguard Worker /**
139*ec779b8eSAndroid Build Coastguard Worker * Build VP9 Codec Feature Metadata (CodecPrivate) to set CSD for VP9 codec.
140*ec779b8eSAndroid Build Coastguard Worker * For reference:
141*ec779b8eSAndroid Build Coastguard Worker * https://www.webmproject.org/docs/container/#vp9-codec-feature-metadata-codecprivate.
142*ec779b8eSAndroid Build Coastguard Worker *
143*ec779b8eSAndroid Build Coastguard Worker * @param meta A pointer to AMediaFormat object.
144*ec779b8eSAndroid Build Coastguard Worker * @param profile The profile value of the VP9 stream.
145*ec779b8eSAndroid Build Coastguard Worker * @param level The VP9 codec level. If the level is unknown, pass -1 to this parameter.
146*ec779b8eSAndroid Build Coastguard Worker * @param bitDepth The bit depth of the luma and color components of the VP9 stream.
147*ec779b8eSAndroid Build Coastguard Worker * @param chromaSubsampling The chroma subsampling of the VP9 stream. If chromaSubsampling is
148*ec779b8eSAndroid Build Coastguard Worker * unknown, pass -1 to this parameter.
149*ec779b8eSAndroid Build Coastguard Worker * @return true if CodecPrivate is set as CSD of AMediaFormat object.
150*ec779b8eSAndroid Build Coastguard Worker *
151*ec779b8eSAndroid Build Coastguard Worker */
MakeVP9CodecPrivate(AMediaFormat * meta,int32_t profile,int32_t level,int32_t bitDepth,int32_t chromaSubsampling)152*ec779b8eSAndroid Build Coastguard Worker static bool MakeVP9CodecPrivate(AMediaFormat* meta, int32_t profile, int32_t level,
153*ec779b8eSAndroid Build Coastguard Worker int32_t bitDepth, int32_t chromaSubsampling) {
154*ec779b8eSAndroid Build Coastguard Worker if (meta == nullptr) {
155*ec779b8eSAndroid Build Coastguard Worker return false;
156*ec779b8eSAndroid Build Coastguard Worker }
157*ec779b8eSAndroid Build Coastguard Worker
158*ec779b8eSAndroid Build Coastguard Worker std::vector<uint8_t> codecPrivate;
159*ec779b8eSAndroid Build Coastguard Worker // Construct CodecPrivate in WebM format (ID | Length | Data).
160*ec779b8eSAndroid Build Coastguard Worker // Helper lambda to add a field to the codec private data
161*ec779b8eSAndroid Build Coastguard Worker auto addField = [&codecPrivate](uint8_t id, uint8_t value) {
162*ec779b8eSAndroid Build Coastguard Worker codecPrivate.push_back(id);
163*ec779b8eSAndroid Build Coastguard Worker codecPrivate.push_back(0x01); // Length is always 1
164*ec779b8eSAndroid Build Coastguard Worker codecPrivate.push_back(value);
165*ec779b8eSAndroid Build Coastguard Worker };
166*ec779b8eSAndroid Build Coastguard Worker
167*ec779b8eSAndroid Build Coastguard Worker // Add fields
168*ec779b8eSAndroid Build Coastguard Worker addField(0x01, static_cast<uint8_t>(profile));
169*ec779b8eSAndroid Build Coastguard Worker if (level >= 0) {
170*ec779b8eSAndroid Build Coastguard Worker addField(0x02, static_cast<uint8_t>(level));
171*ec779b8eSAndroid Build Coastguard Worker }
172*ec779b8eSAndroid Build Coastguard Worker addField(0x03, static_cast<uint8_t>(bitDepth));
173*ec779b8eSAndroid Build Coastguard Worker if (chromaSubsampling >= 0) {
174*ec779b8eSAndroid Build Coastguard Worker addField(0x04, static_cast<uint8_t>(chromaSubsampling));
175*ec779b8eSAndroid Build Coastguard Worker }
176*ec779b8eSAndroid Build Coastguard Worker // Set CSD in the meta format
177*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, codecPrivate.data(), codecPrivate.size());
178*ec779b8eSAndroid Build Coastguard Worker return true;
179*ec779b8eSAndroid Build Coastguard Worker }
180*ec779b8eSAndroid Build Coastguard Worker
181*ec779b8eSAndroid Build Coastguard Worker // The param data contains the first frame data, starting with the uncompressed frame
182*ec779b8eSAndroid Build Coastguard Worker // header. This uncompressed header (refer section 6.2 of the VP9 bitstream spec) is
183*ec779b8eSAndroid Build Coastguard Worker // used to parse profile, bitdepth and subsampling.
MakeVP9CodecSpecificDataFromFirstFrame(AMediaFormat * meta,const uint8_t * data,size_t size)184*ec779b8eSAndroid Build Coastguard Worker bool MakeVP9CodecSpecificDataFromFirstFrame(AMediaFormat* meta, const uint8_t* data, size_t size) {
185*ec779b8eSAndroid Build Coastguard Worker if (meta == nullptr || data == nullptr || size == 0) {
186*ec779b8eSAndroid Build Coastguard Worker return false;
187*ec779b8eSAndroid Build Coastguard Worker }
188*ec779b8eSAndroid Build Coastguard Worker
189*ec779b8eSAndroid Build Coastguard Worker ABitReader bits(data, size);
190*ec779b8eSAndroid Build Coastguard Worker
191*ec779b8eSAndroid Build Coastguard Worker // First 2 bits of the uncompressed header should be the frame_marker.
192*ec779b8eSAndroid Build Coastguard Worker if (bits.getBits(2) != 0b10) {
193*ec779b8eSAndroid Build Coastguard Worker return false;
194*ec779b8eSAndroid Build Coastguard Worker }
195*ec779b8eSAndroid Build Coastguard Worker
196*ec779b8eSAndroid Build Coastguard Worker int32_t profileLowBit = bits.getBits(1);
197*ec779b8eSAndroid Build Coastguard Worker int32_t profileHighBit = bits.getBits(1);
198*ec779b8eSAndroid Build Coastguard Worker int32_t profile = profileHighBit * 2 + profileLowBit;
199*ec779b8eSAndroid Build Coastguard Worker
200*ec779b8eSAndroid Build Coastguard Worker // One reserved '0' bit if profile is 3.
201*ec779b8eSAndroid Build Coastguard Worker if (profile == 3 && bits.getBits(1) != 0) {
202*ec779b8eSAndroid Build Coastguard Worker return false;
203*ec779b8eSAndroid Build Coastguard Worker }
204*ec779b8eSAndroid Build Coastguard Worker
205*ec779b8eSAndroid Build Coastguard Worker // If show_existing_frame is set, we get no more data. Since this is
206*ec779b8eSAndroid Build Coastguard Worker // expected to be the first frame, we can return false which will cascade
207*ec779b8eSAndroid Build Coastguard Worker // into ERROR_MALFORMED.
208*ec779b8eSAndroid Build Coastguard Worker if (bits.getBits(1)) {
209*ec779b8eSAndroid Build Coastguard Worker return false;
210*ec779b8eSAndroid Build Coastguard Worker }
211*ec779b8eSAndroid Build Coastguard Worker
212*ec779b8eSAndroid Build Coastguard Worker int32_t frame_type = bits.getBits(1);
213*ec779b8eSAndroid Build Coastguard Worker
214*ec779b8eSAndroid Build Coastguard Worker // Upto 7 bits could be read till now, which were guaranteed to be available
215*ec779b8eSAndroid Build Coastguard Worker // since size > 0. Check for bits available before reading them from now on.
216*ec779b8eSAndroid Build Coastguard Worker if (bits.numBitsLeft() < 2) {
217*ec779b8eSAndroid Build Coastguard Worker return false;
218*ec779b8eSAndroid Build Coastguard Worker }
219*ec779b8eSAndroid Build Coastguard Worker
220*ec779b8eSAndroid Build Coastguard Worker int32_t show_frame = bits.getBits(1);
221*ec779b8eSAndroid Build Coastguard Worker int32_t error_resilient_mode = bits.getBits(1);
222*ec779b8eSAndroid Build Coastguard Worker int32_t bitDepth = 8;
223*ec779b8eSAndroid Build Coastguard Worker int32_t chromaSubsampling = -1;
224*ec779b8eSAndroid Build Coastguard Worker
225*ec779b8eSAndroid Build Coastguard Worker if (frame_type == 0 /* KEY_FRAME */) {
226*ec779b8eSAndroid Build Coastguard Worker // Check for sync code.
227*ec779b8eSAndroid Build Coastguard Worker if (!isVp9SyncCode(bits)) {
228*ec779b8eSAndroid Build Coastguard Worker return false;
229*ec779b8eSAndroid Build Coastguard Worker }
230*ec779b8eSAndroid Build Coastguard Worker
231*ec779b8eSAndroid Build Coastguard Worker if (!getVp9BitdepthChromaSubSampling(bits, profile, &bitDepth, &chromaSubsampling)) {
232*ec779b8eSAndroid Build Coastguard Worker return false;
233*ec779b8eSAndroid Build Coastguard Worker }
234*ec779b8eSAndroid Build Coastguard Worker } else {
235*ec779b8eSAndroid Build Coastguard Worker int32_t intra_only = 0;
236*ec779b8eSAndroid Build Coastguard Worker if (!show_frame) {
237*ec779b8eSAndroid Build Coastguard Worker if (bits.numBitsLeft() < 1) {
238*ec779b8eSAndroid Build Coastguard Worker return false;
239*ec779b8eSAndroid Build Coastguard Worker }
240*ec779b8eSAndroid Build Coastguard Worker intra_only = bits.getBits(1);
241*ec779b8eSAndroid Build Coastguard Worker }
242*ec779b8eSAndroid Build Coastguard Worker
243*ec779b8eSAndroid Build Coastguard Worker if (!error_resilient_mode) {
244*ec779b8eSAndroid Build Coastguard Worker if (bits.numBitsLeft() < 2) {
245*ec779b8eSAndroid Build Coastguard Worker return false;
246*ec779b8eSAndroid Build Coastguard Worker }
247*ec779b8eSAndroid Build Coastguard Worker // ignore reset_frame_context
248*ec779b8eSAndroid Build Coastguard Worker bits.skipBits(2);
249*ec779b8eSAndroid Build Coastguard Worker }
250*ec779b8eSAndroid Build Coastguard Worker
251*ec779b8eSAndroid Build Coastguard Worker if (!intra_only) {
252*ec779b8eSAndroid Build Coastguard Worker // Require first frame to be either KEY_FRAME or INTER_FRAME with intra_only set to true
253*ec779b8eSAndroid Build Coastguard Worker return false;
254*ec779b8eSAndroid Build Coastguard Worker }
255*ec779b8eSAndroid Build Coastguard Worker
256*ec779b8eSAndroid Build Coastguard Worker // Check for sync code.
257*ec779b8eSAndroid Build Coastguard Worker if (!isVp9SyncCode(bits)) {
258*ec779b8eSAndroid Build Coastguard Worker return false;
259*ec779b8eSAndroid Build Coastguard Worker }
260*ec779b8eSAndroid Build Coastguard Worker
261*ec779b8eSAndroid Build Coastguard Worker if (profile > 0) {
262*ec779b8eSAndroid Build Coastguard Worker if (!getVp9BitdepthChromaSubSampling(bits, profile, &bitDepth, &chromaSubsampling)) {
263*ec779b8eSAndroid Build Coastguard Worker return false;
264*ec779b8eSAndroid Build Coastguard Worker }
265*ec779b8eSAndroid Build Coastguard Worker } else {
266*ec779b8eSAndroid Build Coastguard Worker bitDepth = 8;
267*ec779b8eSAndroid Build Coastguard Worker chromaSubsampling = 3;
268*ec779b8eSAndroid Build Coastguard Worker }
269*ec779b8eSAndroid Build Coastguard Worker }
270*ec779b8eSAndroid Build Coastguard Worker int32_t csdSize = 6;
271*ec779b8eSAndroid Build Coastguard Worker if (chromaSubsampling != -1) {
272*ec779b8eSAndroid Build Coastguard Worker csdSize += 3;
273*ec779b8eSAndroid Build Coastguard Worker }
274*ec779b8eSAndroid Build Coastguard Worker // As level is not present in first frame build CodecPrivate without it.
275*ec779b8eSAndroid Build Coastguard Worker return MakeVP9CodecPrivate(meta, profile, -1, bitDepth, chromaSubsampling);
276*ec779b8eSAndroid Build Coastguard Worker }
277*ec779b8eSAndroid Build Coastguard Worker
MakeVP9CodecPrivateFromVpcC(AMediaFormat * meta,const uint8_t * csdData,size_t size)278*ec779b8eSAndroid Build Coastguard Worker bool MakeVP9CodecPrivateFromVpcC(AMediaFormat* meta, const uint8_t* csdData, size_t size) {
279*ec779b8eSAndroid Build Coastguard Worker if (meta == nullptr || csdData == nullptr || size < 12) {
280*ec779b8eSAndroid Build Coastguard Worker return false;
281*ec779b8eSAndroid Build Coastguard Worker }
282*ec779b8eSAndroid Build Coastguard Worker
283*ec779b8eSAndroid Build Coastguard Worker // Check the first 4 bytes (VersionAndFlags) if they match the required value.
284*ec779b8eSAndroid Build Coastguard Worker if (csdData[0] != 0x01 || csdData[1] != 0x00 || csdData[2] != 0x00 || csdData[3] != 0x00) {
285*ec779b8eSAndroid Build Coastguard Worker return false;
286*ec779b8eSAndroid Build Coastguard Worker }
287*ec779b8eSAndroid Build Coastguard Worker
288*ec779b8eSAndroid Build Coastguard Worker // Create VP9 Codec Feature Metadata (CodecPrivate) that can be parsed.
289*ec779b8eSAndroid Build Coastguard Worker // https://www.webmproject.org/docs/container/#vp9-codec-feature-metadata-codecprivate
290*ec779b8eSAndroid Build Coastguard Worker const uint8_t* vpcCData = csdData + 4; // Skip the first 4 bytes (VersionAndFlags)
291*ec779b8eSAndroid Build Coastguard Worker
292*ec779b8eSAndroid Build Coastguard Worker int32_t profile = vpcCData[0];
293*ec779b8eSAndroid Build Coastguard Worker int32_t level = vpcCData[1];
294*ec779b8eSAndroid Build Coastguard Worker int32_t bitDepth = (vpcCData[2] >> 4) & 0x0F; // Bit Depth (4 bits).
295*ec779b8eSAndroid Build Coastguard Worker int32_t chromaSubsampling = (vpcCData[2] >> 1) & 0x07; // Chroma Subsampling (3 bits).
296*ec779b8eSAndroid Build Coastguard Worker return MakeVP9CodecPrivate(meta, profile, level, bitDepth, chromaSubsampling);
297*ec779b8eSAndroid Build Coastguard Worker }
298*ec779b8eSAndroid Build Coastguard Worker
MakeAACCodecSpecificData(MetaDataBase & meta,const uint8_t * data,size_t size)299*ec779b8eSAndroid Build Coastguard Worker bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
300*ec779b8eSAndroid Build Coastguard Worker if (data == nullptr || size < 7) {
301*ec779b8eSAndroid Build Coastguard Worker return false;
302*ec779b8eSAndroid Build Coastguard Worker }
303*ec779b8eSAndroid Build Coastguard Worker
304*ec779b8eSAndroid Build Coastguard Worker ABitReader bits(data, size);
305*ec779b8eSAndroid Build Coastguard Worker
306*ec779b8eSAndroid Build Coastguard Worker // adts_fixed_header
307*ec779b8eSAndroid Build Coastguard Worker
308*ec779b8eSAndroid Build Coastguard Worker if (bits.getBits(12) != 0xfffu) {
309*ec779b8eSAndroid Build Coastguard Worker ALOGE("Wrong atds_fixed_header");
310*ec779b8eSAndroid Build Coastguard Worker return false;
311*ec779b8eSAndroid Build Coastguard Worker }
312*ec779b8eSAndroid Build Coastguard Worker
313*ec779b8eSAndroid Build Coastguard Worker bits.skipBits(4); // ID, layer, protection_absent
314*ec779b8eSAndroid Build Coastguard Worker
315*ec779b8eSAndroid Build Coastguard Worker unsigned profile = bits.getBits(2);
316*ec779b8eSAndroid Build Coastguard Worker if (profile == 3u) {
317*ec779b8eSAndroid Build Coastguard Worker ALOGE("profile should not be 3");
318*ec779b8eSAndroid Build Coastguard Worker return false;
319*ec779b8eSAndroid Build Coastguard Worker }
320*ec779b8eSAndroid Build Coastguard Worker unsigned sampling_freq_index = bits.getBits(4);
321*ec779b8eSAndroid Build Coastguard Worker bits.getBits(1); // private_bit
322*ec779b8eSAndroid Build Coastguard Worker unsigned channel_configuration = bits.getBits(3);
323*ec779b8eSAndroid Build Coastguard Worker if (channel_configuration == 0u) {
324*ec779b8eSAndroid Build Coastguard Worker ALOGE("channel_config should not be 0");
325*ec779b8eSAndroid Build Coastguard Worker return false;
326*ec779b8eSAndroid Build Coastguard Worker }
327*ec779b8eSAndroid Build Coastguard Worker
328*ec779b8eSAndroid Build Coastguard Worker if (!MakeAACCodecSpecificData(
329*ec779b8eSAndroid Build Coastguard Worker meta, profile, sampling_freq_index, channel_configuration)) {
330*ec779b8eSAndroid Build Coastguard Worker return false;
331*ec779b8eSAndroid Build Coastguard Worker }
332*ec779b8eSAndroid Build Coastguard Worker
333*ec779b8eSAndroid Build Coastguard Worker meta.setInt32(kKeyIsADTS, true);
334*ec779b8eSAndroid Build Coastguard Worker return true;
335*ec779b8eSAndroid Build Coastguard Worker }
336*ec779b8eSAndroid Build Coastguard Worker
MakeAACCodecSpecificData(uint8_t * csd,size_t * esds_size,unsigned profile,unsigned sampling_freq_index,unsigned channel_configuration,int32_t * sampling_rate)337*ec779b8eSAndroid Build Coastguard Worker bool MakeAACCodecSpecificData(
338*ec779b8eSAndroid Build Coastguard Worker uint8_t *csd, /* out */
339*ec779b8eSAndroid Build Coastguard Worker size_t *esds_size, /* in/out */
340*ec779b8eSAndroid Build Coastguard Worker unsigned profile, /* in */
341*ec779b8eSAndroid Build Coastguard Worker unsigned sampling_freq_index, /* in */
342*ec779b8eSAndroid Build Coastguard Worker unsigned channel_configuration, /* in */
343*ec779b8eSAndroid Build Coastguard Worker int32_t *sampling_rate /* out */
344*ec779b8eSAndroid Build Coastguard Worker ) {
345*ec779b8eSAndroid Build Coastguard Worker if(sampling_freq_index > 11u) {
346*ec779b8eSAndroid Build Coastguard Worker return false;
347*ec779b8eSAndroid Build Coastguard Worker }
348*ec779b8eSAndroid Build Coastguard Worker static const int32_t kSamplingFreq[] = {
349*ec779b8eSAndroid Build Coastguard Worker 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
350*ec779b8eSAndroid Build Coastguard Worker 16000, 12000, 11025, 8000
351*ec779b8eSAndroid Build Coastguard Worker };
352*ec779b8eSAndroid Build Coastguard Worker *sampling_rate = kSamplingFreq[sampling_freq_index];
353*ec779b8eSAndroid Build Coastguard Worker
354*ec779b8eSAndroid Build Coastguard Worker static const uint8_t kStaticESDS[] = {
355*ec779b8eSAndroid Build Coastguard Worker 0x03, 22,
356*ec779b8eSAndroid Build Coastguard Worker 0x00, 0x00, // ES_ID
357*ec779b8eSAndroid Build Coastguard Worker 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
358*ec779b8eSAndroid Build Coastguard Worker
359*ec779b8eSAndroid Build Coastguard Worker 0x04, 17,
360*ec779b8eSAndroid Build Coastguard Worker 0x40, // Audio ISO/IEC 14496-3
361*ec779b8eSAndroid Build Coastguard Worker 0x00, 0x00, 0x00, 0x00,
362*ec779b8eSAndroid Build Coastguard Worker 0x00, 0x00, 0x00, 0x00,
363*ec779b8eSAndroid Build Coastguard Worker 0x00, 0x00, 0x00, 0x00,
364*ec779b8eSAndroid Build Coastguard Worker
365*ec779b8eSAndroid Build Coastguard Worker 0x05, 2,
366*ec779b8eSAndroid Build Coastguard Worker // AudioSpecificInfo follows
367*ec779b8eSAndroid Build Coastguard Worker
368*ec779b8eSAndroid Build Coastguard Worker // oooo offf fccc c000
369*ec779b8eSAndroid Build Coastguard Worker // o - audioObjectType
370*ec779b8eSAndroid Build Coastguard Worker // f - samplingFreqIndex
371*ec779b8eSAndroid Build Coastguard Worker // c - channelConfig
372*ec779b8eSAndroid Build Coastguard Worker };
373*ec779b8eSAndroid Build Coastguard Worker
374*ec779b8eSAndroid Build Coastguard Worker size_t csdSize = sizeof(kStaticESDS) + 2;
375*ec779b8eSAndroid Build Coastguard Worker if (csdSize > *esds_size) {
376*ec779b8eSAndroid Build Coastguard Worker return false;
377*ec779b8eSAndroid Build Coastguard Worker }
378*ec779b8eSAndroid Build Coastguard Worker memcpy(csd, kStaticESDS, sizeof(kStaticESDS));
379*ec779b8eSAndroid Build Coastguard Worker
380*ec779b8eSAndroid Build Coastguard Worker csd[sizeof(kStaticESDS)] =
381*ec779b8eSAndroid Build Coastguard Worker ((profile + 1) << 3) | (sampling_freq_index >> 1);
382*ec779b8eSAndroid Build Coastguard Worker
383*ec779b8eSAndroid Build Coastguard Worker csd[sizeof(kStaticESDS) + 1] =
384*ec779b8eSAndroid Build Coastguard Worker ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
385*ec779b8eSAndroid Build Coastguard Worker
386*ec779b8eSAndroid Build Coastguard Worker *esds_size = csdSize;
387*ec779b8eSAndroid Build Coastguard Worker return true;
388*ec779b8eSAndroid Build Coastguard Worker }
389*ec779b8eSAndroid Build Coastguard Worker
MakeAACCodecSpecificData(AMediaFormat * meta,unsigned profile,unsigned sampling_freq_index,unsigned channel_configuration)390*ec779b8eSAndroid Build Coastguard Worker bool MakeAACCodecSpecificData(AMediaFormat *meta, unsigned profile, unsigned sampling_freq_index,
391*ec779b8eSAndroid Build Coastguard Worker unsigned channel_configuration) {
392*ec779b8eSAndroid Build Coastguard Worker
393*ec779b8eSAndroid Build Coastguard Worker if(sampling_freq_index > 11u) {
394*ec779b8eSAndroid Build Coastguard Worker return false;
395*ec779b8eSAndroid Build Coastguard Worker }
396*ec779b8eSAndroid Build Coastguard Worker
397*ec779b8eSAndroid Build Coastguard Worker uint8_t csd[2];
398*ec779b8eSAndroid Build Coastguard Worker csd[0] = ((profile + 1) << 3) | (sampling_freq_index >> 1);
399*ec779b8eSAndroid Build Coastguard Worker csd[1] = ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
400*ec779b8eSAndroid Build Coastguard Worker
401*ec779b8eSAndroid Build Coastguard Worker static const int32_t kSamplingFreq[] = {
402*ec779b8eSAndroid Build Coastguard Worker 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
403*ec779b8eSAndroid Build Coastguard Worker 16000, 12000, 11025, 8000
404*ec779b8eSAndroid Build Coastguard Worker };
405*ec779b8eSAndroid Build Coastguard Worker int32_t sampleRate = kSamplingFreq[sampling_freq_index];
406*ec779b8eSAndroid Build Coastguard Worker
407*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, csd, sizeof(csd));
408*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AAC);
409*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
410*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channel_configuration);
411*ec779b8eSAndroid Build Coastguard Worker
412*ec779b8eSAndroid Build Coastguard Worker return true;
413*ec779b8eSAndroid Build Coastguard Worker }
414*ec779b8eSAndroid Build Coastguard Worker
MakeAACCodecSpecificData(MetaDataBase & meta,unsigned profile,unsigned sampling_freq_index,unsigned channel_configuration)415*ec779b8eSAndroid Build Coastguard Worker bool MakeAACCodecSpecificData(
416*ec779b8eSAndroid Build Coastguard Worker MetaDataBase &meta,
417*ec779b8eSAndroid Build Coastguard Worker unsigned profile, unsigned sampling_freq_index,
418*ec779b8eSAndroid Build Coastguard Worker unsigned channel_configuration) {
419*ec779b8eSAndroid Build Coastguard Worker
420*ec779b8eSAndroid Build Coastguard Worker uint8_t csd[24];
421*ec779b8eSAndroid Build Coastguard Worker size_t csdSize = sizeof(csd);
422*ec779b8eSAndroid Build Coastguard Worker int32_t sampleRate;
423*ec779b8eSAndroid Build Coastguard Worker
424*ec779b8eSAndroid Build Coastguard Worker if (!MakeAACCodecSpecificData(csd, &csdSize, profile, sampling_freq_index,
425*ec779b8eSAndroid Build Coastguard Worker channel_configuration, &sampleRate)) {
426*ec779b8eSAndroid Build Coastguard Worker return false;
427*ec779b8eSAndroid Build Coastguard Worker }
428*ec779b8eSAndroid Build Coastguard Worker
429*ec779b8eSAndroid Build Coastguard Worker meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
430*ec779b8eSAndroid Build Coastguard Worker
431*ec779b8eSAndroid Build Coastguard Worker meta.setInt32(kKeySampleRate, sampleRate);
432*ec779b8eSAndroid Build Coastguard Worker meta.setInt32(kKeyChannelCount, channel_configuration);
433*ec779b8eSAndroid Build Coastguard Worker meta.setData(kKeyESDS, 0, csd, csdSize);
434*ec779b8eSAndroid Build Coastguard Worker return true;
435*ec779b8eSAndroid Build Coastguard Worker }
436*ec779b8eSAndroid Build Coastguard Worker
437*ec779b8eSAndroid Build Coastguard Worker
extractAlbumArt(AMediaFormat * fileMeta,const void * data,size_t size)438*ec779b8eSAndroid Build Coastguard Worker static void extractAlbumArt(
439*ec779b8eSAndroid Build Coastguard Worker AMediaFormat *fileMeta, const void *data, size_t size) {
440*ec779b8eSAndroid Build Coastguard Worker ALOGV("extractAlbumArt from '%s'", (const char *)data);
441*ec779b8eSAndroid Build Coastguard Worker
442*ec779b8eSAndroid Build Coastguard Worker size_t inLen = strnlen((const char *)data, size);
443*ec779b8eSAndroid Build Coastguard Worker size_t flacSize = inLen / 4 * 3;
444*ec779b8eSAndroid Build Coastguard Worker uint8_t *flac = new uint8_t[flacSize];
445*ec779b8eSAndroid Build Coastguard Worker if (!decodeBase64(flac, &flacSize, (const char*)data)) {
446*ec779b8eSAndroid Build Coastguard Worker ALOGE("malformed base64 encoded data.");
447*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
448*ec779b8eSAndroid Build Coastguard Worker return;
449*ec779b8eSAndroid Build Coastguard Worker }
450*ec779b8eSAndroid Build Coastguard Worker
451*ec779b8eSAndroid Build Coastguard Worker ALOGV("got flac of size %zu", flacSize);
452*ec779b8eSAndroid Build Coastguard Worker
453*ec779b8eSAndroid Build Coastguard Worker uint32_t picType;
454*ec779b8eSAndroid Build Coastguard Worker uint32_t typeLen;
455*ec779b8eSAndroid Build Coastguard Worker uint32_t descLen;
456*ec779b8eSAndroid Build Coastguard Worker uint32_t dataLen;
457*ec779b8eSAndroid Build Coastguard Worker char type[128];
458*ec779b8eSAndroid Build Coastguard Worker
459*ec779b8eSAndroid Build Coastguard Worker if (flacSize < 8) {
460*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
461*ec779b8eSAndroid Build Coastguard Worker return;
462*ec779b8eSAndroid Build Coastguard Worker }
463*ec779b8eSAndroid Build Coastguard Worker
464*ec779b8eSAndroid Build Coastguard Worker picType = U32_AT(flac);
465*ec779b8eSAndroid Build Coastguard Worker
466*ec779b8eSAndroid Build Coastguard Worker if (picType != 3) {
467*ec779b8eSAndroid Build Coastguard Worker // This is not a front cover.
468*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
469*ec779b8eSAndroid Build Coastguard Worker return;
470*ec779b8eSAndroid Build Coastguard Worker }
471*ec779b8eSAndroid Build Coastguard Worker
472*ec779b8eSAndroid Build Coastguard Worker typeLen = U32_AT(&flac[4]);
473*ec779b8eSAndroid Build Coastguard Worker if (typeLen > sizeof(type) - 1) {
474*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
475*ec779b8eSAndroid Build Coastguard Worker return;
476*ec779b8eSAndroid Build Coastguard Worker }
477*ec779b8eSAndroid Build Coastguard Worker
478*ec779b8eSAndroid Build Coastguard Worker // we've already checked above that flacSize >= 8
479*ec779b8eSAndroid Build Coastguard Worker if (flacSize - 8 < typeLen) {
480*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
481*ec779b8eSAndroid Build Coastguard Worker return;
482*ec779b8eSAndroid Build Coastguard Worker }
483*ec779b8eSAndroid Build Coastguard Worker
484*ec779b8eSAndroid Build Coastguard Worker memcpy(type, &flac[8], typeLen);
485*ec779b8eSAndroid Build Coastguard Worker type[typeLen] = '\0';
486*ec779b8eSAndroid Build Coastguard Worker
487*ec779b8eSAndroid Build Coastguard Worker ALOGV("picType = %d, type = '%s'", picType, type);
488*ec779b8eSAndroid Build Coastguard Worker
489*ec779b8eSAndroid Build Coastguard Worker if (!strcmp(type, "-->")) {
490*ec779b8eSAndroid Build Coastguard Worker // This is not inline cover art, but an external url instead.
491*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
492*ec779b8eSAndroid Build Coastguard Worker return;
493*ec779b8eSAndroid Build Coastguard Worker }
494*ec779b8eSAndroid Build Coastguard Worker
495*ec779b8eSAndroid Build Coastguard Worker if (flacSize < 32 || flacSize - 32 < typeLen) {
496*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
497*ec779b8eSAndroid Build Coastguard Worker return;
498*ec779b8eSAndroid Build Coastguard Worker }
499*ec779b8eSAndroid Build Coastguard Worker
500*ec779b8eSAndroid Build Coastguard Worker descLen = U32_AT(&flac[8 + typeLen]);
501*ec779b8eSAndroid Build Coastguard Worker if (flacSize - 32 - typeLen < descLen) {
502*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
503*ec779b8eSAndroid Build Coastguard Worker return;
504*ec779b8eSAndroid Build Coastguard Worker }
505*ec779b8eSAndroid Build Coastguard Worker
506*ec779b8eSAndroid Build Coastguard Worker dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
507*ec779b8eSAndroid Build Coastguard Worker
508*ec779b8eSAndroid Build Coastguard Worker // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0
509*ec779b8eSAndroid Build Coastguard Worker if (flacSize - 32 - typeLen - descLen < dataLen) {
510*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
511*ec779b8eSAndroid Build Coastguard Worker return;
512*ec779b8eSAndroid Build Coastguard Worker }
513*ec779b8eSAndroid Build Coastguard Worker
514*ec779b8eSAndroid Build Coastguard Worker ALOGV("got image data, %zu trailing bytes",
515*ec779b8eSAndroid Build Coastguard Worker flacSize - 32 - typeLen - descLen - dataLen);
516*ec779b8eSAndroid Build Coastguard Worker
517*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setBuffer(fileMeta, AMEDIAFORMAT_KEY_ALBUMART,
518*ec779b8eSAndroid Build Coastguard Worker &flac[8 + typeLen + 4 + descLen + 20], dataLen);
519*ec779b8eSAndroid Build Coastguard Worker
520*ec779b8eSAndroid Build Coastguard Worker delete[] flac;
521*ec779b8eSAndroid Build Coastguard Worker }
522*ec779b8eSAndroid Build Coastguard Worker
parseVorbisComment(AMediaFormat * fileMeta,const char * comment,size_t commentLength)523*ec779b8eSAndroid Build Coastguard Worker void parseVorbisComment(
524*ec779b8eSAndroid Build Coastguard Worker AMediaFormat *fileMeta, const char *comment, size_t commentLength) {
525*ec779b8eSAndroid Build Coastguard Worker // Haptic tag is only kept here as it will only be used in extractor to generate channel mask.
526*ec779b8eSAndroid Build Coastguard Worker struct {
527*ec779b8eSAndroid Build Coastguard Worker const char *const mTag;
528*ec779b8eSAndroid Build Coastguard Worker const char *mKey;
529*ec779b8eSAndroid Build Coastguard Worker } kMap[] = {
530*ec779b8eSAndroid Build Coastguard Worker { "TITLE", AMEDIAFORMAT_KEY_TITLE },
531*ec779b8eSAndroid Build Coastguard Worker { "ARTIST", AMEDIAFORMAT_KEY_ARTIST },
532*ec779b8eSAndroid Build Coastguard Worker { "ALBUMARTIST", AMEDIAFORMAT_KEY_ALBUMARTIST },
533*ec779b8eSAndroid Build Coastguard Worker { "ALBUM ARTIST", AMEDIAFORMAT_KEY_ALBUMARTIST },
534*ec779b8eSAndroid Build Coastguard Worker { "COMPILATION", AMEDIAFORMAT_KEY_COMPILATION },
535*ec779b8eSAndroid Build Coastguard Worker { "ALBUM", AMEDIAFORMAT_KEY_ALBUM },
536*ec779b8eSAndroid Build Coastguard Worker { "COMPOSER", AMEDIAFORMAT_KEY_COMPOSER },
537*ec779b8eSAndroid Build Coastguard Worker { "GENRE", AMEDIAFORMAT_KEY_GENRE },
538*ec779b8eSAndroid Build Coastguard Worker { "AUTHOR", AMEDIAFORMAT_KEY_AUTHOR },
539*ec779b8eSAndroid Build Coastguard Worker { "TRACKNUMBER", AMEDIAFORMAT_KEY_CDTRACKNUMBER },
540*ec779b8eSAndroid Build Coastguard Worker { "DISCNUMBER", AMEDIAFORMAT_KEY_DISCNUMBER },
541*ec779b8eSAndroid Build Coastguard Worker { "DATE", AMEDIAFORMAT_KEY_DATE },
542*ec779b8eSAndroid Build Coastguard Worker { "YEAR", AMEDIAFORMAT_KEY_YEAR },
543*ec779b8eSAndroid Build Coastguard Worker { "LYRICIST", AMEDIAFORMAT_KEY_LYRICIST },
544*ec779b8eSAndroid Build Coastguard Worker { "METADATA_BLOCK_PICTURE", AMEDIAFORMAT_KEY_ALBUMART },
545*ec779b8eSAndroid Build Coastguard Worker { "ANDROID_LOOP", AMEDIAFORMAT_KEY_LOOP },
546*ec779b8eSAndroid Build Coastguard Worker { "ANDROID_HAPTIC", AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT },
547*ec779b8eSAndroid Build Coastguard Worker };
548*ec779b8eSAndroid Build Coastguard Worker
549*ec779b8eSAndroid Build Coastguard Worker for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
550*ec779b8eSAndroid Build Coastguard Worker size_t tagLen = strlen(kMap[j].mTag);
551*ec779b8eSAndroid Build Coastguard Worker if (!strncasecmp(kMap[j].mTag, comment, tagLen)
552*ec779b8eSAndroid Build Coastguard Worker && comment[tagLen] == '=') {
553*ec779b8eSAndroid Build Coastguard Worker if (kMap[j].mKey == AMEDIAFORMAT_KEY_ALBUMART) {
554*ec779b8eSAndroid Build Coastguard Worker extractAlbumArt(
555*ec779b8eSAndroid Build Coastguard Worker fileMeta,
556*ec779b8eSAndroid Build Coastguard Worker &comment[tagLen + 1],
557*ec779b8eSAndroid Build Coastguard Worker commentLength - tagLen - 1);
558*ec779b8eSAndroid Build Coastguard Worker } else if (kMap[j].mKey == AMEDIAFORMAT_KEY_LOOP) {
559*ec779b8eSAndroid Build Coastguard Worker if (!strcasecmp(&comment[tagLen + 1], "true")) {
560*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setInt32(fileMeta, AMEDIAFORMAT_KEY_LOOP, 1);
561*ec779b8eSAndroid Build Coastguard Worker }
562*ec779b8eSAndroid Build Coastguard Worker } else if (kMap[j].mKey == AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT) {
563*ec779b8eSAndroid Build Coastguard Worker char *end;
564*ec779b8eSAndroid Build Coastguard Worker errno = 0;
565*ec779b8eSAndroid Build Coastguard Worker const int hapticChannelCount = strtol(&comment[tagLen + 1], &end, 10);
566*ec779b8eSAndroid Build Coastguard Worker if (errno == 0) {
567*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setInt32(fileMeta, kMap[j].mKey, hapticChannelCount);
568*ec779b8eSAndroid Build Coastguard Worker } else {
569*ec779b8eSAndroid Build Coastguard Worker ALOGE("Error(%d) when parsing haptic channel count", errno);
570*ec779b8eSAndroid Build Coastguard Worker }
571*ec779b8eSAndroid Build Coastguard Worker } else {
572*ec779b8eSAndroid Build Coastguard Worker AMediaFormat_setString(fileMeta, kMap[j].mKey, &comment[tagLen + 1]);
573*ec779b8eSAndroid Build Coastguard Worker }
574*ec779b8eSAndroid Build Coastguard Worker }
575*ec779b8eSAndroid Build Coastguard Worker }
576*ec779b8eSAndroid Build Coastguard Worker
577*ec779b8eSAndroid Build Coastguard Worker }
578*ec779b8eSAndroid Build Coastguard Worker
579*ec779b8eSAndroid Build Coastguard Worker } // namespace android
580