1dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupackage com.googlecode.mp4parser.authoring.tracks;
2dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
3dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.*;
4dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.h264.AvcConfigurationBox;
5dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
6dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.authoring.AbstractTrack;
7dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.authoring.TrackMetaData;
8dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.h264.model.PictureParameterSet;
9dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.h264.model.SeqParameterSet;
10dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.h264.read.CAVLCReader;
11dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
12dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.ByteArrayInputStream;
13dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.IOException;
14dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.InputStream;
15dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.nio.ByteBuffer;
16dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.ArrayList;
17dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.Date;
18dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.LinkedList;
19dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.List;
20dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.logging.Logger;
21dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
22dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu/**
23dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * The <code>H264TrackImpl</code> creates a <code>Track</code> from an H.264
24dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * Annex B file.
25dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu */
26dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupublic class H264TrackImpl extends AbstractTrack {
27dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private static final Logger LOG = Logger.getLogger(H264TrackImpl.class.getName());
28dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
29dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    TrackMetaData trackMetaData = new TrackMetaData();
30dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    SampleDescriptionBox sampleDescriptionBox;
31dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
32dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private ReaderWrapper reader;
33dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private List<ByteBuffer> samples;
34dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    boolean readSamples = false;
35dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
36dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    List<TimeToSampleBox.Entry> stts;
37dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    List<CompositionTimeToSample.Entry> ctts;
38dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    List<SampleDependencyTypeBox.Entry> sdtp;
39dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    List<Integer> stss;
40dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
41dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    SeqParameterSet seqParameterSet = null;
42dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    PictureParameterSet pictureParameterSet = null;
43dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    LinkedList<byte[]> seqParameterSetList = new LinkedList<byte[]>();
44dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    LinkedList<byte[]> pictureParameterSetList = new LinkedList<byte[]>();
45dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
46dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private int width;
47dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private int height;
48dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private int timescale;
49dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private int frametick;
50dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private int currentScSize;
51dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private int prevScSize;
52dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
53dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private SEIMessage seiMessage;
54dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    int frameNrInGop = 0;
55dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private boolean determineFrameRate = true;
56dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private String lang = "und";
57dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
58dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public H264TrackImpl(InputStream inputStream, String lang, long timescale) throws IOException {
59dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.lang = lang;
60dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (timescale > 1000) {
61dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            timescale = timescale; //e.g. 23976
62dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            frametick = 1000;
63dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            determineFrameRate = false;
64dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else {
65dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new IllegalArgumentException("Timescale must be specified in milliseconds!");
66dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
67dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        parse(inputStream);
68dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
69dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
70dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public H264TrackImpl(InputStream inputStream, String lang) throws IOException {
71dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.lang = lang;
72dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        parse(inputStream);
73dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
74dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
75dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public H264TrackImpl(InputStream inputStream) throws IOException {
76dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        parse(inputStream);
77dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
78dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
79dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private void parse(InputStream inputStream) throws IOException {
80dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        this.reader = new ReaderWrapper(inputStream);
81dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        stts = new LinkedList<TimeToSampleBox.Entry>();
82dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ctts = new LinkedList<CompositionTimeToSample.Entry>();
83dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        sdtp = new LinkedList<SampleDependencyTypeBox.Entry>();
84dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        stss = new LinkedList<Integer>();
85dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
86dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        samples = new LinkedList<ByteBuffer>();
87dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (!readSamples()) {
88dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new IOException();
89dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
90dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
91dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (!readVariables()) {
92dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new IOException();
93dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
94dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
95dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        sampleDescriptionBox = new SampleDescriptionBox();
96dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        VisualSampleEntry visualSampleEntry = new VisualSampleEntry("avc1");
97dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.setDataReferenceIndex(1);
98dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.setDepth(24);
99dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.setFrameCount(1);
100dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.setHorizresolution(72);
101dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.setVertresolution(72);
102dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.setWidth(width);
103dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.setHeight(height);
104dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.setCompressorname("AVC Coding");
105dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
106dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        AvcConfigurationBox avcConfigurationBox = new AvcConfigurationBox();
107dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
108dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setSequenceParameterSets(seqParameterSetList);
109dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setPictureParameterSets(pictureParameterSetList);
110dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setAvcLevelIndication(seqParameterSet.level_idc);
111dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setAvcProfileIndication(seqParameterSet.profile_idc);
112dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setBitDepthLumaMinus8(seqParameterSet.bit_depth_luma_minus8);
113dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setBitDepthChromaMinus8(seqParameterSet.bit_depth_chroma_minus8);
114dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setChromaFormat(seqParameterSet.chroma_format_idc.getId());
115dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setConfigurationVersion(1);
116dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setLengthSizeMinusOne(3);
117dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        avcConfigurationBox.setProfileCompatibility(seqParameterSetList.get(0)[1]);
118dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
119dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        visualSampleEntry.addBox(avcConfigurationBox);
120dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        sampleDescriptionBox.addBox(visualSampleEntry);
121dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
122dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        trackMetaData.setCreationTime(new Date());
123dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        trackMetaData.setModificationTime(new Date());
124dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        trackMetaData.setLanguage(lang);
125dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        trackMetaData.setTimescale(timescale);
126dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        trackMetaData.setWidth(width);
127dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        trackMetaData.setHeight(height);
128dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
129dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
130dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public SampleDescriptionBox getSampleDescriptionBox() {
131dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return sampleDescriptionBox;
132dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
133dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
134dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
135dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return stts;
136dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
137dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
138dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
139dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return ctts;
140dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
141dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
142dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public long[] getSyncSamples() {
143dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        long[] returns = new long[stss.size()];
144dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (int i = 0; i < stss.size(); i++) {
145dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            returns[i] = stss.get(i);
146dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
147dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return returns;
148dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
149dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
150dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
151dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return sdtp;
152dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
153dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
154dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public TrackMetaData getTrackMetaData() {
155dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return trackMetaData;
156dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
157dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
158dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public String getHandler() {
159dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return "vide";
160dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
161dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
162dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public List<ByteBuffer> getSamples() {
163dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return samples;
164dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
165dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
166dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public AbstractMediaHeaderBox getMediaHeaderBox() {
167dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return new VideoMediaHeaderBox();
168dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
169dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
170dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public SubSampleInformationBox getSubsampleInformationBox() {
171dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return null;
172dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
173dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
174dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private boolean readVariables() {
175dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        width = (seqParameterSet.pic_width_in_mbs_minus1 + 1) * 16;
176dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int mult = 2;
177dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (seqParameterSet.frame_mbs_only_flag) {
178dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            mult = 1;
179dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
180dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        height = 16 * (seqParameterSet.pic_height_in_map_units_minus1 + 1) * mult;
181dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (seqParameterSet.frame_cropping_flag) {
182dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int chromaArrayType = 0;
183dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (seqParameterSet.residual_color_transform_flag == false) {
184dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                chromaArrayType = seqParameterSet.chroma_format_idc.getId();
185dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
186dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int cropUnitX = 1;
187dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int cropUnitY = mult;
188dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (chromaArrayType != 0) {
189dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                cropUnitX = seqParameterSet.chroma_format_idc.getSubWidth();
190dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                cropUnitY = seqParameterSet.chroma_format_idc.getSubHeight() * mult;
191dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
192dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
193dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            width -= cropUnitX * (seqParameterSet.frame_crop_left_offset + seqParameterSet.frame_crop_right_offset);
194dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            height -= cropUnitY * (seqParameterSet.frame_crop_top_offset + seqParameterSet.frame_crop_bottom_offset);
195dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
196dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return true;
197dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
198dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
199dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private boolean findNextStartcode() throws IOException {
200dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        byte[] test = new byte[]{-1, -1, -1, -1};
201dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
202dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int c;
203dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        while ((c = reader.read()) != -1) {
204dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            test[0] = test[1];
205dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            test[1] = test[2];
206dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            test[2] = test[3];
207dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            test[3] = (byte) c;
208dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (test[0] == 0 && test[1] == 0 && test[2] == 0 && test[3] == 1) {
209dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                prevScSize = currentScSize;
210dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                currentScSize = 4;
211dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                return true;
212dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
213dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (test[0] == 0 && test[1] == 0 && test[2] == 1) {
214dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                prevScSize = currentScSize;
215dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                currentScSize = 3;
216dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                return true;
217dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
218dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
219dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return false;
220dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
221dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
222dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private enum NALActions {
223dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        IGNORE, BUFFER, STORE, END
224dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
225dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
226dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private boolean readSamples() throws IOException {
227dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (readSamples) {
228dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return true;
229dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
230dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
231dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        readSamples = true;
232dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
233dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
234dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        findNextStartcode();
235dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        reader.mark();
236dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        long pos = reader.getPos();
237dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
238dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ArrayList<byte[]> buffered = new ArrayList<byte[]>();
239dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
240dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int frameNr = 0;
241dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
242dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        while (findNextStartcode()) {
243dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            long newpos = reader.getPos();
244dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int size = (int) (newpos - pos - prevScSize);
245dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            reader.reset();
246dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            byte[] data = new byte[size ];
247dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            reader.read(data);
248dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int type = data[0];
249dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int nal_ref_idc = (type >> 5) & 3;
250dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int nal_unit_type = type & 0x1f;
251dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            LOG.fine("Found startcode at " + (pos -4)  + " Type: " + nal_unit_type + " ref idc: " + nal_ref_idc + " (size " + size + ")");
252dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            NALActions action = handleNALUnit(nal_ref_idc, nal_unit_type, data);
253dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            switch (action) {
254dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case IGNORE:
255dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    break;
256dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
257dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case BUFFER:
258dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    buffered.add(data);
259dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    break;
260dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
261dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case STORE:
262dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    int stdpValue = 22;
263dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    frameNr++;
264dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    buffered.add(data);
265dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ByteBuffer bb = createSample(buffered);
266dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    boolean IdrPicFlag = false;
267dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (nal_unit_type == 5) {
268dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        stdpValue += 16;
269dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        IdrPicFlag = true;
270dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
271dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ByteArrayInputStream bs = cleanBuffer(buffered.get(buffered.size() - 1));
272dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    SliceHeader sh = new SliceHeader(bs, seqParameterSet, pictureParameterSet, IdrPicFlag);
273dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (sh.slice_type == SliceHeader.SliceType.B) {
274dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        stdpValue += 4;
275dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
276dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    LOG.fine("Adding sample with size " + bb.capacity() + " and header " + sh);
277dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    buffered.clear();
278dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    samples.add(bb);
279dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    stts.add(new TimeToSampleBox.Entry(1, frametick));
280dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (nal_unit_type == 5) { // IDR Picture
281dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        stss.add(frameNr);
282dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
283dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (seiMessage.n_frames == 0) {
284dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        frameNrInGop = 0;
285dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
286dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    int offset = 0;
287dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (seiMessage.clock_timestamp_flag) {
288dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        offset = seiMessage.n_frames - frameNrInGop;
289dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    } else if (seiMessage.removal_delay_flag) {
290dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        offset = seiMessage.dpb_removal_delay / 2;
291dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
292dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ctts.add(new CompositionTimeToSample.Entry(1, offset * frametick));
293dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    sdtp.add(new SampleDependencyTypeBox.Entry(stdpValue));
294dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    frameNrInGop++;
295dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    break;
296dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
297dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case END:
298dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    return true;
299dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
300dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
301dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
302dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            pos = newpos;
303dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            reader.seek(currentScSize);
304dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            reader.mark();
305dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
306dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return true;
307dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
308dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
309dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private ByteBuffer createSample(List<byte[]> buffers) {
310dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int outsize = 0;
311dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (int i = 0; i < buffers.size(); i++) {
312dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            outsize += buffers.get(i).length + 4;
313dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
314dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        byte[] output = new byte[outsize];
315dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
316dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ByteBuffer bb = ByteBuffer.wrap(output);
317dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (int i = 0; i < buffers.size(); i++) {
318dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            bb.putInt(buffers.get(i).length);
319dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            bb.put(buffers.get(i));
320dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
321dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        bb.rewind();
322dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return bb;
323dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
324dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
325dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private ByteArrayInputStream cleanBuffer(byte[] data) {
326dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        byte[] output = new byte[data.length];
327dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int inPos = 0;
328dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int outPos = 0;
329dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        while (inPos < data.length) {
330dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (data[inPos] == 0 && data[inPos + 1] == 0 && data[inPos + 2] == 3) {
331dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                output[outPos] = 0;
332dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                output[outPos + 1] = 0;
333dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                inPos += 3;
334dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                outPos += 2;
335dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else {
336dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                output[outPos] = data[inPos];
337dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                inPos++;
338dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                outPos++;
339dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
340dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
341dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return new ByteArrayInputStream(output, 0, outPos);
342dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
343dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
344dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private NALActions handleNALUnit(int nal_ref_idc, int nal_unit_type, byte[] data) throws IOException {
345dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        NALActions action;
346dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        switch (nal_unit_type) {
347dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 1:
348dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 2:
349dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 3:
350dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 4:
351dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 5:
352dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                action = NALActions.STORE; // Will only work in single slice per frame mode!
353dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                break;
354dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
355dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 6:
356dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                seiMessage = new SEIMessage(cleanBuffer(data), seqParameterSet);
357dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                action = NALActions.BUFFER;
358dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                break;
359dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
360dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 9:
361dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu//                printAccessUnitDelimiter(data);
362dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                int type = data[1] >> 5;
363dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                LOG.fine("Access unit delimiter type: " + type);
364dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                action = NALActions.BUFFER;
365dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                break;
366dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
367dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
368dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 7:
369dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (seqParameterSet == null) {
370dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ByteArrayInputStream is = cleanBuffer(data);
371dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    is.read();
372dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    seqParameterSet = SeqParameterSet.read(is);
373dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    seqParameterSetList.add(data);
374dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    configureFramerate();
375dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
376dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                action = NALActions.IGNORE;
377dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                break;
378dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
379dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 8:
380dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (pictureParameterSet == null) {
381dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ByteArrayInputStream is = new ByteArrayInputStream(data);
382dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    is.read();
383dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    pictureParameterSet = PictureParameterSet.read(is);
384dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    pictureParameterSetList.add(data);
385dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
386dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                action = NALActions.IGNORE;
387dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                break;
388dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
389dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 10:
390dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            case 11:
391dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                action = NALActions.END;
392dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                break;
393dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
394dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            default:
395dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                System.err.println("Unknown NAL unit type: " + nal_unit_type);
396dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                action = NALActions.IGNORE;
397dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
398dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
399dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
400dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return action;
401dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
402dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
403dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private void configureFramerate() {
404dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (determineFrameRate) {
405dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (seqParameterSet.vuiParams != null) {
406dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                timescale = seqParameterSet.vuiParams.time_scale >> 1; // Not sure why, but I found this in several places, and it works...
407dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                frametick = seqParameterSet.vuiParams.num_units_in_tick;
408dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (timescale == 0 || frametick == 0) {
409dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    System.err.println("Warning: vuiParams contain invalid values: time_scale: " + timescale + " and frame_tick: " + frametick + ". Setting frame rate to 25fps");
410dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    timescale = 90000;
411dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    frametick = 3600;
412dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
413dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else {
414dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                System.err.println("Warning: Can't determine frame rate. Guessing 25 fps");
415dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                timescale = 90000;
416dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                frametick = 3600;
417dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
418dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
419dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
420dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
421dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void printAccessUnitDelimiter(byte[] data) {
422dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        LOG.fine("Access unit delimiter: " + (data[1] >> 5));
423dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
424dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
425dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public static class SliceHeader {
426dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
427dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public enum SliceType {
428dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            P, B, I, SP, SI
429dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
430dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
431dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public int first_mb_in_slice;
432dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public SliceType slice_type;
433dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public int pic_parameter_set_id;
434dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public int colour_plane_id;
435dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public int frame_num;
436dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public boolean field_pic_flag = false;
437dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public boolean bottom_field_flag = false;
438dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public int idr_pic_id;
439dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public int pic_order_cnt_lsb;
440dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public int delta_pic_order_cnt_bottom;
441dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
442dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public SliceHeader(InputStream is, SeqParameterSet sps, PictureParameterSet pps, boolean IdrPicFlag) throws IOException {
443dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            is.read();
444dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            CAVLCReader reader = new CAVLCReader(is);
445dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            first_mb_in_slice = reader.readUE("SliceHeader: first_mb_in_slice");
446dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            switch (reader.readUE("SliceHeader: slice_type")) {
447dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 0:
448dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 5:
449dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    slice_type = SliceType.P;
450dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    break;
451dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
452dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 1:
453dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 6:
454dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    slice_type = SliceType.B;
455dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    break;
456dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
457dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 2:
458dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 7:
459dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    slice_type = SliceType.I;
460dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    break;
461dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
462dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 3:
463dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 8:
464dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    slice_type = SliceType.SP;
465dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    break;
466dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
467dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 4:
468dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                case 9:
469dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    slice_type = SliceType.SI;
470dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    break;
471dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
472dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
473dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            pic_parameter_set_id = reader.readUE("SliceHeader: pic_parameter_set_id");
474dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (sps.residual_color_transform_flag) {
475dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                colour_plane_id = reader.readU(2, "SliceHeader: colour_plane_id");
476dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
477dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            frame_num = reader.readU(sps.log2_max_frame_num_minus4 + 4, "SliceHeader: frame_num");
478dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
479dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (!sps.frame_mbs_only_flag) {
480dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                field_pic_flag = reader.readBool("SliceHeader: field_pic_flag");
481dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (field_pic_flag) {
482dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    bottom_field_flag = reader.readBool("SliceHeader: bottom_field_flag");
483dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
484dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
485dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (IdrPicFlag) {
486dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                idr_pic_id = reader.readUE("SliceHeader: idr_pic_id");
487dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (sps.pic_order_cnt_type == 0) {
488dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    pic_order_cnt_lsb = reader.readU(sps.log2_max_pic_order_cnt_lsb_minus4 + 4, "SliceHeader: pic_order_cnt_lsb");
489dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (pps.pic_order_present_flag && !field_pic_flag) {
490dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        delta_pic_order_cnt_bottom = reader.readSE("SliceHeader: delta_pic_order_cnt_bottom");
491dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
492dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
493dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
494dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
495dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
496dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        @Override
497dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public String toString() {
498dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return "SliceHeader{" +
499dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    "first_mb_in_slice=" + first_mb_in_slice +
500dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", slice_type=" + slice_type +
501dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", pic_parameter_set_id=" + pic_parameter_set_id +
502dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", colour_plane_id=" + colour_plane_id +
503dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", frame_num=" + frame_num +
504dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", field_pic_flag=" + field_pic_flag +
505dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", bottom_field_flag=" + bottom_field_flag +
506dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", idr_pic_id=" + idr_pic_id +
507dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", pic_order_cnt_lsb=" + pic_order_cnt_lsb +
508dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", delta_pic_order_cnt_bottom=" + delta_pic_order_cnt_bottom +
509dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    '}';
510dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
511dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
512dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
513dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private class ReaderWrapper {
514dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        private InputStream inputStream;
515dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        private long pos = 0;
516dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
517dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        private long markPos = 0;
518dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
519dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
520dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        private ReaderWrapper(InputStream inputStream) {
521dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            this.inputStream = inputStream;
522dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
523dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
524dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int read() throws IOException {
525dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            pos++;
526dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return inputStream.read();
527dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
528dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
529dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        long read(byte[] data) throws IOException {
530dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            long read = inputStream.read(data);
531dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            pos += read;
532dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return read;
533dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
534dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
535dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        long seek(int dist) throws IOException {
536dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            long seeked = inputStream.skip(dist);
537dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            pos += seeked;
538dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return seeked;
539dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
540dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
541dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public long getPos() {
542dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return pos;
543dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
544dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
545dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public void mark() {
546dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int i = 1048576;
547dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            LOG.fine("Marking with " + i + " at " + pos);
548dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            inputStream.mark(i);
549dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            markPos = pos;
550dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
551dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
552dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
553dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public void reset() throws IOException {
554dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            long diff = pos - markPos;
555dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            LOG.fine("Resetting to " + markPos + " (pos is " + pos + ") which makes the buffersize " + diff);
556dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            inputStream.reset();
557dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            pos = markPos;
558dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
559dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
560dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
561dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public class SEIMessage {
562dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
563dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int payloadType = 0;
564dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int payloadSize = 0;
565dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
566dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        boolean removal_delay_flag;
567dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int cpb_removal_delay;
568dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int dpb_removal_delay;
569dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
570dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        boolean clock_timestamp_flag;
571dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int pic_struct;
572dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int ct_type;
573dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int nuit_field_based_flag;
574dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int counting_type;
575dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int full_timestamp_flag;
576dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int discontinuity_flag;
577dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int cnt_dropped_flag;
578dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int n_frames;
579dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int seconds_value;
580dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int minutes_value;
581dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int hours_value;
582dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int time_offset_length;
583dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int time_offset;
584dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
585dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        SeqParameterSet sps;
586dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
587dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public SEIMessage(InputStream is, SeqParameterSet sps) throws IOException {
588dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            this.sps = sps;
589dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            is.read();
590dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int datasize = is.available();
591dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int read = 0;
592dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            while (read < datasize) {
593dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                payloadType = 0;
594dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                payloadSize = 0;
595dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                int last_payload_type_bytes = is.read();
596dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                read++;
597dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                while (last_payload_type_bytes == 0xff) {
598dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    payloadType += last_payload_type_bytes;
599dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    last_payload_type_bytes = is.read();
600dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    read++;
601dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
602dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                payloadType += last_payload_type_bytes;
603dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                int last_payload_size_bytes = is.read();
604dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                read++;
605dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
606dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                while (last_payload_size_bytes == 0xff) {
607dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    payloadSize += last_payload_size_bytes;
608dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    last_payload_size_bytes = is.read();
609dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    read++;
610dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
611dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                payloadSize += last_payload_size_bytes;
612dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (datasize - read >= payloadSize) {
613dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (payloadType == 1) { // pic_timing is what we are interested in!
614dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        if (sps.vuiParams != null && (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null || sps.vuiParams.pic_struct_present_flag)) {
615dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            byte[] data = new byte[payloadSize];
616dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            is.read(data);
617dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            read += payloadSize;
618dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            CAVLCReader reader = new CAVLCReader(new ByteArrayInputStream(data));
619dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            if (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null) {
620dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                removal_delay_flag = true;
621dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                cpb_removal_delay = reader.readU(sps.vuiParams.nalHRDParams.cpb_removal_delay_length_minus1 + 1, "SEI: cpb_removal_delay");
622dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                dpb_removal_delay = reader.readU(sps.vuiParams.nalHRDParams.dpb_output_delay_length_minus1 + 1, "SEI: dpb_removal_delay");
623dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            } else {
624dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                removal_delay_flag = false;
625dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            }
626dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            if (sps.vuiParams.pic_struct_present_flag) {
627dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                pic_struct = reader.readU(4, "SEI: pic_struct");
628dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                int numClockTS;
629dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                switch (pic_struct) {
630dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 0:
631dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 1:
632dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 2:
633dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    default:
634dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        numClockTS = 1;
635dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        break;
636dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
637dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 3:
638dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 4:
639dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 7:
640dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        numClockTS = 2;
641dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        break;
642dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
643dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 5:
644dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 6:
645dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    case 8:
646dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        numClockTS = 3;
647dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        break;
648dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                }
649dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                for (int i = 0; i < numClockTS; i++) {
650dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    clock_timestamp_flag = reader.readBool("pic_timing SEI: clock_timestamp_flag[" + i + "]");
651dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    if (clock_timestamp_flag) {
652dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        ct_type = reader.readU(2, "pic_timing SEI: ct_type");
653dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        nuit_field_based_flag = reader.readU(1, "pic_timing SEI: nuit_field_based_flag");
654dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        counting_type = reader.readU(5, "pic_timing SEI: counting_type");
655dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        full_timestamp_flag = reader.readU(1, "pic_timing SEI: full_timestamp_flag");
656dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        discontinuity_flag = reader.readU(1, "pic_timing SEI: discontinuity_flag");
657dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        cnt_dropped_flag = reader.readU(1, "pic_timing SEI: cnt_dropped_flag");
658dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        n_frames = reader.readU(8, "pic_timing SEI: n_frames");
659dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        if (full_timestamp_flag == 1) {
660dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            seconds_value = reader.readU(6, "pic_timing SEI: seconds_value");
661dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            minutes_value = reader.readU(6, "pic_timing SEI: minutes_value");
662dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            hours_value = reader.readU(5, "pic_timing SEI: hours_value");
663dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        } else {
664dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            if (reader.readBool("pic_timing SEI: seconds_flag")) {
665dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                seconds_value = reader.readU(6, "pic_timing SEI: seconds_value");
666dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                if (reader.readBool("pic_timing SEI: minutes_flag")) {
667dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                    minutes_value = reader.readU(6, "pic_timing SEI: minutes_value");
668dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                    if (reader.readBool("pic_timing SEI: hours_flag")) {
669dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                        hours_value = reader.readU(5, "pic_timing SEI: hours_value");
670dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                    }
671dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                }
672dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            }
673dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        }
674dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        if (true) {
675dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            if (sps.vuiParams.nalHRDParams != null) {
676dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                time_offset_length = sps.vuiParams.nalHRDParams.time_offset_length;
677dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            } else if (sps.vuiParams.vclHRDParams != null) {
678dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                time_offset_length = sps.vuiParams.vclHRDParams.time_offset_length;
679dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            } else {
680dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                                time_offset_length = 24;
681dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            }
682dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                            time_offset = reader.readU(24, "pic_timing SEI: time_offset");
683dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                        }
684dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                    }
685dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                }
686dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            }
687dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
688dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        } else {
689dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            for (int i = 0; i < payloadSize; i++) {
690dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                is.read();
691dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                read++;
692dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            }
693dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        }
694dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    } else {
695dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        for (int i = 0; i < payloadSize; i++) {
696dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            is.read();
697dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            read++;
698dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        }
699dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
700dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                } else {
701dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    read = datasize;
702dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
703dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                LOG.fine(this.toString());
704dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
705dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
706dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
707dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        @Override
708dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        public String toString() {
709dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            String out = "SEIMessage{" +
710dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    "payloadType=" + payloadType +
711dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    ", payloadSize=" + payloadSize;
712dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (payloadType == 1) {
713dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null) {
714dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
715dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    out += ", cpb_removal_delay=" + cpb_removal_delay +
716dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                            ", dpb_removal_delay=" + dpb_removal_delay;
717dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
718dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                if (sps.vuiParams.pic_struct_present_flag) {
719dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    out += ", pic_struct=" + pic_struct;
720dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    if (clock_timestamp_flag) {
721dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        out += ", ct_type=" + ct_type +
722dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", nuit_field_based_flag=" + nuit_field_based_flag +
723dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", counting_type=" + counting_type +
724dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", full_timestamp_flag=" + full_timestamp_flag +
725dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", discontinuity_flag=" + discontinuity_flag +
726dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", cnt_dropped_flag=" + cnt_dropped_flag +
727dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", n_frames=" + n_frames +
728dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", seconds_value=" + seconds_value +
729dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", minutes_value=" + minutes_value +
730dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", hours_value=" + hours_value +
731dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", time_offset_length=" + time_offset_length +
732dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                                ", time_offset=" + time_offset;
733dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    }
734dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                }
735dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
736dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            out += '}';
737dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return out;
738dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
739dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
740dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu}
741