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_TAG "XINGSEEKER"
18 #include <utils/Log.h>
19 
20 #include "XINGSeeker.h"
21 #include <media/stagefright/foundation/avc_utils.h>
22 
23 #include <media/stagefright/foundation/ByteUtils.h>
24 #include <media/DataSourceBase.h>
25 
26 namespace android {
27 
XINGSeeker()28 XINGSeeker::XINGSeeker()
29     : mDurationUs(-1),
30       mSizeBytes(0),
31       mEncoderDelay(0),
32       mEncoderPadding(0),
33       mTOCValid(false) {
34 }
35 
getDuration(int64_t * durationUs)36 bool XINGSeeker::getDuration(int64_t *durationUs) {
37     if (mDurationUs < 0) {
38         return false;
39     }
40 
41     *durationUs = mDurationUs;
42 
43     return true;
44 }
45 
getOffsetForTime(int64_t * timeUs,off64_t * pos)46 bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) {
47     if (mSizeBytes == 0 || !mTOCValid || mDurationUs < 0) {
48         return false;
49     }
50 
51     float percent = (float)(*timeUs) * 100 / mDurationUs;
52     float fx;
53     if( percent <= 0.0f ) {
54         fx = 0.0f;
55     } else if( percent >= 100.0f ) {
56         fx = 256.0f;
57     } else {
58         int a = (int)percent;
59         float fa, fb;
60         if ( a == 0 ) {
61             fa = 0.0f;
62         } else {
63             fa = (float)mTOC[a-1];
64         }
65         if ( a < 99 ) {
66             fb = (float)mTOC[a];
67         } else {
68             fb = 256.0f;
69         }
70         fx = fa + (fb-fa)*(percent-a);
71     }
72 
73     *pos = (int)((1.0f/256.0f)*fx*mSizeBytes) + mFirstFramePos;
74 
75     return true;
76 }
77 
78 // static
CreateFromSource(DataSourceBase * source,off64_t first_frame_pos)79 XINGSeeker *XINGSeeker::CreateFromSource(
80         DataSourceBase *source, off64_t first_frame_pos) {
81 
82     uint8_t buffer[4];
83     int offset = first_frame_pos;
84     if (source->readAt(offset, &buffer, 4) < 4) { // get header
85         return NULL;
86     }
87     offset += 4;
88 
89     int header = U32_AT(buffer);;
90     size_t xingframesize = 0;
91     int sampling_rate = 0;
92     int num_channels;
93     int samples_per_frame = 0;
94     if (!GetMPEGAudioFrameSize(header, &xingframesize, &sampling_rate, &num_channels,
95                                NULL, &samples_per_frame)) {
96         return NULL;
97     }
98     uint8_t version = (buffer[1] >> 3) & 3;
99 
100     // determine offset of XING header
101     if(version & 1) { // mpeg1
102         if (num_channels != 1) offset += 32;
103         else offset += 17;
104     } else { // mpeg 2 or 2.5
105         if (num_channels != 1) offset += 17;
106         else offset += 9;
107     }
108 
109     int xingbase = offset;
110 
111     if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
112         return NULL;
113     }
114     offset += 4;
115     // Check XING ID
116     if ((buffer[0] != 'X') || (buffer[1] != 'i')
117                 || (buffer[2] != 'n') || (buffer[3] != 'g')) {
118         if ((buffer[0] != 'I') || (buffer[1] != 'n')
119                     || (buffer[2] != 'f') || (buffer[3] != 'o')) {
120             return NULL;
121         }
122     }
123 
124     if (source->readAt(offset, &buffer, 4) < 4) { // flags
125         return NULL;
126     }
127     offset += 4;
128     uint32_t flags = U32_AT(buffer);
129 
130     XINGSeeker *seeker = new XINGSeeker;
131     seeker->mFirstFramePos = first_frame_pos + xingframesize;
132 
133     if (flags & 0x0001) {  // Frames field is present
134         if (source->readAt(offset, buffer, 4) < 4) {
135             delete seeker;
136             return NULL;
137         }
138         int32_t frames = U32_AT(buffer);
139         // only update mDurationUs if the calculated duration is valid (non zero)
140         // otherwise, leave duration at -1 so that getDuration() and getOffsetForTime()
141         // return false when called, to indicate that this xing tag does not have the
142         // requested information
143         if (frames) {
144             seeker->mDurationUs = (int64_t)frames * samples_per_frame * 1000000LL / sampling_rate;
145         }
146         offset += 4;
147     }
148     if (flags & 0x0002) {  // Bytes field is present
149         if (source->readAt(offset, buffer, 4) < 4) {
150             delete seeker;
151             return NULL;
152         }
153         seeker->mSizeBytes = U32_AT(buffer);
154         offset += 4;
155     }
156     if (flags & 0x0004) {  // TOC field is present
157         if (source->readAt(offset + 1, seeker->mTOC, 99) < 99) {
158             delete seeker;
159             return NULL;
160         }
161         seeker->mTOCValid = true;
162         offset += 100;
163     }
164 
165 #if 0
166     if (flags & 0x0008) {  // Quality indicator field is present
167         if (source->readAt(offset, buffer, 4) < 4) {
168             delete seeker;
169             return NULL;
170         }
171         // do something with the quality indicator
172         offset += 4;
173     }
174 
175     if (source->readAt(xingbase + 0xaf - 0x24, &buffer, 1) < 1) { // encoding flags
176         delete seeker;
177         return false;
178     }
179 
180     ALOGV("nogap preceding: %s, nogap continued in next: %s",
181               (buffer[0] & 0x80) ? "true" : "false",
182               (buffer[0] & 0x40) ? "true" : "false");
183 #endif
184 
185     if (source->readAt(xingbase + 0xb1 - 0x24, &buffer, 3) == 3) {
186         seeker->mEncoderDelay = (buffer[0] << 4) + (buffer[1] >> 4);
187         seeker->mEncoderPadding = ((buffer[1] & 0xf) << 8) + buffer[2];
188     }
189 
190     return seeker;
191 }
192 
getEncoderDelay()193 int32_t XINGSeeker::getEncoderDelay() {
194     return mEncoderDelay;
195 }
196 
getEncoderPadding()197 int32_t XINGSeeker::getEncoderPadding() {
198     return mEncoderPadding;
199 }
200 
201 }  // namespace android
202 
203