1dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu/*
2dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuCopyright (c) 2011 Stanislav Vitvitskiy
3dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
4dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuPermission is hereby granted, free of charge, to any person obtaining a copy of this
5dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhusoftware and associated documentation files (the "Software"), to deal in the Software
6dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuwithout restriction, including without limitation the rights to use, copy, modify,
7dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhumerge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupermit persons to whom the Software is furnished to do so, subject to the following
9dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuconditions:
10dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
11dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuThe above copyright notice and this permission notice shall be included in all copies or
12dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhusubstantial portions of the Software.
13dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
14dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuINCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
16dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
17dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuFOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
18dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui ZhuOR OTHER DEALINGS IN THE SOFTWARE.
20dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu*/
21dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupackage com.googlecode.mp4parser.h264.read;
22dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
23dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.h264.CharCache;
24dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
25dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.IOException;
26dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.InputStream;
27dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
28dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu/**
29dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * A dummy implementation of H264 RBSP reading
30dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *
31dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * @author Stanislav Vitvitskiy
32dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu */
33dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupublic class BitstreamReader {
34dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private InputStream is;
35dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private int curByte;
36dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private int nextByte;
37dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    int nBit;
38dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    protected static int bitsRead;
39dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
40dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    protected CharCache debugBits = new CharCache(50);
41dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
42dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public BitstreamReader(InputStream is) throws IOException {
43dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.is = is;
44dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        curByte = is.read();
45dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        nextByte = is.read();
46dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
47dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
48dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /*
49dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * (non-Javadoc)
50dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      *
51dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#read1Bit()
52dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      */
53dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public int read1Bit() throws IOException {
54dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (nBit == 8) {
55dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            advance();
56dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (curByte == -1) {
57dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                return -1;
58dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
59dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
60dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int res = (curByte >> (7 - nBit)) & 1;
61dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        nBit++;
62dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
63dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        debugBits.append(res == 0 ? '0' : '1');
64dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ++bitsRead;
65dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
66dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return res;
67dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
68dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
69dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /*
70dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * (non-Javadoc)
71dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      *
72dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readNBit(int)
73dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      */
74dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public long readNBit(int n) throws IOException {
75dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (n > 64)
76dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new IllegalArgumentException("Can not readByte more then 64 bit");
77dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
78dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        long val = 0;
79dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
80dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (int i = 0; i < n; i++) {
81dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            val <<= 1;
82dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            val |= read1Bit();
83dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
84dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
85dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return val;
86dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
87dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
88dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private void advance() throws IOException {
89dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        curByte = nextByte;
90dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        nextByte = is.read();
91dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        nBit = 0;
92dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
93dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
94dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /*
95dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * (non-Javadoc)
96dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      *
97dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readByte()
98dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      */
99dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public int readByte() throws IOException {
100dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (nBit > 0) {
101dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            advance();
102dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
103dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
104dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int res = curByte;
105dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
106dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        advance();
107dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
108dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return res;
109dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
110dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
111dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /*
112dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * (non-Javadoc)
113dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      *
114dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#moreRBSPData()
115dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      */
116dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public boolean moreRBSPData() throws IOException {
117dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (nBit == 8) {
118dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            advance();
119dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
120dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int tail = 1 << (8 - nBit - 1);
121dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int mask = ((tail << 1) - 1);
122dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        boolean hasTail = (curByte & mask) == tail;
123dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
124dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return !(curByte == -1 || (nextByte == -1 && hasTail));
125dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
126dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
127dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public long getBitPosition() {
128dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return (bitsRead * 8 + (nBit % 8));
129dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
130dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
131dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /*
132dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * (non-Javadoc)
133dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      *
134dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readRemainingByte()
135dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      */
136dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public long readRemainingByte() throws IOException {
137dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return readNBit(8 - nBit);
138dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
139dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
140dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /*
141dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * (non-Javadoc)
142dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      *
143dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#next_bits(int)
144dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      */
145dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public int peakNextBits(int n) throws IOException {
146dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (n > 8)
147dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new IllegalArgumentException("N should be less then 8");
148dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (nBit == 8) {
149dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            advance();
150dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (curByte == -1) {
151dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                return -1;
152dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
153dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
154dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int[] bits = new int[16 - nBit];
155dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
156dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int cnt = 0;
157dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (int i = nBit; i < 8; i++) {
158dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            bits[cnt++] = (curByte >> (7 - i)) & 0x1;
159dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
160dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
161dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (int i = 0; i < 8; i++) {
162dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            bits[cnt++] = (nextByte >> (7 - i)) & 0x1;
163dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
164dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
165dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int result = 0;
166dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (int i = 0; i < n; i++) {
167dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            result <<= 1;
168dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            result |= bits[i];
169dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
170dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
171dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return result;
172dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
173dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
174dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /*
175dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * (non-Javadoc)
176dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      *
177dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#byte_aligned()
178dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      */
179dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public boolean isByteAligned() {
180dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return (nBit % 8) == 0;
181dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
182dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
183dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /*
184dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * (non-Javadoc)
185dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      *
186dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#close()
187dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      */
188dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void close() throws IOException {
189dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
190dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
191dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public int getCurBit() {
192dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return nBit;
193dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
194dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu}