• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.googlecode.mp4parser.authoring.tracks;
2 
3 import com.coremedia.iso.boxes.*;
4 import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
5 import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
6 import com.googlecode.mp4parser.authoring.AbstractTrack;
7 import com.googlecode.mp4parser.authoring.TrackMetaData;
8 import com.googlecode.mp4parser.h264.model.PictureParameterSet;
9 import com.googlecode.mp4parser.h264.model.SeqParameterSet;
10 import com.googlecode.mp4parser.h264.read.CAVLCReader;
11 
12 import java.io.ByteArrayInputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.nio.ByteBuffer;
16 import java.util.ArrayList;
17 import java.util.Date;
18 import java.util.LinkedList;
19 import java.util.List;
20 import 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  */
26 public 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 
H264TrackImpl(InputStream inputStream, String lang, int timescale)58     public H264TrackImpl(InputStream inputStream, String lang, int timescale) throws IOException {
59         this.lang = lang;
60         if (timescale > 1000) {
61             this.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 
H264TrackImpl(InputStream inputStream, String lang)70     public H264TrackImpl(InputStream inputStream, String lang) throws IOException {
71         this.lang = lang;
72         parse(inputStream);
73     }
74 
H264TrackImpl(InputStream inputStream)75     public H264TrackImpl(InputStream inputStream) throws IOException {
76         parse(inputStream);
77     }
78 
parse(InputStream inputStream)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 
getSampleDescriptionBox()130     public SampleDescriptionBox getSampleDescriptionBox() {
131         return sampleDescriptionBox;
132     }
133 
getDecodingTimeEntries()134     public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
135         return stts;
136     }
137 
getCompositionTimeEntries()138     public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
139         return ctts;
140     }
141 
getSyncSamples()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 
getSampleDependencies()150     public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
151         return sdtp;
152     }
153 
getTrackMetaData()154     public TrackMetaData getTrackMetaData() {
155         return trackMetaData;
156     }
157 
getHandler()158     public String getHandler() {
159         return "vide";
160     }
161 
getSamples()162     public List<ByteBuffer> getSamples() {
163         return samples;
164     }
165 
getMediaHeaderBox()166     public AbstractMediaHeaderBox getMediaHeaderBox() {
167         return new VideoMediaHeaderBox();
168     }
169 
getSubsampleInformationBox()170     public SubSampleInformationBox getSubsampleInformationBox() {
171         return null;
172     }
173 
readVariables()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 
findNextStartcode()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 
readSamples()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 
createSample(List<byte[]> buffers)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 
cleanBuffer(byte[] data)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 
handleNALUnit(int nal_ref_idc, int nal_unit_type, byte[] data)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 
configureFramerate()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 
printAccessUnitDelimiter(byte[] data)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 
SliceHeader(InputStream is, SeqParameterSet sps, PictureParameterSet pps, boolean IdrPicFlag)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
toString()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 
ReaderWrapper(InputStream inputStream)520         private ReaderWrapper(InputStream inputStream) {
521             this.inputStream = inputStream;
522         }
523 
read()524         int read() throws IOException {
525             pos++;
526             return inputStream.read();
527         }
528 
read(byte[] data)529         long read(byte[] data) throws IOException {
530             long read = inputStream.read(data);
531             pos += read;
532             return read;
533         }
534 
seek(int dist)535         long seek(int dist) throws IOException {
536             long seeked = inputStream.skip(dist);
537             pos += seeked;
538             return seeked;
539         }
540 
getPos()541         public long getPos() {
542             return pos;
543         }
544 
mark()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 
reset()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 
SEIMessage(InputStream is, SeqParameterSet sps)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
toString()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