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 "include/XINGSeeker.h"
21 #include "include/avc_utils.h"
22 
23 #include <media/stagefright/DataSource.h>
24 #include <media/stagefright/Utils.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(const sp<DataSource> & source,off64_t first_frame_pos)79 sp<XINGSeeker> XINGSeeker::CreateFromSource(
80         const sp<DataSource> &source, off64_t first_frame_pos) {
81     sp<XINGSeeker> seeker = new XINGSeeker;
82 
83     seeker->mFirstFramePos = first_frame_pos;
84 
85     uint8_t buffer[4];
86     int offset = first_frame_pos;
87     if (source->readAt(offset, &buffer, 4) < 4) { // get header
88         return NULL;
89     }
90     offset += 4;
91 
92     int header = U32_AT(buffer);;
93     size_t xingframesize = 0;
94     int sampling_rate = 0;
95     int num_channels;
96     int samples_per_frame = 0;
97     if (!GetMPEGAudioFrameSize(header, &xingframesize, &sampling_rate, &num_channels,
98                                NULL, &samples_per_frame)) {
99         return NULL;
100     }
101     seeker->mFirstFramePos += xingframesize;
102 
103     uint8_t version = (buffer[1] >> 3) & 3;
104 
105     // determine offset of XING header
106     if(version & 1) { // mpeg1
107         if (num_channels != 1) offset += 32;
108         else offset += 17;
109     } else { // mpeg 2 or 2.5
110         if (num_channels != 1) offset += 17;
111         else offset += 9;
112     }
113 
114     int xingbase = offset;
115 
116     if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
117         return NULL;
118     }
119     offset += 4;
120     // Check XING ID
121     if ((buffer[0] != 'X') || (buffer[1] != 'i')
122                 || (buffer[2] != 'n') || (buffer[3] != 'g')) {
123         if ((buffer[0] != 'I') || (buffer[1] != 'n')
124                     || (buffer[2] != 'f') || (buffer[3] != 'o')) {
125             return NULL;
126         }
127     }
128 
129     if (source->readAt(offset, &buffer, 4) < 4) { // flags
130         return NULL;
131     }
132     offset += 4;
133     uint32_t flags = U32_AT(buffer);
134 
135     if (flags & 0x0001) {  // Frames field is present
136         if (source->readAt(offset, buffer, 4) < 4) {
137              return NULL;
138         }
139         int32_t frames = U32_AT(buffer);
140         // only update mDurationUs if the calculated duration is valid (non zero)
141         // otherwise, leave duration at -1 so that getDuration() and getOffsetForTime()
142         // return false when called, to indicate that this xing tag does not have the
143         // requested information
144         if (frames) {
145             seeker->mDurationUs = (int64_t)frames * samples_per_frame * 1000000LL / sampling_rate;
146         }
147         offset += 4;
148     }
149     if (flags & 0x0002) {  // Bytes field is present
150         if (source->readAt(offset, buffer, 4) < 4) {
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             return NULL;
159         }
160         seeker->mTOCValid = true;
161         offset += 100;
162     }
163 
164 #if 0
165     if (flags & 0x0008) {  // Quality indicator field is present
166         if (source->readAt(offset, buffer, 4) < 4) {
167             return NULL;
168         }
169         // do something with the quality indicator
170         offset += 4;
171     }
172 
173     if (source->readAt(xingbase + 0xaf - 0x24, &buffer, 1) < 1) { // encoding flags
174         return false;
175     }
176 
177     ALOGV("nogap preceding: %s, nogap continued in next: %s",
178               (buffer[0] & 0x80) ? "true" : "false",
179               (buffer[0] & 0x40) ? "true" : "false");
180 #endif
181 
182     if (source->readAt(xingbase + 0xb1 - 0x24, &buffer, 3) == 3) {
183         seeker->mEncoderDelay = (buffer[0] << 4) + (buffer[1] >> 4);
184         seeker->mEncoderPadding = ((buffer[1] & 0xf) << 8) + buffer[2];
185     }
186 
187     return seeker;
188 }
189 
getEncoderDelay()190 int32_t XINGSeeker::getEncoderDelay() {
191     return mEncoderDelay;
192 }
193 
getEncoderPadding()194 int32_t XINGSeeker::getEncoderPadding() {
195     return mEncoderPadding;
196 }
197 
198 }  // namespace android
199 
200