1 /*
2  * Copyright (C) 2010 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 "VBRISeeker"
19 
20 #include <inttypes.h>
21 
22 #include <utils/Log.h>
23 
24 #include "VBRISeeker.h"
25 
26 #include <media/stagefright/foundation/avc_utils.h>
27 
28 #include <media/stagefright/foundation/ADebug.h>
29 #include <media/stagefright/foundation/ByteUtils.h>
30 
31 #include <media/MediaExtractorPluginApi.h>
32 #include <media/MediaExtractorPluginHelper.h>
33 
34 namespace android {
35 
U24_AT(const uint8_t * ptr)36 static uint32_t U24_AT(const uint8_t *ptr) {
37     return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
38 }
39 
40 // static
CreateFromSource(DataSourceHelper * source,off64_t post_id3_pos)41 VBRISeeker *VBRISeeker::CreateFromSource(
42         DataSourceHelper *source, off64_t post_id3_pos) {
43     off64_t pos = post_id3_pos;
44 
45     uint8_t header[4];
46     ssize_t n = source->readAt(pos, header, sizeof(header));
47     if (n < (ssize_t)sizeof(header)) {
48         return NULL;
49     }
50 
51     uint32_t tmp = U32_AT(&header[0]);
52     size_t frameSize;
53     int sampleRate;
54     if (!GetMPEGAudioFrameSize(tmp, &frameSize, &sampleRate)) {
55         return NULL;
56     }
57 
58     // VBRI header follows 32 bytes after the header _ends_.
59     pos += sizeof(header) + 32;
60 
61     uint8_t vbriHeader[26];
62     n = source->readAt(pos, vbriHeader, sizeof(vbriHeader));
63     if (n < (ssize_t)sizeof(vbriHeader)) {
64         return NULL;
65     }
66 
67     if (memcmp(vbriHeader, "VBRI", 4)) {
68         return NULL;
69     }
70 
71     size_t numFrames = U32_AT(&vbriHeader[14]);
72 
73     int64_t durationUs =
74         numFrames * 1000000LL * (sampleRate >= 32000 ? 1152 : 576) / sampleRate;
75 
76     ALOGV("duration = %.2f secs", durationUs / 1E6);
77 
78     size_t numEntries = U16_AT(&vbriHeader[18]);
79     size_t entrySize = U16_AT(&vbriHeader[22]);
80     size_t scale = U16_AT(&vbriHeader[20]);
81 
82     ALOGV("%zu entries, scale=%zu, size_per_entry=%zu",
83          numEntries,
84          scale,
85          entrySize);
86 
87     if (entrySize > 4) {
88         ALOGE("invalid VBRI entry size: %zu", entrySize);
89         return NULL;
90     }
91 
92     VBRISeeker *seeker = new (std::nothrow) VBRISeeker;
93     if (seeker == NULL) {
94         ALOGW("Couldn't allocate VBRISeeker");
95         return NULL;
96     }
97 
98     size_t totalEntrySize = numEntries * entrySize;
99     uint8_t *buffer = new (std::nothrow) uint8_t[totalEntrySize];
100     if (!buffer) {
101         ALOGW("Couldn't allocate %zu bytes", totalEntrySize);
102         delete seeker;
103         return NULL;
104     }
105 
106     n = source->readAt(pos + sizeof(vbriHeader), buffer, totalEntrySize);
107     if (n < (ssize_t)totalEntrySize) {
108         delete[] buffer;
109         buffer = NULL;
110         delete seeker;
111         return NULL;
112     }
113 
114     seeker->mBasePos = post_id3_pos + frameSize;
115     // only update mDurationUs if the calculated duration is valid (non zero)
116     // otherwise, leave duration at -1 so that getDuration() and getOffsetForTime()
117     // return false when called, to indicate that this vbri tag does not have the
118     // requested information
119     if (durationUs) {
120         seeker->mDurationUs = durationUs;
121     }
122 
123     off64_t offset = post_id3_pos;
124     for (size_t i = 0; i < numEntries; ++i) {
125         uint32_t numBytes;
126         switch (entrySize) {
127             case 1: numBytes = buffer[i]; break;
128             case 2: numBytes = U16_AT(buffer + 2 * i); break;
129             case 3: numBytes = U24_AT(buffer + 3 * i); break;
130             default:
131             {
132                 CHECK_EQ(entrySize, 4u);
133                 numBytes = U32_AT(buffer + 4 * i); break;
134             }
135         }
136 
137         numBytes *= scale;
138 
139         seeker->mSegments.push(numBytes);
140 
141         ALOGV("entry #%zu: %u offset %#016llx", i, numBytes, (long long)offset);
142         offset += numBytes;
143     }
144 
145     delete[] buffer;
146     buffer = NULL;
147 
148     ALOGI("Found VBRI header.");
149 
150     return seeker;
151 }
152 
VBRISeeker()153 VBRISeeker::VBRISeeker()
154     : mDurationUs(-1) {
155 }
156 
getDuration(int64_t * durationUs)157 bool VBRISeeker::getDuration(int64_t *durationUs) {
158     if (mDurationUs < 0) {
159         return false;
160     }
161 
162     *durationUs = mDurationUs;
163 
164     return true;
165 }
166 
getOffsetForTime(int64_t * timeUs,off64_t * pos)167 bool VBRISeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) {
168     if (mDurationUs < 0 || mSegments.size() == 0) {
169         return false;
170     }
171 
172     int64_t segmentDurationUs = mDurationUs / mSegments.size();
173 
174     int64_t nowUs = 0;
175     *pos = mBasePos;
176     size_t segmentIndex = 0;
177     while (segmentIndex < mSegments.size() && nowUs < *timeUs) {
178         nowUs += segmentDurationUs;
179         *pos += mSegments.itemAt(segmentIndex++);
180     }
181 
182     ALOGV("getOffsetForTime %lld us => 0x%016llx", (long long)*timeUs, (long long)*pos);
183 
184     *timeUs = nowUs;
185 
186     return true;
187 }
188 
189 }  // namespace android
190 
191