XINGSeeker.cpp revision 5102400104ce1f0a6de7bf913381a91e4f18a22a
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#include "include/XINGSeeker.h"
18
19#include <media/stagefright/DataSource.h>
20#include <media/stagefright/Utils.h>
21
22namespace android {
23
24static bool parse_xing_header(
25        const sp<DataSource> &source, off64_t first_frame_pos,
26        int32_t *frame_number = NULL, int32_t *byte_number = NULL,
27        unsigned char *table_of_contents = NULL, bool *toc_is_valid = NULL,
28        int32_t *quality_indicator = NULL, int64_t *duration = NULL);
29
30// static
31sp<XINGSeeker> XINGSeeker::CreateFromSource(
32        const sp<DataSource> &source, off64_t first_frame_pos) {
33    sp<XINGSeeker> seeker = new XINGSeeker;
34
35    seeker->mFirstFramePos = first_frame_pos;
36
37    if (!parse_xing_header(
38                source, first_frame_pos,
39                NULL, &seeker->mSizeBytes, seeker->mTOC, &seeker->mTOCValid,
40                NULL, &seeker->mDurationUs)) {
41        return NULL;
42    }
43
44    return seeker;
45}
46
47XINGSeeker::XINGSeeker()
48    : mDurationUs(-1),
49      mSizeBytes(0) {
50}
51
52bool XINGSeeker::getDuration(int64_t *durationUs) {
53    if (mDurationUs < 0) {
54        return false;
55    }
56
57    *durationUs = mDurationUs;
58
59    return true;
60}
61
62bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) {
63    if (mSizeBytes == 0 || !mTOCValid || mDurationUs < 0) {
64        return false;
65    }
66
67    float percent = (float)(*timeUs) * 100 / mDurationUs;
68    float fx;
69    if( percent <= 0.0f ) {
70        fx = 0.0f;
71    } else if( percent >= 100.0f ) {
72        fx = 256.0f;
73    } else {
74        int a = (int)percent;
75        float fa, fb;
76        if ( a == 0 ) {
77            fa = 0.0f;
78        } else {
79            fa = (float)mTOC[a-1];
80        }
81        if ( a < 99 ) {
82            fb = (float)mTOC[a];
83        } else {
84            fb = 256.0f;
85        }
86        fx = fa + (fb-fa)*(percent-a);
87    }
88
89    *pos = (int)((1.0f/256.0f)*fx*mSizeBytes) + mFirstFramePos;
90
91    return true;
92}
93
94static bool parse_xing_header(
95        const sp<DataSource> &source, off64_t first_frame_pos,
96        int32_t *frame_number, int32_t *byte_number,
97        unsigned char *table_of_contents, bool *toc_valid,
98        int32_t *quality_indicator,
99        int64_t *duration) {
100    if (frame_number) {
101        *frame_number = 0;
102    }
103    if (byte_number) {
104        *byte_number = 0;
105    }
106    if (toc_valid) {
107        *toc_valid = false;
108    }
109    if (quality_indicator) {
110        *quality_indicator = 0;
111    }
112    if (duration) {
113        *duration = 0;
114    }
115
116    uint8_t buffer[4];
117    int offset = first_frame_pos;
118    if (source->readAt(offset, &buffer, 4) < 4) { // get header
119        return false;
120    }
121    offset += 4;
122
123    uint8_t id, layer, sr_index, mode;
124    layer = (buffer[1] >> 1) & 3;
125    id = (buffer[1] >> 3) & 3;
126    sr_index = (buffer[2] >> 2) & 3;
127    mode = (buffer[3] >> 6) & 3;
128    if (layer == 0) {
129        return false;
130    }
131    if (id == 1) {
132        return false;
133    }
134    if (sr_index == 3) {
135        return false;
136    }
137    // determine offset of XING header
138    if(id&1) { // mpeg1
139        if (mode != 3) offset += 32;
140        else offset += 17;
141    } else { // mpeg2
142        if (mode != 3) offset += 17;
143        else offset += 9;
144    }
145
146    if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
147        return false;
148    }
149    offset += 4;
150    // Check XING ID
151    if ((buffer[0] != 'X') || (buffer[1] != 'i')
152                || (buffer[2] != 'n') || (buffer[3] != 'g')) {
153        if ((buffer[0] != 'I') || (buffer[1] != 'n')
154                    || (buffer[2] != 'f') || (buffer[3] != 'o')) {
155            return false;
156        }
157    }
158
159    if (source->readAt(offset, &buffer, 4) < 4) { // flags
160        return false;
161    }
162    offset += 4;
163    uint32_t flags = U32_AT(buffer);
164
165    if (flags & 0x0001) {  // Frames field is present
166        if (source->readAt(offset, buffer, 4) < 4) {
167             return false;
168        }
169        if (frame_number) {
170           *frame_number = U32_AT(buffer);
171        }
172        int32_t frame = U32_AT(buffer);
173        // Samples per Frame: 1. index = MPEG Version ID, 2. index = Layer
174        const int samplesPerFrames[2][3] =
175        {
176            { 384, 1152, 576  }, // MPEG 2, 2.5: layer1, layer2, layer3
177            { 384, 1152, 1152 }, // MPEG 1: layer1, layer2, layer3
178        };
179        // sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
180        const int samplingRates[4][3] =
181        {
182            { 11025, 12000, 8000,  },    // MPEG 2.5
183            { 0,     0,     0,     },    // reserved
184            { 22050, 24000, 16000, },    // MPEG 2
185            { 44100, 48000, 32000, }     // MPEG 1
186        };
187        if (duration) {
188            *duration = (int64_t)frame * samplesPerFrames[id&1][3-layer] * 1000000LL
189                / samplingRates[id][sr_index];
190        }
191        offset += 4;
192    }
193    if (flags & 0x0002) {  // Bytes field is present
194        if (byte_number) {
195            if (source->readAt(offset, buffer, 4) < 4) {
196                return false;
197            }
198            *byte_number = U32_AT(buffer);
199        }
200        offset += 4;
201    }
202    if (flags & 0x0004) {  // TOC field is present
203        if (table_of_contents) {
204            if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
205                return false;
206            }
207            if (toc_valid) {
208                *toc_valid = true;
209            }
210        }
211        offset += 100;
212    }
213    if (flags & 0x0008) {  // Quality indicator field is present
214        if (quality_indicator) {
215            if (source->readAt(offset, buffer, 4) < 4) {
216                return false;
217            }
218            *quality_indicator = U32_AT(buffer);
219        }
220    }
221    return true;
222}
223
224}  // namespace android
225
226