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