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