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