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 28 XINGSeeker::XINGSeeker() 29 : mDurationUs(-1), 30 mSizeBytes(0), 31 mEncoderDelay(0), 32 mEncoderPadding(0), 33 mTOCValid(false) { 34 } 35 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 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 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 193 int32_t XINGSeeker::getEncoderDelay() { 194 return mEncoderDelay; 195 } 196 197 int32_t XINGSeeker::getEncoderPadding() { 198 return mEncoderPadding; 199 } 200 201 } // namespace android 202 203