XINGSeeker.cpp revision 7c1bc4cb2260f666a2fba9e64bb9de95121f0b22
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        char *table_of_contents = NULL, int32_t *quality_indicator = NULL,
28        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->mTableOfContents,
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 || mTableOfContents[0] <= 0 || 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)mTableOfContents[a-1];
80        }
81        if ( a < 99 ) {
82            fb = (float)mTableOfContents[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        char *table_of_contents, int32_t *quality_indicator,
98        int64_t *duration) {
99    if (frame_number) {
100        *frame_number = 0;
101    }
102    if (byte_number) {
103        *byte_number = 0;
104    }
105    if (table_of_contents) {
106        table_of_contents[0] = 0;
107    }
108    if (quality_indicator) {
109        *quality_indicator = 0;
110    }
111    if (duration) {
112        *duration = 0;
113    }
114
115    uint8_t buffer[4];
116    int offset = first_frame_pos;
117    if (source->readAt(offset, &buffer, 4) < 4) { // get header
118        return false;
119    }
120    offset += 4;
121
122    uint8_t id, layer, sr_index, mode;
123    layer = (buffer[1] >> 1) & 3;
124    id = (buffer[1] >> 3) & 3;
125    sr_index = (buffer[2] >> 2) & 3;
126    mode = (buffer[3] >> 6) & 3;
127    if (layer == 0) {
128        return false;
129    }
130    if (id == 1) {
131        return false;
132    }
133    if (sr_index == 3) {
134        return false;
135    }
136    // determine offset of XING header
137    if(id&1) { // mpeg1
138        if (mode != 3) offset += 32;
139        else offset += 17;
140    } else { // mpeg2
141        if (mode != 3) offset += 17;
142        else offset += 9;
143    }
144
145    if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
146        return false;
147    }
148    offset += 4;
149    // Check XING ID
150    if ((buffer[0] != 'X') || (buffer[1] != 'i')
151                || (buffer[2] != 'n') || (buffer[3] != 'g')) {
152        if ((buffer[0] != 'I') || (buffer[1] != 'n')
153                    || (buffer[2] != 'f') || (buffer[3] != 'o')) {
154            return false;
155        }
156    }
157
158    if (source->readAt(offset, &buffer, 4) < 4) { // flags
159        return false;
160    }
161    offset += 4;
162    uint32_t flags = U32_AT(buffer);
163
164    if (flags & 0x0001) {  // Frames field is present
165        if (source->readAt(offset, buffer, 4) < 4) {
166             return false;
167        }
168        if (frame_number) {
169           *frame_number = U32_AT(buffer);
170        }
171        int32_t frame = U32_AT(buffer);
172        // Samples per Frame: 1. index = MPEG Version ID, 2. index = Layer
173        const int samplesPerFrames[2][3] =
174        {
175            { 384, 1152, 576  }, // MPEG 2, 2.5: layer1, layer2, layer3
176            { 384, 1152, 1152 }, // MPEG 1: layer1, layer2, layer3
177        };
178        // sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
179        const int samplingRates[4][3] =
180        {
181            { 11025, 12000, 8000,  },    // MPEG 2.5
182            { 0,     0,     0,     },    // reserved
183            { 22050, 24000, 16000, },    // MPEG 2
184            { 44100, 48000, 32000, }     // MPEG 1
185        };
186        if (duration) {
187            *duration = (int64_t)frame * samplesPerFrames[id&1][3-layer] * 1000000LL
188                / samplingRates[id][sr_index];
189        }
190        offset += 4;
191    }
192    if (flags & 0x0002) {  // Bytes field is present
193        if (byte_number) {
194            if (source->readAt(offset, buffer, 4) < 4) {
195                return false;
196            }
197            *byte_number = U32_AT(buffer);
198        }
199        offset += 4;
200    }
201    if (flags & 0x0004) {  // TOC field is present
202       if (table_of_contents) {
203            if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
204                return false;
205            }
206        }
207        offset += 100;
208    }
209    if (flags & 0x0008) {  // Quality indicator field is present
210        if (quality_indicator) {
211            if (source->readAt(offset, buffer, 4) < 4) {
212                return false;
213            }
214            *quality_indicator = U32_AT(buffer);
215        }
216    }
217    return true;
218}
219
220}  // namespace android
221
222