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 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