summaryrefslogtreecommitdiff
path: root/isoparser/src/main/java/com/googlecode
diff options
context:
space:
mode:
authorTeng-Hui Zhu <ztenghui@google.com>2012-09-20 16:00:17 -0700
committerTeng-Hui Zhu <ztenghui@google.com>2012-09-20 16:25:28 -0700
commitdd9eb897ee7c7b507cbdcf80263bb4b5de6966bf (patch)
treea0f3b67524d3e7beeca5e30878f349d58a65b705 /isoparser/src/main/java/com/googlecode
parent8436c0da2d787a33439f14e9273ea647f346fa9b (diff)
downloadmp4parser-dd9eb897ee7c7b507cbdcf80263bb4b5de6966bf.tar.gz
Initial drop the compilable version of mp4parser, with least modification
bug:7093055 Change-Id: Id9b1b4ec91e26ae6e9fd75d86696aa30f30897b3
Diffstat (limited to 'isoparser/src/main/java/com/googlecode')
-rw-r--r--isoparser/src/main/java/com/googlecode/.svn/all-wcprops5
-rw-r--r--isoparser/src/main/java/com/googlecode/.svn/entries31
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/.svn/all-wcprops41
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/.svn/entries250
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractBox.java.svn-base275
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractContainerBox.java.svn-base171
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractFullBox.java.svn-base74
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/FullContainerBox.java.svn-base159
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/RequiresParseDetailAspect.java.svn-base62
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/Version.java.svn-base27
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/AbstractBox.java275
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/AbstractContainerBox.java171
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/AbstractFullBox.java74
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/FullContainerBox.java159
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/Version.java27
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/all-wcprops17
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/entries96
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/text-base/DoNotParseDetail.java.svn-base43
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/text-base/ParseDetail.java.svn-base33
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/annotations/DoNotParseDetail.java43
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/annotations/ParseDetail.java33
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/all-wcprops41
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/entries244
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/AbstractTrack.java.svn-base60
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/DateHelper.java.svn-base44
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Movie.java.svn-base91
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Mp4TrackImpl.java.svn-base219
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Track.java.svn-base60
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/TrackMetaData.java.svn-base130
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/AbstractTrack.java60
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/DateHelper.java44
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/Movie.java91
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/Mp4TrackImpl.java219
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/Track.java60
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/TrackMetaData.java130
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/all-wcprops47
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/entries266
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/AbstractManifestWriter.java.svn-base126
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/AudioQuality.java.svn-base29
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/FlatManifestWriterImpl.java.svn-base643
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/FlatPackageWriterImpl.java.svn-base197
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/ManifestWriter.java.svn-base31
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/PackageWriter.java.svn-base27
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/VideoQuality.java.svn-base25
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AbstractManifestWriter.java126
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AudioQuality.java29
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatManifestWriterImpl.java643
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatPackageWriterImpl.java197
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/ManifestWriter.java31
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/PackageWriter.java27
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/VideoQuality.java25
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/all-wcprops47
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/entries266
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/ByteBufferHelper.java.svn-base50
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/DefaultMp4Builder.java.svn-base576
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentIntersectionFinder.java.svn-base34
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentedMp4Builder.java.svn-base742
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/Mp4Builder.java.svn-base35
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/SyncSampleIntersectFinderImpl.java.svn-base334
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/TwoSecondIntersectionFinder.java.svn-base86
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/ByteBufferHelper.java50
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/DefaultMp4Builder.java576
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentIntersectionFinder.java34
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentedMp4Builder.java742
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/Mp4Builder.java35
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/SyncSampleIntersectFinderImpl.java334
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/TwoSecondIntersectionFinder.java86
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/.svn/all-wcprops5
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/.svn/entries31
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/all-wcprops11
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/entries62
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/text-base/MovieCreator.java.svn-base40
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/MovieCreator.java40
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/all-wcprops89
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/entries504
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AACTrackImpl.java.svn-base292
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AC3TrackImpl.java.svn-base513
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/Amf0Track.java.svn-base116
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AppendTrack.java.svn-base348
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ChangeTimeScaleTrack.java.svn-base203
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/CroppedTrack.java.svn-base151
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/DivideTimeScaleTrack.java.svn-base126
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/EC3TrackImpl.java.svn-base436
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/H264TrackImpl.java.svn-base740
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/MultiplyTimeScaleTrack.java.svn-base130
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/QuicktimeTextTrackImpl.java.svn-base165
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ReplaceSampleTrack.java.svn-base104
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/SilenceTrackImpl.java.svn-base98
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/TextTrackImpl.java.svn-base165
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java292
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AC3TrackImpl.java513
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/Amf0Track.java116
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AppendTrack.java348
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ChangeTimeScaleTrack.java203
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/CroppedTrack.java151
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/DivideTimeScaleTrack.java126
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/EC3TrackImpl.java436
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/H264TrackImpl.java740
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/MultiplyTimeScaleTrack.java130
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/QuicktimeTextTrackImpl.java165
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ReplaceSampleTrack.java104
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/SilenceTrackImpl.java98
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/TextTrackImpl.java165
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/all-wcprops47
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/entries293
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/prop-base/mp4-boxes.zip.svn-base5
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AC3SpecificBox.java.svn-base119
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AbstractSampleEncryptionBox.java.svn-base350
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AbstractTrackEncryptionBox.java.svn-base93
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/DTSSpecificBox.java.svn-base217
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/EC3SpecificBox.java.svn-base140
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/MLPSpecificBox.java.svn-base76
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/mp4-boxes.zip.svn-basebin0 -> 62700 bytes
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/AC3SpecificBox.java119
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractSampleEncryptionBox.java350
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractTrackEncryptionBox.java93
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/DTSSpecificBox.java217
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/EC3SpecificBox.java140
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/MLPSpecificBox.java76
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/all-wcprops11
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/entries62
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/text-base/ActionMessageFormat0SampleEntryBox.java.svn-base38
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/ActionMessageFormat0SampleEntryBox.java38
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/all-wcprops41
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/entries232
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/BaseMediaInfoAtom.java.svn-base110
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/GenericMediaHeaderAtom.java.svn-base28
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/GenericMediaHeaderTextAtom.java.svn-base130
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/QuicktimeTextSampleEntry.java.svn-base237
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/TaptAtom.java.svn-base16
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/TimeCodeBox.java.svn-base54
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/BaseMediaInfoAtom.java110
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderAtom.java28
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderTextAtom.java130
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/QuicktimeTextSampleEntry.java237
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TaptAtom.java16
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TimeCodeBox.java54
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/all-wcprops23
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/entries130
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/AvcNalUnitStorageBox.java.svn-base97
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/SampleEncryptionBox.java.svn-base24
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/TrackEncryptionBox.java.svn-base12
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/AvcNalUnitStorageBox.java97
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/SampleEncryptionBox.java24
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/TrackEncryptionBox.java12
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/all-wcprops11
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/entries62
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/text-base/ProtectionSystemSpecificHeaderBox.java.svn-base89
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/ProtectionSystemSpecificHeaderBox.java89
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4-boxes.zipbin0 -> 62700 bytes
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/all-wcprops23
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/entries136
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/AbstractDescriptorBox.java.svn-base84
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/ESDescriptorBox.java.svn-base36
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/ObjectDescriptorBox.java.svn-base46
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/AbstractDescriptorBox.java84
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ESDescriptorBox.java36
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ObjectDescriptorBox.java46
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/all-wcprops107
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/entries606
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/AudioSpecificConfig.java.svn-base1176
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BaseDescriptor.java.svn-base99
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BitReaderBuffer.java.svn-base51
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BitWriterBuffer.java.svn-base36
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/DecoderConfigDescriptor.java.svn-base262
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/DecoderSpecificInfo.java.svn-base85
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/Descriptor.java.svn-base39
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ESDescriptor.java.svn-base376
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ExtensionDescriptor.java.svn-base73
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ExtensionProfileLevelDescriptor.java.svn-base51
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/InitialObjectDescriptor.java.svn-base136
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptor.java_bak.svn-base104
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptorBase.java.svn-base27
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptorFactory.java.svn-base189
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ProfileLevelIndicationDescriptor.java.svn-base70
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/SLConfigDescriptor.java.svn-base119
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/UnknownDescriptor.java.svn-base42
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/AudioSpecificConfig.java1176
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BaseDescriptor.java99
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitReaderBuffer.java51
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitWriterBuffer.java36
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderConfigDescriptor.java262
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderSpecificInfo.java85
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/Descriptor.java39
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ESDescriptor.java376
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionDescriptor.java73
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionProfileLevelDescriptor.java51
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/InitialObjectDescriptor.java136
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptor.java_bak104
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorBase.java27
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorFactory.java189
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ProfileLevelIndicationDescriptor.java70
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/SLConfigDescriptor.java119
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/UnknownDescriptor.java42
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/all-wcprops59
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/entries334
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/CencSampleEncryptionInformationGroupEntry.java.svn-base125
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/GroupEntry.java.svn-base28
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RateShareEntry.java.svn-base246
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RollRecoveryEntry.java.svn-base77
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleGroupDescriptionBox.java.svn-base200
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleToGroupBox.java.svn-base174
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/TemporalLevelEntry.java.svn-base82
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/UnknownEntry.java.svn-base83
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/VisualRandomAccessEntry.java.svn-base94
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java125
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java28
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java246
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java77
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java200
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java174
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java82
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java83
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java94
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/all-wcprops47
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/entries266
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PiffSampleEncryptionBox.java.svn-base45
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PiffTrackEncryptionBox.java.svn-base34
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PlayReadyHeader.java.svn-base253
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/ProtectionSpecificHeader.java.svn-base79
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/TfrfBox.java.svn-base129
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/TfxdBox.java.svn-base85
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/UuidBasedProtectionSystemSpecificHeaderBox.java.svn-base106
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffSampleEncryptionBox.java45
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffTrackEncryptionBox.java34
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java253
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/ProtectionSpecificHeader.java79
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfrfBox.java129
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfxdBox.java85
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/UuidBasedProtectionSystemSpecificHeaderBox.java106
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/all-wcprops11
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/entries62
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/text-base/SegmentIndexBox.java.svn-base283
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/SegmentIndexBox.java283
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/all-wcprops11
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/entries62
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/text-base/FontTableBox.java.svn-base95
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/FontTableBox.java95
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/all-wcprops17
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/entries96
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/text-base/AssetInformationBox.java.svn-base75
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/text-base/BaseLocationBox.java.svn-base103
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/AssetInformationBox.java75
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/BaseLocationBox.java103
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/all-wcprops23
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/entries139
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/BTree.java.svn-base69
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/CharCache.java.svn-base57
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/Debug.java.svn-base88
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/BTree.java69
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/CharCache.java57
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/Debug.java88
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/all-wcprops59
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/entries334
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/AspectRatio.java.svn-base50
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/BitstreamElement.java.svn-base29
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ChromaFormat.java.svn-base77
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/HRDParameters.java.svn-base53
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/PictureParameterSet.java.svn-base406
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ScalingList.java.svn-base83
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ScalingMatrix.java.svn-base37
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/SeqParameterSet.java.svn-base556
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/VUIParameters.java.svn-base94
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/AspectRatio.java50
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/BitstreamElement.java29
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ChromaFormat.java77
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/HRDParameters.java53
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/PictureParameterSet.java406
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingList.java83
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingMatrix.java37
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/SeqParameterSet.java556
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/model/VUIParameters.java94
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/all-wcprops17
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/entries96
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/text-base/BitstreamReader.java.svn-base194
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/text-base/CAVLCReader.java.svn-base185
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/read/BitstreamReader.java194
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/read/CAVLCReader.java185
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/all-wcprops17
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/entries96
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/text-base/BitstreamWriter.java.svn-base108
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/text-base/CAVLCWriter.java.svn-base100
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/write/BitstreamWriter.java108
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/h264/write/CAVLCWriter.java100
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/all-wcprops11
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/entries62
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/text-base/SrtParser.java.svn-base59
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/srt/SrtParser.java59
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/all-wcprops35
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/entries198
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/ByteBufferByteChannel.java.svn-base56
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/CastUtils.java.svn-base34
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/Math.java.svn-base30
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/Path.java.svn-base115
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/UUIDConverter.java.svn-base48
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/ByteBufferByteChannel.java56
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/CastUtils.java34
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/Math.java30
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/Path.java115
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/util/UUIDConverter.java48
300 files changed, 41562 insertions, 0 deletions
diff --git a/isoparser/src/main/java/com/googlecode/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/.svn/all-wcprops
new file mode 100644
index 0000000..ca84640
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/776/trunk/isoparser/src/main/java/com/googlecode
+END
diff --git a/isoparser/src/main/java/com/googlecode/.svn/entries b/isoparser/src/main/java/com/googlecode/.svn/entries
new file mode 100644
index 0000000..c776f07
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-09-10T14:34:23.574807Z
+776
+sebastian.annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+mp4parser
+dir
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/all-wcprops
new file mode 100644
index 0000000..31dd55d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/776/trunk/isoparser/src/main/java/com/googlecode/mp4parser
+END
+AbstractContainerBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/616/trunk/isoparser/src/main/java/com/googlecode/mp4parser/AbstractContainerBox.java
+END
+AbstractFullBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/525/trunk/isoparser/src/main/java/com/googlecode/mp4parser/AbstractFullBox.java
+END
+Version.java
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/599/trunk/isoparser/src/main/java/com/googlecode/mp4parser/Version.java
+END
+AbstractBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/772/trunk/isoparser/src/main/java/com/googlecode/mp4parser/AbstractBox.java
+END
+RequiresParseDetailAspect.java
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/705/trunk/isoparser/src/main/java/com/googlecode/mp4parser/RequiresParseDetailAspect.java
+END
+FullContainerBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/616/trunk/isoparser/src/main/java/com/googlecode/mp4parser/FullContainerBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/entries
new file mode 100644
index 0000000..159e3a7
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/entries
@@ -0,0 +1,250 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-09-10T14:34:23.574807Z
+776
+sebastian.annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+AbstractContainerBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.677236Z
+023ce1033e326946fb1e551942a711fe
+2012-05-17T09:04:15.805545Z
+616
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5225
+
+AbstractFullBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.677236Z
+9a0208c01832235bc5b32054aaea4cb5
+2012-04-25T19:24:04.485529Z
+525
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1937
+
+authoring
+dir
+
+boxes
+dir
+
+h264
+dir
+
+Version.java
+file
+
+
+
+
+2012-09-14T17:27:51.677236Z
+6123f036beaaa10451566670349e4454
+2012-05-10T08:19:36.847188Z
+599
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+718
+
+srt
+dir
+
+AbstractBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.677236Z
+35f21f761802c8d830736d617b7dd93a
+2012-09-01T02:22:41.253285Z
+772
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9423
+
+annotations
+dir
+
+util
+dir
+
+RequiresParseDetailAspect.java
+file
+
+
+
+
+2012-09-14T17:27:51.677236Z
+f5277a7d1c211e4312c9ff308ad3b5f9
+2012-07-06T09:18:45.003720Z
+705
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2591
+
+FullContainerBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.677236Z
+96f90fe7c15db4a397da4f38d047702f
+2012-05-17T09:04:15.805545Z
+616
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5054
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractBox.java.svn-base
new file mode 100644
index 0000000..f75bc1d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractBox.java.svn-base
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.ChannelHelper;
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.ContainerBox;
+import com.coremedia.iso.boxes.UserBox;
+import com.googlecode.mp4parser.annotations.DoNotParseDetail;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * A basic on-demand parsing box. Requires the implementation of three methods to become a fully working box:
+ * <ol>
+ * <li>{@link #_parseDetails(java.nio.ByteBuffer)}</li>
+ * <li>{@link #getContent(java.nio.ByteBuffer)}</li>
+ * <li>{@link #getContentSize()}</li>
+ * </ol>
+ * additionally this new box has to be put into the <code>isoparser-default.properties</code> file so that
+ * it is accessible by the <code>PropertyBoxParserImpl</code>
+ */
+public abstract class AbstractBox implements Box {
+ public static int MEM_MAP_THRESHOLD = 100 * 1024;
+ private static Logger LOG = Logger.getLogger(AbstractBox.class.getName());
+
+ protected String type;
+ private byte[] userType;
+ private ContainerBox parent;
+
+ private ByteBuffer content;
+ private ByteBuffer deadBytes = null;
+
+
+ protected AbstractBox(String type) {
+ this.type = type;
+ }
+
+ protected AbstractBox(String type, byte[] userType) {
+ this.type = type;
+ this.userType = userType;
+ }
+
+ /**
+ * Get the box's content size without its header. This must be the exact number of bytes
+ * that <code>getContent(ByteBuffer)</code> writes.
+ *
+ * @return Gets the box's content size in bytes
+ * @see #getContent(java.nio.ByteBuffer)
+ */
+ protected abstract long getContentSize();
+
+ /**
+ * Write the box's content into the given <code>ByteBuffer</code>. This must include flags
+ * and version in case of a full box. <code>byteBuffer</code> has been initialized with
+ * <code>getSize()</code> bytes.
+ *
+ * @param byteBuffer the sink for the box's content
+ */
+ protected abstract void getContent(ByteBuffer byteBuffer);
+
+ /**
+ * Parse the box's fields and child boxes if any.
+ *
+ * @param content the box's raw content beginning after the 4-cc field.
+ */
+ protected abstract void _parseDetails(ByteBuffer content);
+
+ /**
+ * Read the box's content from a byte channel without parsing it. Parsing is done on-demand.
+ *
+ * @param readableByteChannel the (part of the) iso file to parse
+ * @param contentSize expected contentSize of the box
+ * @param boxParser creates inner boxes
+ * @throws IOException in case of an I/O error.
+ */
+ @DoNotParseDetail
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+ if (readableByteChannel instanceof FileChannel && contentSize > MEM_MAP_THRESHOLD) {
+ // todo: if I map this here delayed I could use transferFrom/transferTo in the getBox method
+ // todo: potentially this could speed up writing.
+ //
+ // It's quite expensive to map a file into the memory. Just do it when the box is larger than a MB.
+ content = ((FileChannel) readableByteChannel).map(FileChannel.MapMode.READ_ONLY, ((FileChannel) readableByteChannel).position(), contentSize);
+ ((FileChannel) readableByteChannel).position(((FileChannel) readableByteChannel).position() + contentSize);
+ } else {
+ assert contentSize < Integer.MAX_VALUE;
+ content = ChannelHelper.readFully(readableByteChannel, contentSize);
+ }
+ }
+
+ public void getBox(WritableByteChannel os) throws IOException {
+ ByteBuffer bb = ByteBuffer.allocate(l2i(getSize()));
+ getHeader(bb);
+ if (content == null) {
+ getContent(bb);
+ if (deadBytes != null) {
+ deadBytes.rewind();
+ while (deadBytes.remaining() > 0) {
+ bb.put(deadBytes);
+ }
+ }
+ } else {
+ content.rewind();
+ bb.put(content);
+ }
+ bb.rewind();
+ os.write(bb);
+ }
+
+
+ /**
+ * Parses the raw content of the box. It surrounds the actual parsing
+ * which is done
+ */
+ synchronized final void parseDetails() {
+ if (content != null) {
+ ByteBuffer content = this.content;
+ this.content = null;
+ content.rewind();
+ _parseDetails(content);
+ if (content.remaining() > 0) {
+ deadBytes = content.slice();
+ }
+ assert verify(content);
+ }
+ }
+
+ /**
+ * Sets the 'dead' bytes. These bytes are left if the content of the box
+ * has been parsed but not all bytes have been used up.
+ *
+ * @param newDeadBytes the unused bytes with no meaning but required for bytewise reconstruction
+ */
+ protected void setDeadBytes(ByteBuffer newDeadBytes) {
+ deadBytes = newDeadBytes;
+ }
+
+
+ /**
+ * Gets the full size of the box including header and content.
+ *
+ * @return the box's size
+ */
+ public long getSize() {
+ long size = (content == null ? getContentSize() : content.limit());
+ size += (8 + // size|type
+ (size >= ((1L << 32) - 8) ? 8 : 0) + // 32bit - 8 byte size and type
+ (UserBox.TYPE.equals(getType()) ? 16 : 0));
+ size += (deadBytes == null ? 0 : deadBytes.limit());
+ return size;
+ }
+
+ @DoNotParseDetail
+ public String getType() {
+ return type;
+ }
+
+ @DoNotParseDetail
+ public byte[] getUserType() {
+ return userType;
+ }
+
+ @DoNotParseDetail
+ public ContainerBox getParent() {
+ return parent;
+ }
+
+ @DoNotParseDetail
+ public void setParent(ContainerBox parent) {
+ this.parent = parent;
+ }
+
+ @DoNotParseDetail
+ public IsoFile getIsoFile() {
+ return parent.getIsoFile();
+ }
+
+ /**
+ * Check if details are parsed.
+ *
+ * @return <code>true</code> whenever the content <code>ByteBuffer</code> is not <code>null</code>
+ */
+ public boolean isParsed() {
+ return content == null;
+ }
+
+
+ /**
+ * Verifies that a box can be reconstructed byte-exact after parsing.
+ *
+ * @param content the raw content of the box
+ * @return <code>true</code> if raw content exactly matches the reconstructed content
+ */
+ private boolean verify(ByteBuffer content) {
+ ByteBuffer bb = ByteBuffer.allocate(l2i(getContentSize() + (deadBytes != null ? deadBytes.limit() : 0)));
+ getContent(bb);
+ if (deadBytes != null) {
+ deadBytes.rewind();
+ while (deadBytes.remaining() > 0) {
+ bb.put(deadBytes);
+ }
+ }
+ content.rewind();
+ bb.rewind();
+
+
+ if (content.remaining() != bb.remaining()) {
+ LOG.severe(this.getType() + ": remaining differs " + content.remaining() + " vs. " + bb.remaining());
+ return false;
+ }
+ int p = content.position();
+ for (int i = content.limit() - 1, j = bb.limit() - 1; i >= p; i--, j--) {
+ byte v1 = content.get(i);
+ byte v2 = bb.get(j);
+ if (v1 != v2) {
+ LOG.severe(String.format("%s: buffers differ at %d: %2X/%2X", this.getType(), i, v1, v2));
+ byte[] b1 = new byte[content.remaining()];
+ byte[] b2 = new byte[bb.remaining()];
+ content.get(b1);
+ bb.get(b2);
+ System.err.println("original : " + Hex.encodeHex(b1, 4));
+ System.err.println("reconstructed : " + Hex.encodeHex(b2, 4));
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ private boolean isSmallBox() {
+ return (content == null ? (getContentSize() + (deadBytes != null ? deadBytes.limit() : 0) + 8) : content.limit()) < 1L << 32;
+ }
+
+ private void getHeader(ByteBuffer byteBuffer) {
+ if (isSmallBox()) {
+ IsoTypeWriter.writeUInt32(byteBuffer, this.getSize());
+ byteBuffer.put(IsoFile.fourCCtoBytes(getType()));
+ } else {
+ IsoTypeWriter.writeUInt32(byteBuffer, 1);
+ byteBuffer.put(IsoFile.fourCCtoBytes(getType()));
+ IsoTypeWriter.writeUInt64(byteBuffer, getSize());
+ }
+ if (UserBox.TYPE.equals(getType())) {
+ byteBuffer.put(getUserType());
+ }
+
+
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractContainerBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractContainerBox.java.svn-base
new file mode 100644
index 0000000..93369f3
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractContainerBox.java.svn-base
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2008 CoreMedia AG, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.ContainerBox;
+import com.googlecode.mp4parser.util.ByteBufferByteChannel;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+
+/**
+ * Abstract base class suitable for most boxes acting purely as container for other boxes.
+ */
+public abstract class AbstractContainerBox extends AbstractBox implements ContainerBox {
+ private static Logger LOG = Logger.getLogger(AbstractContainerBox.class.getName());
+
+ protected List<Box> boxes = new LinkedList<Box>();
+ protected BoxParser boxParser;
+
+ @Override
+ protected long getContentSize() {
+ long contentSize = 0;
+ for (Box boxe : boxes) {
+ contentSize += boxe.getSize();
+ }
+ return contentSize;
+ }
+
+ public AbstractContainerBox(String type) {
+ super(type);
+ }
+
+ public List<Box> getBoxes() {
+ return Collections.unmodifiableList(boxes);
+ }
+
+ public void setBoxes(List<Box> boxes) {
+ this.boxes = new LinkedList<Box>(boxes);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Box> List<T> getBoxes(Class<T> clazz) {
+ return getBoxes(clazz, false);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) {
+ List<T> boxesToBeReturned = new ArrayList<T>(2);
+ for (Box boxe : boxes) {
+ //clazz.isInstance(boxe) / clazz == boxe.getClass()?
+ // I hereby finally decide to use isInstance
+
+ if (clazz.isInstance(boxe)) {
+ boxesToBeReturned.add((T) boxe);
+ }
+
+ if (recursive && boxe instanceof ContainerBox) {
+ boxesToBeReturned.addAll(((ContainerBox) boxe).getBoxes(clazz, recursive));
+ }
+ }
+ return boxesToBeReturned;
+ }
+
+ /**
+ * Add <code>b</code> to the container and sets the parent correctly.
+ *
+ * @param b will be added to the container
+ */
+ public void addBox(Box b) {
+ b.setParent(this);
+ boxes.add(b);
+ }
+
+ public void removeBox(Box b) {
+ b.setParent(this);
+ boxes.remove(b);
+ }
+
+ @Override
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+ super.parse(readableByteChannel, header, contentSize, boxParser);
+ this.boxParser = boxParser;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseChildBoxes(content);
+ }
+
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append(this.getClass().getSimpleName()).append("[");
+ for (int i = 0; i < boxes.size(); i++) {
+ if (i > 0) {
+ buffer.append(";");
+ }
+ buffer.append(boxes.get(i).toString());
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+ /**
+ * The number of bytes from box start (first length byte) to the
+ * first length byte of the first child box
+ *
+ * @return offset to first child box
+ */
+ public long getNumOfBytesToFirstChild() {
+ return 8;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeChildBoxes(byteBuffer);
+ }
+
+ protected final void parseChildBoxes(ByteBuffer content) {
+ try {
+ while (content.remaining() >= 8) { // 8 is the minimal size for a sane box
+ boxes.add(boxParser.parseBox(new ByteBufferByteChannel(content), this));
+ }
+
+ if (content.remaining() != 0) {
+ setDeadBytes(content.slice());
+ LOG.warning("Something's wrong with the sizes. There are dead bytes in a container box.");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected final void writeChildBoxes(ByteBuffer bb) {
+ WritableByteChannel wbc = new ByteBufferByteChannel(bb);
+ for (Box box : boxes) {
+ try {
+ box.getBox(wbc);
+ } catch (IOException e) {
+ // My WritableByteChannel won't throw any excpetion
+ throw new RuntimeException("Cannot happen to me", e);
+ }
+ }
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractFullBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractFullBox.java.svn-base
new file mode 100644
index 0000000..bec8975
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/AbstractFullBox.java.svn-base
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2008 CoreMedia AG, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser;
+
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.FullBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Base class for all ISO Full boxes.
+ */
+public abstract class AbstractFullBox extends AbstractBox implements FullBox {
+ private int version;
+ private int flags;
+
+ protected AbstractFullBox(String type) {
+ super(type);
+ }
+
+ protected AbstractFullBox(String type, byte[] userType) {
+ super(type, userType);
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+
+ public void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+
+ /**
+ * Parses the version/flags header and returns the remaining box size.
+ *
+ * @param content
+ * @return number of bytes read
+ */
+ protected final long parseVersionAndFlags(ByteBuffer content) {
+ version = IsoTypeReader.readUInt8(content);
+ flags = IsoTypeReader.readUInt24(content);
+ return 4;
+ }
+
+ protected final void writeVersionAndFlags(ByteBuffer bb) {
+ IsoTypeWriter.writeUInt8(bb, version);
+ IsoTypeWriter.writeUInt24(bb, flags);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/FullContainerBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/FullContainerBox.java.svn-base
new file mode 100644
index 0000000..d16e47d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/FullContainerBox.java.svn-base
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2008 CoreMedia AG, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.ContainerBox;
+import com.googlecode.mp4parser.util.ByteBufferByteChannel;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * Abstract base class for a full iso box only containing ither boxes.
+ */
+public abstract class FullContainerBox extends AbstractFullBox implements ContainerBox {
+ protected List<Box> boxes = new LinkedList<Box>();
+ private static Logger LOG = Logger.getLogger(FullContainerBox.class.getName());
+ BoxParser boxParser;
+
+ public void setBoxes(List<Box> boxes) {
+ this.boxes = new LinkedList<Box>(boxes);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Box> List<T> getBoxes(Class<T> clazz) {
+ return getBoxes(clazz, false);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) {
+ List<T> boxesToBeReturned = new ArrayList<T>(2);
+ for (Box boxe : boxes) { //clazz.isInstance(boxe) / clazz == boxe.getClass()?
+ if (clazz == boxe.getClass()) {
+ boxesToBeReturned.add((T) boxe);
+ }
+
+ if (recursive && boxe instanceof ContainerBox) {
+ boxesToBeReturned.addAll((((ContainerBox) boxe).getBoxes(clazz, recursive)));
+ }
+ }
+ // Optimize here! Spare object creation work on arrays directly! System.arrayCopy
+ return boxesToBeReturned;
+ //return (T[]) boxesToBeReturned.toArray();
+ }
+
+ protected long getContentSize() {
+ long contentSize = 4; // flags and version
+ for (Box boxe : boxes) {
+ contentSize += boxe.getSize();
+ }
+ return contentSize;
+ }
+
+ public void addBox(Box b) {
+ b.setParent(this);
+ boxes.add(b);
+ }
+
+ public void removeBox(Box b) {
+ b.setParent(null);
+ boxes.remove(b);
+ }
+
+ public FullContainerBox(String type) {
+ super(type);
+ }
+
+ public List<Box> getBoxes() {
+ return boxes;
+ }
+
+ @Override
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+ super.parse(readableByteChannel, header, contentSize, boxParser);
+ this.boxParser = boxParser;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ parseChildBoxes(content);
+ }
+
+ protected final void parseChildBoxes(ByteBuffer content) {
+ try {
+ while (content.remaining() >= 8) { // 8 is the minimal size for a sane box
+ boxes.add(boxParser.parseBox(new ByteBufferByteChannel(content), this));
+ }
+
+ if (content.remaining() != 0) {
+ setDeadBytes(content.slice());
+ LOG.severe("Some sizes are wrong");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(this.getClass().getSimpleName()).append("[");
+ for (int i = 0; i < boxes.size(); i++) {
+ if (i > 0) {
+ buffer.append(";");
+ }
+ buffer.append(boxes.get(i).toString());
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ writeChildBoxes(byteBuffer);
+ }
+
+ protected final void writeChildBoxes(ByteBuffer bb) {
+ WritableByteChannel wbc = new ByteBufferByteChannel(bb);
+ for (Box box : boxes) {
+ try {
+ box.getBox(wbc);
+ } catch (IOException e) {
+ // cannot happen since my WritableByteChannel won't throw any excpetion
+ throw new RuntimeException("Cannot happen.", e);
+ }
+
+ }
+ }
+
+ public long getNumOfBytesToFirstChild() {
+ long sizeOfChildren = 0;
+ for (Box box : boxes) {
+ sizeOfChildren += box.getSize();
+ }
+ return getSize() - sizeOfChildren;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/RequiresParseDetailAspect.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/RequiresParseDetailAspect.java.svn-base
new file mode 100644
index 0000000..5009230
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/RequiresParseDetailAspect.java.svn-base
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+
+@Aspect
+/**
+ * An aspect to trigger the actual parsing of boxes' content when it is actually needed. This aspect
+ * calls {@link com.googlecode.mp4parser.AbstractBox#parseDetails()} before actually executing
+ * the method.
+ */
+public class RequiresParseDetailAspect {
+
+
+ @Before("this(com.googlecode.mp4parser.AbstractBox) && ((execution(public * * (..)) && !( " +
+ "execution(* parseDetails()) || " +
+ "execution(* getNumOfBytesToFirstChild()) || " +
+ "execution(* getType()) || " +
+ "execution(* isParsed()) || " +
+ "execution(* getHeader(*)) || " +
+ "execution(* parse()) || " +
+ "execution(* getBox(*)) || " +
+ "execution(* getSize()) || " +
+ "execution(* parseDetails()) || " +
+ "execution(* _parseDetails(*)) || " +
+ "execution(* parse(*,*,*,*)) || " +
+ "execution(* getIsoFile()) || " +
+ "execution(* getParent()) || " +
+ "execution(* setParent(*)) || " +
+ "execution(* getUserType()) || " +
+ "execution(* setUserType(*))) && " +
+ "!@annotation(com.googlecode.mp4parser.annotations.DoNotParseDetail)) || @annotation(com.googlecode.mp4parser.annotations.ParseDetail))")
+ public void before(JoinPoint joinPoint) {
+ if (joinPoint.getTarget() instanceof AbstractBox) {
+ if (!((AbstractBox) joinPoint.getTarget()).isParsed()) {
+ //System.err.println(String.format("parsed detail %s", joinPoint.getTarget().getClass().getSimpleName()));
+ ((AbstractBox) joinPoint.getTarget()).parseDetails();
+ }
+ } else {
+ throw new RuntimeException("Only methods in subclasses of " + AbstractBox.class.getName() + " can be annotated with ParseDetail");
+ }
+
+ }
+
+
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/Version.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/Version.java.svn-base
new file mode 100644
index 0000000..f93816f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/.svn/text-base/Version.java.svn-base
@@ -0,0 +1,27 @@
+package com.googlecode.mp4parser;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.util.logging.Logger;
+
+/**
+ * The classic version object.
+ */
+public class Version {
+ private static final Logger LOG = Logger.getLogger(Version.class.getName());
+ public static final String VERSION;
+
+ static {
+ LineNumberReader lnr = new LineNumberReader(new InputStreamReader(Version.class.getResourceAsStream("/version.txt")));
+ String version;
+ try {
+ version = lnr.readLine();
+ } catch (IOException e) {
+ LOG.warning(e.getMessage());
+ version = "unknown";
+ }
+ VERSION = version;
+
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/AbstractBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/AbstractBox.java
new file mode 100644
index 0000000..f75bc1d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/AbstractBox.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.ChannelHelper;
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.ContainerBox;
+import com.coremedia.iso.boxes.UserBox;
+import com.googlecode.mp4parser.annotations.DoNotParseDetail;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * A basic on-demand parsing box. Requires the implementation of three methods to become a fully working box:
+ * <ol>
+ * <li>{@link #_parseDetails(java.nio.ByteBuffer)}</li>
+ * <li>{@link #getContent(java.nio.ByteBuffer)}</li>
+ * <li>{@link #getContentSize()}</li>
+ * </ol>
+ * additionally this new box has to be put into the <code>isoparser-default.properties</code> file so that
+ * it is accessible by the <code>PropertyBoxParserImpl</code>
+ */
+public abstract class AbstractBox implements Box {
+ public static int MEM_MAP_THRESHOLD = 100 * 1024;
+ private static Logger LOG = Logger.getLogger(AbstractBox.class.getName());
+
+ protected String type;
+ private byte[] userType;
+ private ContainerBox parent;
+
+ private ByteBuffer content;
+ private ByteBuffer deadBytes = null;
+
+
+ protected AbstractBox(String type) {
+ this.type = type;
+ }
+
+ protected AbstractBox(String type, byte[] userType) {
+ this.type = type;
+ this.userType = userType;
+ }
+
+ /**
+ * Get the box's content size without its header. This must be the exact number of bytes
+ * that <code>getContent(ByteBuffer)</code> writes.
+ *
+ * @return Gets the box's content size in bytes
+ * @see #getContent(java.nio.ByteBuffer)
+ */
+ protected abstract long getContentSize();
+
+ /**
+ * Write the box's content into the given <code>ByteBuffer</code>. This must include flags
+ * and version in case of a full box. <code>byteBuffer</code> has been initialized with
+ * <code>getSize()</code> bytes.
+ *
+ * @param byteBuffer the sink for the box's content
+ */
+ protected abstract void getContent(ByteBuffer byteBuffer);
+
+ /**
+ * Parse the box's fields and child boxes if any.
+ *
+ * @param content the box's raw content beginning after the 4-cc field.
+ */
+ protected abstract void _parseDetails(ByteBuffer content);
+
+ /**
+ * Read the box's content from a byte channel without parsing it. Parsing is done on-demand.
+ *
+ * @param readableByteChannel the (part of the) iso file to parse
+ * @param contentSize expected contentSize of the box
+ * @param boxParser creates inner boxes
+ * @throws IOException in case of an I/O error.
+ */
+ @DoNotParseDetail
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+ if (readableByteChannel instanceof FileChannel && contentSize > MEM_MAP_THRESHOLD) {
+ // todo: if I map this here delayed I could use transferFrom/transferTo in the getBox method
+ // todo: potentially this could speed up writing.
+ //
+ // It's quite expensive to map a file into the memory. Just do it when the box is larger than a MB.
+ content = ((FileChannel) readableByteChannel).map(FileChannel.MapMode.READ_ONLY, ((FileChannel) readableByteChannel).position(), contentSize);
+ ((FileChannel) readableByteChannel).position(((FileChannel) readableByteChannel).position() + contentSize);
+ } else {
+ assert contentSize < Integer.MAX_VALUE;
+ content = ChannelHelper.readFully(readableByteChannel, contentSize);
+ }
+ }
+
+ public void getBox(WritableByteChannel os) throws IOException {
+ ByteBuffer bb = ByteBuffer.allocate(l2i(getSize()));
+ getHeader(bb);
+ if (content == null) {
+ getContent(bb);
+ if (deadBytes != null) {
+ deadBytes.rewind();
+ while (deadBytes.remaining() > 0) {
+ bb.put(deadBytes);
+ }
+ }
+ } else {
+ content.rewind();
+ bb.put(content);
+ }
+ bb.rewind();
+ os.write(bb);
+ }
+
+
+ /**
+ * Parses the raw content of the box. It surrounds the actual parsing
+ * which is done
+ */
+ synchronized final void parseDetails() {
+ if (content != null) {
+ ByteBuffer content = this.content;
+ this.content = null;
+ content.rewind();
+ _parseDetails(content);
+ if (content.remaining() > 0) {
+ deadBytes = content.slice();
+ }
+ assert verify(content);
+ }
+ }
+
+ /**
+ * Sets the 'dead' bytes. These bytes are left if the content of the box
+ * has been parsed but not all bytes have been used up.
+ *
+ * @param newDeadBytes the unused bytes with no meaning but required for bytewise reconstruction
+ */
+ protected void setDeadBytes(ByteBuffer newDeadBytes) {
+ deadBytes = newDeadBytes;
+ }
+
+
+ /**
+ * Gets the full size of the box including header and content.
+ *
+ * @return the box's size
+ */
+ public long getSize() {
+ long size = (content == null ? getContentSize() : content.limit());
+ size += (8 + // size|type
+ (size >= ((1L << 32) - 8) ? 8 : 0) + // 32bit - 8 byte size and type
+ (UserBox.TYPE.equals(getType()) ? 16 : 0));
+ size += (deadBytes == null ? 0 : deadBytes.limit());
+ return size;
+ }
+
+ @DoNotParseDetail
+ public String getType() {
+ return type;
+ }
+
+ @DoNotParseDetail
+ public byte[] getUserType() {
+ return userType;
+ }
+
+ @DoNotParseDetail
+ public ContainerBox getParent() {
+ return parent;
+ }
+
+ @DoNotParseDetail
+ public void setParent(ContainerBox parent) {
+ this.parent = parent;
+ }
+
+ @DoNotParseDetail
+ public IsoFile getIsoFile() {
+ return parent.getIsoFile();
+ }
+
+ /**
+ * Check if details are parsed.
+ *
+ * @return <code>true</code> whenever the content <code>ByteBuffer</code> is not <code>null</code>
+ */
+ public boolean isParsed() {
+ return content == null;
+ }
+
+
+ /**
+ * Verifies that a box can be reconstructed byte-exact after parsing.
+ *
+ * @param content the raw content of the box
+ * @return <code>true</code> if raw content exactly matches the reconstructed content
+ */
+ private boolean verify(ByteBuffer content) {
+ ByteBuffer bb = ByteBuffer.allocate(l2i(getContentSize() + (deadBytes != null ? deadBytes.limit() : 0)));
+ getContent(bb);
+ if (deadBytes != null) {
+ deadBytes.rewind();
+ while (deadBytes.remaining() > 0) {
+ bb.put(deadBytes);
+ }
+ }
+ content.rewind();
+ bb.rewind();
+
+
+ if (content.remaining() != bb.remaining()) {
+ LOG.severe(this.getType() + ": remaining differs " + content.remaining() + " vs. " + bb.remaining());
+ return false;
+ }
+ int p = content.position();
+ for (int i = content.limit() - 1, j = bb.limit() - 1; i >= p; i--, j--) {
+ byte v1 = content.get(i);
+ byte v2 = bb.get(j);
+ if (v1 != v2) {
+ LOG.severe(String.format("%s: buffers differ at %d: %2X/%2X", this.getType(), i, v1, v2));
+ byte[] b1 = new byte[content.remaining()];
+ byte[] b2 = new byte[bb.remaining()];
+ content.get(b1);
+ bb.get(b2);
+ System.err.println("original : " + Hex.encodeHex(b1, 4));
+ System.err.println("reconstructed : " + Hex.encodeHex(b2, 4));
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ private boolean isSmallBox() {
+ return (content == null ? (getContentSize() + (deadBytes != null ? deadBytes.limit() : 0) + 8) : content.limit()) < 1L << 32;
+ }
+
+ private void getHeader(ByteBuffer byteBuffer) {
+ if (isSmallBox()) {
+ IsoTypeWriter.writeUInt32(byteBuffer, this.getSize());
+ byteBuffer.put(IsoFile.fourCCtoBytes(getType()));
+ } else {
+ IsoTypeWriter.writeUInt32(byteBuffer, 1);
+ byteBuffer.put(IsoFile.fourCCtoBytes(getType()));
+ IsoTypeWriter.writeUInt64(byteBuffer, getSize());
+ }
+ if (UserBox.TYPE.equals(getType())) {
+ byteBuffer.put(getUserType());
+ }
+
+
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/AbstractContainerBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/AbstractContainerBox.java
new file mode 100644
index 0000000..93369f3
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/AbstractContainerBox.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2008 CoreMedia AG, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.ContainerBox;
+import com.googlecode.mp4parser.util.ByteBufferByteChannel;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+
+/**
+ * Abstract base class suitable for most boxes acting purely as container for other boxes.
+ */
+public abstract class AbstractContainerBox extends AbstractBox implements ContainerBox {
+ private static Logger LOG = Logger.getLogger(AbstractContainerBox.class.getName());
+
+ protected List<Box> boxes = new LinkedList<Box>();
+ protected BoxParser boxParser;
+
+ @Override
+ protected long getContentSize() {
+ long contentSize = 0;
+ for (Box boxe : boxes) {
+ contentSize += boxe.getSize();
+ }
+ return contentSize;
+ }
+
+ public AbstractContainerBox(String type) {
+ super(type);
+ }
+
+ public List<Box> getBoxes() {
+ return Collections.unmodifiableList(boxes);
+ }
+
+ public void setBoxes(List<Box> boxes) {
+ this.boxes = new LinkedList<Box>(boxes);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Box> List<T> getBoxes(Class<T> clazz) {
+ return getBoxes(clazz, false);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) {
+ List<T> boxesToBeReturned = new ArrayList<T>(2);
+ for (Box boxe : boxes) {
+ //clazz.isInstance(boxe) / clazz == boxe.getClass()?
+ // I hereby finally decide to use isInstance
+
+ if (clazz.isInstance(boxe)) {
+ boxesToBeReturned.add((T) boxe);
+ }
+
+ if (recursive && boxe instanceof ContainerBox) {
+ boxesToBeReturned.addAll(((ContainerBox) boxe).getBoxes(clazz, recursive));
+ }
+ }
+ return boxesToBeReturned;
+ }
+
+ /**
+ * Add <code>b</code> to the container and sets the parent correctly.
+ *
+ * @param b will be added to the container
+ */
+ public void addBox(Box b) {
+ b.setParent(this);
+ boxes.add(b);
+ }
+
+ public void removeBox(Box b) {
+ b.setParent(this);
+ boxes.remove(b);
+ }
+
+ @Override
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+ super.parse(readableByteChannel, header, contentSize, boxParser);
+ this.boxParser = boxParser;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseChildBoxes(content);
+ }
+
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append(this.getClass().getSimpleName()).append("[");
+ for (int i = 0; i < boxes.size(); i++) {
+ if (i > 0) {
+ buffer.append(";");
+ }
+ buffer.append(boxes.get(i).toString());
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+ /**
+ * The number of bytes from box start (first length byte) to the
+ * first length byte of the first child box
+ *
+ * @return offset to first child box
+ */
+ public long getNumOfBytesToFirstChild() {
+ return 8;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeChildBoxes(byteBuffer);
+ }
+
+ protected final void parseChildBoxes(ByteBuffer content) {
+ try {
+ while (content.remaining() >= 8) { // 8 is the minimal size for a sane box
+ boxes.add(boxParser.parseBox(new ByteBufferByteChannel(content), this));
+ }
+
+ if (content.remaining() != 0) {
+ setDeadBytes(content.slice());
+ LOG.warning("Something's wrong with the sizes. There are dead bytes in a container box.");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected final void writeChildBoxes(ByteBuffer bb) {
+ WritableByteChannel wbc = new ByteBufferByteChannel(bb);
+ for (Box box : boxes) {
+ try {
+ box.getBox(wbc);
+ } catch (IOException e) {
+ // My WritableByteChannel won't throw any excpetion
+ throw new RuntimeException("Cannot happen to me", e);
+ }
+ }
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/AbstractFullBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/AbstractFullBox.java
new file mode 100644
index 0000000..bec8975
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/AbstractFullBox.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2008 CoreMedia AG, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser;
+
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.FullBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Base class for all ISO Full boxes.
+ */
+public abstract class AbstractFullBox extends AbstractBox implements FullBox {
+ private int version;
+ private int flags;
+
+ protected AbstractFullBox(String type) {
+ super(type);
+ }
+
+ protected AbstractFullBox(String type, byte[] userType) {
+ super(type, userType);
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+
+ public void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+
+ /**
+ * Parses the version/flags header and returns the remaining box size.
+ *
+ * @param content
+ * @return number of bytes read
+ */
+ protected final long parseVersionAndFlags(ByteBuffer content) {
+ version = IsoTypeReader.readUInt8(content);
+ flags = IsoTypeReader.readUInt24(content);
+ return 4;
+ }
+
+ protected final void writeVersionAndFlags(ByteBuffer bb) {
+ IsoTypeWriter.writeUInt8(bb, version);
+ IsoTypeWriter.writeUInt24(bb, flags);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/FullContainerBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/FullContainerBox.java
new file mode 100644
index 0000000..d16e47d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/FullContainerBox.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2008 CoreMedia AG, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.ContainerBox;
+import com.googlecode.mp4parser.util.ByteBufferByteChannel;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * Abstract base class for a full iso box only containing ither boxes.
+ */
+public abstract class FullContainerBox extends AbstractFullBox implements ContainerBox {
+ protected List<Box> boxes = new LinkedList<Box>();
+ private static Logger LOG = Logger.getLogger(FullContainerBox.class.getName());
+ BoxParser boxParser;
+
+ public void setBoxes(List<Box> boxes) {
+ this.boxes = new LinkedList<Box>(boxes);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Box> List<T> getBoxes(Class<T> clazz) {
+ return getBoxes(clazz, false);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) {
+ List<T> boxesToBeReturned = new ArrayList<T>(2);
+ for (Box boxe : boxes) { //clazz.isInstance(boxe) / clazz == boxe.getClass()?
+ if (clazz == boxe.getClass()) {
+ boxesToBeReturned.add((T) boxe);
+ }
+
+ if (recursive && boxe instanceof ContainerBox) {
+ boxesToBeReturned.addAll((((ContainerBox) boxe).getBoxes(clazz, recursive)));
+ }
+ }
+ // Optimize here! Spare object creation work on arrays directly! System.arrayCopy
+ return boxesToBeReturned;
+ //return (T[]) boxesToBeReturned.toArray();
+ }
+
+ protected long getContentSize() {
+ long contentSize = 4; // flags and version
+ for (Box boxe : boxes) {
+ contentSize += boxe.getSize();
+ }
+ return contentSize;
+ }
+
+ public void addBox(Box b) {
+ b.setParent(this);
+ boxes.add(b);
+ }
+
+ public void removeBox(Box b) {
+ b.setParent(null);
+ boxes.remove(b);
+ }
+
+ public FullContainerBox(String type) {
+ super(type);
+ }
+
+ public List<Box> getBoxes() {
+ return boxes;
+ }
+
+ @Override
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+ super.parse(readableByteChannel, header, contentSize, boxParser);
+ this.boxParser = boxParser;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ parseChildBoxes(content);
+ }
+
+ protected final void parseChildBoxes(ByteBuffer content) {
+ try {
+ while (content.remaining() >= 8) { // 8 is the minimal size for a sane box
+ boxes.add(boxParser.parseBox(new ByteBufferByteChannel(content), this));
+ }
+
+ if (content.remaining() != 0) {
+ setDeadBytes(content.slice());
+ LOG.severe("Some sizes are wrong");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(this.getClass().getSimpleName()).append("[");
+ for (int i = 0; i < boxes.size(); i++) {
+ if (i > 0) {
+ buffer.append(";");
+ }
+ buffer.append(boxes.get(i).toString());
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ writeChildBoxes(byteBuffer);
+ }
+
+ protected final void writeChildBoxes(ByteBuffer bb) {
+ WritableByteChannel wbc = new ByteBufferByteChannel(bb);
+ for (Box box : boxes) {
+ try {
+ box.getBox(wbc);
+ } catch (IOException e) {
+ // cannot happen since my WritableByteChannel won't throw any excpetion
+ throw new RuntimeException("Cannot happen.", e);
+ }
+
+ }
+ }
+
+ public long getNumOfBytesToFirstChild() {
+ long sizeOfChildren = 0;
+ for (Box box : boxes) {
+ sizeOfChildren += box.getSize();
+ }
+ return getSize() - sizeOfChildren;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/Version.java b/isoparser/src/main/java/com/googlecode/mp4parser/Version.java
new file mode 100644
index 0000000..f93816f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/Version.java
@@ -0,0 +1,27 @@
+package com.googlecode.mp4parser;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.util.logging.Logger;
+
+/**
+ * The classic version object.
+ */
+public class Version {
+ private static final Logger LOG = Logger.getLogger(Version.class.getName());
+ public static final String VERSION;
+
+ static {
+ LineNumberReader lnr = new LineNumberReader(new InputStreamReader(Version.class.getResourceAsStream("/version.txt")));
+ String version;
+ try {
+ version = lnr.readLine();
+ } catch (IOException e) {
+ LOG.warning(e.getMessage());
+ version = "unknown";
+ }
+ VERSION = version;
+
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/all-wcprops
new file mode 100644
index 0000000..a072f85
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/510/trunk/isoparser/src/main/java/com/googlecode/mp4parser/annotations
+END
+DoNotParseDetail.java
+K 25
+svn:wc:ra_dav:version-url
+V 106
+/svn/!svn/ver/510/trunk/isoparser/src/main/java/com/googlecode/mp4parser/annotations/DoNotParseDetail.java
+END
+ParseDetail.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/505/trunk/isoparser/src/main/java/com/googlecode/mp4parser/annotations/ParseDetail.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/entries
new file mode 100644
index 0000000..c1fdd1c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/annotations
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-04-22T09:16:09.151890Z
+510
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+DoNotParseDetail.java
+file
+
+
+
+
+2012-09-14T17:27:51.617235Z
+fc2fddc66ebab5d81ec1c3a226b350b2
+2012-04-22T09:16:09.151890Z
+510
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1277
+
+ParseDetail.java
+file
+
+
+
+
+2012-09-14T17:27:51.617235Z
+14c265468fb110b7dc7f59ba02b532a0
+2012-04-21T21:18:31.685061Z
+505
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1037
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/text-base/DoNotParseDetail.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/text-base/DoNotParseDetail.java.svn-base
new file mode 100644
index 0000000..c08460f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/text-base/DoNotParseDetail.java.svn-base
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ */
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+/**
+ * Mark a method with this annotation to prevent triggering the call of
+ * <code>AbstractBox#parseDetails()</code> before actually executing the
+ * method.
+ * @see com.googlecode.mp4parser.RequiresParseDetailAspect
+ */
+public @interface DoNotParseDetail {
+}
+
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/text-base/ParseDetail.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/text-base/ParseDetail.java.svn-base
new file mode 100644
index 0000000..7b66d53
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/.svn/text-base/ParseDetail.java.svn-base
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface ParseDetail {
+}
+
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/annotations/DoNotParseDetail.java b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/DoNotParseDetail.java
new file mode 100644
index 0000000..c08460f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/DoNotParseDetail.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ */
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+/**
+ * Mark a method with this annotation to prevent triggering the call of
+ * <code>AbstractBox#parseDetails()</code> before actually executing the
+ * method.
+ * @see com.googlecode.mp4parser.RequiresParseDetailAspect
+ */
+public @interface DoNotParseDetail {
+}
+
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/annotations/ParseDetail.java b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/ParseDetail.java
new file mode 100644
index 0000000..7b66d53
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/annotations/ParseDetail.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface ParseDetail {
+}
+
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/all-wcprops
new file mode 100644
index 0000000..89054c9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/776/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring
+END
+Movie.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/514/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Movie.java
+END
+Track.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Track.java
+END
+TrackMetaData.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/745/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/TrackMetaData.java
+END
+Mp4TrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/765/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Mp4TrackImpl.java
+END
+AbstractTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/418/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/AbstractTrack.java
+END
+DateHelper.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/418/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/DateHelper.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/entries
new file mode 100644
index 0000000..7d2b29e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/entries
@@ -0,0 +1,244 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-09-10T14:34:23.574807Z
+776
+sebastian.annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+Movie.java
+file
+
+
+
+
+2012-09-14T17:27:50.517219Z
+e3a56133cfdfacb92ed0a54177e847b4
+2012-04-22T10:09:06.632613Z
+514
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2558
+
+container
+dir
+
+Track.java
+file
+
+
+
+
+2012-09-14T17:27:50.517219Z
+9537fa79b71fe26727e56e84e94bbdb8
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1607
+
+TrackMetaData.java
+file
+
+
+
+
+2012-09-14T17:27:50.527219Z
+cbb770cca0ee421026eec0a2f40d2376
+2012-08-14T19:18:50.777750Z
+745
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2948
+
+builder
+dir
+
+adaptivestreaming
+dir
+
+tracks
+dir
+
+Mp4TrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.527219Z
+c57930172e9d0da9e881d8dc8ecf2924
+2012-08-29T08:26:56.932482Z
+765
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10958
+
+AbstractTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.527219Z
+973f4f354fb6f575dd1a0c8a68d54653
+2012-03-11T20:54:45.638478Z
+418
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1481
+
+DateHelper.java
+file
+
+
+
+
+2012-09-14T17:27:50.527219Z
+765e3f37d7bb369f569aa91e326a90b8
+2012-03-11T20:54:45.638478Z
+418
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1349
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/AbstractTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/AbstractTrack.java.svn-base
new file mode 100644
index 0000000..fb0e224
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/AbstractTrack.java.svn-base
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.authoring;
+
+/**
+ *
+ */
+public abstract class AbstractTrack implements Track {
+ private boolean enabled = true;
+ private boolean inMovie = true;
+ private boolean inPreview = true;
+ private boolean inPoster = true;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public boolean isInMovie() {
+ return inMovie;
+ }
+
+ public boolean isInPreview() {
+ return inPreview;
+ }
+
+ public boolean isInPoster() {
+ return inPoster;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public void setInMovie(boolean inMovie) {
+ this.inMovie = inMovie;
+ }
+
+ public void setInPreview(boolean inPreview) {
+ this.inPreview = inPreview;
+ }
+
+ public void setInPoster(boolean inPoster) {
+ this.inPoster = inPoster;
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/DateHelper.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/DateHelper.java.svn-base
new file mode 100644
index 0000000..0252859
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/DateHelper.java.svn-base
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import java.util.Date;
+
+/**
+ * Converts ISO Dates (seconds since 1/1/1904) to Date and vice versa.
+ */
+public class DateHelper {
+ /**
+ * Converts a long value with seconds since 1/1/1904 to Date.
+ *
+ * @param secondsSince seconds since 1/1/1904
+ * @return date the corresponding <code>Date</code>
+ */
+ static public Date convert(long secondsSince) {
+ return new Date((secondsSince - 2082844800L) * 1000L);
+ }
+
+
+ /**
+ * Converts a date as long to a mac date as long
+ *
+ * @param date date to convert
+ * @return date in mac format
+ */
+ static public long convert(Date date) {
+ return (date.getTime() / 1000L) + 2082844800L;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Movie.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Movie.java.svn-base
new file mode 100644
index 0000000..0658682
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Movie.java.svn-base
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class Movie {
+ List<Track> tracks = new LinkedList<Track>();
+
+ public List<Track> getTracks() {
+ return tracks;
+ }
+
+ public void setTracks(List<Track> tracks) {
+ this.tracks = tracks;
+ }
+
+ public void addTrack(Track nuTrack) {
+ // do some checking
+ // perhaps the movie needs to get longer!
+ if (getTrackByTrackId(nuTrack.getTrackMetaData().getTrackId()) != null) {
+ // We already have a track with that trackId. Create a new one
+ nuTrack.getTrackMetaData().setTrackId(getNextTrackId());
+ }
+ tracks.add(nuTrack);
+ }
+
+
+ @Override
+ public String toString() {
+ String s = "Movie{ ";
+ for (Track track : tracks) {
+ s += "track_" + track.getTrackMetaData().getTrackId() + " (" + track.getHandler() + ") ";
+ }
+
+ s += '}';
+ return s;
+ }
+
+ public long getNextTrackId() {
+ long nextTrackId = 0;
+ for (Track track : tracks) {
+ nextTrackId = nextTrackId < track.getTrackMetaData().getTrackId() ? track.getTrackMetaData().getTrackId() : nextTrackId;
+ }
+ return ++nextTrackId;
+ }
+
+
+ public Track getTrackByTrackId(long trackId) {
+ for (Track track : tracks) {
+ if (track.getTrackMetaData().getTrackId() == trackId) {
+ return track;
+ }
+ }
+ return null;
+ }
+
+
+ public long getTimescale() {
+ long timescale = this.getTracks().iterator().next().getTrackMetaData().getTimescale();
+ for (Track track : this.getTracks()) {
+ timescale = gcd(track.getTrackMetaData().getTimescale(), timescale);
+ }
+ return timescale;
+ }
+
+ public static long gcd(long a, long b) {
+ if (b == 0) {
+ return a;
+ }
+ return gcd(b, a % b);
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Mp4TrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Mp4TrackImpl.java.svn-base
new file mode 100644
index 0000000..3bff1a5
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Mp4TrackImpl.java.svn-base
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.fragment.*;
+import com.coremedia.iso.boxes.mdat.SampleList;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Represents a single track of an MP4 file.
+ */
+public class Mp4TrackImpl extends AbstractTrack {
+ private List<ByteBuffer> samples;
+ private SampleDescriptionBox sampleDescriptionBox;
+ private List<TimeToSampleBox.Entry> decodingTimeEntries;
+ private List<CompositionTimeToSample.Entry> compositionTimeEntries;
+ private long[] syncSamples = new long[0];
+ private List<SampleDependencyTypeBox.Entry> sampleDependencies;
+ private TrackMetaData trackMetaData = new TrackMetaData();
+ private String handler;
+ private AbstractMediaHeaderBox mihd;
+
+ public Mp4TrackImpl(TrackBox trackBox) {
+ final long trackId = trackBox.getTrackHeaderBox().getTrackId();
+ samples = new SampleList(trackBox);
+ SampleTableBox stbl = trackBox.getMediaBox().getMediaInformationBox().getSampleTableBox();
+ handler = trackBox.getMediaBox().getHandlerBox().getHandlerType();
+
+ mihd = trackBox.getMediaBox().getMediaInformationBox().getMediaHeaderBox();
+ decodingTimeEntries = new LinkedList<TimeToSampleBox.Entry>();
+ compositionTimeEntries = new LinkedList<CompositionTimeToSample.Entry>();
+ sampleDependencies = new LinkedList<SampleDependencyTypeBox.Entry>();
+
+ decodingTimeEntries.addAll(stbl.getTimeToSampleBox().getEntries());
+ if (stbl.getCompositionTimeToSample() != null) {
+ compositionTimeEntries.addAll(stbl.getCompositionTimeToSample().getEntries());
+ }
+ if (stbl.getSampleDependencyTypeBox() != null) {
+ sampleDependencies.addAll(stbl.getSampleDependencyTypeBox().getEntries());
+ }
+ if (stbl.getSyncSampleBox() != null) {
+ syncSamples = stbl.getSyncSampleBox().getSampleNumber();
+ }
+
+
+ sampleDescriptionBox = stbl.getSampleDescriptionBox();
+ final List<MovieExtendsBox> movieExtendsBoxes = trackBox.getParent().getBoxes(MovieExtendsBox.class);
+ if (movieExtendsBoxes.size() > 0) {
+ for (MovieExtendsBox mvex : movieExtendsBoxes) {
+ final List<TrackExtendsBox> trackExtendsBoxes = mvex.getBoxes(TrackExtendsBox.class);
+ for (TrackExtendsBox trex : trackExtendsBoxes) {
+ if (trex.getTrackId() == trackId) {
+ List<Long> syncSampleList = new LinkedList<Long>();
+
+ long sampleNumber = 1;
+ for (MovieFragmentBox movieFragmentBox : trackBox.getIsoFile().getBoxes(MovieFragmentBox.class)) {
+ List<TrackFragmentBox> trafs = movieFragmentBox.getBoxes(TrackFragmentBox.class);
+ for (TrackFragmentBox traf : trafs) {
+ if (traf.getTrackFragmentHeaderBox().getTrackId() == trackId) {
+ List<TrackRunBox> truns = traf.getBoxes(TrackRunBox.class);
+ for (TrackRunBox trun : truns) {
+ final TrackFragmentHeaderBox tfhd = ((TrackFragmentBox) trun.getParent()).getTrackFragmentHeaderBox();
+ boolean first = true;
+ for (TrackRunBox.Entry entry : trun.getEntries()) {
+ if (trun.isSampleDurationPresent()) {
+ if (decodingTimeEntries.size() == 0 ||
+ decodingTimeEntries.get(decodingTimeEntries.size() - 1).getDelta() != entry.getSampleDuration()) {
+ decodingTimeEntries.add(new TimeToSampleBox.Entry(1, entry.getSampleDuration()));
+ } else {
+ TimeToSampleBox.Entry e = decodingTimeEntries.get(decodingTimeEntries.size() - 1);
+ e.setCount(e.getCount() + 1);
+ }
+ } else {
+ if (tfhd.hasDefaultSampleDuration()) {
+ decodingTimeEntries.add(new TimeToSampleBox.Entry(1, tfhd.getDefaultSampleDuration()));
+ } else {
+ decodingTimeEntries.add(new TimeToSampleBox.Entry(1, trex.getDefaultSampleDuration()));
+ }
+ }
+
+ if (trun.isSampleCompositionTimeOffsetPresent()) {
+ if (compositionTimeEntries.size() == 0 ||
+ compositionTimeEntries.get(compositionTimeEntries.size() - 1).getOffset() != entry.getSampleCompositionTimeOffset()) {
+ compositionTimeEntries.add(new CompositionTimeToSample.Entry(1, l2i(entry.getSampleCompositionTimeOffset())));
+ } else {
+ CompositionTimeToSample.Entry e = compositionTimeEntries.get(compositionTimeEntries.size() - 1);
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ final SampleFlags sampleFlags;
+ if (trun.isSampleFlagsPresent()) {
+ sampleFlags = entry.getSampleFlags();
+ } else {
+ if (first && trun.isFirstSampleFlagsPresent()) {
+ sampleFlags = trun.getFirstSampleFlags();
+ } else {
+ if (tfhd.hasDefaultSampleFlags()) {
+ sampleFlags = tfhd.getDefaultSampleFlags();
+ } else {
+ sampleFlags = trex.getDefaultSampleFlags();
+ }
+ }
+ }
+ if (sampleFlags != null && !sampleFlags.isSampleIsDifferenceSample()) {
+ //iframe
+ syncSampleList.add(sampleNumber);
+ }
+ sampleNumber++;
+ first = false;
+ }
+ }
+ }
+ }
+ }
+ // Warning: Crappy code
+ long[] oldSS = syncSamples;
+ syncSamples = new long[syncSamples.length + syncSampleList.size()];
+ System.arraycopy(oldSS, 0, syncSamples, 0, oldSS.length);
+ final Iterator<Long> iterator = syncSampleList.iterator();
+ int i = oldSS.length;
+ while (iterator.hasNext()) {
+ Long syncSampleNumber = iterator.next();
+ syncSamples[i++] = syncSampleNumber;
+ }
+ }
+ }
+ }
+ }
+ MediaHeaderBox mdhd = trackBox.getMediaBox().getMediaHeaderBox();
+ TrackHeaderBox tkhd = trackBox.getTrackHeaderBox();
+
+ setEnabled(tkhd.isEnabled());
+ setInMovie(tkhd.isInMovie());
+ setInPoster(tkhd.isInPoster());
+ setInPreview(tkhd.isInPreview());
+
+ trackMetaData.setTrackId(tkhd.getTrackId());
+ trackMetaData.setCreationTime(DateHelper.convert(mdhd.getCreationTime()));
+ trackMetaData.setLanguage(mdhd.getLanguage());
+/* System.err.println(mdhd.getModificationTime());
+ System.err.println(DateHelper.convert(mdhd.getModificationTime()));
+ System.err.println(DateHelper.convert(DateHelper.convert(mdhd.getModificationTime())));
+ System.err.println(DateHelper.convert(DateHelper.convert(DateHelper.convert(mdhd.getModificationTime()))));*/
+
+ trackMetaData.setModificationTime(DateHelper.convert(mdhd.getModificationTime()));
+ trackMetaData.setTimescale(mdhd.getTimescale());
+ trackMetaData.setHeight(tkhd.getHeight());
+ trackMetaData.setWidth(tkhd.getWidth());
+ trackMetaData.setLayer(tkhd.getLayer());
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return decodingTimeEntries;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return compositionTimeEntries;
+ }
+
+ public long[] getSyncSamples() {
+ return syncSamples;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return sampleDependencies;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return handler;
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return mihd;
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "Mp4TrackImpl{" +
+ "handler='" + handler + '\'' +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Track.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Track.java.svn-base
new file mode 100644
index 0000000..1f4b363
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/Track.java.svn-base
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import com.coremedia.iso.boxes.*;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Represents a Track. A track is a timed sequence of related samples.
+ * <p/>
+ * <b>NOTE: </b><br/
+ * For media data, a track corresponds to a sequence of images or sampled audio; for hint tracks, a track
+ * corresponds to a streaming channel.
+ */
+public interface Track {
+
+ SampleDescriptionBox getSampleDescriptionBox();
+
+ List<TimeToSampleBox.Entry> getDecodingTimeEntries();
+
+ List<CompositionTimeToSample.Entry> getCompositionTimeEntries();
+
+ long[] getSyncSamples();
+
+ List<SampleDependencyTypeBox.Entry> getSampleDependencies();
+
+ TrackMetaData getTrackMetaData();
+
+ String getHandler();
+
+ boolean isEnabled();
+
+ boolean isInMovie();
+
+ boolean isInPreview();
+
+ boolean isInPoster();
+
+ List<ByteBuffer> getSamples();
+
+ public Box getMediaHeaderBox();
+
+ public SubSampleInformationBox getSubsampleInformationBox();
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/TrackMetaData.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/TrackMetaData.java.svn-base
new file mode 100644
index 0000000..c262309
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/.svn/text-base/TrackMetaData.java.svn-base
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import java.util.Date;
+
+/**
+ *
+ */
+public class TrackMetaData implements Cloneable {
+ private String language;
+ private long timescale;
+ private Date modificationTime = new Date();
+ private Date creationTime = new Date();
+ private double width;
+ private double height;
+ private float volume;
+ private long trackId = 1; // zero is not allowed
+ private int group = 0;
+
+
+ /**
+ * specifies the front-to-back ordering of video tracks; tracks with lower
+ * numbers are closer to the viewer. 0 is the normal value, and -1 would be
+ * in front of track 0, and so on.
+ */
+ int layer;
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public long getTimescale() {
+ return timescale;
+ }
+
+ public void setTimescale(long timescale) {
+ this.timescale = timescale;
+ }
+
+ public Date getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Date modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public Date getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Date creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public double getWidth() {
+ return width;
+ }
+
+ public void setWidth(double width) {
+ this.width = width;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+
+ public void setHeight(double height) {
+ this.height = height;
+ }
+
+ public long getTrackId() {
+ return trackId;
+ }
+
+ public void setTrackId(long trackId) {
+ this.trackId = trackId;
+ }
+
+ public int getLayer() {
+ return layer;
+ }
+
+ public void setLayer(int layer) {
+ this.layer = layer;
+ }
+
+ public float getVolume() {
+ return volume;
+ }
+
+ public void setVolume(float volume) {
+ this.volume = volume;
+ }
+
+ public int getGroup() {
+ return group;
+ }
+
+ public void setGroup(int group) {
+ this.group = group;
+ }
+
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/AbstractTrack.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/AbstractTrack.java
new file mode 100644
index 0000000..fb0e224
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/AbstractTrack.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.authoring;
+
+/**
+ *
+ */
+public abstract class AbstractTrack implements Track {
+ private boolean enabled = true;
+ private boolean inMovie = true;
+ private boolean inPreview = true;
+ private boolean inPoster = true;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public boolean isInMovie() {
+ return inMovie;
+ }
+
+ public boolean isInPreview() {
+ return inPreview;
+ }
+
+ public boolean isInPoster() {
+ return inPoster;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public void setInMovie(boolean inMovie) {
+ this.inMovie = inMovie;
+ }
+
+ public void setInPreview(boolean inPreview) {
+ this.inPreview = inPreview;
+ }
+
+ public void setInPoster(boolean inPoster) {
+ this.inPoster = inPoster;
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/DateHelper.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/DateHelper.java
new file mode 100644
index 0000000..0252859
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/DateHelper.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import java.util.Date;
+
+/**
+ * Converts ISO Dates (seconds since 1/1/1904) to Date and vice versa.
+ */
+public class DateHelper {
+ /**
+ * Converts a long value with seconds since 1/1/1904 to Date.
+ *
+ * @param secondsSince seconds since 1/1/1904
+ * @return date the corresponding <code>Date</code>
+ */
+ static public Date convert(long secondsSince) {
+ return new Date((secondsSince - 2082844800L) * 1000L);
+ }
+
+
+ /**
+ * Converts a date as long to a mac date as long
+ *
+ * @param date date to convert
+ * @return date in mac format
+ */
+ static public long convert(Date date) {
+ return (date.getTime() / 1000L) + 2082844800L;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Movie.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Movie.java
new file mode 100644
index 0000000..0658682
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Movie.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class Movie {
+ List<Track> tracks = new LinkedList<Track>();
+
+ public List<Track> getTracks() {
+ return tracks;
+ }
+
+ public void setTracks(List<Track> tracks) {
+ this.tracks = tracks;
+ }
+
+ public void addTrack(Track nuTrack) {
+ // do some checking
+ // perhaps the movie needs to get longer!
+ if (getTrackByTrackId(nuTrack.getTrackMetaData().getTrackId()) != null) {
+ // We already have a track with that trackId. Create a new one
+ nuTrack.getTrackMetaData().setTrackId(getNextTrackId());
+ }
+ tracks.add(nuTrack);
+ }
+
+
+ @Override
+ public String toString() {
+ String s = "Movie{ ";
+ for (Track track : tracks) {
+ s += "track_" + track.getTrackMetaData().getTrackId() + " (" + track.getHandler() + ") ";
+ }
+
+ s += '}';
+ return s;
+ }
+
+ public long getNextTrackId() {
+ long nextTrackId = 0;
+ for (Track track : tracks) {
+ nextTrackId = nextTrackId < track.getTrackMetaData().getTrackId() ? track.getTrackMetaData().getTrackId() : nextTrackId;
+ }
+ return ++nextTrackId;
+ }
+
+
+ public Track getTrackByTrackId(long trackId) {
+ for (Track track : tracks) {
+ if (track.getTrackMetaData().getTrackId() == trackId) {
+ return track;
+ }
+ }
+ return null;
+ }
+
+
+ public long getTimescale() {
+ long timescale = this.getTracks().iterator().next().getTrackMetaData().getTimescale();
+ for (Track track : this.getTracks()) {
+ timescale = gcd(track.getTrackMetaData().getTimescale(), timescale);
+ }
+ return timescale;
+ }
+
+ public static long gcd(long a, long b) {
+ if (b == 0) {
+ return a;
+ }
+ return gcd(b, a % b);
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Mp4TrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Mp4TrackImpl.java
new file mode 100644
index 0000000..3bff1a5
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Mp4TrackImpl.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.fragment.*;
+import com.coremedia.iso.boxes.mdat.SampleList;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Represents a single track of an MP4 file.
+ */
+public class Mp4TrackImpl extends AbstractTrack {
+ private List<ByteBuffer> samples;
+ private SampleDescriptionBox sampleDescriptionBox;
+ private List<TimeToSampleBox.Entry> decodingTimeEntries;
+ private List<CompositionTimeToSample.Entry> compositionTimeEntries;
+ private long[] syncSamples = new long[0];
+ private List<SampleDependencyTypeBox.Entry> sampleDependencies;
+ private TrackMetaData trackMetaData = new TrackMetaData();
+ private String handler;
+ private AbstractMediaHeaderBox mihd;
+
+ public Mp4TrackImpl(TrackBox trackBox) {
+ final long trackId = trackBox.getTrackHeaderBox().getTrackId();
+ samples = new SampleList(trackBox);
+ SampleTableBox stbl = trackBox.getMediaBox().getMediaInformationBox().getSampleTableBox();
+ handler = trackBox.getMediaBox().getHandlerBox().getHandlerType();
+
+ mihd = trackBox.getMediaBox().getMediaInformationBox().getMediaHeaderBox();
+ decodingTimeEntries = new LinkedList<TimeToSampleBox.Entry>();
+ compositionTimeEntries = new LinkedList<CompositionTimeToSample.Entry>();
+ sampleDependencies = new LinkedList<SampleDependencyTypeBox.Entry>();
+
+ decodingTimeEntries.addAll(stbl.getTimeToSampleBox().getEntries());
+ if (stbl.getCompositionTimeToSample() != null) {
+ compositionTimeEntries.addAll(stbl.getCompositionTimeToSample().getEntries());
+ }
+ if (stbl.getSampleDependencyTypeBox() != null) {
+ sampleDependencies.addAll(stbl.getSampleDependencyTypeBox().getEntries());
+ }
+ if (stbl.getSyncSampleBox() != null) {
+ syncSamples = stbl.getSyncSampleBox().getSampleNumber();
+ }
+
+
+ sampleDescriptionBox = stbl.getSampleDescriptionBox();
+ final List<MovieExtendsBox> movieExtendsBoxes = trackBox.getParent().getBoxes(MovieExtendsBox.class);
+ if (movieExtendsBoxes.size() > 0) {
+ for (MovieExtendsBox mvex : movieExtendsBoxes) {
+ final List<TrackExtendsBox> trackExtendsBoxes = mvex.getBoxes(TrackExtendsBox.class);
+ for (TrackExtendsBox trex : trackExtendsBoxes) {
+ if (trex.getTrackId() == trackId) {
+ List<Long> syncSampleList = new LinkedList<Long>();
+
+ long sampleNumber = 1;
+ for (MovieFragmentBox movieFragmentBox : trackBox.getIsoFile().getBoxes(MovieFragmentBox.class)) {
+ List<TrackFragmentBox> trafs = movieFragmentBox.getBoxes(TrackFragmentBox.class);
+ for (TrackFragmentBox traf : trafs) {
+ if (traf.getTrackFragmentHeaderBox().getTrackId() == trackId) {
+ List<TrackRunBox> truns = traf.getBoxes(TrackRunBox.class);
+ for (TrackRunBox trun : truns) {
+ final TrackFragmentHeaderBox tfhd = ((TrackFragmentBox) trun.getParent()).getTrackFragmentHeaderBox();
+ boolean first = true;
+ for (TrackRunBox.Entry entry : trun.getEntries()) {
+ if (trun.isSampleDurationPresent()) {
+ if (decodingTimeEntries.size() == 0 ||
+ decodingTimeEntries.get(decodingTimeEntries.size() - 1).getDelta() != entry.getSampleDuration()) {
+ decodingTimeEntries.add(new TimeToSampleBox.Entry(1, entry.getSampleDuration()));
+ } else {
+ TimeToSampleBox.Entry e = decodingTimeEntries.get(decodingTimeEntries.size() - 1);
+ e.setCount(e.getCount() + 1);
+ }
+ } else {
+ if (tfhd.hasDefaultSampleDuration()) {
+ decodingTimeEntries.add(new TimeToSampleBox.Entry(1, tfhd.getDefaultSampleDuration()));
+ } else {
+ decodingTimeEntries.add(new TimeToSampleBox.Entry(1, trex.getDefaultSampleDuration()));
+ }
+ }
+
+ if (trun.isSampleCompositionTimeOffsetPresent()) {
+ if (compositionTimeEntries.size() == 0 ||
+ compositionTimeEntries.get(compositionTimeEntries.size() - 1).getOffset() != entry.getSampleCompositionTimeOffset()) {
+ compositionTimeEntries.add(new CompositionTimeToSample.Entry(1, l2i(entry.getSampleCompositionTimeOffset())));
+ } else {
+ CompositionTimeToSample.Entry e = compositionTimeEntries.get(compositionTimeEntries.size() - 1);
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ final SampleFlags sampleFlags;
+ if (trun.isSampleFlagsPresent()) {
+ sampleFlags = entry.getSampleFlags();
+ } else {
+ if (first && trun.isFirstSampleFlagsPresent()) {
+ sampleFlags = trun.getFirstSampleFlags();
+ } else {
+ if (tfhd.hasDefaultSampleFlags()) {
+ sampleFlags = tfhd.getDefaultSampleFlags();
+ } else {
+ sampleFlags = trex.getDefaultSampleFlags();
+ }
+ }
+ }
+ if (sampleFlags != null && !sampleFlags.isSampleIsDifferenceSample()) {
+ //iframe
+ syncSampleList.add(sampleNumber);
+ }
+ sampleNumber++;
+ first = false;
+ }
+ }
+ }
+ }
+ }
+ // Warning: Crappy code
+ long[] oldSS = syncSamples;
+ syncSamples = new long[syncSamples.length + syncSampleList.size()];
+ System.arraycopy(oldSS, 0, syncSamples, 0, oldSS.length);
+ final Iterator<Long> iterator = syncSampleList.iterator();
+ int i = oldSS.length;
+ while (iterator.hasNext()) {
+ Long syncSampleNumber = iterator.next();
+ syncSamples[i++] = syncSampleNumber;
+ }
+ }
+ }
+ }
+ }
+ MediaHeaderBox mdhd = trackBox.getMediaBox().getMediaHeaderBox();
+ TrackHeaderBox tkhd = trackBox.getTrackHeaderBox();
+
+ setEnabled(tkhd.isEnabled());
+ setInMovie(tkhd.isInMovie());
+ setInPoster(tkhd.isInPoster());
+ setInPreview(tkhd.isInPreview());
+
+ trackMetaData.setTrackId(tkhd.getTrackId());
+ trackMetaData.setCreationTime(DateHelper.convert(mdhd.getCreationTime()));
+ trackMetaData.setLanguage(mdhd.getLanguage());
+/* System.err.println(mdhd.getModificationTime());
+ System.err.println(DateHelper.convert(mdhd.getModificationTime()));
+ System.err.println(DateHelper.convert(DateHelper.convert(mdhd.getModificationTime())));
+ System.err.println(DateHelper.convert(DateHelper.convert(DateHelper.convert(mdhd.getModificationTime()))));*/
+
+ trackMetaData.setModificationTime(DateHelper.convert(mdhd.getModificationTime()));
+ trackMetaData.setTimescale(mdhd.getTimescale());
+ trackMetaData.setHeight(tkhd.getHeight());
+ trackMetaData.setWidth(tkhd.getWidth());
+ trackMetaData.setLayer(tkhd.getLayer());
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return decodingTimeEntries;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return compositionTimeEntries;
+ }
+
+ public long[] getSyncSamples() {
+ return syncSamples;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return sampleDependencies;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return handler;
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return mihd;
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "Mp4TrackImpl{" +
+ "handler='" + handler + '\'' +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Track.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Track.java
new file mode 100644
index 0000000..1f4b363
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/Track.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import com.coremedia.iso.boxes.*;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Represents a Track. A track is a timed sequence of related samples.
+ * <p/>
+ * <b>NOTE: </b><br/
+ * For media data, a track corresponds to a sequence of images or sampled audio; for hint tracks, a track
+ * corresponds to a streaming channel.
+ */
+public interface Track {
+
+ SampleDescriptionBox getSampleDescriptionBox();
+
+ List<TimeToSampleBox.Entry> getDecodingTimeEntries();
+
+ List<CompositionTimeToSample.Entry> getCompositionTimeEntries();
+
+ long[] getSyncSamples();
+
+ List<SampleDependencyTypeBox.Entry> getSampleDependencies();
+
+ TrackMetaData getTrackMetaData();
+
+ String getHandler();
+
+ boolean isEnabled();
+
+ boolean isInMovie();
+
+ boolean isInPreview();
+
+ boolean isInPoster();
+
+ List<ByteBuffer> getSamples();
+
+ public Box getMediaHeaderBox();
+
+ public SubSampleInformationBox getSubsampleInformationBox();
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/TrackMetaData.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/TrackMetaData.java
new file mode 100644
index 0000000..c262309
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/TrackMetaData.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring;
+
+import java.util.Date;
+
+/**
+ *
+ */
+public class TrackMetaData implements Cloneable {
+ private String language;
+ private long timescale;
+ private Date modificationTime = new Date();
+ private Date creationTime = new Date();
+ private double width;
+ private double height;
+ private float volume;
+ private long trackId = 1; // zero is not allowed
+ private int group = 0;
+
+
+ /**
+ * specifies the front-to-back ordering of video tracks; tracks with lower
+ * numbers are closer to the viewer. 0 is the normal value, and -1 would be
+ * in front of track 0, and so on.
+ */
+ int layer;
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public long getTimescale() {
+ return timescale;
+ }
+
+ public void setTimescale(long timescale) {
+ this.timescale = timescale;
+ }
+
+ public Date getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Date modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public Date getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Date creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public double getWidth() {
+ return width;
+ }
+
+ public void setWidth(double width) {
+ this.width = width;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+
+ public void setHeight(double height) {
+ this.height = height;
+ }
+
+ public long getTrackId() {
+ return trackId;
+ }
+
+ public void setTrackId(long trackId) {
+ this.trackId = trackId;
+ }
+
+ public int getLayer() {
+ return layer;
+ }
+
+ public void setLayer(int layer) {
+ this.layer = layer;
+ }
+
+ public float getVolume() {
+ return volume;
+ }
+
+ public void setVolume(float volume) {
+ this.volume = volume;
+ }
+
+ public int getGroup() {
+ return group;
+ }
+
+ public void setGroup(int group) {
+ this.group = group;
+ }
+
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/all-wcprops
new file mode 100644
index 0000000..7d70c40
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/773/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming
+END
+VideoQuality.java
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/svn/!svn/ver/760/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/VideoQuality.java
+END
+FlatPackageWriterImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 127
+/svn/!svn/ver/760/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatPackageWriterImpl.java
+END
+ManifestWriter.java
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/svn/!svn/ver/755/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/ManifestWriter.java
+END
+AbstractManifestWriter.java
+K 25
+svn:wc:ra_dav:version-url
+V 128
+/svn/!svn/ver/757/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AbstractManifestWriter.java
+END
+PackageWriter.java
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/svn/!svn/ver/755/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/PackageWriter.java
+END
+AudioQuality.java
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/svn/!svn/ver/760/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AudioQuality.java
+END
+FlatManifestWriterImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 128
+/svn/!svn/ver/773/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatManifestWriterImpl.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/entries
new file mode 100644
index 0000000..619b17c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/entries
@@ -0,0 +1,266 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-09-01T21:55:19.768646Z
+773
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+VideoQuality.java
+file
+
+
+
+
+2012-09-14T17:27:50.317216Z
+356fcadf80f684d83b5f30afd5cb26e4
+2012-08-17T15:20:10.783404Z
+760
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+807
+
+FlatPackageWriterImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.317216Z
+f38a8b91e1b8abd48e1ae26b23b060fa
+2012-08-17T15:20:10.783404Z
+760
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8285
+
+ManifestWriter.java
+file
+
+
+
+
+2012-09-14T17:27:50.317216Z
+4fc006c7919c1ab4ed498340dfa133b3
+2012-08-17T01:13:17.213046Z
+755
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+992
+
+AbstractManifestWriter.java
+file
+
+
+
+
+2012-09-14T17:27:50.317216Z
+1ce766c781ae825fb0620a61eb2b2e1c
+2012-08-17T05:55:12.215481Z
+757
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5030
+
+PackageWriter.java
+file
+
+
+
+
+2012-09-14T17:27:50.317216Z
+ffdb02efc14eeadf6c1ba9c5e500e76c
+2012-08-17T01:13:17.213046Z
+755
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+878
+
+AudioQuality.java
+file
+
+
+
+
+2012-09-14T17:27:50.317216Z
+c2b5ada192ff228aac261452067773fd
+2012-08-17T15:20:10.783404Z
+760
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+887
+
+FlatManifestWriterImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.317216Z
+d45a45107db5f4c43765d95708382310
+2012-09-01T21:55:19.768646Z
+773
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+30095
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/AbstractManifestWriter.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/AbstractManifestWriter.java.svn-base
new file mode 100644
index 0000000..6ee4ffa
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/AbstractManifestWriter.java.svn-base
@@ -0,0 +1,126 @@
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+import com.coremedia.iso.boxes.OriginalFormatBox;
+import com.coremedia.iso.boxes.TimeToSampleBox;
+import com.coremedia.iso.boxes.sampleentry.SampleEntry;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.builder.FragmentIntersectionFinder;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mstattma
+ * Date: 17.08.12
+ * Time: 02:51
+ * To change this template use File | Settings | File Templates.
+ */
+public abstract class AbstractManifestWriter implements ManifestWriter {
+ private static final Logger LOG = Logger.getLogger(AbstractManifestWriter.class.getName());
+
+ private FragmentIntersectionFinder intersectionFinder;
+ protected long[] audioFragmentsDurations;
+ protected long[] videoFragmentsDurations;
+
+ protected AbstractManifestWriter(FragmentIntersectionFinder intersectionFinder) {
+ this.intersectionFinder = intersectionFinder;
+ }
+
+ /**
+ * Calculates the length of each fragment in the given <code>track</code> (as part of <code>movie</code>).
+ *
+ * @param track target of calculation
+ * @param movie the <code>track</code> must be part of this <code>movie</code>
+ * @return the duration of each fragment in track timescale
+ */
+ public long[] calculateFragmentDurations(Track track, Movie movie) {
+ long[] startSamples = intersectionFinder.sampleNumbers(track, movie);
+ long[] durations = new long[startSamples.length];
+ int currentFragment = 0;
+ int currentSample = 1; // sync samples start with 1 !
+
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ for (int max = currentSample + l2i(entry.getCount()); currentSample < max; currentSample++) {
+ // in this loop we go through the entry.getCount() samples starting from current sample.
+ // the next entry.getCount() samples have the same decoding time.
+ if (currentFragment != startSamples.length - 1 && currentSample == startSamples[currentFragment + 1]) {
+ // we are not in the last fragment && the current sample is the start sample of the next fragment
+ currentFragment++;
+ }
+ durations[currentFragment] += entry.getDelta();
+
+
+ }
+ }
+ return durations;
+
+ }
+
+ public long getBitrate(Track track) {
+ long bitrate = 0;
+ for (ByteBuffer sample : track.getSamples()) {
+ bitrate += sample.limit();
+ }
+ bitrate *= 8; // from bytes to bits
+ bitrate /= ((double) getDuration(track)) / track.getTrackMetaData().getTimescale(); // per second
+ return bitrate;
+ }
+
+ protected static long getDuration(Track track) {
+ long duration = 0;
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ duration += entry.getCount() * entry.getDelta();
+ }
+ return duration;
+ }
+
+ protected long[] checkFragmentsAlign(long[] referenceTimes, long[] checkTimes) throws IOException {
+
+ if (referenceTimes == null || referenceTimes.length == 0) {
+ return checkTimes;
+ }
+ long[] referenceTimesMinusLast = new long[referenceTimes.length - 1];
+ System.arraycopy(referenceTimes, 0, referenceTimesMinusLast, 0, referenceTimes.length - 1);
+ long[] checkTimesMinusLast = new long[checkTimes.length - 1];
+ System.arraycopy(checkTimes, 0, checkTimesMinusLast, 0, checkTimes.length - 1);
+
+ if (!Arrays.equals(checkTimesMinusLast, referenceTimesMinusLast)) {
+ String log = "";
+ log += (referenceTimes.length);
+ log += ("Reference : [");
+ for (long l : referenceTimes) {
+ log += (String.format("%10d,", l));
+ }
+ log += ("]");
+ LOG.warning(log);
+ log = "";
+
+ log += (checkTimes.length);
+ log += ("Current : [");
+ for (long l : checkTimes) {
+ log += (String.format("%10d,", l));
+ }
+ log += ("]");
+ LOG.warning(log);
+ throw new IOException("Track does not have the same fragment borders as its predecessor.");
+
+ } else {
+ return checkTimes;
+ }
+ }
+
+ protected String getFormat(SampleEntry se) {
+ String type = se.getType();
+ if (type.equals("encv") || type.equals("enca") || type.equals("encv")) {
+ OriginalFormatBox frma = se.getBoxes(OriginalFormatBox.class, true).get(0);
+ type = frma.getDataFormat();
+ }
+ return type;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/AudioQuality.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/AudioQuality.java.svn-base
new file mode 100644
index 0000000..39e115f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/AudioQuality.java.svn-base
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+
+public class AudioQuality {
+ String fourCC;
+ long bitrate;
+ int audioTag;
+ long samplingRate;
+ int channels;
+ int bitPerSample;
+ int packetSize;
+ String language;
+ String codecPrivateData;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/FlatManifestWriterImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/FlatManifestWriterImpl.java.svn-base
new file mode 100644
index 0000000..5cc9be9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/FlatManifestWriterImpl.java.svn-base
@@ -0,0 +1,643 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.boxes.SampleDescriptionBox;
+import com.coremedia.iso.boxes.SoundMediaHeaderBox;
+import com.coremedia.iso.boxes.VideoMediaHeaderBox;
+import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
+import com.googlecode.mp4parser.Version;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.builder.FragmentIntersectionFinder;
+import com.googlecode.mp4parser.boxes.DTSSpecificBox;
+import com.googlecode.mp4parser.boxes.EC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.AudioSpecificConfig;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+public class FlatManifestWriterImpl extends AbstractManifestWriter {
+ private static final Logger LOG = Logger.getLogger(FlatManifestWriterImpl.class.getName());
+
+ protected FlatManifestWriterImpl(FragmentIntersectionFinder intersectionFinder) {
+ super(intersectionFinder);
+ }
+
+ /**
+ * Overwrite this method in subclasses to add your specialities.
+ *
+ * @param manifest the original manifest
+ * @return your customized version of the manifest
+ */
+ protected Document customizeManifest(Document manifest) {
+ return manifest;
+ }
+
+ public String getManifest(Movie movie) throws IOException {
+
+ LinkedList<VideoQuality> videoQualities = new LinkedList<VideoQuality>();
+ long videoTimescale = -1;
+
+ LinkedList<AudioQuality> audioQualities = new LinkedList<AudioQuality>();
+ long audioTimescale = -1;
+
+ for (Track track : movie.getTracks()) {
+ if (track.getMediaHeaderBox() instanceof VideoMediaHeaderBox) {
+ videoFragmentsDurations = checkFragmentsAlign(videoFragmentsDurations, calculateFragmentDurations(track, movie));
+ SampleDescriptionBox stsd = track.getSampleDescriptionBox();
+ videoQualities.add(getVideoQuality(track, (VisualSampleEntry) stsd.getSampleEntry()));
+ if (videoTimescale == -1) {
+ videoTimescale = track.getTrackMetaData().getTimescale();
+ } else {
+ assert videoTimescale == track.getTrackMetaData().getTimescale();
+ }
+ }
+ if (track.getMediaHeaderBox() instanceof SoundMediaHeaderBox) {
+ audioFragmentsDurations = checkFragmentsAlign(audioFragmentsDurations, calculateFragmentDurations(track, movie));
+ SampleDescriptionBox stsd = track.getSampleDescriptionBox();
+ audioQualities.add(getAudioQuality(track, (AudioSampleEntry) stsd.getSampleEntry()));
+ if (audioTimescale == -1) {
+ audioTimescale = track.getTrackMetaData().getTimescale();
+ } else {
+ assert audioTimescale == track.getTrackMetaData().getTimescale();
+ }
+
+ }
+ }
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder;
+ try {
+ documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new IOException(e);
+ }
+ Document document = documentBuilder.newDocument();
+
+
+ Element smoothStreamingMedia = document.createElement("SmoothStreamingMedia");
+ document.appendChild(smoothStreamingMedia);
+ smoothStreamingMedia.setAttribute("MajorVersion", "2");
+ smoothStreamingMedia.setAttribute("MinorVersion", "1");
+// silverlight ignores the timescale attr smoothStreamingMedia.addAttribute(new Attribute("TimeScale", Long.toString(movieTimeScale)));
+ smoothStreamingMedia.setAttribute("Duration", "0");
+
+ smoothStreamingMedia.appendChild(document.createComment(Version.VERSION));
+ Element videoStreamIndex = document.createElement("StreamIndex");
+ videoStreamIndex.setAttribute("Type", "video");
+ videoStreamIndex.setAttribute("TimeScale", Long.toString(videoTimescale)); // silverlight ignores the timescale attr
+ videoStreamIndex.setAttribute("Chunks", Integer.toString(videoFragmentsDurations.length));
+ videoStreamIndex.setAttribute("Url", "video/{bitrate}/{start time}");
+ videoStreamIndex.setAttribute("QualityLevels", Integer.toString(videoQualities.size()));
+ smoothStreamingMedia.appendChild(videoStreamIndex);
+
+ for (int i = 0; i < videoQualities.size(); i++) {
+ VideoQuality vq = videoQualities.get(i);
+ Element qualityLevel = document.createElement("QualityLevel");
+ qualityLevel.setAttribute("Index", Integer.toString(i));
+ qualityLevel.setAttribute("Bitrate", Long.toString(vq.bitrate));
+ qualityLevel.setAttribute("FourCC", vq.fourCC);
+ qualityLevel.setAttribute("MaxWidth", Long.toString(vq.width));
+ qualityLevel.setAttribute("MaxHeight", Long.toString(vq.height));
+ qualityLevel.setAttribute("CodecPrivateData", vq.codecPrivateData);
+ qualityLevel.setAttribute("NALUnitLengthField", Integer.toString(vq.nalLength));
+ videoStreamIndex.appendChild(qualityLevel);
+ }
+
+ for (int i = 0; i < videoFragmentsDurations.length; i++) {
+ Element c = document.createElement("c");
+ c.setAttribute("n", Integer.toString(i));
+ c.setAttribute("d", Long.toString(videoFragmentsDurations[i]));
+ videoStreamIndex.appendChild(c);
+ }
+
+ if (audioFragmentsDurations != null) {
+ Element audioStreamIndex = document.createElement("StreamIndex");
+ audioStreamIndex.setAttribute("Type", "audio");
+ audioStreamIndex.setAttribute("TimeScale", Long.toString(audioTimescale)); // silverlight ignores the timescale attr
+ audioStreamIndex.setAttribute("Chunks", Integer.toString(audioFragmentsDurations.length));
+ audioStreamIndex.setAttribute("Url", "audio/{bitrate}/{start time}");
+ audioStreamIndex.setAttribute("QualityLevels", Integer.toString(audioQualities.size()));
+ smoothStreamingMedia.appendChild(audioStreamIndex);
+
+ for (int i = 0; i < audioQualities.size(); i++) {
+ AudioQuality aq = audioQualities.get(i);
+ Element qualityLevel = document.createElement("QualityLevel");
+ qualityLevel.setAttribute("Index", Integer.toString(i));
+ qualityLevel.setAttribute("FourCC", aq.fourCC);
+ qualityLevel.setAttribute("Bitrate", Long.toString(aq.bitrate));
+ qualityLevel.setAttribute("AudioTag", Integer.toString(aq.audioTag));
+ qualityLevel.setAttribute("SamplingRate", Long.toString(aq.samplingRate));
+ qualityLevel.setAttribute("Channels", Integer.toString(aq.channels));
+ qualityLevel.setAttribute("BitsPerSample", Integer.toString(aq.bitPerSample));
+ qualityLevel.setAttribute("PacketSize", Integer.toString(aq.packetSize));
+ qualityLevel.setAttribute("CodecPrivateData", aq.codecPrivateData);
+ audioStreamIndex.appendChild(qualityLevel);
+ }
+ for (int i = 0; i < audioFragmentsDurations.length; i++) {
+ Element c = document.createElement("c");
+ c.setAttribute("n", Integer.toString(i));
+ c.setAttribute("d", Long.toString(audioFragmentsDurations[i]));
+ audioStreamIndex.appendChild(c);
+ }
+ }
+
+ document.setXmlStandalone(true);
+ Source source = new DOMSource(document);
+ StringWriter stringWriter = new StringWriter();
+ Result result = new StreamResult(stringWriter);
+ TransformerFactory factory = TransformerFactory.newInstance();
+ Transformer transformer;
+ try {
+ transformer = factory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.transform(source, result);
+ } catch (TransformerConfigurationException e) {
+ throw new IOException(e);
+ } catch (TransformerException e) {
+ throw new IOException(e);
+ }
+ return stringWriter.getBuffer().toString();
+
+
+ }
+
+ private AudioQuality getAudioQuality(Track track, AudioSampleEntry ase) {
+ if (getFormat(ase).equals("mp4a")) {
+ return getAacAudioQuality(track, ase);
+ } else if (getFormat(ase).equals("ec-3")) {
+ return getEc3AudioQuality(track, ase);
+ } else if (getFormat(ase).startsWith("dts")) {
+ return getDtsAudioQuality(track, ase);
+ } else {
+ throw new InternalError("I don't know what to do with audio of type " + getFormat(ase));
+ }
+
+ }
+
+ private AudioQuality getAacAudioQuality(Track track, AudioSampleEntry ase) {
+ AudioQuality l = new AudioQuality();
+ final ESDescriptorBox esDescriptorBox = ase.getBoxes(ESDescriptorBox.class).get(0);
+ final AudioSpecificConfig audioSpecificConfig = esDescriptorBox.getEsDescriptor().getDecoderConfigDescriptor().getAudioSpecificInfo();
+ if (audioSpecificConfig.getSbrPresentFlag() == 1) {
+ l.fourCC = "AACH";
+ } else if (audioSpecificConfig.getPsPresentFlag() == 1) {
+ l.fourCC = "AACP"; //I'm not sure if that's what MS considers as AAC+ - because actually AAC+ and AAC-HE should be the same...
+ } else {
+ l.fourCC = "AACL";
+ }
+ l.bitrate = getBitrate(track);
+ l.audioTag = 255;
+ l.samplingRate = ase.getSampleRate();
+ l.channels = ase.getChannelCount();
+ l.bitPerSample = ase.getSampleSize();
+ l.packetSize = 4;
+ l.codecPrivateData = getAudioCodecPrivateData(audioSpecificConfig);
+ //Index="0" Bitrate="103000" AudioTag="255" SamplingRate="44100" Channels="2" BitsPerSample="16" packetSize="4" CodecPrivateData=""
+ return l;
+ }
+
+ private AudioQuality getEc3AudioQuality(Track track, AudioSampleEntry ase) {
+ final EC3SpecificBox ec3SpecificBox = ase.getBoxes(EC3SpecificBox.class).get(0);
+ if (ec3SpecificBox == null) {
+ throw new RuntimeException("EC-3 track misses EC3SpecificBox!");
+ }
+
+ short nfchans = 0; //full bandwidth channels
+ short lfechans = 0;
+ byte dWChannelMaskFirstByte = 0;
+ byte dWChannelMaskSecondByte = 0;
+ for (EC3SpecificBox.Entry entry : ec3SpecificBox.getEntries()) {
+ /*
+ Table 4.3: Audio coding mode
+ acmod Audio coding mode Nfchans Channel array ordering
+ 000 1 + 1 2 Ch1, Ch2
+ 001 1/0 1 C
+ 010 2/0 2 L, R
+ 011 3/0 3 L, C, R
+ 100 2/1 3 L, R, S
+ 101 3/1 4 L, C, R, S
+ 110 2/2 4 L, R, SL, SR
+ 111 3/2 5 L, C, R, SL, SR
+
+ Table F.2: Chan_loc field bit assignments
+ Bit Location
+ 0 Lc/Rc pair
+ 1 Lrs/Rrs pair
+ 2 Cs
+ 3 Ts
+ 4 Lsd/Rsd pair
+ 5 Lw/Rw pair
+ 6 Lvh/Rvh pair
+ 7 Cvh
+ 8 LFE2
+ */
+ switch (entry.acmod) {
+ case 0: //1+1; Ch1, Ch2
+ nfchans += 2;
+ throw new RuntimeException("Smooth Streaming doesn't support DDP 1+1 mode");
+ case 1: //1/0; C
+ nfchans += 1;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0x20;
+ }
+ break;
+ case 2: //2/0; L, R
+ nfchans += 2;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xC0;
+ }
+ break;
+ case 3: //3/0; L, C, R
+ nfchans += 3;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xE0;
+ }
+ break;
+ case 4: //2/1; L, R, S
+ nfchans += 3;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xC0;
+ dWChannelMaskSecondByte |= 0x80;
+ }
+ break;
+ case 5: //3/1; L, C, R, S
+ nfchans += 4;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xE0;
+ dWChannelMaskSecondByte |= 0x80;
+ }
+ break;
+ case 6: //2/2; L, R, SL, SR
+ nfchans += 4;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xCC;
+ }
+ break;
+ case 7: //3/2; L, C, R, SL, SR
+ nfchans += 5;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xEC;
+ }
+ break;
+ }
+ if (entry.lfeon == 1) {
+ lfechans ++;
+ dWChannelMaskFirstByte |= 0x10;
+ }
+ }
+
+ final ByteBuffer waveformatex = ByteBuffer.allocate(22);
+ waveformatex.put(new byte[]{0x00, 0x06}); //1536 wSamplesPerBlock - little endian
+ waveformatex.put(dWChannelMaskFirstByte);
+ waveformatex.put(dWChannelMaskSecondByte);
+ waveformatex.put(new byte[]{0x00, 0x00}); //pad dwChannelMask to 32bit
+ waveformatex.put(new byte[]{(byte)0xAF, (byte)0x87, (byte)0xFB, (byte)0xA7, 0x02, 0x2D, (byte)0xFB, 0x42, (byte)0xA4, (byte)0xD4, 0x05, (byte)0xCD, (byte)0x93, (byte)0x84, 0x3B, (byte)0xDD}); //SubFormat - Dolby Digital Plus GUID
+
+ final ByteBuffer dec3Content = ByteBuffer.allocate((int) ec3SpecificBox.getContentSize());
+ ec3SpecificBox.getContent(dec3Content);
+
+ AudioQuality l = new AudioQuality();
+ l.fourCC = "EC-3";
+ l.bitrate = getBitrate(track);
+ l.audioTag = 65534;
+ l.samplingRate = ase.getSampleRate();
+ l.channels = nfchans + lfechans;
+ l.bitPerSample = 16;
+ l.packetSize = track.getSamples().get(0).limit(); //assuming all are same size
+ l.codecPrivateData = Hex.encodeHex(waveformatex.array()) + Hex.encodeHex(dec3Content.array()); //append EC3SpecificBox (big endian) at the end of waveformatex
+ return l;
+ }
+
+ private AudioQuality getDtsAudioQuality(Track track, AudioSampleEntry ase) {
+ final DTSSpecificBox dtsSpecificBox = ase.getBoxes(DTSSpecificBox.class).get(0);
+ if (dtsSpecificBox == null) {
+ throw new RuntimeException("DTS track misses DTSSpecificBox!");
+ }
+
+ final ByteBuffer waveformatex = ByteBuffer.allocate(22);
+ final int frameDuration = dtsSpecificBox.getFrameDuration();
+ short samplesPerBlock = 0;
+ switch (frameDuration) {
+ case 0:
+ samplesPerBlock = 512;
+ break;
+ case 1:
+ samplesPerBlock = 1024;
+ break;
+ case 2:
+ samplesPerBlock = 2048;
+ break;
+ case 3:
+ samplesPerBlock = 4096;
+ break;
+ }
+ waveformatex.put((byte) (samplesPerBlock & 0xff));
+ waveformatex.put((byte) (samplesPerBlock >>> 8));
+ final int dwChannelMask = getNumChannelsAndMask(dtsSpecificBox)[1];
+ waveformatex.put((byte) (dwChannelMask & 0xff));
+ waveformatex.put((byte) (dwChannelMask >>> 8));
+ waveformatex.put((byte) (dwChannelMask >>> 16));
+ waveformatex.put((byte) (dwChannelMask >>> 24));
+ waveformatex.put(new byte[]{(byte)0xAE, (byte)0xE4, (byte)0xBF, (byte)0x5E, (byte)0x61, (byte)0x5E, (byte)0x41, (byte)0x87, (byte)0x92, (byte)0xFC, (byte)0xA4, (byte)0x81, (byte)0x26, (byte)0x99, (byte)0x02, (byte)0x11}); //DTS-HD GUID
+
+ final ByteBuffer dtsCodecPrivateData = ByteBuffer.allocate(8);
+ dtsCodecPrivateData.put((byte) dtsSpecificBox.getStreamConstruction());
+
+ final int channelLayout = dtsSpecificBox.getChannelLayout();
+ dtsCodecPrivateData.put((byte) (channelLayout & 0xff));
+ dtsCodecPrivateData.put((byte) (channelLayout >>> 8));
+ dtsCodecPrivateData.put((byte) (channelLayout >>> 16));
+ dtsCodecPrivateData.put((byte) (channelLayout >>> 24));
+
+ byte dtsFlags = (byte) (dtsSpecificBox.getMultiAssetFlag() << 1);
+ dtsFlags |= dtsSpecificBox.getLBRDurationMod();
+ dtsCodecPrivateData.put(dtsFlags);
+ dtsCodecPrivateData.put(new byte[]{0x00, 0x00}); //reserved
+
+ AudioQuality l = new AudioQuality();
+ l.fourCC = getFormat(ase);
+ l.bitrate = dtsSpecificBox.getAvgBitRate();
+ l.audioTag = 65534;
+ l.samplingRate = dtsSpecificBox.getDTSSamplingFrequency();
+ l.channels = getNumChannelsAndMask(dtsSpecificBox)[0];
+ l.bitPerSample = 16;
+ l.packetSize = track.getSamples().get(0).limit(); //assuming all are same size
+ l.codecPrivateData = Hex.encodeHex(waveformatex.array()) + Hex.encodeHex(dtsCodecPrivateData.array());
+ return l;
+
+ }
+
+ /* dwChannelMask
+ L SPEAKER_FRONT_LEFT 0x00000001
+ R SPEAKER_FRONT_RIGHT 0x00000002
+ C SPEAKER_FRONT_CENTER 0x00000004
+ LFE1 SPEAKER_LOW_FREQUENCY 0x00000008
+ Ls or Lsr* SPEAKER_BACK_LEFT 0x00000010
+ Rs or Rsr* SPEAKER_BACK_RIGHT 0x00000020
+ Lc SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040
+ Rc SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080
+ Cs SPEAKER_BACK_CENTER 0x00000100
+ Lss SPEAKER_SIDE_LEFT 0x00000200
+ Rss SPEAKER_SIDE_RIGHT 0x00000400
+ Oh SPEAKER_TOP_CENTER 0x00000800
+ Lh SPEAKER_TOP_FRONT_LEFT 0x00001000
+ Ch SPEAKER_TOP_FRONT_CENTER 0x00002000
+ Rh SPEAKER_TOP_FRONT_RIGHT 0x00004000
+ Lhr SPEAKER_TOP_BACK_LEFT 0x00008000
+ Chf SPEAKER_TOP_BACK_CENTER 0x00010000
+ Rhr SPEAKER_TOP_BACK_RIGHT 0x00020000
+ SPEAKER_RESERVED 0x80000000
+
+ * if Lss, Rss exist, then this position is equivalent to Lsr, Rsr respectively
+ */
+ private int[] getNumChannelsAndMask(DTSSpecificBox dtsSpecificBox) {
+ final int channelLayout = dtsSpecificBox.getChannelLayout();
+ int numChannels = 0;
+ int dwChannelMask = 0;
+ if ((channelLayout & 0x0001) == 0x0001) {
+ //0001h Center in front of listener 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000004; //SPEAKER_FRONT_CENTER
+ }
+ if ((channelLayout & 0x0002) == 0x0002) {
+ //0002h Left/Right in front 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000001; //SPEAKER_FRONT_LEFT
+ dwChannelMask |= 0x00000002; //SPEAKER_FRONT_RIGHT
+ }
+ if ((channelLayout & 0x0004) == 0x0004) {
+ //0004h Left/Right surround on side in rear 2
+ numChannels += 2;
+ //* if Lss, Rss exist, then this position is equivalent to Lsr, Rsr respectively
+ dwChannelMask |= 0x00000010; //SPEAKER_BACK_LEFT
+ dwChannelMask |= 0x00000020; //SPEAKER_BACK_RIGHT
+ }
+ if ((channelLayout & 0x0008) == 0x0008) {
+ //0008h Low frequency effects subwoofer 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000008; //SPEAKER_LOW_FREQUENCY
+ }
+ if ((channelLayout & 0x0010) == 0x0010) {
+ //0010h Center surround in rear 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000100; //SPEAKER_BACK_CENTER
+ }
+ if ((channelLayout & 0x0020) == 0x0020) {
+ //0020h Left/Right height in front 2
+ numChannels += 2;
+ dwChannelMask |= 0x00001000; //SPEAKER_TOP_FRONT_LEFT
+ dwChannelMask |= 0x00004000; //SPEAKER_TOP_FRONT_RIGHT
+ }
+ if ((channelLayout & 0x0040) == 0x0040) {
+ //0040h Left/Right surround in rear 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000010; //SPEAKER_BACK_LEFT
+ dwChannelMask |= 0x00000020; //SPEAKER_BACK_RIGHT
+ }
+ if ((channelLayout & 0x0080) == 0x0080) {
+ //0080h Center Height in front 1
+ numChannels += 1;
+ dwChannelMask |= 0x00002000; //SPEAKER_TOP_FRONT_CENTER
+ }
+ if ((channelLayout & 0x0100) == 0x0100) {
+ //0100h Over the listener’s head 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000800; //SPEAKER_TOP_CENTER
+ }
+ if ((channelLayout & 0x0200) == 0x0200) {
+ //0200h Between left/right and center in front 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000040; //SPEAKER_FRONT_LEFT_OF_CENTER
+ dwChannelMask |= 0x00000080; //SPEAKER_FRONT_RIGHT_OF_CENTER
+ }
+ if ((channelLayout & 0x0400) == 0x0400) {
+ //0400h Left/Right on side in front 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000200; //SPEAKER_SIDE_LEFT
+ dwChannelMask |= 0x00000400; //SPEAKER_SIDE_RIGHT
+ }
+ if ((channelLayout & 0x0800) == 0x0800) {
+ //0800h Left/Right surround on side 2
+ numChannels += 2;
+ //* if Lss, Rss exist, then this position is equivalent to Lsr, Rsr respectively
+ dwChannelMask |= 0x00000010; //SPEAKER_BACK_LEFT
+ dwChannelMask |= 0x00000020; //SPEAKER_BACK_RIGHT
+ }
+ if ((channelLayout & 0x1000) == 0x1000) {
+ //1000h Second low frequency effects subwoofer 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000008; //SPEAKER_LOW_FREQUENCY
+ }
+ if ((channelLayout & 0x2000) == 0x2000) {
+ //2000h Left/Right height on side 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000010; //SPEAKER_BACK_LEFT
+ dwChannelMask |= 0x00000020; //SPEAKER_BACK_RIGHT
+ }
+ if ((channelLayout & 0x4000) == 0x4000) {
+ //4000h Center height in rear 1
+ numChannels += 1;
+ dwChannelMask |= 0x00010000; //SPEAKER_TOP_BACK_CENTER
+ }
+ if ((channelLayout & 0x8000) == 0x8000) {
+ //8000h Left/Right height in rear 2
+ numChannels += 2;
+ dwChannelMask |= 0x00008000; //SPEAKER_TOP_BACK_LEFT
+ dwChannelMask |= 0x00020000; //SPEAKER_TOP_BACK_RIGHT
+ }
+ if ((channelLayout & 0x10000) == 0x10000) {
+ //10000h Center below in front
+ numChannels += 1;
+ }
+ if ((channelLayout & 0x20000) == 0x20000) {
+ //20000h Left/Right below in front
+ numChannels += 2;
+ }
+ return new int[]{numChannels, dwChannelMask};
+ }
+
+ private String getAudioCodecPrivateData(AudioSpecificConfig audioSpecificConfig) {
+ byte[] configByteArray = audioSpecificConfig.getConfigBytes();
+ return Hex.encodeHex(configByteArray);
+ }
+
+ private VideoQuality getVideoQuality(Track track, VisualSampleEntry vse) {
+ VideoQuality l;
+ if ("avc1".equals(getFormat(vse))) {
+ AvcConfigurationBox avcConfigurationBox = vse.getBoxes(AvcConfigurationBox.class).get(0);
+ l = new VideoQuality();
+ l.bitrate = getBitrate(track);
+ l.codecPrivateData = Hex.encodeHex(getAvcCodecPrivateData(avcConfigurationBox));
+ l.fourCC = "AVC1";
+ l.width = vse.getWidth();
+ l.height = vse.getHeight();
+ l.nalLength = avcConfigurationBox.getLengthSizeMinusOne() + 1;
+ } else {
+ throw new InternalError("I don't know how to handle video of type " + getFormat(vse));
+ }
+ return l;
+ }
+
+ private byte[] getAvcCodecPrivateData(AvcConfigurationBox avcConfigurationBox) {
+ List<byte[]> sps = avcConfigurationBox.getSequenceParameterSets();
+ List<byte[]> pps = avcConfigurationBox.getPictureParameterSets();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ baos.write(new byte[]{0, 0, 0, 1});
+
+ for (byte[] sp : sps) {
+ baos.write(sp);
+ }
+ baos.write(new byte[]{0, 0, 0, 1});
+ for (byte[] pp : pps) {
+ baos.write(pp);
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException("ByteArrayOutputStream do not throw IOException ?!?!?");
+ }
+ return baos.toByteArray();
+ }
+
+ private class DependentSubstreamMask {
+ private byte dWChannelMaskFirstByte;
+ private byte dWChannelMaskSecondByte;
+ private EC3SpecificBox.Entry entry;
+
+ public DependentSubstreamMask(byte dWChannelMaskFirstByte, byte dWChannelMaskSecondByte, EC3SpecificBox.Entry entry) {
+ this.dWChannelMaskFirstByte = dWChannelMaskFirstByte;
+ this.dWChannelMaskSecondByte = dWChannelMaskSecondByte;
+ this.entry = entry;
+ }
+
+ public byte getdWChannelMaskFirstByte() {
+ return dWChannelMaskFirstByte;
+ }
+
+ public byte getdWChannelMaskSecondByte() {
+ return dWChannelMaskSecondByte;
+ }
+
+ public DependentSubstreamMask process() {
+ switch (entry.chan_loc) {
+ case 0:
+ dWChannelMaskFirstByte |= 0x3;
+ break;
+ case 1:
+ dWChannelMaskFirstByte |= 0xC;
+ break;
+ case 2:
+ dWChannelMaskSecondByte |= 0x80;
+ break;
+ case 3:
+ dWChannelMaskSecondByte |= 0x8;
+ break;
+ case 6:
+ dWChannelMaskSecondByte |= 0x5;
+ break;
+ case 7:
+ dWChannelMaskSecondByte |= 0x2;
+ break;
+ }
+ return this;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/FlatPackageWriterImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/FlatPackageWriterImpl.java.svn-base
new file mode 100644
index 0000000..3e3847c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/FlatPackageWriterImpl.java.svn-base
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.SoundMediaHeaderBox;
+import com.coremedia.iso.boxes.VideoMediaHeaderBox;
+import com.coremedia.iso.boxes.fragment.MovieFragmentBox;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.builder.*;
+import com.googlecode.mp4parser.authoring.tracks.ChangeTimeScaleTrack;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.Iterator;
+import java.util.logging.Logger;
+
+public class FlatPackageWriterImpl implements PackageWriter {
+ private static Logger LOG = Logger.getLogger(FlatPackageWriterImpl.class.getName());
+ long timeScale = 10000000;
+
+ private File outputDirectory;
+ private boolean debugOutput;
+ private FragmentedMp4Builder ismvBuilder;
+ ManifestWriter manifestWriter;
+
+ public FlatPackageWriterImpl() {
+ ismvBuilder = new FragmentedMp4Builder();
+ FragmentIntersectionFinder intersectionFinder = new SyncSampleIntersectFinderImpl();
+ ismvBuilder.setIntersectionFinder(intersectionFinder);
+ manifestWriter = new FlatManifestWriterImpl(intersectionFinder);
+ }
+
+ /**
+ * Creates a factory for a smooth streaming package. A smooth streaming package is
+ * a collection of files that can be served by a webserver as a smooth streaming
+ * stream.
+ * @param minFragmentDuration the smallest allowable duration of a fragment (0 == no restriction).
+ */
+ public FlatPackageWriterImpl(int minFragmentDuration) {
+ ismvBuilder = new FragmentedMp4Builder();
+ FragmentIntersectionFinder intersectionFinder = new SyncSampleIntersectFinderImpl(minFragmentDuration);
+ ismvBuilder.setIntersectionFinder(intersectionFinder);
+ manifestWriter = new FlatManifestWriterImpl(intersectionFinder);
+ }
+
+ public void setOutputDirectory(File outputDirectory) {
+ assert outputDirectory.isDirectory();
+ this.outputDirectory = outputDirectory;
+
+ }
+
+ public void setDebugOutput(boolean debugOutput) {
+ this.debugOutput = debugOutput;
+ }
+
+ public void setIsmvBuilder(FragmentedMp4Builder ismvBuilder) {
+ this.ismvBuilder = ismvBuilder;
+ this.manifestWriter = new FlatManifestWriterImpl(ismvBuilder.getFragmentIntersectionFinder());
+ }
+
+ public void setManifestWriter(ManifestWriter manifestWriter) {
+ this.manifestWriter = manifestWriter;
+ }
+
+ /**
+ * Writes the movie given as <code>qualities</code> flattened into the
+ * <code>outputDirectory</code>.
+ *
+ * @param source the source movie with all qualities
+ * @throws IOException
+ */
+ public void write(Movie source) throws IOException {
+
+ if (debugOutput) {
+ outputDirectory.mkdirs();
+ DefaultMp4Builder defaultMp4Builder = new DefaultMp4Builder();
+ IsoFile muxed = defaultMp4Builder.build(source);
+ File muxedFile = new File(outputDirectory, "debug_1_muxed.mp4");
+ FileOutputStream muxedFileOutputStream = new FileOutputStream(muxedFile);
+ muxed.getBox(muxedFileOutputStream.getChannel());
+ muxedFileOutputStream.close();
+ }
+ Movie cleanedSource = removeUnknownTracks(source);
+ Movie movieWithAdjustedTimescale = correctTimescale(cleanedSource);
+
+ if (debugOutput) {
+ DefaultMp4Builder defaultMp4Builder = new DefaultMp4Builder();
+ IsoFile muxed = defaultMp4Builder.build(movieWithAdjustedTimescale);
+ File muxedFile = new File(outputDirectory, "debug_2_timescale.mp4");
+ FileOutputStream muxedFileOutputStream = new FileOutputStream(muxedFile);
+ muxed.getBox(muxedFileOutputStream.getChannel());
+ muxedFileOutputStream.close();
+ }
+ IsoFile isoFile = ismvBuilder.build(movieWithAdjustedTimescale);
+ if (debugOutput) {
+ File allQualities = new File(outputDirectory, "debug_3_fragmented.mp4");
+ FileOutputStream allQualis = new FileOutputStream(allQualities);
+ isoFile.getBox(allQualis.getChannel());
+ allQualis.close();
+ }
+
+
+ for (Track track : movieWithAdjustedTimescale.getTracks()) {
+ String bitrate = Long.toString(manifestWriter.getBitrate(track));
+ long trackId = track.getTrackMetaData().getTrackId();
+ Iterator<Box> boxIt = isoFile.getBoxes().iterator();
+ File mediaOutDir;
+ if (track.getMediaHeaderBox() instanceof SoundMediaHeaderBox) {
+ mediaOutDir = new File(outputDirectory, "audio");
+
+ } else if (track.getMediaHeaderBox() instanceof VideoMediaHeaderBox) {
+ mediaOutDir = new File(outputDirectory, "video");
+ } else {
+ System.err.println("Skipping Track with handler " + track.getHandler() + " and " + track.getMediaHeaderBox().getClass().getSimpleName());
+ continue;
+ }
+ File bitRateOutputDir = new File(mediaOutDir, bitrate);
+ bitRateOutputDir.mkdirs();
+ LOG.finer("Created : " + bitRateOutputDir.getCanonicalPath());
+
+ long[] fragmentTimes = manifestWriter.calculateFragmentDurations(track, movieWithAdjustedTimescale);
+ long startTime = 0;
+ int currentFragment = 0;
+ while (boxIt.hasNext()) {
+ Box b = boxIt.next();
+ if (b instanceof MovieFragmentBox) {
+ assert ((MovieFragmentBox) b).getTrackCount() == 1;
+ if (((MovieFragmentBox) b).getTrackNumbers()[0] == trackId) {
+ FileOutputStream fos = new FileOutputStream(new File(bitRateOutputDir, Long.toString(startTime)));
+ startTime += fragmentTimes[currentFragment++];
+ FileChannel fc = fos.getChannel();
+ Box mdat = boxIt.next();
+ assert mdat.getType().equals("mdat");
+ b.getBox(fc); // moof
+ mdat.getBox(fc); // mdat
+ fc.truncate(fc.position());
+ fc.close();
+ }
+ }
+
+ }
+ }
+ FileWriter fw = new FileWriter(new File(outputDirectory, "Manifest"));
+ fw.write(manifestWriter.getManifest(movieWithAdjustedTimescale));
+ fw.close();
+
+ }
+
+ private Movie removeUnknownTracks(Movie source) {
+ Movie nuMovie = new Movie();
+ for (Track track : source.getTracks()) {
+ if ("vide".equals(track.getHandler()) || "soun".equals(track.getHandler())) {
+ nuMovie.addTrack(track);
+ } else {
+ LOG.fine("Removed track " + track);
+ }
+ }
+ return nuMovie;
+ }
+
+
+ /**
+ * Returns a new <code>Movie</code> in that all tracks have the timescale 10000000. CTS & DTS are modified
+ * in a way that even with more than one framerate the fragments exactly begin at the same time.
+ *
+ * @param movie
+ * @return a movie with timescales suitable for smooth streaming manifests
+ */
+ public Movie correctTimescale(Movie movie) {
+ Movie nuMovie = new Movie();
+ for (Track track : movie.getTracks()) {
+ nuMovie.addTrack(new ChangeTimeScaleTrack(track, timeScale, ismvBuilder.getFragmentIntersectionFinder().sampleNumbers(track, movie)));
+ }
+ return nuMovie;
+
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/ManifestWriter.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/ManifestWriter.java.svn-base
new file mode 100644
index 0000000..2b2ba7d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/ManifestWriter.java.svn-base
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.io.IOException;
+
+public interface ManifestWriter {
+ String getManifest(Movie inputs) throws IOException;
+
+ long getBitrate(Track track);
+
+ long[] calculateFragmentDurations(Track track, Movie movie);
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/PackageWriter.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/PackageWriter.java.svn-base
new file mode 100644
index 0000000..0d97fc5
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/PackageWriter.java.svn-base
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+import com.googlecode.mp4parser.authoring.Movie;
+
+import java.io.IOException;
+
+/**
+ * Writes the whole package.
+ */
+public interface PackageWriter {
+ public void write(Movie qualities) throws IOException;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/VideoQuality.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/VideoQuality.java.svn-base
new file mode 100644
index 0000000..4a70e47
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/.svn/text-base/VideoQuality.java.svn-base
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+class VideoQuality {
+ long bitrate;
+ String fourCC;
+ int width;
+ int height;
+ String codecPrivateData;
+ int nalLength;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AbstractManifestWriter.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AbstractManifestWriter.java
new file mode 100644
index 0000000..6ee4ffa
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AbstractManifestWriter.java
@@ -0,0 +1,126 @@
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+import com.coremedia.iso.boxes.OriginalFormatBox;
+import com.coremedia.iso.boxes.TimeToSampleBox;
+import com.coremedia.iso.boxes.sampleentry.SampleEntry;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.builder.FragmentIntersectionFinder;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mstattma
+ * Date: 17.08.12
+ * Time: 02:51
+ * To change this template use File | Settings | File Templates.
+ */
+public abstract class AbstractManifestWriter implements ManifestWriter {
+ private static final Logger LOG = Logger.getLogger(AbstractManifestWriter.class.getName());
+
+ private FragmentIntersectionFinder intersectionFinder;
+ protected long[] audioFragmentsDurations;
+ protected long[] videoFragmentsDurations;
+
+ protected AbstractManifestWriter(FragmentIntersectionFinder intersectionFinder) {
+ this.intersectionFinder = intersectionFinder;
+ }
+
+ /**
+ * Calculates the length of each fragment in the given <code>track</code> (as part of <code>movie</code>).
+ *
+ * @param track target of calculation
+ * @param movie the <code>track</code> must be part of this <code>movie</code>
+ * @return the duration of each fragment in track timescale
+ */
+ public long[] calculateFragmentDurations(Track track, Movie movie) {
+ long[] startSamples = intersectionFinder.sampleNumbers(track, movie);
+ long[] durations = new long[startSamples.length];
+ int currentFragment = 0;
+ int currentSample = 1; // sync samples start with 1 !
+
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ for (int max = currentSample + l2i(entry.getCount()); currentSample < max; currentSample++) {
+ // in this loop we go through the entry.getCount() samples starting from current sample.
+ // the next entry.getCount() samples have the same decoding time.
+ if (currentFragment != startSamples.length - 1 && currentSample == startSamples[currentFragment + 1]) {
+ // we are not in the last fragment && the current sample is the start sample of the next fragment
+ currentFragment++;
+ }
+ durations[currentFragment] += entry.getDelta();
+
+
+ }
+ }
+ return durations;
+
+ }
+
+ public long getBitrate(Track track) {
+ long bitrate = 0;
+ for (ByteBuffer sample : track.getSamples()) {
+ bitrate += sample.limit();
+ }
+ bitrate *= 8; // from bytes to bits
+ bitrate /= ((double) getDuration(track)) / track.getTrackMetaData().getTimescale(); // per second
+ return bitrate;
+ }
+
+ protected static long getDuration(Track track) {
+ long duration = 0;
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ duration += entry.getCount() * entry.getDelta();
+ }
+ return duration;
+ }
+
+ protected long[] checkFragmentsAlign(long[] referenceTimes, long[] checkTimes) throws IOException {
+
+ if (referenceTimes == null || referenceTimes.length == 0) {
+ return checkTimes;
+ }
+ long[] referenceTimesMinusLast = new long[referenceTimes.length - 1];
+ System.arraycopy(referenceTimes, 0, referenceTimesMinusLast, 0, referenceTimes.length - 1);
+ long[] checkTimesMinusLast = new long[checkTimes.length - 1];
+ System.arraycopy(checkTimes, 0, checkTimesMinusLast, 0, checkTimes.length - 1);
+
+ if (!Arrays.equals(checkTimesMinusLast, referenceTimesMinusLast)) {
+ String log = "";
+ log += (referenceTimes.length);
+ log += ("Reference : [");
+ for (long l : referenceTimes) {
+ log += (String.format("%10d,", l));
+ }
+ log += ("]");
+ LOG.warning(log);
+ log = "";
+
+ log += (checkTimes.length);
+ log += ("Current : [");
+ for (long l : checkTimes) {
+ log += (String.format("%10d,", l));
+ }
+ log += ("]");
+ LOG.warning(log);
+ throw new IOException("Track does not have the same fragment borders as its predecessor.");
+
+ } else {
+ return checkTimes;
+ }
+ }
+
+ protected String getFormat(SampleEntry se) {
+ String type = se.getType();
+ if (type.equals("encv") || type.equals("enca") || type.equals("encv")) {
+ OriginalFormatBox frma = se.getBoxes(OriginalFormatBox.class, true).get(0);
+ type = frma.getDataFormat();
+ }
+ return type;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AudioQuality.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AudioQuality.java
new file mode 100644
index 0000000..39e115f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/AudioQuality.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+
+public class AudioQuality {
+ String fourCC;
+ long bitrate;
+ int audioTag;
+ long samplingRate;
+ int channels;
+ int bitPerSample;
+ int packetSize;
+ String language;
+ String codecPrivateData;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatManifestWriterImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatManifestWriterImpl.java
new file mode 100644
index 0000000..5cc9be9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatManifestWriterImpl.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.boxes.SampleDescriptionBox;
+import com.coremedia.iso.boxes.SoundMediaHeaderBox;
+import com.coremedia.iso.boxes.VideoMediaHeaderBox;
+import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
+import com.googlecode.mp4parser.Version;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.builder.FragmentIntersectionFinder;
+import com.googlecode.mp4parser.boxes.DTSSpecificBox;
+import com.googlecode.mp4parser.boxes.EC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.AudioSpecificConfig;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+public class FlatManifestWriterImpl extends AbstractManifestWriter {
+ private static final Logger LOG = Logger.getLogger(FlatManifestWriterImpl.class.getName());
+
+ protected FlatManifestWriterImpl(FragmentIntersectionFinder intersectionFinder) {
+ super(intersectionFinder);
+ }
+
+ /**
+ * Overwrite this method in subclasses to add your specialities.
+ *
+ * @param manifest the original manifest
+ * @return your customized version of the manifest
+ */
+ protected Document customizeManifest(Document manifest) {
+ return manifest;
+ }
+
+ public String getManifest(Movie movie) throws IOException {
+
+ LinkedList<VideoQuality> videoQualities = new LinkedList<VideoQuality>();
+ long videoTimescale = -1;
+
+ LinkedList<AudioQuality> audioQualities = new LinkedList<AudioQuality>();
+ long audioTimescale = -1;
+
+ for (Track track : movie.getTracks()) {
+ if (track.getMediaHeaderBox() instanceof VideoMediaHeaderBox) {
+ videoFragmentsDurations = checkFragmentsAlign(videoFragmentsDurations, calculateFragmentDurations(track, movie));
+ SampleDescriptionBox stsd = track.getSampleDescriptionBox();
+ videoQualities.add(getVideoQuality(track, (VisualSampleEntry) stsd.getSampleEntry()));
+ if (videoTimescale == -1) {
+ videoTimescale = track.getTrackMetaData().getTimescale();
+ } else {
+ assert videoTimescale == track.getTrackMetaData().getTimescale();
+ }
+ }
+ if (track.getMediaHeaderBox() instanceof SoundMediaHeaderBox) {
+ audioFragmentsDurations = checkFragmentsAlign(audioFragmentsDurations, calculateFragmentDurations(track, movie));
+ SampleDescriptionBox stsd = track.getSampleDescriptionBox();
+ audioQualities.add(getAudioQuality(track, (AudioSampleEntry) stsd.getSampleEntry()));
+ if (audioTimescale == -1) {
+ audioTimescale = track.getTrackMetaData().getTimescale();
+ } else {
+ assert audioTimescale == track.getTrackMetaData().getTimescale();
+ }
+
+ }
+ }
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder;
+ try {
+ documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new IOException(e);
+ }
+ Document document = documentBuilder.newDocument();
+
+
+ Element smoothStreamingMedia = document.createElement("SmoothStreamingMedia");
+ document.appendChild(smoothStreamingMedia);
+ smoothStreamingMedia.setAttribute("MajorVersion", "2");
+ smoothStreamingMedia.setAttribute("MinorVersion", "1");
+// silverlight ignores the timescale attr smoothStreamingMedia.addAttribute(new Attribute("TimeScale", Long.toString(movieTimeScale)));
+ smoothStreamingMedia.setAttribute("Duration", "0");
+
+ smoothStreamingMedia.appendChild(document.createComment(Version.VERSION));
+ Element videoStreamIndex = document.createElement("StreamIndex");
+ videoStreamIndex.setAttribute("Type", "video");
+ videoStreamIndex.setAttribute("TimeScale", Long.toString(videoTimescale)); // silverlight ignores the timescale attr
+ videoStreamIndex.setAttribute("Chunks", Integer.toString(videoFragmentsDurations.length));
+ videoStreamIndex.setAttribute("Url", "video/{bitrate}/{start time}");
+ videoStreamIndex.setAttribute("QualityLevels", Integer.toString(videoQualities.size()));
+ smoothStreamingMedia.appendChild(videoStreamIndex);
+
+ for (int i = 0; i < videoQualities.size(); i++) {
+ VideoQuality vq = videoQualities.get(i);
+ Element qualityLevel = document.createElement("QualityLevel");
+ qualityLevel.setAttribute("Index", Integer.toString(i));
+ qualityLevel.setAttribute("Bitrate", Long.toString(vq.bitrate));
+ qualityLevel.setAttribute("FourCC", vq.fourCC);
+ qualityLevel.setAttribute("MaxWidth", Long.toString(vq.width));
+ qualityLevel.setAttribute("MaxHeight", Long.toString(vq.height));
+ qualityLevel.setAttribute("CodecPrivateData", vq.codecPrivateData);
+ qualityLevel.setAttribute("NALUnitLengthField", Integer.toString(vq.nalLength));
+ videoStreamIndex.appendChild(qualityLevel);
+ }
+
+ for (int i = 0; i < videoFragmentsDurations.length; i++) {
+ Element c = document.createElement("c");
+ c.setAttribute("n", Integer.toString(i));
+ c.setAttribute("d", Long.toString(videoFragmentsDurations[i]));
+ videoStreamIndex.appendChild(c);
+ }
+
+ if (audioFragmentsDurations != null) {
+ Element audioStreamIndex = document.createElement("StreamIndex");
+ audioStreamIndex.setAttribute("Type", "audio");
+ audioStreamIndex.setAttribute("TimeScale", Long.toString(audioTimescale)); // silverlight ignores the timescale attr
+ audioStreamIndex.setAttribute("Chunks", Integer.toString(audioFragmentsDurations.length));
+ audioStreamIndex.setAttribute("Url", "audio/{bitrate}/{start time}");
+ audioStreamIndex.setAttribute("QualityLevels", Integer.toString(audioQualities.size()));
+ smoothStreamingMedia.appendChild(audioStreamIndex);
+
+ for (int i = 0; i < audioQualities.size(); i++) {
+ AudioQuality aq = audioQualities.get(i);
+ Element qualityLevel = document.createElement("QualityLevel");
+ qualityLevel.setAttribute("Index", Integer.toString(i));
+ qualityLevel.setAttribute("FourCC", aq.fourCC);
+ qualityLevel.setAttribute("Bitrate", Long.toString(aq.bitrate));
+ qualityLevel.setAttribute("AudioTag", Integer.toString(aq.audioTag));
+ qualityLevel.setAttribute("SamplingRate", Long.toString(aq.samplingRate));
+ qualityLevel.setAttribute("Channels", Integer.toString(aq.channels));
+ qualityLevel.setAttribute("BitsPerSample", Integer.toString(aq.bitPerSample));
+ qualityLevel.setAttribute("PacketSize", Integer.toString(aq.packetSize));
+ qualityLevel.setAttribute("CodecPrivateData", aq.codecPrivateData);
+ audioStreamIndex.appendChild(qualityLevel);
+ }
+ for (int i = 0; i < audioFragmentsDurations.length; i++) {
+ Element c = document.createElement("c");
+ c.setAttribute("n", Integer.toString(i));
+ c.setAttribute("d", Long.toString(audioFragmentsDurations[i]));
+ audioStreamIndex.appendChild(c);
+ }
+ }
+
+ document.setXmlStandalone(true);
+ Source source = new DOMSource(document);
+ StringWriter stringWriter = new StringWriter();
+ Result result = new StreamResult(stringWriter);
+ TransformerFactory factory = TransformerFactory.newInstance();
+ Transformer transformer;
+ try {
+ transformer = factory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.transform(source, result);
+ } catch (TransformerConfigurationException e) {
+ throw new IOException(e);
+ } catch (TransformerException e) {
+ throw new IOException(e);
+ }
+ return stringWriter.getBuffer().toString();
+
+
+ }
+
+ private AudioQuality getAudioQuality(Track track, AudioSampleEntry ase) {
+ if (getFormat(ase).equals("mp4a")) {
+ return getAacAudioQuality(track, ase);
+ } else if (getFormat(ase).equals("ec-3")) {
+ return getEc3AudioQuality(track, ase);
+ } else if (getFormat(ase).startsWith("dts")) {
+ return getDtsAudioQuality(track, ase);
+ } else {
+ throw new InternalError("I don't know what to do with audio of type " + getFormat(ase));
+ }
+
+ }
+
+ private AudioQuality getAacAudioQuality(Track track, AudioSampleEntry ase) {
+ AudioQuality l = new AudioQuality();
+ final ESDescriptorBox esDescriptorBox = ase.getBoxes(ESDescriptorBox.class).get(0);
+ final AudioSpecificConfig audioSpecificConfig = esDescriptorBox.getEsDescriptor().getDecoderConfigDescriptor().getAudioSpecificInfo();
+ if (audioSpecificConfig.getSbrPresentFlag() == 1) {
+ l.fourCC = "AACH";
+ } else if (audioSpecificConfig.getPsPresentFlag() == 1) {
+ l.fourCC = "AACP"; //I'm not sure if that's what MS considers as AAC+ - because actually AAC+ and AAC-HE should be the same...
+ } else {
+ l.fourCC = "AACL";
+ }
+ l.bitrate = getBitrate(track);
+ l.audioTag = 255;
+ l.samplingRate = ase.getSampleRate();
+ l.channels = ase.getChannelCount();
+ l.bitPerSample = ase.getSampleSize();
+ l.packetSize = 4;
+ l.codecPrivateData = getAudioCodecPrivateData(audioSpecificConfig);
+ //Index="0" Bitrate="103000" AudioTag="255" SamplingRate="44100" Channels="2" BitsPerSample="16" packetSize="4" CodecPrivateData=""
+ return l;
+ }
+
+ private AudioQuality getEc3AudioQuality(Track track, AudioSampleEntry ase) {
+ final EC3SpecificBox ec3SpecificBox = ase.getBoxes(EC3SpecificBox.class).get(0);
+ if (ec3SpecificBox == null) {
+ throw new RuntimeException("EC-3 track misses EC3SpecificBox!");
+ }
+
+ short nfchans = 0; //full bandwidth channels
+ short lfechans = 0;
+ byte dWChannelMaskFirstByte = 0;
+ byte dWChannelMaskSecondByte = 0;
+ for (EC3SpecificBox.Entry entry : ec3SpecificBox.getEntries()) {
+ /*
+ Table 4.3: Audio coding mode
+ acmod Audio coding mode Nfchans Channel array ordering
+ 000 1 + 1 2 Ch1, Ch2
+ 001 1/0 1 C
+ 010 2/0 2 L, R
+ 011 3/0 3 L, C, R
+ 100 2/1 3 L, R, S
+ 101 3/1 4 L, C, R, S
+ 110 2/2 4 L, R, SL, SR
+ 111 3/2 5 L, C, R, SL, SR
+
+ Table F.2: Chan_loc field bit assignments
+ Bit Location
+ 0 Lc/Rc pair
+ 1 Lrs/Rrs pair
+ 2 Cs
+ 3 Ts
+ 4 Lsd/Rsd pair
+ 5 Lw/Rw pair
+ 6 Lvh/Rvh pair
+ 7 Cvh
+ 8 LFE2
+ */
+ switch (entry.acmod) {
+ case 0: //1+1; Ch1, Ch2
+ nfchans += 2;
+ throw new RuntimeException("Smooth Streaming doesn't support DDP 1+1 mode");
+ case 1: //1/0; C
+ nfchans += 1;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0x20;
+ }
+ break;
+ case 2: //2/0; L, R
+ nfchans += 2;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xC0;
+ }
+ break;
+ case 3: //3/0; L, C, R
+ nfchans += 3;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xE0;
+ }
+ break;
+ case 4: //2/1; L, R, S
+ nfchans += 3;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xC0;
+ dWChannelMaskSecondByte |= 0x80;
+ }
+ break;
+ case 5: //3/1; L, C, R, S
+ nfchans += 4;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xE0;
+ dWChannelMaskSecondByte |= 0x80;
+ }
+ break;
+ case 6: //2/2; L, R, SL, SR
+ nfchans += 4;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xCC;
+ }
+ break;
+ case 7: //3/2; L, C, R, SL, SR
+ nfchans += 5;
+ if (entry.num_dep_sub > 0) {
+ DependentSubstreamMask dependentSubstreamMask = new DependentSubstreamMask(dWChannelMaskFirstByte, dWChannelMaskSecondByte, entry).process();
+ dWChannelMaskFirstByte |= dependentSubstreamMask.getdWChannelMaskFirstByte();
+ dWChannelMaskSecondByte |= dependentSubstreamMask.getdWChannelMaskSecondByte();
+ } else {
+ dWChannelMaskFirstByte |= 0xEC;
+ }
+ break;
+ }
+ if (entry.lfeon == 1) {
+ lfechans ++;
+ dWChannelMaskFirstByte |= 0x10;
+ }
+ }
+
+ final ByteBuffer waveformatex = ByteBuffer.allocate(22);
+ waveformatex.put(new byte[]{0x00, 0x06}); //1536 wSamplesPerBlock - little endian
+ waveformatex.put(dWChannelMaskFirstByte);
+ waveformatex.put(dWChannelMaskSecondByte);
+ waveformatex.put(new byte[]{0x00, 0x00}); //pad dwChannelMask to 32bit
+ waveformatex.put(new byte[]{(byte)0xAF, (byte)0x87, (byte)0xFB, (byte)0xA7, 0x02, 0x2D, (byte)0xFB, 0x42, (byte)0xA4, (byte)0xD4, 0x05, (byte)0xCD, (byte)0x93, (byte)0x84, 0x3B, (byte)0xDD}); //SubFormat - Dolby Digital Plus GUID
+
+ final ByteBuffer dec3Content = ByteBuffer.allocate((int) ec3SpecificBox.getContentSize());
+ ec3SpecificBox.getContent(dec3Content);
+
+ AudioQuality l = new AudioQuality();
+ l.fourCC = "EC-3";
+ l.bitrate = getBitrate(track);
+ l.audioTag = 65534;
+ l.samplingRate = ase.getSampleRate();
+ l.channels = nfchans + lfechans;
+ l.bitPerSample = 16;
+ l.packetSize = track.getSamples().get(0).limit(); //assuming all are same size
+ l.codecPrivateData = Hex.encodeHex(waveformatex.array()) + Hex.encodeHex(dec3Content.array()); //append EC3SpecificBox (big endian) at the end of waveformatex
+ return l;
+ }
+
+ private AudioQuality getDtsAudioQuality(Track track, AudioSampleEntry ase) {
+ final DTSSpecificBox dtsSpecificBox = ase.getBoxes(DTSSpecificBox.class).get(0);
+ if (dtsSpecificBox == null) {
+ throw new RuntimeException("DTS track misses DTSSpecificBox!");
+ }
+
+ final ByteBuffer waveformatex = ByteBuffer.allocate(22);
+ final int frameDuration = dtsSpecificBox.getFrameDuration();
+ short samplesPerBlock = 0;
+ switch (frameDuration) {
+ case 0:
+ samplesPerBlock = 512;
+ break;
+ case 1:
+ samplesPerBlock = 1024;
+ break;
+ case 2:
+ samplesPerBlock = 2048;
+ break;
+ case 3:
+ samplesPerBlock = 4096;
+ break;
+ }
+ waveformatex.put((byte) (samplesPerBlock & 0xff));
+ waveformatex.put((byte) (samplesPerBlock >>> 8));
+ final int dwChannelMask = getNumChannelsAndMask(dtsSpecificBox)[1];
+ waveformatex.put((byte) (dwChannelMask & 0xff));
+ waveformatex.put((byte) (dwChannelMask >>> 8));
+ waveformatex.put((byte) (dwChannelMask >>> 16));
+ waveformatex.put((byte) (dwChannelMask >>> 24));
+ waveformatex.put(new byte[]{(byte)0xAE, (byte)0xE4, (byte)0xBF, (byte)0x5E, (byte)0x61, (byte)0x5E, (byte)0x41, (byte)0x87, (byte)0x92, (byte)0xFC, (byte)0xA4, (byte)0x81, (byte)0x26, (byte)0x99, (byte)0x02, (byte)0x11}); //DTS-HD GUID
+
+ final ByteBuffer dtsCodecPrivateData = ByteBuffer.allocate(8);
+ dtsCodecPrivateData.put((byte) dtsSpecificBox.getStreamConstruction());
+
+ final int channelLayout = dtsSpecificBox.getChannelLayout();
+ dtsCodecPrivateData.put((byte) (channelLayout & 0xff));
+ dtsCodecPrivateData.put((byte) (channelLayout >>> 8));
+ dtsCodecPrivateData.put((byte) (channelLayout >>> 16));
+ dtsCodecPrivateData.put((byte) (channelLayout >>> 24));
+
+ byte dtsFlags = (byte) (dtsSpecificBox.getMultiAssetFlag() << 1);
+ dtsFlags |= dtsSpecificBox.getLBRDurationMod();
+ dtsCodecPrivateData.put(dtsFlags);
+ dtsCodecPrivateData.put(new byte[]{0x00, 0x00}); //reserved
+
+ AudioQuality l = new AudioQuality();
+ l.fourCC = getFormat(ase);
+ l.bitrate = dtsSpecificBox.getAvgBitRate();
+ l.audioTag = 65534;
+ l.samplingRate = dtsSpecificBox.getDTSSamplingFrequency();
+ l.channels = getNumChannelsAndMask(dtsSpecificBox)[0];
+ l.bitPerSample = 16;
+ l.packetSize = track.getSamples().get(0).limit(); //assuming all are same size
+ l.codecPrivateData = Hex.encodeHex(waveformatex.array()) + Hex.encodeHex(dtsCodecPrivateData.array());
+ return l;
+
+ }
+
+ /* dwChannelMask
+ L SPEAKER_FRONT_LEFT 0x00000001
+ R SPEAKER_FRONT_RIGHT 0x00000002
+ C SPEAKER_FRONT_CENTER 0x00000004
+ LFE1 SPEAKER_LOW_FREQUENCY 0x00000008
+ Ls or Lsr* SPEAKER_BACK_LEFT 0x00000010
+ Rs or Rsr* SPEAKER_BACK_RIGHT 0x00000020
+ Lc SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040
+ Rc SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080
+ Cs SPEAKER_BACK_CENTER 0x00000100
+ Lss SPEAKER_SIDE_LEFT 0x00000200
+ Rss SPEAKER_SIDE_RIGHT 0x00000400
+ Oh SPEAKER_TOP_CENTER 0x00000800
+ Lh SPEAKER_TOP_FRONT_LEFT 0x00001000
+ Ch SPEAKER_TOP_FRONT_CENTER 0x00002000
+ Rh SPEAKER_TOP_FRONT_RIGHT 0x00004000
+ Lhr SPEAKER_TOP_BACK_LEFT 0x00008000
+ Chf SPEAKER_TOP_BACK_CENTER 0x00010000
+ Rhr SPEAKER_TOP_BACK_RIGHT 0x00020000
+ SPEAKER_RESERVED 0x80000000
+
+ * if Lss, Rss exist, then this position is equivalent to Lsr, Rsr respectively
+ */
+ private int[] getNumChannelsAndMask(DTSSpecificBox dtsSpecificBox) {
+ final int channelLayout = dtsSpecificBox.getChannelLayout();
+ int numChannels = 0;
+ int dwChannelMask = 0;
+ if ((channelLayout & 0x0001) == 0x0001) {
+ //0001h Center in front of listener 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000004; //SPEAKER_FRONT_CENTER
+ }
+ if ((channelLayout & 0x0002) == 0x0002) {
+ //0002h Left/Right in front 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000001; //SPEAKER_FRONT_LEFT
+ dwChannelMask |= 0x00000002; //SPEAKER_FRONT_RIGHT
+ }
+ if ((channelLayout & 0x0004) == 0x0004) {
+ //0004h Left/Right surround on side in rear 2
+ numChannels += 2;
+ //* if Lss, Rss exist, then this position is equivalent to Lsr, Rsr respectively
+ dwChannelMask |= 0x00000010; //SPEAKER_BACK_LEFT
+ dwChannelMask |= 0x00000020; //SPEAKER_BACK_RIGHT
+ }
+ if ((channelLayout & 0x0008) == 0x0008) {
+ //0008h Low frequency effects subwoofer 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000008; //SPEAKER_LOW_FREQUENCY
+ }
+ if ((channelLayout & 0x0010) == 0x0010) {
+ //0010h Center surround in rear 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000100; //SPEAKER_BACK_CENTER
+ }
+ if ((channelLayout & 0x0020) == 0x0020) {
+ //0020h Left/Right height in front 2
+ numChannels += 2;
+ dwChannelMask |= 0x00001000; //SPEAKER_TOP_FRONT_LEFT
+ dwChannelMask |= 0x00004000; //SPEAKER_TOP_FRONT_RIGHT
+ }
+ if ((channelLayout & 0x0040) == 0x0040) {
+ //0040h Left/Right surround in rear 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000010; //SPEAKER_BACK_LEFT
+ dwChannelMask |= 0x00000020; //SPEAKER_BACK_RIGHT
+ }
+ if ((channelLayout & 0x0080) == 0x0080) {
+ //0080h Center Height in front 1
+ numChannels += 1;
+ dwChannelMask |= 0x00002000; //SPEAKER_TOP_FRONT_CENTER
+ }
+ if ((channelLayout & 0x0100) == 0x0100) {
+ //0100h Over the listener’s head 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000800; //SPEAKER_TOP_CENTER
+ }
+ if ((channelLayout & 0x0200) == 0x0200) {
+ //0200h Between left/right and center in front 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000040; //SPEAKER_FRONT_LEFT_OF_CENTER
+ dwChannelMask |= 0x00000080; //SPEAKER_FRONT_RIGHT_OF_CENTER
+ }
+ if ((channelLayout & 0x0400) == 0x0400) {
+ //0400h Left/Right on side in front 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000200; //SPEAKER_SIDE_LEFT
+ dwChannelMask |= 0x00000400; //SPEAKER_SIDE_RIGHT
+ }
+ if ((channelLayout & 0x0800) == 0x0800) {
+ //0800h Left/Right surround on side 2
+ numChannels += 2;
+ //* if Lss, Rss exist, then this position is equivalent to Lsr, Rsr respectively
+ dwChannelMask |= 0x00000010; //SPEAKER_BACK_LEFT
+ dwChannelMask |= 0x00000020; //SPEAKER_BACK_RIGHT
+ }
+ if ((channelLayout & 0x1000) == 0x1000) {
+ //1000h Second low frequency effects subwoofer 1
+ numChannels += 1;
+ dwChannelMask |= 0x00000008; //SPEAKER_LOW_FREQUENCY
+ }
+ if ((channelLayout & 0x2000) == 0x2000) {
+ //2000h Left/Right height on side 2
+ numChannels += 2;
+ dwChannelMask |= 0x00000010; //SPEAKER_BACK_LEFT
+ dwChannelMask |= 0x00000020; //SPEAKER_BACK_RIGHT
+ }
+ if ((channelLayout & 0x4000) == 0x4000) {
+ //4000h Center height in rear 1
+ numChannels += 1;
+ dwChannelMask |= 0x00010000; //SPEAKER_TOP_BACK_CENTER
+ }
+ if ((channelLayout & 0x8000) == 0x8000) {
+ //8000h Left/Right height in rear 2
+ numChannels += 2;
+ dwChannelMask |= 0x00008000; //SPEAKER_TOP_BACK_LEFT
+ dwChannelMask |= 0x00020000; //SPEAKER_TOP_BACK_RIGHT
+ }
+ if ((channelLayout & 0x10000) == 0x10000) {
+ //10000h Center below in front
+ numChannels += 1;
+ }
+ if ((channelLayout & 0x20000) == 0x20000) {
+ //20000h Left/Right below in front
+ numChannels += 2;
+ }
+ return new int[]{numChannels, dwChannelMask};
+ }
+
+ private String getAudioCodecPrivateData(AudioSpecificConfig audioSpecificConfig) {
+ byte[] configByteArray = audioSpecificConfig.getConfigBytes();
+ return Hex.encodeHex(configByteArray);
+ }
+
+ private VideoQuality getVideoQuality(Track track, VisualSampleEntry vse) {
+ VideoQuality l;
+ if ("avc1".equals(getFormat(vse))) {
+ AvcConfigurationBox avcConfigurationBox = vse.getBoxes(AvcConfigurationBox.class).get(0);
+ l = new VideoQuality();
+ l.bitrate = getBitrate(track);
+ l.codecPrivateData = Hex.encodeHex(getAvcCodecPrivateData(avcConfigurationBox));
+ l.fourCC = "AVC1";
+ l.width = vse.getWidth();
+ l.height = vse.getHeight();
+ l.nalLength = avcConfigurationBox.getLengthSizeMinusOne() + 1;
+ } else {
+ throw new InternalError("I don't know how to handle video of type " + getFormat(vse));
+ }
+ return l;
+ }
+
+ private byte[] getAvcCodecPrivateData(AvcConfigurationBox avcConfigurationBox) {
+ List<byte[]> sps = avcConfigurationBox.getSequenceParameterSets();
+ List<byte[]> pps = avcConfigurationBox.getPictureParameterSets();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ baos.write(new byte[]{0, 0, 0, 1});
+
+ for (byte[] sp : sps) {
+ baos.write(sp);
+ }
+ baos.write(new byte[]{0, 0, 0, 1});
+ for (byte[] pp : pps) {
+ baos.write(pp);
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException("ByteArrayOutputStream do not throw IOException ?!?!?");
+ }
+ return baos.toByteArray();
+ }
+
+ private class DependentSubstreamMask {
+ private byte dWChannelMaskFirstByte;
+ private byte dWChannelMaskSecondByte;
+ private EC3SpecificBox.Entry entry;
+
+ public DependentSubstreamMask(byte dWChannelMaskFirstByte, byte dWChannelMaskSecondByte, EC3SpecificBox.Entry entry) {
+ this.dWChannelMaskFirstByte = dWChannelMaskFirstByte;
+ this.dWChannelMaskSecondByte = dWChannelMaskSecondByte;
+ this.entry = entry;
+ }
+
+ public byte getdWChannelMaskFirstByte() {
+ return dWChannelMaskFirstByte;
+ }
+
+ public byte getdWChannelMaskSecondByte() {
+ return dWChannelMaskSecondByte;
+ }
+
+ public DependentSubstreamMask process() {
+ switch (entry.chan_loc) {
+ case 0:
+ dWChannelMaskFirstByte |= 0x3;
+ break;
+ case 1:
+ dWChannelMaskFirstByte |= 0xC;
+ break;
+ case 2:
+ dWChannelMaskSecondByte |= 0x80;
+ break;
+ case 3:
+ dWChannelMaskSecondByte |= 0x8;
+ break;
+ case 6:
+ dWChannelMaskSecondByte |= 0x5;
+ break;
+ case 7:
+ dWChannelMaskSecondByte |= 0x2;
+ break;
+ }
+ return this;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatPackageWriterImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatPackageWriterImpl.java
new file mode 100644
index 0000000..3e3847c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/FlatPackageWriterImpl.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.SoundMediaHeaderBox;
+import com.coremedia.iso.boxes.VideoMediaHeaderBox;
+import com.coremedia.iso.boxes.fragment.MovieFragmentBox;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.builder.*;
+import com.googlecode.mp4parser.authoring.tracks.ChangeTimeScaleTrack;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.Iterator;
+import java.util.logging.Logger;
+
+public class FlatPackageWriterImpl implements PackageWriter {
+ private static Logger LOG = Logger.getLogger(FlatPackageWriterImpl.class.getName());
+ long timeScale = 10000000;
+
+ private File outputDirectory;
+ private boolean debugOutput;
+ private FragmentedMp4Builder ismvBuilder;
+ ManifestWriter manifestWriter;
+
+ public FlatPackageWriterImpl() {
+ ismvBuilder = new FragmentedMp4Builder();
+ FragmentIntersectionFinder intersectionFinder = new SyncSampleIntersectFinderImpl();
+ ismvBuilder.setIntersectionFinder(intersectionFinder);
+ manifestWriter = new FlatManifestWriterImpl(intersectionFinder);
+ }
+
+ /**
+ * Creates a factory for a smooth streaming package. A smooth streaming package is
+ * a collection of files that can be served by a webserver as a smooth streaming
+ * stream.
+ * @param minFragmentDuration the smallest allowable duration of a fragment (0 == no restriction).
+ */
+ public FlatPackageWriterImpl(int minFragmentDuration) {
+ ismvBuilder = new FragmentedMp4Builder();
+ FragmentIntersectionFinder intersectionFinder = new SyncSampleIntersectFinderImpl(minFragmentDuration);
+ ismvBuilder.setIntersectionFinder(intersectionFinder);
+ manifestWriter = new FlatManifestWriterImpl(intersectionFinder);
+ }
+
+ public void setOutputDirectory(File outputDirectory) {
+ assert outputDirectory.isDirectory();
+ this.outputDirectory = outputDirectory;
+
+ }
+
+ public void setDebugOutput(boolean debugOutput) {
+ this.debugOutput = debugOutput;
+ }
+
+ public void setIsmvBuilder(FragmentedMp4Builder ismvBuilder) {
+ this.ismvBuilder = ismvBuilder;
+ this.manifestWriter = new FlatManifestWriterImpl(ismvBuilder.getFragmentIntersectionFinder());
+ }
+
+ public void setManifestWriter(ManifestWriter manifestWriter) {
+ this.manifestWriter = manifestWriter;
+ }
+
+ /**
+ * Writes the movie given as <code>qualities</code> flattened into the
+ * <code>outputDirectory</code>.
+ *
+ * @param source the source movie with all qualities
+ * @throws IOException
+ */
+ public void write(Movie source) throws IOException {
+
+ if (debugOutput) {
+ outputDirectory.mkdirs();
+ DefaultMp4Builder defaultMp4Builder = new DefaultMp4Builder();
+ IsoFile muxed = defaultMp4Builder.build(source);
+ File muxedFile = new File(outputDirectory, "debug_1_muxed.mp4");
+ FileOutputStream muxedFileOutputStream = new FileOutputStream(muxedFile);
+ muxed.getBox(muxedFileOutputStream.getChannel());
+ muxedFileOutputStream.close();
+ }
+ Movie cleanedSource = removeUnknownTracks(source);
+ Movie movieWithAdjustedTimescale = correctTimescale(cleanedSource);
+
+ if (debugOutput) {
+ DefaultMp4Builder defaultMp4Builder = new DefaultMp4Builder();
+ IsoFile muxed = defaultMp4Builder.build(movieWithAdjustedTimescale);
+ File muxedFile = new File(outputDirectory, "debug_2_timescale.mp4");
+ FileOutputStream muxedFileOutputStream = new FileOutputStream(muxedFile);
+ muxed.getBox(muxedFileOutputStream.getChannel());
+ muxedFileOutputStream.close();
+ }
+ IsoFile isoFile = ismvBuilder.build(movieWithAdjustedTimescale);
+ if (debugOutput) {
+ File allQualities = new File(outputDirectory, "debug_3_fragmented.mp4");
+ FileOutputStream allQualis = new FileOutputStream(allQualities);
+ isoFile.getBox(allQualis.getChannel());
+ allQualis.close();
+ }
+
+
+ for (Track track : movieWithAdjustedTimescale.getTracks()) {
+ String bitrate = Long.toString(manifestWriter.getBitrate(track));
+ long trackId = track.getTrackMetaData().getTrackId();
+ Iterator<Box> boxIt = isoFile.getBoxes().iterator();
+ File mediaOutDir;
+ if (track.getMediaHeaderBox() instanceof SoundMediaHeaderBox) {
+ mediaOutDir = new File(outputDirectory, "audio");
+
+ } else if (track.getMediaHeaderBox() instanceof VideoMediaHeaderBox) {
+ mediaOutDir = new File(outputDirectory, "video");
+ } else {
+ System.err.println("Skipping Track with handler " + track.getHandler() + " and " + track.getMediaHeaderBox().getClass().getSimpleName());
+ continue;
+ }
+ File bitRateOutputDir = new File(mediaOutDir, bitrate);
+ bitRateOutputDir.mkdirs();
+ LOG.finer("Created : " + bitRateOutputDir.getCanonicalPath());
+
+ long[] fragmentTimes = manifestWriter.calculateFragmentDurations(track, movieWithAdjustedTimescale);
+ long startTime = 0;
+ int currentFragment = 0;
+ while (boxIt.hasNext()) {
+ Box b = boxIt.next();
+ if (b instanceof MovieFragmentBox) {
+ assert ((MovieFragmentBox) b).getTrackCount() == 1;
+ if (((MovieFragmentBox) b).getTrackNumbers()[0] == trackId) {
+ FileOutputStream fos = new FileOutputStream(new File(bitRateOutputDir, Long.toString(startTime)));
+ startTime += fragmentTimes[currentFragment++];
+ FileChannel fc = fos.getChannel();
+ Box mdat = boxIt.next();
+ assert mdat.getType().equals("mdat");
+ b.getBox(fc); // moof
+ mdat.getBox(fc); // mdat
+ fc.truncate(fc.position());
+ fc.close();
+ }
+ }
+
+ }
+ }
+ FileWriter fw = new FileWriter(new File(outputDirectory, "Manifest"));
+ fw.write(manifestWriter.getManifest(movieWithAdjustedTimescale));
+ fw.close();
+
+ }
+
+ private Movie removeUnknownTracks(Movie source) {
+ Movie nuMovie = new Movie();
+ for (Track track : source.getTracks()) {
+ if ("vide".equals(track.getHandler()) || "soun".equals(track.getHandler())) {
+ nuMovie.addTrack(track);
+ } else {
+ LOG.fine("Removed track " + track);
+ }
+ }
+ return nuMovie;
+ }
+
+
+ /**
+ * Returns a new <code>Movie</code> in that all tracks have the timescale 10000000. CTS & DTS are modified
+ * in a way that even with more than one framerate the fragments exactly begin at the same time.
+ *
+ * @param movie
+ * @return a movie with timescales suitable for smooth streaming manifests
+ */
+ public Movie correctTimescale(Movie movie) {
+ Movie nuMovie = new Movie();
+ for (Track track : movie.getTracks()) {
+ nuMovie.addTrack(new ChangeTimeScaleTrack(track, timeScale, ismvBuilder.getFragmentIntersectionFinder().sampleNumbers(track, movie)));
+ }
+ return nuMovie;
+
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/ManifestWriter.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/ManifestWriter.java
new file mode 100644
index 0000000..2b2ba7d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/ManifestWriter.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.io.IOException;
+
+public interface ManifestWriter {
+ String getManifest(Movie inputs) throws IOException;
+
+ long getBitrate(Track track);
+
+ long[] calculateFragmentDurations(Track track, Movie movie);
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/PackageWriter.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/PackageWriter.java
new file mode 100644
index 0000000..0d97fc5
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/PackageWriter.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+import com.googlecode.mp4parser.authoring.Movie;
+
+import java.io.IOException;
+
+/**
+ * Writes the whole package.
+ */
+public interface PackageWriter {
+ public void write(Movie qualities) throws IOException;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/VideoQuality.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/VideoQuality.java
new file mode 100644
index 0000000..4a70e47
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/adaptivestreaming/VideoQuality.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.adaptivestreaming;
+
+class VideoQuality {
+ long bitrate;
+ String fourCC;
+ int width;
+ int height;
+ String codecPrivateData;
+ int nalLength;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/all-wcprops
new file mode 100644
index 0000000..9204edf
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/776/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder
+END
+FragmentIntersectionFinder.java
+K 25
+svn:wc:ra_dav:version-url
+V 122
+/svn/!svn/ver/455/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentIntersectionFinder.java
+END
+TwoSecondIntersectionFinder.java
+K 25
+svn:wc:ra_dav:version-url
+V 123
+/svn/!svn/ver/658/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/TwoSecondIntersectionFinder.java
+END
+FragmentedMp4Builder.java
+K 25
+svn:wc:ra_dav:version-url
+V 116
+/svn/!svn/ver/707/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentedMp4Builder.java
+END
+Mp4Builder.java
+K 25
+svn:wc:ra_dav:version-url
+V 106
+/svn/!svn/ver/672/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/Mp4Builder.java
+END
+SyncSampleIntersectFinderImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/svn/!svn/ver/774/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/SyncSampleIntersectFinderImpl.java
+END
+DefaultMp4Builder.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/776/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/DefaultMp4Builder.java
+END
+ByteBufferHelper.java
+K 25
+svn:wc:ra_dav:version-url
+V 112
+/svn/!svn/ver/530/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/ByteBufferHelper.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/entries
new file mode 100644
index 0000000..2c6e266
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/entries
@@ -0,0 +1,266 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-09-10T14:34:23.574807Z
+776
+sebastian.annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+FragmentIntersectionFinder.java
+file
+
+
+
+
+2012-09-14T17:27:50.237215Z
+979df582987d3797c42ae315b3d2555a
+2012-04-10T10:20:46.357991Z
+455
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1160
+
+TwoSecondIntersectionFinder.java
+file
+
+
+
+
+2012-09-14T17:27:50.237215Z
+47aa683919ce24da51b82236e8f27426
+2012-06-06T10:36:10.590512Z
+658
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2817
+
+FragmentedMp4Builder.java
+file
+
+
+
+
+2012-09-14T17:27:50.237215Z
+f81cbeb22aee5ab0405ad38e67b9e4e1
+2012-07-10T16:32:53.757675Z
+707
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+31005
+
+Mp4Builder.java
+file
+
+
+
+
+2012-09-14T17:27:50.237215Z
+ea548a5ace2a4480472b90d9f820bceb
+2012-06-11T22:10:18.183835Z
+672
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1156
+
+SyncSampleIntersectFinderImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.237215Z
+d49823bb95a5920f2df6899195c28a72
+2012-09-06T12:33:39.419429Z
+774
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+14915
+
+DefaultMp4Builder.java
+file
+
+
+
+
+2012-09-14T17:27:50.237215Z
+15d93ea29e24e257604301ad5e73d623
+2012-09-10T14:34:23.574807Z
+776
+sebastian.annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22096
+
+ByteBufferHelper.java
+file
+
+
+
+
+2012-09-14T17:27:50.237215Z
+a103511eeddf1e0d1962704ae23ba4a5
+2012-04-27T09:17:25.544414Z
+530
+hoemmagnus@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2344
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/ByteBufferHelper.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/ByteBufferHelper.java.svn-base
new file mode 100644
index 0000000..ad21b11
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/ByteBufferHelper.java.svn-base
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used to merge adjacent byte buffers.
+ */
+public class ByteBufferHelper {
+ public static List<ByteBuffer> mergeAdjacentBuffers(List<ByteBuffer> samples) {
+ ArrayList<ByteBuffer> nuSamples = new ArrayList<ByteBuffer>(samples.size());
+ for (ByteBuffer buffer : samples) {
+ int lastIndex = nuSamples.size() - 1;
+ if (lastIndex >= 0 && buffer.hasArray() && nuSamples.get(lastIndex).hasArray() && buffer.array() == nuSamples.get(lastIndex).array() &&
+ nuSamples.get(lastIndex).arrayOffset() + nuSamples.get(lastIndex).limit() == buffer.arrayOffset()) {
+ ByteBuffer oldBuffer = nuSamples.remove(lastIndex);
+ ByteBuffer nu = ByteBuffer.wrap(buffer.array(), oldBuffer.arrayOffset(), oldBuffer.limit() + buffer.limit()).slice();
+ // We need to slice here since wrap([], offset, length) just sets position and not the arrayOffset.
+ nuSamples.add(nu);
+ } else if (lastIndex >= 0 &&
+ buffer instanceof MappedByteBuffer && nuSamples.get(lastIndex) instanceof MappedByteBuffer &&
+ nuSamples.get(lastIndex).limit() == nuSamples.get(lastIndex).capacity() - buffer.capacity()) {
+ // This can go wrong - but will it?
+ ByteBuffer oldBuffer = nuSamples.get(lastIndex);
+ oldBuffer.limit(buffer.limit() + oldBuffer.limit());
+ } else {
+ buffer.rewind();
+ nuSamples.add(buffer);
+ }
+ }
+ return nuSamples;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/DefaultMp4Builder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/DefaultMp4Builder.java.svn-base
new file mode 100644
index 0000000..9bd1ca6
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/DefaultMp4Builder.java.svn-base
@@ -0,0 +1,576 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.CompositionTimeToSample;
+import com.coremedia.iso.boxes.ContainerBox;
+import com.coremedia.iso.boxes.DataEntryUrlBox;
+import com.coremedia.iso.boxes.DataInformationBox;
+import com.coremedia.iso.boxes.DataReferenceBox;
+import com.coremedia.iso.boxes.FileTypeBox;
+import com.coremedia.iso.boxes.HandlerBox;
+import com.coremedia.iso.boxes.MediaBox;
+import com.coremedia.iso.boxes.MediaHeaderBox;
+import com.coremedia.iso.boxes.MediaInformationBox;
+import com.coremedia.iso.boxes.MovieBox;
+import com.coremedia.iso.boxes.MovieHeaderBox;
+import com.coremedia.iso.boxes.SampleDependencyTypeBox;
+import com.coremedia.iso.boxes.SampleSizeBox;
+import com.coremedia.iso.boxes.SampleTableBox;
+import com.coremedia.iso.boxes.SampleToChunkBox;
+import com.coremedia.iso.boxes.StaticChunkOffsetBox;
+import com.coremedia.iso.boxes.SyncSampleBox;
+import com.coremedia.iso.boxes.TimeToSampleBox;
+import com.coremedia.iso.boxes.TrackBox;
+import com.coremedia.iso.boxes.TrackHeaderBox;
+import com.googlecode.mp4parser.authoring.DateHelper;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.GatheringByteChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Creates a plain MP4 file from a video. Plain as plain can be.
+ */
+public class DefaultMp4Builder implements Mp4Builder {
+
+ public int STEPSIZE = 64;
+ Set<StaticChunkOffsetBox> chunkOffsetBoxes = new HashSet<StaticChunkOffsetBox>();
+ private static Logger LOG = Logger.getLogger(DefaultMp4Builder.class.getName());
+
+ HashMap<Track, List<ByteBuffer>> track2Sample = new HashMap<Track, List<ByteBuffer>>();
+ HashMap<Track, long[]> track2SampleSizes = new HashMap<Track, long[]>();
+ private FragmentIntersectionFinder intersectionFinder = new TwoSecondIntersectionFinder();
+
+ public void setIntersectionFinder(FragmentIntersectionFinder intersectionFinder) {
+ this.intersectionFinder = intersectionFinder;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public IsoFile build(Movie movie) {
+ LOG.fine("Creating movie " + movie);
+ for (Track track : movie.getTracks()) {
+ // getting the samples may be a time consuming activity
+ List<ByteBuffer> samples = track.getSamples();
+ putSamples(track, samples);
+ long[] sizes = new long[samples.size()];
+ for (int i = 0; i < sizes.length; i++) {
+ sizes[i] = samples.get(i).limit();
+ }
+ putSampleSizes(track, sizes);
+ }
+
+ IsoFile isoFile = new IsoFile();
+ // ouch that is ugly but I don't know how to do it else
+ List<String> minorBrands = new LinkedList<String>();
+ minorBrands.add("isom");
+ minorBrands.add("iso2");
+ minorBrands.add("avc1");
+
+ isoFile.addBox(new FileTypeBox("isom", 0, minorBrands));
+ isoFile.addBox(createMovieBox(movie));
+ InterleaveChunkMdat mdat = new InterleaveChunkMdat(movie);
+ isoFile.addBox(mdat);
+
+ /*
+ dataOffset is where the first sample starts. In this special mdat the samples always start
+ at offset 16 so that we can use the same offset for large boxes and small boxes
+ */
+ long dataOffset = mdat.getDataOffset();
+ for (StaticChunkOffsetBox chunkOffsetBox : chunkOffsetBoxes) {
+ long[] offsets = chunkOffsetBox.getChunkOffsets();
+ for (int i = 0; i < offsets.length; i++) {
+ offsets[i] += dataOffset;
+ }
+ }
+
+
+ return isoFile;
+ }
+
+ public FragmentIntersectionFinder getFragmentIntersectionFinder() {
+ throw new UnsupportedOperationException("No fragment intersection finder in default MP4 builder!");
+ }
+
+ protected long[] putSampleSizes(Track track, long[] sizes) {
+ return track2SampleSizes.put(track, sizes);
+ }
+
+ protected List<ByteBuffer> putSamples(Track track, List<ByteBuffer> samples) {
+ return track2Sample.put(track, samples);
+ }
+
+ private MovieBox createMovieBox(Movie movie) {
+ MovieBox movieBox = new MovieBox();
+ MovieHeaderBox mvhd = new MovieHeaderBox();
+
+ mvhd.setCreationTime(DateHelper.convert(new Date()));
+ mvhd.setModificationTime(DateHelper.convert(new Date()));
+
+ long movieTimeScale = getTimescale(movie);
+ long duration = 0;
+
+ for (Track track : movie.getTracks()) {
+ long tracksDuration = getDuration(track) * movieTimeScale / track.getTrackMetaData().getTimescale();
+ if (tracksDuration > duration) {
+ duration = tracksDuration;
+ }
+
+
+ }
+
+ mvhd.setDuration(duration);
+ mvhd.setTimescale(movieTimeScale);
+ // find the next available trackId
+ long nextTrackId = 0;
+ for (Track track : movie.getTracks()) {
+ nextTrackId = nextTrackId < track.getTrackMetaData().getTrackId() ? track.getTrackMetaData().getTrackId() : nextTrackId;
+ }
+ mvhd.setNextTrackId(++nextTrackId);
+ if (mvhd.getCreationTime() >= 1l << 32 ||
+ mvhd.getModificationTime() >= 1l << 32 ||
+ mvhd.getDuration() >= 1l << 32) {
+ mvhd.setVersion(1);
+ }
+
+ movieBox.addBox(mvhd);
+ for (Track track : movie.getTracks()) {
+ movieBox.addBox(createTrackBox(track, movie));
+ }
+ // metadata here
+ Box udta = createUdta(movie);
+ if (udta != null) {
+ movieBox.addBox(udta);
+ }
+ return movieBox;
+
+ }
+
+ /**
+ * Override to create a user data box that may contain metadata.
+ *
+ * @return a 'udta' box or <code>null</code> if none provided
+ */
+ protected Box createUdta(Movie movie) {
+ return null;
+ }
+
+ private TrackBox createTrackBox(Track track, Movie movie) {
+
+ LOG.info("Creating Mp4TrackImpl " + track);
+ TrackBox trackBox = new TrackBox();
+ TrackHeaderBox tkhd = new TrackHeaderBox();
+ int flags = 0;
+ if (track.isEnabled()) {
+ flags += 1;
+ }
+
+ if (track.isInMovie()) {
+ flags += 2;
+ }
+
+ if (track.isInPreview()) {
+ flags += 4;
+ }
+
+ if (track.isInPoster()) {
+ flags += 8;
+ }
+ tkhd.setFlags(flags);
+
+ tkhd.setAlternateGroup(track.getTrackMetaData().getGroup());
+ tkhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime()));
+ // We need to take edit list box into account in trackheader duration
+ // but as long as I don't support edit list boxes it is sufficient to
+ // just translate media duration to movie timescale
+ tkhd.setDuration(getDuration(track) * getTimescale(movie) / track.getTrackMetaData().getTimescale());
+ tkhd.setHeight(track.getTrackMetaData().getHeight());
+ tkhd.setWidth(track.getTrackMetaData().getWidth());
+ tkhd.setLayer(track.getTrackMetaData().getLayer());
+ tkhd.setModificationTime(DateHelper.convert(new Date()));
+ tkhd.setTrackId(track.getTrackMetaData().getTrackId());
+ tkhd.setVolume(track.getTrackMetaData().getVolume());
+ if (tkhd.getCreationTime() >= 1l << 32 ||
+ tkhd.getModificationTime() >= 1l << 32 ||
+ tkhd.getDuration() >= 1l << 32) {
+ tkhd.setVersion(1);
+ }
+
+ trackBox.addBox(tkhd);
+
+/*
+ EditBox edit = new EditBox();
+ EditListBox editListBox = new EditListBox();
+ editListBox.setEntries(Collections.singletonList(
+ new EditListBox.Entry(editListBox, (long) (track.getTrackMetaData().getStartTime() * getTimescale(movie)), -1, 1)));
+ edit.addBox(editListBox);
+ trackBox.addBox(edit);
+*/
+
+ MediaBox mdia = new MediaBox();
+ trackBox.addBox(mdia);
+ MediaHeaderBox mdhd = new MediaHeaderBox();
+ mdhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime()));
+ mdhd.setDuration(getDuration(track));
+ mdhd.setTimescale(track.getTrackMetaData().getTimescale());
+ mdhd.setLanguage(track.getTrackMetaData().getLanguage());
+ mdia.addBox(mdhd);
+ HandlerBox hdlr = new HandlerBox();
+ mdia.addBox(hdlr);
+
+ hdlr.setHandlerType(track.getHandler());
+
+ MediaInformationBox minf = new MediaInformationBox();
+ minf.addBox(track.getMediaHeaderBox());
+
+ // dinf: all these three boxes tell us is that the actual
+ // data is in the current file and not somewhere external
+ DataInformationBox dinf = new DataInformationBox();
+ DataReferenceBox dref = new DataReferenceBox();
+ dinf.addBox(dref);
+ DataEntryUrlBox url = new DataEntryUrlBox();
+ url.setFlags(1);
+ dref.addBox(url);
+ minf.addBox(dinf);
+ //
+
+ SampleTableBox stbl = new SampleTableBox();
+
+ stbl.addBox(track.getSampleDescriptionBox());
+
+ List<TimeToSampleBox.Entry> decodingTimeToSampleEntries = track.getDecodingTimeEntries();
+ if (decodingTimeToSampleEntries != null && !track.getDecodingTimeEntries().isEmpty()) {
+ TimeToSampleBox stts = new TimeToSampleBox();
+ stts.setEntries(track.getDecodingTimeEntries());
+ stbl.addBox(stts);
+ }
+
+ List<CompositionTimeToSample.Entry> compositionTimeToSampleEntries = track.getCompositionTimeEntries();
+ if (compositionTimeToSampleEntries != null && !compositionTimeToSampleEntries.isEmpty()) {
+ CompositionTimeToSample ctts = new CompositionTimeToSample();
+ ctts.setEntries(compositionTimeToSampleEntries);
+ stbl.addBox(ctts);
+ }
+
+ long[] syncSamples = track.getSyncSamples();
+ if (syncSamples != null && syncSamples.length > 0) {
+ SyncSampleBox stss = new SyncSampleBox();
+ stss.setSampleNumber(syncSamples);
+ stbl.addBox(stss);
+ }
+
+ if (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty()) {
+ SampleDependencyTypeBox sdtp = new SampleDependencyTypeBox();
+ sdtp.setEntries(track.getSampleDependencies());
+ stbl.addBox(sdtp);
+ }
+ HashMap<Track, int[]> track2ChunkSizes = new HashMap<Track, int[]>();
+ for (Track current : movie.getTracks()) {
+ track2ChunkSizes.put(current, getChunkSizes(current, movie));
+ }
+ int[] tracksChunkSizes = track2ChunkSizes.get(track);
+
+ SampleToChunkBox stsc = new SampleToChunkBox();
+ stsc.setEntries(new LinkedList<SampleToChunkBox.Entry>());
+ long lastChunkSize = Integer.MIN_VALUE; // to be sure the first chunks hasn't got the same size
+ for (int i = 0; i < tracksChunkSizes.length; i++) {
+ // The sample description index references the sample description box
+ // that describes the samples of this chunk. My Tracks cannot have more
+ // than one sample description box. Therefore 1 is always right
+ // the first chunk has the number '1'
+ if (lastChunkSize != tracksChunkSizes[i]) {
+ stsc.getEntries().add(new SampleToChunkBox.Entry(i + 1, tracksChunkSizes[i], 1));
+ lastChunkSize = tracksChunkSizes[i];
+ }
+ }
+ stbl.addBox(stsc);
+
+ SampleSizeBox stsz = new SampleSizeBox();
+ stsz.setSampleSizes(track2SampleSizes.get(track));
+
+ stbl.addBox(stsz);
+ // The ChunkOffsetBox we create here is just a stub
+ // since we haven't created the whole structure we can't tell where the
+ // first chunk starts (mdat box). So I just let the chunk offset
+ // start at zero and I will add the mdat offset later.
+ StaticChunkOffsetBox stco = new StaticChunkOffsetBox();
+ this.chunkOffsetBoxes.add(stco);
+ long offset = 0;
+ long[] chunkOffset = new long[tracksChunkSizes.length];
+ // all tracks have the same number of chunks
+ if (LOG.isLoggable(Level.FINE)) {
+ LOG.fine("Calculating chunk offsets for track_" + track.getTrackMetaData().getTrackId());
+ }
+
+
+ for (int i = 0; i < tracksChunkSizes.length; i++) {
+ // The filelayout will be:
+ // chunk_1_track_1,... ,chunk_1_track_n, chunk_2_track_1,... ,chunk_2_track_n, ... , chunk_m_track_1,... ,chunk_m_track_n
+ // calculating the offsets
+ if (LOG.isLoggable(Level.FINER)) {
+ LOG.finer("Calculating chunk offsets for track_" + track.getTrackMetaData().getTrackId() + " chunk " + i);
+ }
+ for (Track current : movie.getTracks()) {
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Adding offsets of track_" + current.getTrackMetaData().getTrackId());
+ }
+ int[] chunkSizes = track2ChunkSizes.get(current);
+ long firstSampleOfChunk = 0;
+ for (int j = 0; j < i; j++) {
+ firstSampleOfChunk += chunkSizes[j];
+ }
+ if (current == track) {
+ chunkOffset[i] = offset;
+ }
+ for (int j = l2i(firstSampleOfChunk); j < firstSampleOfChunk + chunkSizes[i]; j++) {
+ offset += track2SampleSizes.get(current)[j];
+ }
+ }
+ }
+ stco.setChunkOffsets(chunkOffset);
+ stbl.addBox(stco);
+ minf.addBox(stbl);
+ mdia.addBox(minf);
+
+ return trackBox;
+ }
+
+ private class InterleaveChunkMdat implements Box {
+ List<Track> tracks;
+ List<ByteBuffer> samples = new ArrayList<ByteBuffer>();
+ ContainerBox parent;
+
+ long contentSize = 0;
+
+ public ContainerBox getParent() {
+ return parent;
+ }
+
+ public void setParent(ContainerBox parent) {
+ this.parent = parent;
+ }
+
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+ }
+
+ private InterleaveChunkMdat(Movie movie) {
+
+ tracks = movie.getTracks();
+ Map<Track, int[]> chunks = new HashMap<Track, int[]>();
+ for (Track track : movie.getTracks()) {
+ chunks.put(track, getChunkSizes(track, movie));
+ }
+
+ for (int i = 0; i < chunks.values().iterator().next().length; i++) {
+ for (Track track : tracks) {
+
+ int[] chunkSizes = chunks.get(track);
+ long firstSampleOfChunk = 0;
+ for (int j = 0; j < i; j++) {
+ firstSampleOfChunk += chunkSizes[j];
+ }
+
+ for (int j = l2i(firstSampleOfChunk); j < firstSampleOfChunk + chunkSizes[i]; j++) {
+
+ ByteBuffer s = DefaultMp4Builder.this.track2Sample.get(track).get(j);
+ contentSize += s.limit();
+ samples.add((ByteBuffer) s.rewind());
+ }
+
+ }
+
+ }
+
+ }
+
+ public long getDataOffset() {
+ Box b = this;
+ long offset = 16;
+ while (b.getParent() != null) {
+ for (Box box : b.getParent().getBoxes()) {
+ if (b == box) {
+ break;
+ }
+ offset += box.getSize();
+ }
+ b = b.getParent();
+ }
+ return offset;
+ }
+
+
+ public String getType() {
+ return "mdat";
+ }
+
+ public long getSize() {
+ return 16 + contentSize;
+ }
+
+ private boolean isSmallBox(long contentSize) {
+ return (contentSize + 8) < 4294967296L;
+ }
+
+
+ public void getBox(WritableByteChannel writableByteChannel) throws IOException {
+ ByteBuffer bb = ByteBuffer.allocate(16);
+ long size = getSize();
+ if (isSmallBox(size)) {
+ IsoTypeWriter.writeUInt32(bb, size);
+ } else {
+ IsoTypeWriter.writeUInt32(bb, 1);
+ }
+ bb.put(IsoFile.fourCCtoBytes("mdat"));
+ if (isSmallBox(size)) {
+ bb.put(new byte[8]);
+ } else {
+ IsoTypeWriter.writeUInt64(bb, size);
+ }
+ bb.rewind();
+ writableByteChannel.write(bb);
+ if (writableByteChannel instanceof GatheringByteChannel) {
+ List<ByteBuffer> nuSamples = unifyAdjacentBuffers(samples);
+
+
+ for (int i = 0; i < Math.ceil((double) nuSamples.size() / STEPSIZE); i++) {
+ List<ByteBuffer> sublist = nuSamples.subList(
+ i * STEPSIZE, // start
+ (i + 1) * STEPSIZE < nuSamples.size() ? (i + 1) * STEPSIZE : nuSamples.size()); // end
+ ByteBuffer sampleArray[] = sublist.toArray(new ByteBuffer[sublist.size()]);
+ do {
+ ((GatheringByteChannel) writableByteChannel).write(sampleArray);
+ } while (sampleArray[sampleArray.length - 1].remaining() > 0);
+ }
+ //System.err.println(bytesWritten);
+ } else {
+ for (ByteBuffer sample : samples) {
+ sample.rewind();
+ writableByteChannel.write(sample);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Gets the chunk sizes for the given track.
+ *
+ * @param track
+ * @param movie
+ * @return
+ */
+ int[] getChunkSizes(Track track, Movie movie) {
+
+ long[] referenceChunkStarts = intersectionFinder.sampleNumbers(track, movie);
+ int[] chunkSizes = new int[referenceChunkStarts.length];
+
+
+ for (int i = 0; i < referenceChunkStarts.length; i++) {
+ long start = referenceChunkStarts[i] - 1;
+ long end;
+ if (referenceChunkStarts.length == i + 1) {
+ end = track.getSamples().size();
+ } else {
+ end = referenceChunkStarts[i + 1] - 1;
+ }
+
+ chunkSizes[i] = l2i(end - start);
+ // The Stretch makes sure that there are as much audio and video chunks!
+ }
+ assert DefaultMp4Builder.this.track2Sample.get(track).size() == sum(chunkSizes) : "The number of samples and the sum of all chunk lengths must be equal";
+ return chunkSizes;
+
+
+ }
+
+
+ private static long sum(int[] ls) {
+ long rc = 0;
+ for (long l : ls) {
+ rc += l;
+ }
+ return rc;
+ }
+
+ protected static long getDuration(Track track) {
+ long duration = 0;
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ duration += entry.getCount() * entry.getDelta();
+ }
+ return duration;
+ }
+
+ public long getTimescale(Movie movie) {
+ long timescale = movie.getTracks().iterator().next().getTrackMetaData().getTimescale();
+ for (Track track : movie.getTracks()) {
+ timescale = gcd(track.getTrackMetaData().getTimescale(), timescale);
+ }
+ return timescale;
+ }
+
+ public static long gcd(long a, long b) {
+ if (b == 0) {
+ return a;
+ }
+ return gcd(b, a % b);
+ }
+
+ public List<ByteBuffer> unifyAdjacentBuffers(List<ByteBuffer> samples) {
+ ArrayList<ByteBuffer> nuSamples = new ArrayList<ByteBuffer>(samples.size());
+ for (ByteBuffer buffer : samples) {
+ int lastIndex = nuSamples.size() - 1;
+ if (lastIndex >= 0 && buffer.hasArray() && nuSamples.get(lastIndex).hasArray() && buffer.array() == nuSamples.get(lastIndex).array() &&
+ nuSamples.get(lastIndex).arrayOffset() + nuSamples.get(lastIndex).limit() == buffer.arrayOffset()) {
+ ByteBuffer oldBuffer = nuSamples.remove(lastIndex);
+ ByteBuffer nu = ByteBuffer.wrap(buffer.array(), oldBuffer.arrayOffset(), oldBuffer.limit() + buffer.limit()).slice();
+ // We need to slice here since wrap([], offset, length) just sets position and not the arrayOffset.
+ nuSamples.add(nu);
+ } else if (lastIndex >= 0 &&
+ buffer instanceof MappedByteBuffer && nuSamples.get(lastIndex) instanceof MappedByteBuffer &&
+ nuSamples.get(lastIndex).limit() == nuSamples.get(lastIndex).capacity() - buffer.capacity()) {
+ // This can go wrong - but will it?
+ ByteBuffer oldBuffer = nuSamples.get(lastIndex);
+ oldBuffer.limit(buffer.limit() + oldBuffer.limit());
+ } else {
+ nuSamples.add(buffer);
+ }
+ }
+ return nuSamples;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentIntersectionFinder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentIntersectionFinder.java.svn-base
new file mode 100644
index 0000000..1224bbf
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentIntersectionFinder.java.svn-base
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+/**
+ *
+ */
+public interface FragmentIntersectionFinder {
+ /**
+ * Gets the ordinal number of the samples which will be the first sample
+ * in each fragment.
+ *
+ * @param track concerned track
+ * @param movie the context of the track
+ * @return an array containing the ordinal of each fragment's first sample
+ */
+ public long[] sampleNumbers(Track track, Movie movie);
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentedMp4Builder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentedMp4Builder.java.svn-base
new file mode 100644
index 0000000..c65ff1c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentedMp4Builder.java.svn-base
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.fragment.*;
+import com.googlecode.mp4parser.authoring.DateHelper;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.GatheringByteChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.*;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Creates a fragmented MP4 file.
+ */
+public class FragmentedMp4Builder implements Mp4Builder {
+ private static final Logger LOG = Logger.getLogger(FragmentedMp4Builder.class.getName());
+
+ protected FragmentIntersectionFinder intersectionFinder;
+
+ public FragmentedMp4Builder() {
+ this.intersectionFinder = new SyncSampleIntersectFinderImpl();
+ }
+
+ public List<String> getAllowedHandlers() {
+ return Arrays.asList("soun", "vide");
+ }
+
+ public Box createFtyp(Movie movie) {
+ List<String> minorBrands = new LinkedList<String>();
+ minorBrands.add("isom");
+ minorBrands.add("iso2");
+ minorBrands.add("avc1");
+ return new FileTypeBox("isom", 0, minorBrands);
+ }
+
+ /**
+ * Some formats require sorting of the fragments. E.g. Ultraviolet CFF files are required
+ * to contain the fragments size sort:
+ * <ul>
+ * <li>video[1].getBytes().length < audio[1].getBytes().length < subs[1].getBytes().length</li>
+ * <li> audio[2].getBytes().length < video[2].getBytes().length < subs[2].getBytes().length</li>
+ * </ul>
+ *
+ * make this fragment:
+ *
+ * <ol>
+ * <li>video[1]</li>
+ * <li>audio[1]</li>
+ * <li>subs[1]</li>
+ * <li>audio[2]</li>
+ * <li>video[2]</li>
+ * <li>subs[2]</li>
+ * </ol>
+ *
+ * @param tracks the list of tracks to returned sorted
+ * @param cycle current fragment (sorting may vary between the fragments)
+ * @param intersectionMap a map from tracks to their fragments' first samples.
+ * @return the list of tracks in order of appearance in the fragment
+ */
+ protected List<Track> sortTracksInSequence(List<Track> tracks, final int cycle, final Map<Track, long[]> intersectionMap) {
+ tracks = new LinkedList<Track>(tracks);
+ Collections.sort(tracks, new Comparator<Track>() {
+ public int compare(Track o1, Track o2) {
+ long[] startSamples1 = intersectionMap.get(o1);
+ long startSample1 = startSamples1[cycle];
+ // one based sample numbers - the first sample is 1
+ long endSample1 = cycle + 1 < startSamples1.length ? startSamples1[cycle + 1] : o1.getSamples().size() + 1;
+ long[] startSamples2 = intersectionMap.get(o2);
+ long startSample2 = startSamples2[cycle];
+ // one based sample numbers - the first sample is 1
+ long endSample2 = cycle + 1 < startSamples2.length ? startSamples2[cycle + 1] : o2.getSamples().size() + 1;
+ List<ByteBuffer> samples1 = o1.getSamples().subList(l2i(startSample1) - 1, l2i(endSample1) - 1);
+ List<ByteBuffer> samples2 = o2.getSamples().subList(l2i(startSample2) - 1, l2i(endSample2) - 1);
+ int size1 = 0;
+ for (ByteBuffer byteBuffer : samples1) {
+ size1 += byteBuffer.limit();
+ }
+ int size2 = 0;
+ for (ByteBuffer byteBuffer : samples2) {
+ size2 += byteBuffer.limit();
+ }
+ return size1 - size2;
+ }
+ });
+ return tracks;
+ }
+
+ protected List<Box> createMoofMdat(final Movie movie) {
+ List<Box> boxes = new LinkedList<Box>();
+ HashMap<Track, long[]> intersectionMap = new HashMap<Track, long[]>();
+ int maxNumberOfFragments = 0;
+ for (Track track : movie.getTracks()) {
+ long[] intersects = intersectionFinder.sampleNumbers(track, movie);
+ intersectionMap.put(track, intersects);
+ maxNumberOfFragments = Math.max(maxNumberOfFragments, intersects.length);
+ }
+
+
+ int sequence = 1;
+ // this loop has two indices:
+
+ for (int cycle = 0; cycle < maxNumberOfFragments; cycle++) {
+
+ final List<Track> sortedTracks = sortTracksInSequence(movie.getTracks(), cycle, intersectionMap);
+
+ for (Track track : sortedTracks) {
+ if (getAllowedHandlers().isEmpty() || getAllowedHandlers().contains(track.getHandler())) {
+ long[] startSamples = intersectionMap.get(track);
+ //some tracks may have less fragments -> skip them
+ if (cycle < startSamples.length) {
+
+ long startSample = startSamples[cycle];
+ // one based sample numbers - the first sample is 1
+ long endSample = cycle + 1 < startSamples.length ? startSamples[cycle + 1] : track.getSamples().size() + 1;
+
+ // if startSample == endSample the cycle is empty!
+ if (startSample != endSample) {
+ boxes.add(createMoof(startSample, endSample, track, sequence));
+ boxes.add(createMdat(startSample, endSample, track, sequence++));
+ }
+ }
+ }
+ }
+ }
+ return boxes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public IsoFile build(Movie movie) {
+ LOG.fine("Creating movie " + movie);
+ IsoFile isoFile = new IsoFile();
+
+
+ isoFile.addBox(createFtyp(movie));
+ isoFile.addBox(createMoov(movie));
+
+ for (Box box : createMoofMdat(movie)) {
+ isoFile.addBox(box);
+ }
+ isoFile.addBox(createMfra(movie, isoFile));
+
+ return isoFile;
+ }
+
+ protected Box createMdat(final long startSample, final long endSample, final Track track, final int i) {
+
+ class Mdat implements Box {
+ ContainerBox parent;
+
+ public ContainerBox getParent() {
+ return parent;
+ }
+
+ public void setParent(ContainerBox parent) {
+ this.parent = parent;
+ }
+
+ public long getSize() {
+ long size = 8; // I don't expect 2gig fragments
+ for (ByteBuffer sample : getSamples(startSample, endSample, track, i)) {
+ size += sample.limit();
+ }
+ return size;
+ }
+
+ public String getType() {
+ return "mdat";
+ }
+
+ public void getBox(WritableByteChannel writableByteChannel) throws IOException {
+ List<ByteBuffer> bbs = getSamples(startSample, endSample, track, i);
+ final List<ByteBuffer> samples = ByteBufferHelper.mergeAdjacentBuffers(bbs);
+ ByteBuffer header = ByteBuffer.allocate(8);
+ IsoTypeWriter.writeUInt32(header, l2i(getSize()));
+ header.put(IsoFile.fourCCtoBytes(getType()));
+ header.rewind();
+ writableByteChannel.write(header);
+ if (writableByteChannel instanceof GatheringByteChannel) {
+
+ int STEPSIZE = 1024;
+ // This is required to prevent android from crashing
+ // it seems that {@link GatheringByteChannel#write(java.nio.ByteBuffer[])}
+ // just handles up to 1024 buffers
+ for (int i = 0; i < Math.ceil((double) samples.size() / STEPSIZE); i++) {
+ List<ByteBuffer> sublist = samples.subList(
+ i * STEPSIZE, // start
+ (i + 1) * STEPSIZE < samples.size() ? (i + 1) * STEPSIZE : samples.size()); // end
+ ByteBuffer sampleArray[] = sublist.toArray(new ByteBuffer[sublist.size()]);
+ do {
+ ((GatheringByteChannel) writableByteChannel).write(sampleArray);
+ } while (sampleArray[sampleArray.length - 1].remaining() > 0);
+ }
+ //System.err.println(bytesWritten);
+ } else {
+ for (ByteBuffer sample : samples) {
+ sample.rewind();
+ writableByteChannel.write(sample);
+ }
+ }
+
+ }
+
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+
+ }
+ }
+
+ return new Mdat();
+ }
+
+ protected Box createTfhd(long startSample, long endSample, Track track, int sequenceNumber) {
+ TrackFragmentHeaderBox tfhd = new TrackFragmentHeaderBox();
+ SampleFlags sf = new SampleFlags();
+
+ tfhd.setDefaultSampleFlags(sf);
+ tfhd.setBaseDataOffset(-1);
+ tfhd.setTrackId(track.getTrackMetaData().getTrackId());
+ return tfhd;
+ }
+
+ protected Box createMfhd(long startSample, long endSample, Track track, int sequenceNumber) {
+ MovieFragmentHeaderBox mfhd = new MovieFragmentHeaderBox();
+ mfhd.setSequenceNumber(sequenceNumber);
+ return mfhd;
+ }
+
+ protected Box createTraf(long startSample, long endSample, Track track, int sequenceNumber) {
+ TrackFragmentBox traf = new TrackFragmentBox();
+ traf.addBox(createTfhd(startSample, endSample, track, sequenceNumber));
+ for (Box trun : createTruns(startSample, endSample, track, sequenceNumber)) {
+ traf.addBox(trun);
+ }
+
+ return traf;
+ }
+
+
+ /**
+ * Gets the all samples starting with <code>startSample</code> (one based -> one is the first) and
+ * ending with <code>endSample</code> (exclusive).
+ *
+ * @param startSample low endpoint (inclusive) of the sample sequence
+ * @param endSample high endpoint (exclusive) of the sample sequence
+ * @param track source of the samples
+ * @param sequenceNumber the fragment index of the requested list of samples
+ * @return a <code>List&lt;ByteBuffer></code> of raw samples
+ */
+ protected List<ByteBuffer> getSamples(long startSample, long endSample, Track track, int sequenceNumber) {
+ // since startSample and endSample are one-based substract 1 before addressing list elements
+ return track.getSamples().subList(l2i(startSample) - 1, l2i(endSample) - 1);
+ }
+
+ /**
+ * Gets the sizes of a sequence of samples-
+ *
+ * @param startSample low endpoint (inclusive) of the sample sequence
+ * @param endSample high endpoint (exclusive) of the sample sequence
+ * @param track source of the samples
+ * @param sequenceNumber the fragment index of the requested list of samples
+ * @return
+ */
+ protected long[] getSampleSizes(long startSample, long endSample, Track track, int sequenceNumber) {
+ List<ByteBuffer> samples = getSamples(startSample, endSample, track, sequenceNumber);
+
+ long[] sampleSizes = new long[samples.size()];
+ for (int i = 0; i < sampleSizes.length; i++) {
+ sampleSizes[i] = samples.get(i).limit();
+ }
+ return sampleSizes;
+ }
+
+ /**
+ * Creates one or more track run boxes for a given sequence.
+ *
+ * @param startSample low endpoint (inclusive) of the sample sequence
+ * @param endSample high endpoint (exclusive) of the sample sequence
+ * @param track source of the samples
+ * @param sequenceNumber the fragment index of the requested list of samples
+ * @return the list of TrackRun boxes.
+ */
+ protected List<? extends Box> createTruns(long startSample, long endSample, Track track, int sequenceNumber) {
+ TrackRunBox trun = new TrackRunBox();
+ long[] sampleSizes = getSampleSizes(startSample, endSample, track, sequenceNumber);
+
+ trun.setSampleDurationPresent(true);
+ trun.setSampleSizePresent(true);
+ List<TrackRunBox.Entry> entries = new ArrayList<TrackRunBox.Entry>(l2i(endSample - startSample));
+
+
+ Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries());
+ long left = startSample - 1;
+ long curEntryLeft = timeQueue.peek().getCount();
+ while (left > curEntryLeft) {
+ left -= curEntryLeft;
+ timeQueue.remove();
+ curEntryLeft = timeQueue.peek().getCount();
+ }
+ curEntryLeft -= left;
+
+
+ Queue<CompositionTimeToSample.Entry> compositionTimeQueue =
+ track.getCompositionTimeEntries() != null && track.getCompositionTimeEntries().size() > 0 ?
+ new LinkedList<CompositionTimeToSample.Entry>(track.getCompositionTimeEntries()) : null;
+ long compositionTimeEntriesLeft = compositionTimeQueue != null ? compositionTimeQueue.peek().getCount() : -1;
+
+
+ trun.setSampleCompositionTimeOffsetPresent(compositionTimeEntriesLeft > 0);
+
+ // fast forward composition stuff
+ for (long i = 1; i < startSample; i++) {
+ if (compositionTimeQueue != null) {
+ //trun.setSampleCompositionTimeOffsetPresent(true);
+ if (--compositionTimeEntriesLeft == 0 && compositionTimeQueue.size() > 1) {
+ compositionTimeQueue.remove();
+ compositionTimeEntriesLeft = compositionTimeQueue.element().getCount();
+ }
+ }
+ }
+
+ boolean sampleFlagsRequired = (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty() ||
+ track.getSyncSamples() != null && track.getSyncSamples().length != 0);
+
+ trun.setSampleFlagsPresent(sampleFlagsRequired);
+
+ for (int i = 0; i < sampleSizes.length; i++) {
+ TrackRunBox.Entry entry = new TrackRunBox.Entry();
+ entry.setSampleSize(sampleSizes[i]);
+ if (sampleFlagsRequired) {
+ //if (false) {
+ SampleFlags sflags = new SampleFlags();
+
+ if (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty()) {
+ SampleDependencyTypeBox.Entry e = track.getSampleDependencies().get(i);
+ sflags.setSampleDependsOn(e.getSampleDependsOn());
+ sflags.setSampleIsDependedOn(e.getSampleIsDependentOn());
+ sflags.setSampleHasRedundancy(e.getSampleHasRedundancy());
+ }
+ if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
+ // we have to mark non-sync samples!
+ if (Arrays.binarySearch(track.getSyncSamples(), startSample + i) >= 0) {
+ sflags.setSampleIsDifferenceSample(false);
+ sflags.setSampleDependsOn(2);
+ } else {
+ sflags.setSampleIsDifferenceSample(true);
+ sflags.setSampleDependsOn(1);
+ }
+ }
+ // i don't have sample degradation
+ entry.setSampleFlags(sflags);
+
+ }
+
+ entry.setSampleDuration(timeQueue.peek().getDelta());
+ if (--curEntryLeft == 0 && timeQueue.size() > 1) {
+ timeQueue.remove();
+ curEntryLeft = timeQueue.peek().getCount();
+ }
+
+ if (compositionTimeQueue != null) {
+ entry.setSampleCompositionTimeOffset(compositionTimeQueue.peek().getOffset());
+ if (--compositionTimeEntriesLeft == 0 && compositionTimeQueue.size() > 1) {
+ compositionTimeQueue.remove();
+ compositionTimeEntriesLeft = compositionTimeQueue.element().getCount();
+ }
+ }
+ entries.add(entry);
+ }
+
+ trun.setEntries(entries);
+
+ return Collections.singletonList(trun);
+ }
+
+ /**
+ * Creates a 'moof' box for a given sequence of samples.
+ *
+ * @param startSample low endpoint (inclusive) of the sample sequence
+ * @param endSample high endpoint (exclusive) of the sample sequence
+ * @param track source of the samples
+ * @param sequenceNumber the fragment index of the requested list of samples
+ * @return the list of TrackRun boxes.
+ */
+ protected Box createMoof(long startSample, long endSample, Track track, int sequenceNumber) {
+ MovieFragmentBox moof = new MovieFragmentBox();
+ moof.addBox(createMfhd(startSample, endSample, track, sequenceNumber));
+ moof.addBox(createTraf(startSample, endSample, track, sequenceNumber));
+
+ TrackRunBox firstTrun = moof.getTrackRunBoxes().get(0);
+ firstTrun.setDataOffset(1); // dummy to make size correct
+ firstTrun.setDataOffset((int) (8 + moof.getSize())); // mdat header + moof size
+
+ return moof;
+ }
+
+ /**
+ * Creates a single 'mvhd' movie header box for a given movie.
+ *
+ * @param movie the concerned movie
+ * @return an 'mvhd' box
+ */
+ protected Box createMvhd(Movie movie) {
+ MovieHeaderBox mvhd = new MovieHeaderBox();
+ mvhd.setVersion(1);
+ mvhd.setCreationTime(DateHelper.convert(new Date()));
+ mvhd.setModificationTime(DateHelper.convert(new Date()));
+ long movieTimeScale = movie.getTimescale();
+ long duration = 0;
+
+ for (Track track : movie.getTracks()) {
+ long tracksDuration = getDuration(track) * movieTimeScale / track.getTrackMetaData().getTimescale();
+ if (tracksDuration > duration) {
+ duration = tracksDuration;
+ }
+
+
+ }
+
+ mvhd.setDuration(duration);
+ mvhd.setTimescale(movieTimeScale);
+ // find the next available trackId
+ long nextTrackId = 0;
+ for (Track track : movie.getTracks()) {
+ nextTrackId = nextTrackId < track.getTrackMetaData().getTrackId() ? track.getTrackMetaData().getTrackId() : nextTrackId;
+ }
+ mvhd.setNextTrackId(++nextTrackId);
+ return mvhd;
+ }
+
+ /**
+ * Creates a fully populated 'moov' box with all child boxes. Child boxes are:
+ * <ul>
+ * <li>{@link #createMvhd(com.googlecode.mp4parser.authoring.Movie) mvhd}</li>
+ * <li>{@link #createMvex(com.googlecode.mp4parser.authoring.Movie) mvex}</li>
+ * <li>a {@link #createTrak(com.googlecode.mp4parser.authoring.Track, com.googlecode.mp4parser.authoring.Movie) trak} for every track</li>
+ * </ul>
+ *
+ * @param movie the concerned movie
+ * @return fully populated 'moov'
+ */
+ protected Box createMoov(Movie movie) {
+ MovieBox movieBox = new MovieBox();
+
+ movieBox.addBox(createMvhd(movie));
+ movieBox.addBox(createMvex(movie));
+
+ for (Track track : movie.getTracks()) {
+ movieBox.addBox(createTrak(track, movie));
+ }
+ // metadata here
+ return movieBox;
+
+ }
+
+ /**
+ * Creates a 'tfra' - track fragment random access box for the given track with the isoFile.
+ * The tfra contains a map of random access points with time as key and offset within the isofile
+ * as value.
+ *
+ * @param track the concerned track
+ * @param isoFile the track is contained in
+ * @return a track fragment random access box.
+ */
+ protected Box createTfra(Track track, IsoFile isoFile) {
+ TrackFragmentRandomAccessBox tfra = new TrackFragmentRandomAccessBox();
+ tfra.setVersion(1); // use long offsets and times
+ List<TrackFragmentRandomAccessBox.Entry> offset2timeEntries = new LinkedList<TrackFragmentRandomAccessBox.Entry>();
+ List<Box> boxes = isoFile.getBoxes();
+ long offset = 0;
+ long duration = 0;
+ for (Box box : boxes) {
+ if (box instanceof MovieFragmentBox) {
+ List<TrackFragmentBox> trafs = ((MovieFragmentBox) box).getBoxes(TrackFragmentBox.class);
+ for (int i = 0; i < trafs.size(); i++) {
+ TrackFragmentBox traf = trafs.get(i);
+ if (traf.getTrackFragmentHeaderBox().getTrackId() == track.getTrackMetaData().getTrackId()) {
+ // here we are at the offset required for the current entry.
+ List<TrackRunBox> truns = traf.getBoxes(TrackRunBox.class);
+ for (int j = 0; j < truns.size(); j++) {
+ List<TrackFragmentRandomAccessBox.Entry> offset2timeEntriesThisTrun = new LinkedList<TrackFragmentRandomAccessBox.Entry>();
+ TrackRunBox trun = truns.get(j);
+ for (int k = 0; k < trun.getEntries().size(); k++) {
+ TrackRunBox.Entry trunEntry = trun.getEntries().get(k);
+ SampleFlags sf = null;
+ if (k == 0 && trun.isFirstSampleFlagsPresent()) {
+ sf = trun.getFirstSampleFlags();
+ } else if (trun.isSampleFlagsPresent()) {
+ sf = trunEntry.getSampleFlags();
+ } else {
+ List<MovieExtendsBox> mvexs = isoFile.getMovieBox().getBoxes(MovieExtendsBox.class);
+ for (MovieExtendsBox mvex : mvexs) {
+ List<TrackExtendsBox> trexs = mvex.getBoxes(TrackExtendsBox.class);
+ for (TrackExtendsBox trex : trexs) {
+ if (trex.getTrackId() == track.getTrackMetaData().getTrackId()) {
+ sf = trex.getDefaultSampleFlags();
+ }
+ }
+ }
+
+ }
+ if (sf == null) {
+ throw new RuntimeException("Could not find any SampleFlags to indicate random access or not");
+ }
+ if (sf.getSampleDependsOn() == 2) {
+ offset2timeEntriesThisTrun.add(new TrackFragmentRandomAccessBox.Entry(
+ duration,
+ offset,
+ i + 1, j + 1, k + 1));
+ }
+ duration += trunEntry.getSampleDuration();
+ }
+ if (offset2timeEntriesThisTrun.size() == trun.getEntries().size() && trun.getEntries().size() > 0) {
+ // Oooops every sample seems to be random access sample
+ // is this an audio track? I don't care.
+ // I just use the first for trun sample for tfra random access
+ offset2timeEntries.add(offset2timeEntriesThisTrun.get(0));
+ } else {
+ offset2timeEntries.addAll(offset2timeEntriesThisTrun);
+ }
+ }
+ }
+ }
+ }
+
+
+ offset += box.getSize();
+ }
+ tfra.setEntries(offset2timeEntries);
+ tfra.setTrackId(track.getTrackMetaData().getTrackId());
+ return tfra;
+ }
+
+ /**
+ * Creates a 'mfra' - movie fragment random access box for the given movie in the given
+ * isofile. Uses {@link #createTfra(com.googlecode.mp4parser.authoring.Track, com.coremedia.iso.IsoFile)}
+ * to generate the child boxes.
+ *
+ * @param movie concerned movie
+ * @param isoFile concerned isofile
+ * @return a complete 'mfra' box
+ */
+ protected Box createMfra(Movie movie, IsoFile isoFile) {
+ MovieFragmentRandomAccessBox mfra = new MovieFragmentRandomAccessBox();
+ for (Track track : movie.getTracks()) {
+ mfra.addBox(createTfra(track, isoFile));
+ }
+
+ MovieFragmentRandomAccessOffsetBox mfro = new MovieFragmentRandomAccessOffsetBox();
+ mfra.addBox(mfro);
+ mfro.setMfraSize(mfra.getSize());
+ return mfra;
+ }
+
+ protected Box createTrex(Movie movie, Track track) {
+ TrackExtendsBox trex = new TrackExtendsBox();
+ trex.setTrackId(track.getTrackMetaData().getTrackId());
+ trex.setDefaultSampleDescriptionIndex(1);
+ trex.setDefaultSampleDuration(0);
+ trex.setDefaultSampleSize(0);
+ SampleFlags sf = new SampleFlags();
+ if ("soun".equals(track.getHandler())) {
+ // as far as I know there is no audio encoding
+ // where the sample are not self contained.
+ sf.setSampleDependsOn(2);
+ sf.setSampleIsDependedOn(2);
+ }
+ trex.setDefaultSampleFlags(sf);
+ return trex;
+ }
+
+ /**
+ * Creates a 'mvex' - movie extends box and populates it with 'trex' boxes
+ * by calling {@link #createTrex(com.googlecode.mp4parser.authoring.Movie, com.googlecode.mp4parser.authoring.Track)}
+ * for each track to generate them
+ *
+ * @param movie the source movie
+ * @return a complete 'mvex'
+ */
+ protected Box createMvex(Movie movie) {
+ MovieExtendsBox mvex = new MovieExtendsBox();
+ final MovieExtendsHeaderBox mved = new MovieExtendsHeaderBox();
+ for (Track track : movie.getTracks()) {
+ final long trackDuration = getTrackDuration(movie, track);
+ if (mved.getFragmentDuration() < trackDuration) {
+ mved.setFragmentDuration(trackDuration);
+ }
+ }
+ mvex.addBox(mved);
+
+ for (Track track : movie.getTracks()) {
+ mvex.addBox(createTrex(movie, track));
+ }
+ return mvex;
+ }
+
+ protected Box createTkhd(Movie movie, Track track) {
+ TrackHeaderBox tkhd = new TrackHeaderBox();
+ tkhd.setVersion(1);
+ int flags = 0;
+ if (track.isEnabled()) {
+ flags += 1;
+ }
+
+ if (track.isInMovie()) {
+ flags += 2;
+ }
+
+ if (track.isInPreview()) {
+ flags += 4;
+ }
+
+ if (track.isInPoster()) {
+ flags += 8;
+ }
+ tkhd.setFlags(flags);
+
+ tkhd.setAlternateGroup(track.getTrackMetaData().getGroup());
+ tkhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime()));
+ // We need to take edit list box into account in trackheader duration
+ // but as long as I don't support edit list boxes it is sufficient to
+ // just translate media duration to movie timescale
+ tkhd.setDuration(getTrackDuration(movie, track));
+ tkhd.setHeight(track.getTrackMetaData().getHeight());
+ tkhd.setWidth(track.getTrackMetaData().getWidth());
+ tkhd.setLayer(track.getTrackMetaData().getLayer());
+ tkhd.setModificationTime(DateHelper.convert(new Date()));
+ tkhd.setTrackId(track.getTrackMetaData().getTrackId());
+ tkhd.setVolume(track.getTrackMetaData().getVolume());
+ return tkhd;
+ }
+
+ private long getTrackDuration(Movie movie, Track track) {
+ return getDuration(track) * movie.getTimescale() / track.getTrackMetaData().getTimescale();
+ }
+
+ protected Box createMdhd(Movie movie, Track track) {
+ MediaHeaderBox mdhd = new MediaHeaderBox();
+ mdhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime()));
+ mdhd.setDuration(getDuration(track));
+ mdhd.setTimescale(track.getTrackMetaData().getTimescale());
+ mdhd.setLanguage(track.getTrackMetaData().getLanguage());
+ return mdhd;
+ }
+
+ protected Box createStbl(Movie movie, Track track) {
+ SampleTableBox stbl = new SampleTableBox();
+
+ stbl.addBox(track.getSampleDescriptionBox());
+ stbl.addBox(new TimeToSampleBox());
+ //stbl.addBox(new SampleToChunkBox());
+ stbl.addBox(new StaticChunkOffsetBox());
+ return stbl;
+ }
+
+ protected Box createMinf(Track track, Movie movie) {
+ MediaInformationBox minf = new MediaInformationBox();
+ minf.addBox(track.getMediaHeaderBox());
+ minf.addBox(createDinf(movie, track));
+ minf.addBox(createStbl(movie, track));
+ return minf;
+ }
+
+ protected Box createMdiaHdlr(Track track, Movie movie) {
+ HandlerBox hdlr = new HandlerBox();
+ hdlr.setHandlerType(track.getHandler());
+ return hdlr;
+ }
+
+ protected Box createMdia(Track track, Movie movie) {
+ MediaBox mdia = new MediaBox();
+ mdia.addBox(createMdhd(movie, track));
+
+
+ mdia.addBox(createMdiaHdlr(track, movie));
+
+
+ mdia.addBox(createMinf(track, movie));
+ return mdia;
+ }
+
+ protected Box createTrak(Track track, Movie movie) {
+ LOG.fine("Creating Track " + track);
+ TrackBox trackBox = new TrackBox();
+ trackBox.addBox(createTkhd(movie, track));
+ trackBox.addBox(createMdia(track, movie));
+ return trackBox;
+ }
+
+ protected DataInformationBox createDinf(Movie movie, Track track) {
+ DataInformationBox dinf = new DataInformationBox();
+ DataReferenceBox dref = new DataReferenceBox();
+ dinf.addBox(dref);
+ DataEntryUrlBox url = new DataEntryUrlBox();
+ url.setFlags(1);
+ dref.addBox(url);
+ return dinf;
+ }
+
+ public FragmentIntersectionFinder getFragmentIntersectionFinder() {
+ return intersectionFinder;
+ }
+
+ public void setIntersectionFinder(FragmentIntersectionFinder intersectionFinder) {
+ this.intersectionFinder = intersectionFinder;
+ }
+
+ protected long getDuration(Track track) {
+ long duration = 0;
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ duration += entry.getCount() * entry.getDelta();
+ }
+ return duration;
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/Mp4Builder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/Mp4Builder.java.svn-base
new file mode 100644
index 0000000..725745e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/Mp4Builder.java.svn-base
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.IsoFile;
+import com.googlecode.mp4parser.authoring.Movie;
+
+/**
+ * Transforms a <code>Movie</code> object to an IsoFile. Implementations can
+ * determine the specific format: Fragmented MP4, MP4, MP4 with Apple Metadata,
+ * MP4 with 3GPP Metadata, MOV.
+ */
+public interface Mp4Builder {
+ /**
+ * Builds the actual IsoFile from the Movie.
+ *
+ * @param movie data source
+ * @return the freshly built IsoFile
+ */
+ public IsoFile build(Movie movie);
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/SyncSampleIntersectFinderImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/SyncSampleIntersectFinderImpl.java.svn-base
new file mode 100644
index 0000000..2766c5e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/SyncSampleIntersectFinderImpl.java.svn-base
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.boxes.TimeToSampleBox;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.Math.lcm;
+
+/**
+ * This <code>FragmentIntersectionFinder</code> cuts the input movie video tracks in
+ * fragments of the same length exactly before the sync samples. Audio tracks are cut
+ * into pieces of similar length.
+ */
+public class SyncSampleIntersectFinderImpl implements FragmentIntersectionFinder {
+
+ private static Logger LOG = Logger.getLogger(SyncSampleIntersectFinderImpl.class.getName());
+ private static Map<CacheTuple, long[]> getTimesCache = new ConcurrentHashMap<CacheTuple, long[]>();
+ private static Map<CacheTuple, long[]> getSampleNumbersCache = new ConcurrentHashMap<CacheTuple, long[]>();
+
+ private final int minFragmentDurationSeconds;
+
+ public SyncSampleIntersectFinderImpl() {
+ minFragmentDurationSeconds = 0;
+ }
+
+ /**
+ * Creates a <code>SyncSampleIntersectFinderImpl</code> that will not create any fragment
+ * smaller than the given <code>minFragmentDurationSeconds</code>
+ *
+ * @param minFragmentDurationSeconds the smallest allowable duration of a fragment.
+ */
+ public SyncSampleIntersectFinderImpl(int minFragmentDurationSeconds) {
+ this.minFragmentDurationSeconds = minFragmentDurationSeconds;
+ }
+
+ /**
+ * Gets an array of sample numbers that are meant to be the first sample of each
+ * chunk or fragment.
+ *
+ * @param track concerned track
+ * @param movie the context of the track
+ * @return an array containing the ordinal of each fragment's first sample
+ */
+ public long[] sampleNumbers(Track track, Movie movie) {
+ final CacheTuple key = new CacheTuple(track, movie);
+ final long[] result = getSampleNumbersCache.get(key);
+ if (result != null) {
+ return result;
+ }
+
+ if ("vide".equals(track.getHandler())) {
+ if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
+ List<long[]> times = getSyncSamplesTimestamps(movie, track);
+ final long[] commonIndices = getCommonIndices(track.getSyncSamples(), getTimes(track, movie), track.getTrackMetaData().getTimescale(), times.toArray(new long[times.size()][]));
+ getSampleNumbersCache.put(key, commonIndices);
+ return commonIndices;
+ } else {
+ throw new RuntimeException("Video Tracks need sync samples. Only tracks other than video may have no sync samples.");
+ }
+ } else if ("soun".equals(track.getHandler())) {
+ Track referenceTrack = null;
+ for (Track candidate : movie.getTracks()) {
+ if (candidate.getSyncSamples() != null && "vide".equals(candidate.getHandler()) && candidate.getSyncSamples().length > 0) {
+ referenceTrack = candidate;
+ }
+ }
+ if (referenceTrack != null) {
+
+ // Gets the reference track's fra
+ long[] refSyncSamples = sampleNumbers(referenceTrack, movie);
+
+ int refSampleCount = referenceTrack.getSamples().size();
+
+ long[] syncSamples = new long[refSyncSamples.length];
+ long minSampleRate = 192000;
+ for (Track testTrack : movie.getTracks()) {
+ if ("soun".equals(testTrack.getHandler())) {
+ AudioSampleEntry ase = (AudioSampleEntry) testTrack.getSampleDescriptionBox().getSampleEntry();
+ if (ase.getSampleRate() < minSampleRate) {
+ minSampleRate = ase.getSampleRate();
+ long sc = testTrack.getSamples().size();
+ double stretch = (double) sc / refSampleCount;
+ TimeToSampleBox.Entry sttsEntry = testTrack.getDecodingTimeEntries().get(0);
+ long samplesPerFrame = sttsEntry.getDelta(); // Assuming all audio tracks have the same number of samples per frame, which they do for all known types
+
+ for (int i = 0; i < syncSamples.length; i++) {
+ long start = (long) Math.ceil(stretch * (refSyncSamples[i] - 1) * samplesPerFrame);
+ syncSamples[i] = start;
+ // The Stretch makes sure that there are as much audio and video chunks!
+ }
+ break;
+ }
+ }
+ }
+ AudioSampleEntry ase = (AudioSampleEntry) track.getSampleDescriptionBox().getSampleEntry();
+ TimeToSampleBox.Entry sttsEntry = track.getDecodingTimeEntries().get(0);
+ long samplesPerFrame = sttsEntry.getDelta(); // Assuming all audio tracks have the same number of samples per frame, which they do for all known types
+ double factor = (double) ase.getSampleRate() / (double) minSampleRate;
+ if (factor != Math.rint(factor)) { // Not an integer
+ throw new RuntimeException("Sample rates must be a multiple of the lowest sample rate to create a correct file!");
+ }
+ for (int i = 0; i < syncSamples.length; i++) {
+ syncSamples[i] = (long) (1 + syncSamples[i] * factor / (double) samplesPerFrame);
+ }
+ getSampleNumbersCache.put(key, syncSamples);
+ return syncSamples;
+ }
+ throw new RuntimeException("There was absolutely no Track with sync samples. I can't work with that!");
+ } else {
+ // Ok, my track has no sync samples - let's find one with sync samples.
+ for (Track candidate : movie.getTracks()) {
+ if (candidate.getSyncSamples() != null && candidate.getSyncSamples().length > 0) {
+ long[] refSyncSamples = sampleNumbers(candidate, movie);
+ int refSampleCount = candidate.getSamples().size();
+
+ long[] syncSamples = new long[refSyncSamples.length];
+ long sc = track.getSamples().size();
+ double stretch = (double) sc / refSampleCount;
+
+ for (int i = 0; i < syncSamples.length; i++) {
+ long start = (long) Math.ceil(stretch * (refSyncSamples[i] - 1)) + 1;
+ syncSamples[i] = start;
+ // The Stretch makes sure that there are as much audio and video chunks!
+ }
+ getSampleNumbersCache.put(key, syncSamples);
+ return syncSamples;
+ }
+ }
+ throw new RuntimeException("There was absolutely no Track with sync samples. I can't work with that!");
+ }
+
+
+ }
+
+ /**
+ * Calculates the timestamp of all tracks' sync samples.
+ *
+ * @param movie
+ * @param track
+ * @return
+ */
+ public static List<long[]> getSyncSamplesTimestamps(Movie movie, Track track) {
+ List<long[]> times = new LinkedList<long[]>();
+ for (Track currentTrack : movie.getTracks()) {
+ if (currentTrack.getHandler().equals(track.getHandler())) {
+ long[] currentTrackSyncSamples = currentTrack.getSyncSamples();
+ if (currentTrackSyncSamples != null && currentTrackSyncSamples.length > 0) {
+ final long[] currentTrackTimes = getTimes(currentTrack, movie);
+ times.add(currentTrackTimes);
+ }
+ }
+ }
+ return times;
+ }
+
+ public long[] getCommonIndices(long[] syncSamples, long[] syncSampleTimes, long timeScale, long[]... otherTracksTimes) {
+ List<Long> nuSyncSamples = new LinkedList<Long>();
+ List<Long> nuSyncSampleTimes = new LinkedList<Long>();
+
+
+ for (int i = 0; i < syncSampleTimes.length; i++) {
+ boolean foundInEveryRef = true;
+ for (long[] times : otherTracksTimes) {
+ foundInEveryRef &= (Arrays.binarySearch(times, syncSampleTimes[i]) >= 0);
+ }
+
+ if (foundInEveryRef) {
+ // use sample only if found in every other track.
+ nuSyncSamples.add(syncSamples[i]);
+ nuSyncSampleTimes.add(syncSampleTimes[i]);
+ }
+ }
+ // We have two arrays now:
+ // nuSyncSamples: Contains all common sync samples
+ // nuSyncSampleTimes: Contains the times of all sync samples
+
+ // Start: Warn user if samples are not matching!
+ if (nuSyncSamples.size() < (syncSamples.length * 0.25)) {
+ String log = "";
+ log += String.format("%5d - Common: [", nuSyncSamples.size());
+ for (long l : nuSyncSamples) {
+ log += (String.format("%10d,", l));
+ }
+ log += ("]");
+ LOG.warning(log);
+ log = "";
+
+ log += String.format("%5d - In : [", syncSamples.length);
+ for (long l : syncSamples) {
+ log += (String.format("%10d,", l));
+ }
+ log += ("]");
+ LOG.warning(log);
+ LOG.warning("There are less than 25% of common sync samples in the given track.");
+ throw new RuntimeException("There are less than 25% of common sync samples in the given track.");
+ } else if (nuSyncSamples.size() < (syncSamples.length * 0.5)) {
+ LOG.fine("There are less than 50% of common sync samples in the given track. This is implausible but I'm ok to continue.");
+ } else if (nuSyncSamples.size() < syncSamples.length) {
+ LOG.finest("Common SyncSample positions vs. this tracks SyncSample positions: " + nuSyncSamples.size() + " vs. " + syncSamples.length);
+ }
+ // End: Warn user if samples are not matching!
+
+
+
+
+ List<Long> finalSampleList = new LinkedList<Long>();
+
+ if (minFragmentDurationSeconds > 0) {
+ // if minFragmentDurationSeconds is greater 0
+ // we need to throw away certain samples.
+ long lastSyncSampleTime = -1;
+ Iterator<Long> nuSyncSamplesIterator = nuSyncSamples.iterator();
+ Iterator<Long> nuSyncSampleTimesIterator = nuSyncSampleTimes.iterator();
+ while (nuSyncSamplesIterator.hasNext() && nuSyncSampleTimesIterator.hasNext()) {
+ long curSyncSample = nuSyncSamplesIterator.next();
+ long curSyncSampleTime = nuSyncSampleTimesIterator.next();
+ if (lastSyncSampleTime == -1 || (curSyncSampleTime - lastSyncSampleTime) / timeScale >= minFragmentDurationSeconds) {
+ finalSampleList.add(curSyncSample);
+ lastSyncSampleTime = curSyncSampleTime;
+ }
+ }
+ } else {
+ // the list of all samples is the final list of samples
+ // since minFragmentDurationSeconds ist not used.
+ finalSampleList = nuSyncSamples;
+ }
+
+
+ // transform the list to an array
+ long[] finalSampleArray = new long[finalSampleList.size()];
+ for (int i = 0; i < finalSampleArray.length; i++) {
+ finalSampleArray[i] = finalSampleList.get(i);
+ }
+ return finalSampleArray;
+
+ }
+
+
+ private static long[] getTimes(Track track, Movie m) {
+ final CacheTuple key = new CacheTuple(track, m);
+ final long[] result = getTimesCache.get(key);
+ if (result != null) {
+ return result;
+ }
+
+ long[] syncSamples = track.getSyncSamples();
+ long[] syncSampleTimes = new long[syncSamples.length];
+ Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries());
+
+ int currentSample = 1; // first syncsample is 1
+ long currentDuration = 0;
+ long currentDelta = 0;
+ int currentSyncSampleIndex = 0;
+ long left = 0;
+
+ final long scalingFactor = calculateTracktimesScalingFactor(m, track);
+
+ while (currentSample <= syncSamples[syncSamples.length - 1]) {
+ if (currentSample++ == syncSamples[currentSyncSampleIndex]) {
+ syncSampleTimes[currentSyncSampleIndex++] = currentDuration * scalingFactor;
+ }
+ if (left-- == 0) {
+ TimeToSampleBox.Entry entry = timeQueue.poll();
+ left = entry.getCount() - 1;
+ currentDelta = entry.getDelta();
+ }
+ currentDuration += currentDelta;
+ }
+ getTimesCache.put(key, syncSampleTimes);
+ return syncSampleTimes;
+ }
+
+ private static long calculateTracktimesScalingFactor(Movie m, Track track) {
+ long timeScale = 1;
+ for (Track track1 : m.getTracks()) {
+ if (track1.getHandler().equals(track.getHandler())) {
+ if (track1.getTrackMetaData().getTimescale() != track.getTrackMetaData().getTimescale()) {
+ timeScale = lcm(timeScale, track1.getTrackMetaData().getTimescale());
+ }
+ }
+ }
+ return timeScale;
+ }
+
+ public static class CacheTuple {
+ Track track;
+ Movie movie;
+
+ public CacheTuple(Track track, Movie movie) {
+ this.track = track;
+ this.movie = movie;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CacheTuple that = (CacheTuple) o;
+
+ if (movie != null ? !movie.equals(that.movie) : that.movie != null) return false;
+ if (track != null ? !track.equals(that.track) : that.track != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = track != null ? track.hashCode() : 0;
+ result = 31 * result + (movie != null ? movie.hashCode() : 0);
+ return result;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/TwoSecondIntersectionFinder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/TwoSecondIntersectionFinder.java.svn-base
new file mode 100644
index 0000000..88aa4ab
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/TwoSecondIntersectionFinder.java.svn-base
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.boxes.TimeToSampleBox;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This <code>FragmentIntersectionFinder</code> cuts the input movie in 2 second
+ * snippets.
+ */
+public class TwoSecondIntersectionFinder implements FragmentIntersectionFinder {
+
+ protected long getDuration(Track track) {
+ long duration = 0;
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ duration += entry.getCount() * entry.getDelta();
+ }
+ return duration;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long[] sampleNumbers(Track track, Movie movie) {
+ List<TimeToSampleBox.Entry> entries = track.getDecodingTimeEntries();
+
+ double trackLength = 0;
+ for (Track thisTrack : movie.getTracks()) {
+ double thisTracksLength = getDuration(thisTrack) / thisTrack.getTrackMetaData().getTimescale();
+ if (trackLength < thisTracksLength) {
+ trackLength = thisTracksLength;
+ }
+ }
+
+ int fragmentCount = (int)Math.ceil(trackLength / 2) - 1;
+ if (fragmentCount < 1) {
+ fragmentCount = 1;
+ }
+
+ long fragments[] = new long[fragmentCount];
+ Arrays.fill(fragments, -1);
+ fragments[0] = 1;
+
+ long time = 0;
+ int samples = 0;
+ for (TimeToSampleBox.Entry entry : entries) {
+ for (int i = 0; i < entry.getCount(); i++) {
+ int currentFragment = (int) (time / track.getTrackMetaData().getTimescale() / 2) + 1;
+ if (currentFragment >= fragments.length) {
+ break;
+ }
+ fragments[currentFragment] = samples++ + 1;
+ time += entry.getDelta();
+ }
+ }
+ long last = samples + 1;
+ // fill all -1 ones.
+ for (int i = fragments.length - 1; i >= 0; i--) {
+ if (fragments[i] == -1) {
+ fragments[i] = last ;
+ }
+ last = fragments[i];
+ }
+ return fragments;
+
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/ByteBufferHelper.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/ByteBufferHelper.java
new file mode 100644
index 0000000..ad21b11
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/ByteBufferHelper.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used to merge adjacent byte buffers.
+ */
+public class ByteBufferHelper {
+ public static List<ByteBuffer> mergeAdjacentBuffers(List<ByteBuffer> samples) {
+ ArrayList<ByteBuffer> nuSamples = new ArrayList<ByteBuffer>(samples.size());
+ for (ByteBuffer buffer : samples) {
+ int lastIndex = nuSamples.size() - 1;
+ if (lastIndex >= 0 && buffer.hasArray() && nuSamples.get(lastIndex).hasArray() && buffer.array() == nuSamples.get(lastIndex).array() &&
+ nuSamples.get(lastIndex).arrayOffset() + nuSamples.get(lastIndex).limit() == buffer.arrayOffset()) {
+ ByteBuffer oldBuffer = nuSamples.remove(lastIndex);
+ ByteBuffer nu = ByteBuffer.wrap(buffer.array(), oldBuffer.arrayOffset(), oldBuffer.limit() + buffer.limit()).slice();
+ // We need to slice here since wrap([], offset, length) just sets position and not the arrayOffset.
+ nuSamples.add(nu);
+ } else if (lastIndex >= 0 &&
+ buffer instanceof MappedByteBuffer && nuSamples.get(lastIndex) instanceof MappedByteBuffer &&
+ nuSamples.get(lastIndex).limit() == nuSamples.get(lastIndex).capacity() - buffer.capacity()) {
+ // This can go wrong - but will it?
+ ByteBuffer oldBuffer = nuSamples.get(lastIndex);
+ oldBuffer.limit(buffer.limit() + oldBuffer.limit());
+ } else {
+ buffer.rewind();
+ nuSamples.add(buffer);
+ }
+ }
+ return nuSamples;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/DefaultMp4Builder.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/DefaultMp4Builder.java
new file mode 100644
index 0000000..9bd1ca6
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/DefaultMp4Builder.java
@@ -0,0 +1,576 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.CompositionTimeToSample;
+import com.coremedia.iso.boxes.ContainerBox;
+import com.coremedia.iso.boxes.DataEntryUrlBox;
+import com.coremedia.iso.boxes.DataInformationBox;
+import com.coremedia.iso.boxes.DataReferenceBox;
+import com.coremedia.iso.boxes.FileTypeBox;
+import com.coremedia.iso.boxes.HandlerBox;
+import com.coremedia.iso.boxes.MediaBox;
+import com.coremedia.iso.boxes.MediaHeaderBox;
+import com.coremedia.iso.boxes.MediaInformationBox;
+import com.coremedia.iso.boxes.MovieBox;
+import com.coremedia.iso.boxes.MovieHeaderBox;
+import com.coremedia.iso.boxes.SampleDependencyTypeBox;
+import com.coremedia.iso.boxes.SampleSizeBox;
+import com.coremedia.iso.boxes.SampleTableBox;
+import com.coremedia.iso.boxes.SampleToChunkBox;
+import com.coremedia.iso.boxes.StaticChunkOffsetBox;
+import com.coremedia.iso.boxes.SyncSampleBox;
+import com.coremedia.iso.boxes.TimeToSampleBox;
+import com.coremedia.iso.boxes.TrackBox;
+import com.coremedia.iso.boxes.TrackHeaderBox;
+import com.googlecode.mp4parser.authoring.DateHelper;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.GatheringByteChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Creates a plain MP4 file from a video. Plain as plain can be.
+ */
+public class DefaultMp4Builder implements Mp4Builder {
+
+ public int STEPSIZE = 64;
+ Set<StaticChunkOffsetBox> chunkOffsetBoxes = new HashSet<StaticChunkOffsetBox>();
+ private static Logger LOG = Logger.getLogger(DefaultMp4Builder.class.getName());
+
+ HashMap<Track, List<ByteBuffer>> track2Sample = new HashMap<Track, List<ByteBuffer>>();
+ HashMap<Track, long[]> track2SampleSizes = new HashMap<Track, long[]>();
+ private FragmentIntersectionFinder intersectionFinder = new TwoSecondIntersectionFinder();
+
+ public void setIntersectionFinder(FragmentIntersectionFinder intersectionFinder) {
+ this.intersectionFinder = intersectionFinder;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public IsoFile build(Movie movie) {
+ LOG.fine("Creating movie " + movie);
+ for (Track track : movie.getTracks()) {
+ // getting the samples may be a time consuming activity
+ List<ByteBuffer> samples = track.getSamples();
+ putSamples(track, samples);
+ long[] sizes = new long[samples.size()];
+ for (int i = 0; i < sizes.length; i++) {
+ sizes[i] = samples.get(i).limit();
+ }
+ putSampleSizes(track, sizes);
+ }
+
+ IsoFile isoFile = new IsoFile();
+ // ouch that is ugly but I don't know how to do it else
+ List<String> minorBrands = new LinkedList<String>();
+ minorBrands.add("isom");
+ minorBrands.add("iso2");
+ minorBrands.add("avc1");
+
+ isoFile.addBox(new FileTypeBox("isom", 0, minorBrands));
+ isoFile.addBox(createMovieBox(movie));
+ InterleaveChunkMdat mdat = new InterleaveChunkMdat(movie);
+ isoFile.addBox(mdat);
+
+ /*
+ dataOffset is where the first sample starts. In this special mdat the samples always start
+ at offset 16 so that we can use the same offset for large boxes and small boxes
+ */
+ long dataOffset = mdat.getDataOffset();
+ for (StaticChunkOffsetBox chunkOffsetBox : chunkOffsetBoxes) {
+ long[] offsets = chunkOffsetBox.getChunkOffsets();
+ for (int i = 0; i < offsets.length; i++) {
+ offsets[i] += dataOffset;
+ }
+ }
+
+
+ return isoFile;
+ }
+
+ public FragmentIntersectionFinder getFragmentIntersectionFinder() {
+ throw new UnsupportedOperationException("No fragment intersection finder in default MP4 builder!");
+ }
+
+ protected long[] putSampleSizes(Track track, long[] sizes) {
+ return track2SampleSizes.put(track, sizes);
+ }
+
+ protected List<ByteBuffer> putSamples(Track track, List<ByteBuffer> samples) {
+ return track2Sample.put(track, samples);
+ }
+
+ private MovieBox createMovieBox(Movie movie) {
+ MovieBox movieBox = new MovieBox();
+ MovieHeaderBox mvhd = new MovieHeaderBox();
+
+ mvhd.setCreationTime(DateHelper.convert(new Date()));
+ mvhd.setModificationTime(DateHelper.convert(new Date()));
+
+ long movieTimeScale = getTimescale(movie);
+ long duration = 0;
+
+ for (Track track : movie.getTracks()) {
+ long tracksDuration = getDuration(track) * movieTimeScale / track.getTrackMetaData().getTimescale();
+ if (tracksDuration > duration) {
+ duration = tracksDuration;
+ }
+
+
+ }
+
+ mvhd.setDuration(duration);
+ mvhd.setTimescale(movieTimeScale);
+ // find the next available trackId
+ long nextTrackId = 0;
+ for (Track track : movie.getTracks()) {
+ nextTrackId = nextTrackId < track.getTrackMetaData().getTrackId() ? track.getTrackMetaData().getTrackId() : nextTrackId;
+ }
+ mvhd.setNextTrackId(++nextTrackId);
+ if (mvhd.getCreationTime() >= 1l << 32 ||
+ mvhd.getModificationTime() >= 1l << 32 ||
+ mvhd.getDuration() >= 1l << 32) {
+ mvhd.setVersion(1);
+ }
+
+ movieBox.addBox(mvhd);
+ for (Track track : movie.getTracks()) {
+ movieBox.addBox(createTrackBox(track, movie));
+ }
+ // metadata here
+ Box udta = createUdta(movie);
+ if (udta != null) {
+ movieBox.addBox(udta);
+ }
+ return movieBox;
+
+ }
+
+ /**
+ * Override to create a user data box that may contain metadata.
+ *
+ * @return a 'udta' box or <code>null</code> if none provided
+ */
+ protected Box createUdta(Movie movie) {
+ return null;
+ }
+
+ private TrackBox createTrackBox(Track track, Movie movie) {
+
+ LOG.info("Creating Mp4TrackImpl " + track);
+ TrackBox trackBox = new TrackBox();
+ TrackHeaderBox tkhd = new TrackHeaderBox();
+ int flags = 0;
+ if (track.isEnabled()) {
+ flags += 1;
+ }
+
+ if (track.isInMovie()) {
+ flags += 2;
+ }
+
+ if (track.isInPreview()) {
+ flags += 4;
+ }
+
+ if (track.isInPoster()) {
+ flags += 8;
+ }
+ tkhd.setFlags(flags);
+
+ tkhd.setAlternateGroup(track.getTrackMetaData().getGroup());
+ tkhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime()));
+ // We need to take edit list box into account in trackheader duration
+ // but as long as I don't support edit list boxes it is sufficient to
+ // just translate media duration to movie timescale
+ tkhd.setDuration(getDuration(track) * getTimescale(movie) / track.getTrackMetaData().getTimescale());
+ tkhd.setHeight(track.getTrackMetaData().getHeight());
+ tkhd.setWidth(track.getTrackMetaData().getWidth());
+ tkhd.setLayer(track.getTrackMetaData().getLayer());
+ tkhd.setModificationTime(DateHelper.convert(new Date()));
+ tkhd.setTrackId(track.getTrackMetaData().getTrackId());
+ tkhd.setVolume(track.getTrackMetaData().getVolume());
+ if (tkhd.getCreationTime() >= 1l << 32 ||
+ tkhd.getModificationTime() >= 1l << 32 ||
+ tkhd.getDuration() >= 1l << 32) {
+ tkhd.setVersion(1);
+ }
+
+ trackBox.addBox(tkhd);
+
+/*
+ EditBox edit = new EditBox();
+ EditListBox editListBox = new EditListBox();
+ editListBox.setEntries(Collections.singletonList(
+ new EditListBox.Entry(editListBox, (long) (track.getTrackMetaData().getStartTime() * getTimescale(movie)), -1, 1)));
+ edit.addBox(editListBox);
+ trackBox.addBox(edit);
+*/
+
+ MediaBox mdia = new MediaBox();
+ trackBox.addBox(mdia);
+ MediaHeaderBox mdhd = new MediaHeaderBox();
+ mdhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime()));
+ mdhd.setDuration(getDuration(track));
+ mdhd.setTimescale(track.getTrackMetaData().getTimescale());
+ mdhd.setLanguage(track.getTrackMetaData().getLanguage());
+ mdia.addBox(mdhd);
+ HandlerBox hdlr = new HandlerBox();
+ mdia.addBox(hdlr);
+
+ hdlr.setHandlerType(track.getHandler());
+
+ MediaInformationBox minf = new MediaInformationBox();
+ minf.addBox(track.getMediaHeaderBox());
+
+ // dinf: all these three boxes tell us is that the actual
+ // data is in the current file and not somewhere external
+ DataInformationBox dinf = new DataInformationBox();
+ DataReferenceBox dref = new DataReferenceBox();
+ dinf.addBox(dref);
+ DataEntryUrlBox url = new DataEntryUrlBox();
+ url.setFlags(1);
+ dref.addBox(url);
+ minf.addBox(dinf);
+ //
+
+ SampleTableBox stbl = new SampleTableBox();
+
+ stbl.addBox(track.getSampleDescriptionBox());
+
+ List<TimeToSampleBox.Entry> decodingTimeToSampleEntries = track.getDecodingTimeEntries();
+ if (decodingTimeToSampleEntries != null && !track.getDecodingTimeEntries().isEmpty()) {
+ TimeToSampleBox stts = new TimeToSampleBox();
+ stts.setEntries(track.getDecodingTimeEntries());
+ stbl.addBox(stts);
+ }
+
+ List<CompositionTimeToSample.Entry> compositionTimeToSampleEntries = track.getCompositionTimeEntries();
+ if (compositionTimeToSampleEntries != null && !compositionTimeToSampleEntries.isEmpty()) {
+ CompositionTimeToSample ctts = new CompositionTimeToSample();
+ ctts.setEntries(compositionTimeToSampleEntries);
+ stbl.addBox(ctts);
+ }
+
+ long[] syncSamples = track.getSyncSamples();
+ if (syncSamples != null && syncSamples.length > 0) {
+ SyncSampleBox stss = new SyncSampleBox();
+ stss.setSampleNumber(syncSamples);
+ stbl.addBox(stss);
+ }
+
+ if (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty()) {
+ SampleDependencyTypeBox sdtp = new SampleDependencyTypeBox();
+ sdtp.setEntries(track.getSampleDependencies());
+ stbl.addBox(sdtp);
+ }
+ HashMap<Track, int[]> track2ChunkSizes = new HashMap<Track, int[]>();
+ for (Track current : movie.getTracks()) {
+ track2ChunkSizes.put(current, getChunkSizes(current, movie));
+ }
+ int[] tracksChunkSizes = track2ChunkSizes.get(track);
+
+ SampleToChunkBox stsc = new SampleToChunkBox();
+ stsc.setEntries(new LinkedList<SampleToChunkBox.Entry>());
+ long lastChunkSize = Integer.MIN_VALUE; // to be sure the first chunks hasn't got the same size
+ for (int i = 0; i < tracksChunkSizes.length; i++) {
+ // The sample description index references the sample description box
+ // that describes the samples of this chunk. My Tracks cannot have more
+ // than one sample description box. Therefore 1 is always right
+ // the first chunk has the number '1'
+ if (lastChunkSize != tracksChunkSizes[i]) {
+ stsc.getEntries().add(new SampleToChunkBox.Entry(i + 1, tracksChunkSizes[i], 1));
+ lastChunkSize = tracksChunkSizes[i];
+ }
+ }
+ stbl.addBox(stsc);
+
+ SampleSizeBox stsz = new SampleSizeBox();
+ stsz.setSampleSizes(track2SampleSizes.get(track));
+
+ stbl.addBox(stsz);
+ // The ChunkOffsetBox we create here is just a stub
+ // since we haven't created the whole structure we can't tell where the
+ // first chunk starts (mdat box). So I just let the chunk offset
+ // start at zero and I will add the mdat offset later.
+ StaticChunkOffsetBox stco = new StaticChunkOffsetBox();
+ this.chunkOffsetBoxes.add(stco);
+ long offset = 0;
+ long[] chunkOffset = new long[tracksChunkSizes.length];
+ // all tracks have the same number of chunks
+ if (LOG.isLoggable(Level.FINE)) {
+ LOG.fine("Calculating chunk offsets for track_" + track.getTrackMetaData().getTrackId());
+ }
+
+
+ for (int i = 0; i < tracksChunkSizes.length; i++) {
+ // The filelayout will be:
+ // chunk_1_track_1,... ,chunk_1_track_n, chunk_2_track_1,... ,chunk_2_track_n, ... , chunk_m_track_1,... ,chunk_m_track_n
+ // calculating the offsets
+ if (LOG.isLoggable(Level.FINER)) {
+ LOG.finer("Calculating chunk offsets for track_" + track.getTrackMetaData().getTrackId() + " chunk " + i);
+ }
+ for (Track current : movie.getTracks()) {
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Adding offsets of track_" + current.getTrackMetaData().getTrackId());
+ }
+ int[] chunkSizes = track2ChunkSizes.get(current);
+ long firstSampleOfChunk = 0;
+ for (int j = 0; j < i; j++) {
+ firstSampleOfChunk += chunkSizes[j];
+ }
+ if (current == track) {
+ chunkOffset[i] = offset;
+ }
+ for (int j = l2i(firstSampleOfChunk); j < firstSampleOfChunk + chunkSizes[i]; j++) {
+ offset += track2SampleSizes.get(current)[j];
+ }
+ }
+ }
+ stco.setChunkOffsets(chunkOffset);
+ stbl.addBox(stco);
+ minf.addBox(stbl);
+ mdia.addBox(minf);
+
+ return trackBox;
+ }
+
+ private class InterleaveChunkMdat implements Box {
+ List<Track> tracks;
+ List<ByteBuffer> samples = new ArrayList<ByteBuffer>();
+ ContainerBox parent;
+
+ long contentSize = 0;
+
+ public ContainerBox getParent() {
+ return parent;
+ }
+
+ public void setParent(ContainerBox parent) {
+ this.parent = parent;
+ }
+
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+ }
+
+ private InterleaveChunkMdat(Movie movie) {
+
+ tracks = movie.getTracks();
+ Map<Track, int[]> chunks = new HashMap<Track, int[]>();
+ for (Track track : movie.getTracks()) {
+ chunks.put(track, getChunkSizes(track, movie));
+ }
+
+ for (int i = 0; i < chunks.values().iterator().next().length; i++) {
+ for (Track track : tracks) {
+
+ int[] chunkSizes = chunks.get(track);
+ long firstSampleOfChunk = 0;
+ for (int j = 0; j < i; j++) {
+ firstSampleOfChunk += chunkSizes[j];
+ }
+
+ for (int j = l2i(firstSampleOfChunk); j < firstSampleOfChunk + chunkSizes[i]; j++) {
+
+ ByteBuffer s = DefaultMp4Builder.this.track2Sample.get(track).get(j);
+ contentSize += s.limit();
+ samples.add((ByteBuffer) s.rewind());
+ }
+
+ }
+
+ }
+
+ }
+
+ public long getDataOffset() {
+ Box b = this;
+ long offset = 16;
+ while (b.getParent() != null) {
+ for (Box box : b.getParent().getBoxes()) {
+ if (b == box) {
+ break;
+ }
+ offset += box.getSize();
+ }
+ b = b.getParent();
+ }
+ return offset;
+ }
+
+
+ public String getType() {
+ return "mdat";
+ }
+
+ public long getSize() {
+ return 16 + contentSize;
+ }
+
+ private boolean isSmallBox(long contentSize) {
+ return (contentSize + 8) < 4294967296L;
+ }
+
+
+ public void getBox(WritableByteChannel writableByteChannel) throws IOException {
+ ByteBuffer bb = ByteBuffer.allocate(16);
+ long size = getSize();
+ if (isSmallBox(size)) {
+ IsoTypeWriter.writeUInt32(bb, size);
+ } else {
+ IsoTypeWriter.writeUInt32(bb, 1);
+ }
+ bb.put(IsoFile.fourCCtoBytes("mdat"));
+ if (isSmallBox(size)) {
+ bb.put(new byte[8]);
+ } else {
+ IsoTypeWriter.writeUInt64(bb, size);
+ }
+ bb.rewind();
+ writableByteChannel.write(bb);
+ if (writableByteChannel instanceof GatheringByteChannel) {
+ List<ByteBuffer> nuSamples = unifyAdjacentBuffers(samples);
+
+
+ for (int i = 0; i < Math.ceil((double) nuSamples.size() / STEPSIZE); i++) {
+ List<ByteBuffer> sublist = nuSamples.subList(
+ i * STEPSIZE, // start
+ (i + 1) * STEPSIZE < nuSamples.size() ? (i + 1) * STEPSIZE : nuSamples.size()); // end
+ ByteBuffer sampleArray[] = sublist.toArray(new ByteBuffer[sublist.size()]);
+ do {
+ ((GatheringByteChannel) writableByteChannel).write(sampleArray);
+ } while (sampleArray[sampleArray.length - 1].remaining() > 0);
+ }
+ //System.err.println(bytesWritten);
+ } else {
+ for (ByteBuffer sample : samples) {
+ sample.rewind();
+ writableByteChannel.write(sample);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Gets the chunk sizes for the given track.
+ *
+ * @param track
+ * @param movie
+ * @return
+ */
+ int[] getChunkSizes(Track track, Movie movie) {
+
+ long[] referenceChunkStarts = intersectionFinder.sampleNumbers(track, movie);
+ int[] chunkSizes = new int[referenceChunkStarts.length];
+
+
+ for (int i = 0; i < referenceChunkStarts.length; i++) {
+ long start = referenceChunkStarts[i] - 1;
+ long end;
+ if (referenceChunkStarts.length == i + 1) {
+ end = track.getSamples().size();
+ } else {
+ end = referenceChunkStarts[i + 1] - 1;
+ }
+
+ chunkSizes[i] = l2i(end - start);
+ // The Stretch makes sure that there are as much audio and video chunks!
+ }
+ assert DefaultMp4Builder.this.track2Sample.get(track).size() == sum(chunkSizes) : "The number of samples and the sum of all chunk lengths must be equal";
+ return chunkSizes;
+
+
+ }
+
+
+ private static long sum(int[] ls) {
+ long rc = 0;
+ for (long l : ls) {
+ rc += l;
+ }
+ return rc;
+ }
+
+ protected static long getDuration(Track track) {
+ long duration = 0;
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ duration += entry.getCount() * entry.getDelta();
+ }
+ return duration;
+ }
+
+ public long getTimescale(Movie movie) {
+ long timescale = movie.getTracks().iterator().next().getTrackMetaData().getTimescale();
+ for (Track track : movie.getTracks()) {
+ timescale = gcd(track.getTrackMetaData().getTimescale(), timescale);
+ }
+ return timescale;
+ }
+
+ public static long gcd(long a, long b) {
+ if (b == 0) {
+ return a;
+ }
+ return gcd(b, a % b);
+ }
+
+ public List<ByteBuffer> unifyAdjacentBuffers(List<ByteBuffer> samples) {
+ ArrayList<ByteBuffer> nuSamples = new ArrayList<ByteBuffer>(samples.size());
+ for (ByteBuffer buffer : samples) {
+ int lastIndex = nuSamples.size() - 1;
+ if (lastIndex >= 0 && buffer.hasArray() && nuSamples.get(lastIndex).hasArray() && buffer.array() == nuSamples.get(lastIndex).array() &&
+ nuSamples.get(lastIndex).arrayOffset() + nuSamples.get(lastIndex).limit() == buffer.arrayOffset()) {
+ ByteBuffer oldBuffer = nuSamples.remove(lastIndex);
+ ByteBuffer nu = ByteBuffer.wrap(buffer.array(), oldBuffer.arrayOffset(), oldBuffer.limit() + buffer.limit()).slice();
+ // We need to slice here since wrap([], offset, length) just sets position and not the arrayOffset.
+ nuSamples.add(nu);
+ } else if (lastIndex >= 0 &&
+ buffer instanceof MappedByteBuffer && nuSamples.get(lastIndex) instanceof MappedByteBuffer &&
+ nuSamples.get(lastIndex).limit() == nuSamples.get(lastIndex).capacity() - buffer.capacity()) {
+ // This can go wrong - but will it?
+ ByteBuffer oldBuffer = nuSamples.get(lastIndex);
+ oldBuffer.limit(buffer.limit() + oldBuffer.limit());
+ } else {
+ nuSamples.add(buffer);
+ }
+ }
+ return nuSamples;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentIntersectionFinder.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentIntersectionFinder.java
new file mode 100644
index 0000000..1224bbf
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentIntersectionFinder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+/**
+ *
+ */
+public interface FragmentIntersectionFinder {
+ /**
+ * Gets the ordinal number of the samples which will be the first sample
+ * in each fragment.
+ *
+ * @param track concerned track
+ * @param movie the context of the track
+ * @return an array containing the ordinal of each fragment's first sample
+ */
+ public long[] sampleNumbers(Track track, Movie movie);
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentedMp4Builder.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentedMp4Builder.java
new file mode 100644
index 0000000..c65ff1c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentedMp4Builder.java
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.fragment.*;
+import com.googlecode.mp4parser.authoring.DateHelper;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.GatheringByteChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.*;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Creates a fragmented MP4 file.
+ */
+public class FragmentedMp4Builder implements Mp4Builder {
+ private static final Logger LOG = Logger.getLogger(FragmentedMp4Builder.class.getName());
+
+ protected FragmentIntersectionFinder intersectionFinder;
+
+ public FragmentedMp4Builder() {
+ this.intersectionFinder = new SyncSampleIntersectFinderImpl();
+ }
+
+ public List<String> getAllowedHandlers() {
+ return Arrays.asList("soun", "vide");
+ }
+
+ public Box createFtyp(Movie movie) {
+ List<String> minorBrands = new LinkedList<String>();
+ minorBrands.add("isom");
+ minorBrands.add("iso2");
+ minorBrands.add("avc1");
+ return new FileTypeBox("isom", 0, minorBrands);
+ }
+
+ /**
+ * Some formats require sorting of the fragments. E.g. Ultraviolet CFF files are required
+ * to contain the fragments size sort:
+ * <ul>
+ * <li>video[1].getBytes().length < audio[1].getBytes().length < subs[1].getBytes().length</li>
+ * <li> audio[2].getBytes().length < video[2].getBytes().length < subs[2].getBytes().length</li>
+ * </ul>
+ *
+ * make this fragment:
+ *
+ * <ol>
+ * <li>video[1]</li>
+ * <li>audio[1]</li>
+ * <li>subs[1]</li>
+ * <li>audio[2]</li>
+ * <li>video[2]</li>
+ * <li>subs[2]</li>
+ * </ol>
+ *
+ * @param tracks the list of tracks to returned sorted
+ * @param cycle current fragment (sorting may vary between the fragments)
+ * @param intersectionMap a map from tracks to their fragments' first samples.
+ * @return the list of tracks in order of appearance in the fragment
+ */
+ protected List<Track> sortTracksInSequence(List<Track> tracks, final int cycle, final Map<Track, long[]> intersectionMap) {
+ tracks = new LinkedList<Track>(tracks);
+ Collections.sort(tracks, new Comparator<Track>() {
+ public int compare(Track o1, Track o2) {
+ long[] startSamples1 = intersectionMap.get(o1);
+ long startSample1 = startSamples1[cycle];
+ // one based sample numbers - the first sample is 1
+ long endSample1 = cycle + 1 < startSamples1.length ? startSamples1[cycle + 1] : o1.getSamples().size() + 1;
+ long[] startSamples2 = intersectionMap.get(o2);
+ long startSample2 = startSamples2[cycle];
+ // one based sample numbers - the first sample is 1
+ long endSample2 = cycle + 1 < startSamples2.length ? startSamples2[cycle + 1] : o2.getSamples().size() + 1;
+ List<ByteBuffer> samples1 = o1.getSamples().subList(l2i(startSample1) - 1, l2i(endSample1) - 1);
+ List<ByteBuffer> samples2 = o2.getSamples().subList(l2i(startSample2) - 1, l2i(endSample2) - 1);
+ int size1 = 0;
+ for (ByteBuffer byteBuffer : samples1) {
+ size1 += byteBuffer.limit();
+ }
+ int size2 = 0;
+ for (ByteBuffer byteBuffer : samples2) {
+ size2 += byteBuffer.limit();
+ }
+ return size1 - size2;
+ }
+ });
+ return tracks;
+ }
+
+ protected List<Box> createMoofMdat(final Movie movie) {
+ List<Box> boxes = new LinkedList<Box>();
+ HashMap<Track, long[]> intersectionMap = new HashMap<Track, long[]>();
+ int maxNumberOfFragments = 0;
+ for (Track track : movie.getTracks()) {
+ long[] intersects = intersectionFinder.sampleNumbers(track, movie);
+ intersectionMap.put(track, intersects);
+ maxNumberOfFragments = Math.max(maxNumberOfFragments, intersects.length);
+ }
+
+
+ int sequence = 1;
+ // this loop has two indices:
+
+ for (int cycle = 0; cycle < maxNumberOfFragments; cycle++) {
+
+ final List<Track> sortedTracks = sortTracksInSequence(movie.getTracks(), cycle, intersectionMap);
+
+ for (Track track : sortedTracks) {
+ if (getAllowedHandlers().isEmpty() || getAllowedHandlers().contains(track.getHandler())) {
+ long[] startSamples = intersectionMap.get(track);
+ //some tracks may have less fragments -> skip them
+ if (cycle < startSamples.length) {
+
+ long startSample = startSamples[cycle];
+ // one based sample numbers - the first sample is 1
+ long endSample = cycle + 1 < startSamples.length ? startSamples[cycle + 1] : track.getSamples().size() + 1;
+
+ // if startSample == endSample the cycle is empty!
+ if (startSample != endSample) {
+ boxes.add(createMoof(startSample, endSample, track, sequence));
+ boxes.add(createMdat(startSample, endSample, track, sequence++));
+ }
+ }
+ }
+ }
+ }
+ return boxes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public IsoFile build(Movie movie) {
+ LOG.fine("Creating movie " + movie);
+ IsoFile isoFile = new IsoFile();
+
+
+ isoFile.addBox(createFtyp(movie));
+ isoFile.addBox(createMoov(movie));
+
+ for (Box box : createMoofMdat(movie)) {
+ isoFile.addBox(box);
+ }
+ isoFile.addBox(createMfra(movie, isoFile));
+
+ return isoFile;
+ }
+
+ protected Box createMdat(final long startSample, final long endSample, final Track track, final int i) {
+
+ class Mdat implements Box {
+ ContainerBox parent;
+
+ public ContainerBox getParent() {
+ return parent;
+ }
+
+ public void setParent(ContainerBox parent) {
+ this.parent = parent;
+ }
+
+ public long getSize() {
+ long size = 8; // I don't expect 2gig fragments
+ for (ByteBuffer sample : getSamples(startSample, endSample, track, i)) {
+ size += sample.limit();
+ }
+ return size;
+ }
+
+ public String getType() {
+ return "mdat";
+ }
+
+ public void getBox(WritableByteChannel writableByteChannel) throws IOException {
+ List<ByteBuffer> bbs = getSamples(startSample, endSample, track, i);
+ final List<ByteBuffer> samples = ByteBufferHelper.mergeAdjacentBuffers(bbs);
+ ByteBuffer header = ByteBuffer.allocate(8);
+ IsoTypeWriter.writeUInt32(header, l2i(getSize()));
+ header.put(IsoFile.fourCCtoBytes(getType()));
+ header.rewind();
+ writableByteChannel.write(header);
+ if (writableByteChannel instanceof GatheringByteChannel) {
+
+ int STEPSIZE = 1024;
+ // This is required to prevent android from crashing
+ // it seems that {@link GatheringByteChannel#write(java.nio.ByteBuffer[])}
+ // just handles up to 1024 buffers
+ for (int i = 0; i < Math.ceil((double) samples.size() / STEPSIZE); i++) {
+ List<ByteBuffer> sublist = samples.subList(
+ i * STEPSIZE, // start
+ (i + 1) * STEPSIZE < samples.size() ? (i + 1) * STEPSIZE : samples.size()); // end
+ ByteBuffer sampleArray[] = sublist.toArray(new ByteBuffer[sublist.size()]);
+ do {
+ ((GatheringByteChannel) writableByteChannel).write(sampleArray);
+ } while (sampleArray[sampleArray.length - 1].remaining() > 0);
+ }
+ //System.err.println(bytesWritten);
+ } else {
+ for (ByteBuffer sample : samples) {
+ sample.rewind();
+ writableByteChannel.write(sample);
+ }
+ }
+
+ }
+
+ public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
+
+ }
+ }
+
+ return new Mdat();
+ }
+
+ protected Box createTfhd(long startSample, long endSample, Track track, int sequenceNumber) {
+ TrackFragmentHeaderBox tfhd = new TrackFragmentHeaderBox();
+ SampleFlags sf = new SampleFlags();
+
+ tfhd.setDefaultSampleFlags(sf);
+ tfhd.setBaseDataOffset(-1);
+ tfhd.setTrackId(track.getTrackMetaData().getTrackId());
+ return tfhd;
+ }
+
+ protected Box createMfhd(long startSample, long endSample, Track track, int sequenceNumber) {
+ MovieFragmentHeaderBox mfhd = new MovieFragmentHeaderBox();
+ mfhd.setSequenceNumber(sequenceNumber);
+ return mfhd;
+ }
+
+ protected Box createTraf(long startSample, long endSample, Track track, int sequenceNumber) {
+ TrackFragmentBox traf = new TrackFragmentBox();
+ traf.addBox(createTfhd(startSample, endSample, track, sequenceNumber));
+ for (Box trun : createTruns(startSample, endSample, track, sequenceNumber)) {
+ traf.addBox(trun);
+ }
+
+ return traf;
+ }
+
+
+ /**
+ * Gets the all samples starting with <code>startSample</code> (one based -> one is the first) and
+ * ending with <code>endSample</code> (exclusive).
+ *
+ * @param startSample low endpoint (inclusive) of the sample sequence
+ * @param endSample high endpoint (exclusive) of the sample sequence
+ * @param track source of the samples
+ * @param sequenceNumber the fragment index of the requested list of samples
+ * @return a <code>List&lt;ByteBuffer></code> of raw samples
+ */
+ protected List<ByteBuffer> getSamples(long startSample, long endSample, Track track, int sequenceNumber) {
+ // since startSample and endSample are one-based substract 1 before addressing list elements
+ return track.getSamples().subList(l2i(startSample) - 1, l2i(endSample) - 1);
+ }
+
+ /**
+ * Gets the sizes of a sequence of samples-
+ *
+ * @param startSample low endpoint (inclusive) of the sample sequence
+ * @param endSample high endpoint (exclusive) of the sample sequence
+ * @param track source of the samples
+ * @param sequenceNumber the fragment index of the requested list of samples
+ * @return
+ */
+ protected long[] getSampleSizes(long startSample, long endSample, Track track, int sequenceNumber) {
+ List<ByteBuffer> samples = getSamples(startSample, endSample, track, sequenceNumber);
+
+ long[] sampleSizes = new long[samples.size()];
+ for (int i = 0; i < sampleSizes.length; i++) {
+ sampleSizes[i] = samples.get(i).limit();
+ }
+ return sampleSizes;
+ }
+
+ /**
+ * Creates one or more track run boxes for a given sequence.
+ *
+ * @param startSample low endpoint (inclusive) of the sample sequence
+ * @param endSample high endpoint (exclusive) of the sample sequence
+ * @param track source of the samples
+ * @param sequenceNumber the fragment index of the requested list of samples
+ * @return the list of TrackRun boxes.
+ */
+ protected List<? extends Box> createTruns(long startSample, long endSample, Track track, int sequenceNumber) {
+ TrackRunBox trun = new TrackRunBox();
+ long[] sampleSizes = getSampleSizes(startSample, endSample, track, sequenceNumber);
+
+ trun.setSampleDurationPresent(true);
+ trun.setSampleSizePresent(true);
+ List<TrackRunBox.Entry> entries = new ArrayList<TrackRunBox.Entry>(l2i(endSample - startSample));
+
+
+ Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries());
+ long left = startSample - 1;
+ long curEntryLeft = timeQueue.peek().getCount();
+ while (left > curEntryLeft) {
+ left -= curEntryLeft;
+ timeQueue.remove();
+ curEntryLeft = timeQueue.peek().getCount();
+ }
+ curEntryLeft -= left;
+
+
+ Queue<CompositionTimeToSample.Entry> compositionTimeQueue =
+ track.getCompositionTimeEntries() != null && track.getCompositionTimeEntries().size() > 0 ?
+ new LinkedList<CompositionTimeToSample.Entry>(track.getCompositionTimeEntries()) : null;
+ long compositionTimeEntriesLeft = compositionTimeQueue != null ? compositionTimeQueue.peek().getCount() : -1;
+
+
+ trun.setSampleCompositionTimeOffsetPresent(compositionTimeEntriesLeft > 0);
+
+ // fast forward composition stuff
+ for (long i = 1; i < startSample; i++) {
+ if (compositionTimeQueue != null) {
+ //trun.setSampleCompositionTimeOffsetPresent(true);
+ if (--compositionTimeEntriesLeft == 0 && compositionTimeQueue.size() > 1) {
+ compositionTimeQueue.remove();
+ compositionTimeEntriesLeft = compositionTimeQueue.element().getCount();
+ }
+ }
+ }
+
+ boolean sampleFlagsRequired = (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty() ||
+ track.getSyncSamples() != null && track.getSyncSamples().length != 0);
+
+ trun.setSampleFlagsPresent(sampleFlagsRequired);
+
+ for (int i = 0; i < sampleSizes.length; i++) {
+ TrackRunBox.Entry entry = new TrackRunBox.Entry();
+ entry.setSampleSize(sampleSizes[i]);
+ if (sampleFlagsRequired) {
+ //if (false) {
+ SampleFlags sflags = new SampleFlags();
+
+ if (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty()) {
+ SampleDependencyTypeBox.Entry e = track.getSampleDependencies().get(i);
+ sflags.setSampleDependsOn(e.getSampleDependsOn());
+ sflags.setSampleIsDependedOn(e.getSampleIsDependentOn());
+ sflags.setSampleHasRedundancy(e.getSampleHasRedundancy());
+ }
+ if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
+ // we have to mark non-sync samples!
+ if (Arrays.binarySearch(track.getSyncSamples(), startSample + i) >= 0) {
+ sflags.setSampleIsDifferenceSample(false);
+ sflags.setSampleDependsOn(2);
+ } else {
+ sflags.setSampleIsDifferenceSample(true);
+ sflags.setSampleDependsOn(1);
+ }
+ }
+ // i don't have sample degradation
+ entry.setSampleFlags(sflags);
+
+ }
+
+ entry.setSampleDuration(timeQueue.peek().getDelta());
+ if (--curEntryLeft == 0 && timeQueue.size() > 1) {
+ timeQueue.remove();
+ curEntryLeft = timeQueue.peek().getCount();
+ }
+
+ if (compositionTimeQueue != null) {
+ entry.setSampleCompositionTimeOffset(compositionTimeQueue.peek().getOffset());
+ if (--compositionTimeEntriesLeft == 0 && compositionTimeQueue.size() > 1) {
+ compositionTimeQueue.remove();
+ compositionTimeEntriesLeft = compositionTimeQueue.element().getCount();
+ }
+ }
+ entries.add(entry);
+ }
+
+ trun.setEntries(entries);
+
+ return Collections.singletonList(trun);
+ }
+
+ /**
+ * Creates a 'moof' box for a given sequence of samples.
+ *
+ * @param startSample low endpoint (inclusive) of the sample sequence
+ * @param endSample high endpoint (exclusive) of the sample sequence
+ * @param track source of the samples
+ * @param sequenceNumber the fragment index of the requested list of samples
+ * @return the list of TrackRun boxes.
+ */
+ protected Box createMoof(long startSample, long endSample, Track track, int sequenceNumber) {
+ MovieFragmentBox moof = new MovieFragmentBox();
+ moof.addBox(createMfhd(startSample, endSample, track, sequenceNumber));
+ moof.addBox(createTraf(startSample, endSample, track, sequenceNumber));
+
+ TrackRunBox firstTrun = moof.getTrackRunBoxes().get(0);
+ firstTrun.setDataOffset(1); // dummy to make size correct
+ firstTrun.setDataOffset((int) (8 + moof.getSize())); // mdat header + moof size
+
+ return moof;
+ }
+
+ /**
+ * Creates a single 'mvhd' movie header box for a given movie.
+ *
+ * @param movie the concerned movie
+ * @return an 'mvhd' box
+ */
+ protected Box createMvhd(Movie movie) {
+ MovieHeaderBox mvhd = new MovieHeaderBox();
+ mvhd.setVersion(1);
+ mvhd.setCreationTime(DateHelper.convert(new Date()));
+ mvhd.setModificationTime(DateHelper.convert(new Date()));
+ long movieTimeScale = movie.getTimescale();
+ long duration = 0;
+
+ for (Track track : movie.getTracks()) {
+ long tracksDuration = getDuration(track) * movieTimeScale / track.getTrackMetaData().getTimescale();
+ if (tracksDuration > duration) {
+ duration = tracksDuration;
+ }
+
+
+ }
+
+ mvhd.setDuration(duration);
+ mvhd.setTimescale(movieTimeScale);
+ // find the next available trackId
+ long nextTrackId = 0;
+ for (Track track : movie.getTracks()) {
+ nextTrackId = nextTrackId < track.getTrackMetaData().getTrackId() ? track.getTrackMetaData().getTrackId() : nextTrackId;
+ }
+ mvhd.setNextTrackId(++nextTrackId);
+ return mvhd;
+ }
+
+ /**
+ * Creates a fully populated 'moov' box with all child boxes. Child boxes are:
+ * <ul>
+ * <li>{@link #createMvhd(com.googlecode.mp4parser.authoring.Movie) mvhd}</li>
+ * <li>{@link #createMvex(com.googlecode.mp4parser.authoring.Movie) mvex}</li>
+ * <li>a {@link #createTrak(com.googlecode.mp4parser.authoring.Track, com.googlecode.mp4parser.authoring.Movie) trak} for every track</li>
+ * </ul>
+ *
+ * @param movie the concerned movie
+ * @return fully populated 'moov'
+ */
+ protected Box createMoov(Movie movie) {
+ MovieBox movieBox = new MovieBox();
+
+ movieBox.addBox(createMvhd(movie));
+ movieBox.addBox(createMvex(movie));
+
+ for (Track track : movie.getTracks()) {
+ movieBox.addBox(createTrak(track, movie));
+ }
+ // metadata here
+ return movieBox;
+
+ }
+
+ /**
+ * Creates a 'tfra' - track fragment random access box for the given track with the isoFile.
+ * The tfra contains a map of random access points with time as key and offset within the isofile
+ * as value.
+ *
+ * @param track the concerned track
+ * @param isoFile the track is contained in
+ * @return a track fragment random access box.
+ */
+ protected Box createTfra(Track track, IsoFile isoFile) {
+ TrackFragmentRandomAccessBox tfra = new TrackFragmentRandomAccessBox();
+ tfra.setVersion(1); // use long offsets and times
+ List<TrackFragmentRandomAccessBox.Entry> offset2timeEntries = new LinkedList<TrackFragmentRandomAccessBox.Entry>();
+ List<Box> boxes = isoFile.getBoxes();
+ long offset = 0;
+ long duration = 0;
+ for (Box box : boxes) {
+ if (box instanceof MovieFragmentBox) {
+ List<TrackFragmentBox> trafs = ((MovieFragmentBox) box).getBoxes(TrackFragmentBox.class);
+ for (int i = 0; i < trafs.size(); i++) {
+ TrackFragmentBox traf = trafs.get(i);
+ if (traf.getTrackFragmentHeaderBox().getTrackId() == track.getTrackMetaData().getTrackId()) {
+ // here we are at the offset required for the current entry.
+ List<TrackRunBox> truns = traf.getBoxes(TrackRunBox.class);
+ for (int j = 0; j < truns.size(); j++) {
+ List<TrackFragmentRandomAccessBox.Entry> offset2timeEntriesThisTrun = new LinkedList<TrackFragmentRandomAccessBox.Entry>();
+ TrackRunBox trun = truns.get(j);
+ for (int k = 0; k < trun.getEntries().size(); k++) {
+ TrackRunBox.Entry trunEntry = trun.getEntries().get(k);
+ SampleFlags sf = null;
+ if (k == 0 && trun.isFirstSampleFlagsPresent()) {
+ sf = trun.getFirstSampleFlags();
+ } else if (trun.isSampleFlagsPresent()) {
+ sf = trunEntry.getSampleFlags();
+ } else {
+ List<MovieExtendsBox> mvexs = isoFile.getMovieBox().getBoxes(MovieExtendsBox.class);
+ for (MovieExtendsBox mvex : mvexs) {
+ List<TrackExtendsBox> trexs = mvex.getBoxes(TrackExtendsBox.class);
+ for (TrackExtendsBox trex : trexs) {
+ if (trex.getTrackId() == track.getTrackMetaData().getTrackId()) {
+ sf = trex.getDefaultSampleFlags();
+ }
+ }
+ }
+
+ }
+ if (sf == null) {
+ throw new RuntimeException("Could not find any SampleFlags to indicate random access or not");
+ }
+ if (sf.getSampleDependsOn() == 2) {
+ offset2timeEntriesThisTrun.add(new TrackFragmentRandomAccessBox.Entry(
+ duration,
+ offset,
+ i + 1, j + 1, k + 1));
+ }
+ duration += trunEntry.getSampleDuration();
+ }
+ if (offset2timeEntriesThisTrun.size() == trun.getEntries().size() && trun.getEntries().size() > 0) {
+ // Oooops every sample seems to be random access sample
+ // is this an audio track? I don't care.
+ // I just use the first for trun sample for tfra random access
+ offset2timeEntries.add(offset2timeEntriesThisTrun.get(0));
+ } else {
+ offset2timeEntries.addAll(offset2timeEntriesThisTrun);
+ }
+ }
+ }
+ }
+ }
+
+
+ offset += box.getSize();
+ }
+ tfra.setEntries(offset2timeEntries);
+ tfra.setTrackId(track.getTrackMetaData().getTrackId());
+ return tfra;
+ }
+
+ /**
+ * Creates a 'mfra' - movie fragment random access box for the given movie in the given
+ * isofile. Uses {@link #createTfra(com.googlecode.mp4parser.authoring.Track, com.coremedia.iso.IsoFile)}
+ * to generate the child boxes.
+ *
+ * @param movie concerned movie
+ * @param isoFile concerned isofile
+ * @return a complete 'mfra' box
+ */
+ protected Box createMfra(Movie movie, IsoFile isoFile) {
+ MovieFragmentRandomAccessBox mfra = new MovieFragmentRandomAccessBox();
+ for (Track track : movie.getTracks()) {
+ mfra.addBox(createTfra(track, isoFile));
+ }
+
+ MovieFragmentRandomAccessOffsetBox mfro = new MovieFragmentRandomAccessOffsetBox();
+ mfra.addBox(mfro);
+ mfro.setMfraSize(mfra.getSize());
+ return mfra;
+ }
+
+ protected Box createTrex(Movie movie, Track track) {
+ TrackExtendsBox trex = new TrackExtendsBox();
+ trex.setTrackId(track.getTrackMetaData().getTrackId());
+ trex.setDefaultSampleDescriptionIndex(1);
+ trex.setDefaultSampleDuration(0);
+ trex.setDefaultSampleSize(0);
+ SampleFlags sf = new SampleFlags();
+ if ("soun".equals(track.getHandler())) {
+ // as far as I know there is no audio encoding
+ // where the sample are not self contained.
+ sf.setSampleDependsOn(2);
+ sf.setSampleIsDependedOn(2);
+ }
+ trex.setDefaultSampleFlags(sf);
+ return trex;
+ }
+
+ /**
+ * Creates a 'mvex' - movie extends box and populates it with 'trex' boxes
+ * by calling {@link #createTrex(com.googlecode.mp4parser.authoring.Movie, com.googlecode.mp4parser.authoring.Track)}
+ * for each track to generate them
+ *
+ * @param movie the source movie
+ * @return a complete 'mvex'
+ */
+ protected Box createMvex(Movie movie) {
+ MovieExtendsBox mvex = new MovieExtendsBox();
+ final MovieExtendsHeaderBox mved = new MovieExtendsHeaderBox();
+ for (Track track : movie.getTracks()) {
+ final long trackDuration = getTrackDuration(movie, track);
+ if (mved.getFragmentDuration() < trackDuration) {
+ mved.setFragmentDuration(trackDuration);
+ }
+ }
+ mvex.addBox(mved);
+
+ for (Track track : movie.getTracks()) {
+ mvex.addBox(createTrex(movie, track));
+ }
+ return mvex;
+ }
+
+ protected Box createTkhd(Movie movie, Track track) {
+ TrackHeaderBox tkhd = new TrackHeaderBox();
+ tkhd.setVersion(1);
+ int flags = 0;
+ if (track.isEnabled()) {
+ flags += 1;
+ }
+
+ if (track.isInMovie()) {
+ flags += 2;
+ }
+
+ if (track.isInPreview()) {
+ flags += 4;
+ }
+
+ if (track.isInPoster()) {
+ flags += 8;
+ }
+ tkhd.setFlags(flags);
+
+ tkhd.setAlternateGroup(track.getTrackMetaData().getGroup());
+ tkhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime()));
+ // We need to take edit list box into account in trackheader duration
+ // but as long as I don't support edit list boxes it is sufficient to
+ // just translate media duration to movie timescale
+ tkhd.setDuration(getTrackDuration(movie, track));
+ tkhd.setHeight(track.getTrackMetaData().getHeight());
+ tkhd.setWidth(track.getTrackMetaData().getWidth());
+ tkhd.setLayer(track.getTrackMetaData().getLayer());
+ tkhd.setModificationTime(DateHelper.convert(new Date()));
+ tkhd.setTrackId(track.getTrackMetaData().getTrackId());
+ tkhd.setVolume(track.getTrackMetaData().getVolume());
+ return tkhd;
+ }
+
+ private long getTrackDuration(Movie movie, Track track) {
+ return getDuration(track) * movie.getTimescale() / track.getTrackMetaData().getTimescale();
+ }
+
+ protected Box createMdhd(Movie movie, Track track) {
+ MediaHeaderBox mdhd = new MediaHeaderBox();
+ mdhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime()));
+ mdhd.setDuration(getDuration(track));
+ mdhd.setTimescale(track.getTrackMetaData().getTimescale());
+ mdhd.setLanguage(track.getTrackMetaData().getLanguage());
+ return mdhd;
+ }
+
+ protected Box createStbl(Movie movie, Track track) {
+ SampleTableBox stbl = new SampleTableBox();
+
+ stbl.addBox(track.getSampleDescriptionBox());
+ stbl.addBox(new TimeToSampleBox());
+ //stbl.addBox(new SampleToChunkBox());
+ stbl.addBox(new StaticChunkOffsetBox());
+ return stbl;
+ }
+
+ protected Box createMinf(Track track, Movie movie) {
+ MediaInformationBox minf = new MediaInformationBox();
+ minf.addBox(track.getMediaHeaderBox());
+ minf.addBox(createDinf(movie, track));
+ minf.addBox(createStbl(movie, track));
+ return minf;
+ }
+
+ protected Box createMdiaHdlr(Track track, Movie movie) {
+ HandlerBox hdlr = new HandlerBox();
+ hdlr.setHandlerType(track.getHandler());
+ return hdlr;
+ }
+
+ protected Box createMdia(Track track, Movie movie) {
+ MediaBox mdia = new MediaBox();
+ mdia.addBox(createMdhd(movie, track));
+
+
+ mdia.addBox(createMdiaHdlr(track, movie));
+
+
+ mdia.addBox(createMinf(track, movie));
+ return mdia;
+ }
+
+ protected Box createTrak(Track track, Movie movie) {
+ LOG.fine("Creating Track " + track);
+ TrackBox trackBox = new TrackBox();
+ trackBox.addBox(createTkhd(movie, track));
+ trackBox.addBox(createMdia(track, movie));
+ return trackBox;
+ }
+
+ protected DataInformationBox createDinf(Movie movie, Track track) {
+ DataInformationBox dinf = new DataInformationBox();
+ DataReferenceBox dref = new DataReferenceBox();
+ dinf.addBox(dref);
+ DataEntryUrlBox url = new DataEntryUrlBox();
+ url.setFlags(1);
+ dref.addBox(url);
+ return dinf;
+ }
+
+ public FragmentIntersectionFinder getFragmentIntersectionFinder() {
+ return intersectionFinder;
+ }
+
+ public void setIntersectionFinder(FragmentIntersectionFinder intersectionFinder) {
+ this.intersectionFinder = intersectionFinder;
+ }
+
+ protected long getDuration(Track track) {
+ long duration = 0;
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ duration += entry.getCount() * entry.getDelta();
+ }
+ return duration;
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/Mp4Builder.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/Mp4Builder.java
new file mode 100644
index 0000000..725745e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/Mp4Builder.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.IsoFile;
+import com.googlecode.mp4parser.authoring.Movie;
+
+/**
+ * Transforms a <code>Movie</code> object to an IsoFile. Implementations can
+ * determine the specific format: Fragmented MP4, MP4, MP4 with Apple Metadata,
+ * MP4 with 3GPP Metadata, MOV.
+ */
+public interface Mp4Builder {
+ /**
+ * Builds the actual IsoFile from the Movie.
+ *
+ * @param movie data source
+ * @return the freshly built IsoFile
+ */
+ public IsoFile build(Movie movie);
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/SyncSampleIntersectFinderImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/SyncSampleIntersectFinderImpl.java
new file mode 100644
index 0000000..2766c5e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/SyncSampleIntersectFinderImpl.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.boxes.TimeToSampleBox;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import static com.googlecode.mp4parser.util.Math.lcm;
+
+/**
+ * This <code>FragmentIntersectionFinder</code> cuts the input movie video tracks in
+ * fragments of the same length exactly before the sync samples. Audio tracks are cut
+ * into pieces of similar length.
+ */
+public class SyncSampleIntersectFinderImpl implements FragmentIntersectionFinder {
+
+ private static Logger LOG = Logger.getLogger(SyncSampleIntersectFinderImpl.class.getName());
+ private static Map<CacheTuple, long[]> getTimesCache = new ConcurrentHashMap<CacheTuple, long[]>();
+ private static Map<CacheTuple, long[]> getSampleNumbersCache = new ConcurrentHashMap<CacheTuple, long[]>();
+
+ private final int minFragmentDurationSeconds;
+
+ public SyncSampleIntersectFinderImpl() {
+ minFragmentDurationSeconds = 0;
+ }
+
+ /**
+ * Creates a <code>SyncSampleIntersectFinderImpl</code> that will not create any fragment
+ * smaller than the given <code>minFragmentDurationSeconds</code>
+ *
+ * @param minFragmentDurationSeconds the smallest allowable duration of a fragment.
+ */
+ public SyncSampleIntersectFinderImpl(int minFragmentDurationSeconds) {
+ this.minFragmentDurationSeconds = minFragmentDurationSeconds;
+ }
+
+ /**
+ * Gets an array of sample numbers that are meant to be the first sample of each
+ * chunk or fragment.
+ *
+ * @param track concerned track
+ * @param movie the context of the track
+ * @return an array containing the ordinal of each fragment's first sample
+ */
+ public long[] sampleNumbers(Track track, Movie movie) {
+ final CacheTuple key = new CacheTuple(track, movie);
+ final long[] result = getSampleNumbersCache.get(key);
+ if (result != null) {
+ return result;
+ }
+
+ if ("vide".equals(track.getHandler())) {
+ if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
+ List<long[]> times = getSyncSamplesTimestamps(movie, track);
+ final long[] commonIndices = getCommonIndices(track.getSyncSamples(), getTimes(track, movie), track.getTrackMetaData().getTimescale(), times.toArray(new long[times.size()][]));
+ getSampleNumbersCache.put(key, commonIndices);
+ return commonIndices;
+ } else {
+ throw new RuntimeException("Video Tracks need sync samples. Only tracks other than video may have no sync samples.");
+ }
+ } else if ("soun".equals(track.getHandler())) {
+ Track referenceTrack = null;
+ for (Track candidate : movie.getTracks()) {
+ if (candidate.getSyncSamples() != null && "vide".equals(candidate.getHandler()) && candidate.getSyncSamples().length > 0) {
+ referenceTrack = candidate;
+ }
+ }
+ if (referenceTrack != null) {
+
+ // Gets the reference track's fra
+ long[] refSyncSamples = sampleNumbers(referenceTrack, movie);
+
+ int refSampleCount = referenceTrack.getSamples().size();
+
+ long[] syncSamples = new long[refSyncSamples.length];
+ long minSampleRate = 192000;
+ for (Track testTrack : movie.getTracks()) {
+ if ("soun".equals(testTrack.getHandler())) {
+ AudioSampleEntry ase = (AudioSampleEntry) testTrack.getSampleDescriptionBox().getSampleEntry();
+ if (ase.getSampleRate() < minSampleRate) {
+ minSampleRate = ase.getSampleRate();
+ long sc = testTrack.getSamples().size();
+ double stretch = (double) sc / refSampleCount;
+ TimeToSampleBox.Entry sttsEntry = testTrack.getDecodingTimeEntries().get(0);
+ long samplesPerFrame = sttsEntry.getDelta(); // Assuming all audio tracks have the same number of samples per frame, which they do for all known types
+
+ for (int i = 0; i < syncSamples.length; i++) {
+ long start = (long) Math.ceil(stretch * (refSyncSamples[i] - 1) * samplesPerFrame);
+ syncSamples[i] = start;
+ // The Stretch makes sure that there are as much audio and video chunks!
+ }
+ break;
+ }
+ }
+ }
+ AudioSampleEntry ase = (AudioSampleEntry) track.getSampleDescriptionBox().getSampleEntry();
+ TimeToSampleBox.Entry sttsEntry = track.getDecodingTimeEntries().get(0);
+ long samplesPerFrame = sttsEntry.getDelta(); // Assuming all audio tracks have the same number of samples per frame, which they do for all known types
+ double factor = (double) ase.getSampleRate() / (double) minSampleRate;
+ if (factor != Math.rint(factor)) { // Not an integer
+ throw new RuntimeException("Sample rates must be a multiple of the lowest sample rate to create a correct file!");
+ }
+ for (int i = 0; i < syncSamples.length; i++) {
+ syncSamples[i] = (long) (1 + syncSamples[i] * factor / (double) samplesPerFrame);
+ }
+ getSampleNumbersCache.put(key, syncSamples);
+ return syncSamples;
+ }
+ throw new RuntimeException("There was absolutely no Track with sync samples. I can't work with that!");
+ } else {
+ // Ok, my track has no sync samples - let's find one with sync samples.
+ for (Track candidate : movie.getTracks()) {
+ if (candidate.getSyncSamples() != null && candidate.getSyncSamples().length > 0) {
+ long[] refSyncSamples = sampleNumbers(candidate, movie);
+ int refSampleCount = candidate.getSamples().size();
+
+ long[] syncSamples = new long[refSyncSamples.length];
+ long sc = track.getSamples().size();
+ double stretch = (double) sc / refSampleCount;
+
+ for (int i = 0; i < syncSamples.length; i++) {
+ long start = (long) Math.ceil(stretch * (refSyncSamples[i] - 1)) + 1;
+ syncSamples[i] = start;
+ // The Stretch makes sure that there are as much audio and video chunks!
+ }
+ getSampleNumbersCache.put(key, syncSamples);
+ return syncSamples;
+ }
+ }
+ throw new RuntimeException("There was absolutely no Track with sync samples. I can't work with that!");
+ }
+
+
+ }
+
+ /**
+ * Calculates the timestamp of all tracks' sync samples.
+ *
+ * @param movie
+ * @param track
+ * @return
+ */
+ public static List<long[]> getSyncSamplesTimestamps(Movie movie, Track track) {
+ List<long[]> times = new LinkedList<long[]>();
+ for (Track currentTrack : movie.getTracks()) {
+ if (currentTrack.getHandler().equals(track.getHandler())) {
+ long[] currentTrackSyncSamples = currentTrack.getSyncSamples();
+ if (currentTrackSyncSamples != null && currentTrackSyncSamples.length > 0) {
+ final long[] currentTrackTimes = getTimes(currentTrack, movie);
+ times.add(currentTrackTimes);
+ }
+ }
+ }
+ return times;
+ }
+
+ public long[] getCommonIndices(long[] syncSamples, long[] syncSampleTimes, long timeScale, long[]... otherTracksTimes) {
+ List<Long> nuSyncSamples = new LinkedList<Long>();
+ List<Long> nuSyncSampleTimes = new LinkedList<Long>();
+
+
+ for (int i = 0; i < syncSampleTimes.length; i++) {
+ boolean foundInEveryRef = true;
+ for (long[] times : otherTracksTimes) {
+ foundInEveryRef &= (Arrays.binarySearch(times, syncSampleTimes[i]) >= 0);
+ }
+
+ if (foundInEveryRef) {
+ // use sample only if found in every other track.
+ nuSyncSamples.add(syncSamples[i]);
+ nuSyncSampleTimes.add(syncSampleTimes[i]);
+ }
+ }
+ // We have two arrays now:
+ // nuSyncSamples: Contains all common sync samples
+ // nuSyncSampleTimes: Contains the times of all sync samples
+
+ // Start: Warn user if samples are not matching!
+ if (nuSyncSamples.size() < (syncSamples.length * 0.25)) {
+ String log = "";
+ log += String.format("%5d - Common: [", nuSyncSamples.size());
+ for (long l : nuSyncSamples) {
+ log += (String.format("%10d,", l));
+ }
+ log += ("]");
+ LOG.warning(log);
+ log = "";
+
+ log += String.format("%5d - In : [", syncSamples.length);
+ for (long l : syncSamples) {
+ log += (String.format("%10d,", l));
+ }
+ log += ("]");
+ LOG.warning(log);
+ LOG.warning("There are less than 25% of common sync samples in the given track.");
+ throw new RuntimeException("There are less than 25% of common sync samples in the given track.");
+ } else if (nuSyncSamples.size() < (syncSamples.length * 0.5)) {
+ LOG.fine("There are less than 50% of common sync samples in the given track. This is implausible but I'm ok to continue.");
+ } else if (nuSyncSamples.size() < syncSamples.length) {
+ LOG.finest("Common SyncSample positions vs. this tracks SyncSample positions: " + nuSyncSamples.size() + " vs. " + syncSamples.length);
+ }
+ // End: Warn user if samples are not matching!
+
+
+
+
+ List<Long> finalSampleList = new LinkedList<Long>();
+
+ if (minFragmentDurationSeconds > 0) {
+ // if minFragmentDurationSeconds is greater 0
+ // we need to throw away certain samples.
+ long lastSyncSampleTime = -1;
+ Iterator<Long> nuSyncSamplesIterator = nuSyncSamples.iterator();
+ Iterator<Long> nuSyncSampleTimesIterator = nuSyncSampleTimes.iterator();
+ while (nuSyncSamplesIterator.hasNext() && nuSyncSampleTimesIterator.hasNext()) {
+ long curSyncSample = nuSyncSamplesIterator.next();
+ long curSyncSampleTime = nuSyncSampleTimesIterator.next();
+ if (lastSyncSampleTime == -1 || (curSyncSampleTime - lastSyncSampleTime) / timeScale >= minFragmentDurationSeconds) {
+ finalSampleList.add(curSyncSample);
+ lastSyncSampleTime = curSyncSampleTime;
+ }
+ }
+ } else {
+ // the list of all samples is the final list of samples
+ // since minFragmentDurationSeconds ist not used.
+ finalSampleList = nuSyncSamples;
+ }
+
+
+ // transform the list to an array
+ long[] finalSampleArray = new long[finalSampleList.size()];
+ for (int i = 0; i < finalSampleArray.length; i++) {
+ finalSampleArray[i] = finalSampleList.get(i);
+ }
+ return finalSampleArray;
+
+ }
+
+
+ private static long[] getTimes(Track track, Movie m) {
+ final CacheTuple key = new CacheTuple(track, m);
+ final long[] result = getTimesCache.get(key);
+ if (result != null) {
+ return result;
+ }
+
+ long[] syncSamples = track.getSyncSamples();
+ long[] syncSampleTimes = new long[syncSamples.length];
+ Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries());
+
+ int currentSample = 1; // first syncsample is 1
+ long currentDuration = 0;
+ long currentDelta = 0;
+ int currentSyncSampleIndex = 0;
+ long left = 0;
+
+ final long scalingFactor = calculateTracktimesScalingFactor(m, track);
+
+ while (currentSample <= syncSamples[syncSamples.length - 1]) {
+ if (currentSample++ == syncSamples[currentSyncSampleIndex]) {
+ syncSampleTimes[currentSyncSampleIndex++] = currentDuration * scalingFactor;
+ }
+ if (left-- == 0) {
+ TimeToSampleBox.Entry entry = timeQueue.poll();
+ left = entry.getCount() - 1;
+ currentDelta = entry.getDelta();
+ }
+ currentDuration += currentDelta;
+ }
+ getTimesCache.put(key, syncSampleTimes);
+ return syncSampleTimes;
+ }
+
+ private static long calculateTracktimesScalingFactor(Movie m, Track track) {
+ long timeScale = 1;
+ for (Track track1 : m.getTracks()) {
+ if (track1.getHandler().equals(track.getHandler())) {
+ if (track1.getTrackMetaData().getTimescale() != track.getTrackMetaData().getTimescale()) {
+ timeScale = lcm(timeScale, track1.getTrackMetaData().getTimescale());
+ }
+ }
+ }
+ return timeScale;
+ }
+
+ public static class CacheTuple {
+ Track track;
+ Movie movie;
+
+ public CacheTuple(Track track, Movie movie) {
+ this.track = track;
+ this.movie = movie;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CacheTuple that = (CacheTuple) o;
+
+ if (movie != null ? !movie.equals(that.movie) : that.movie != null) return false;
+ if (track != null ? !track.equals(that.track) : that.track != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = track != null ? track.hashCode() : 0;
+ result = 31 * result + (movie != null ? movie.hashCode() : 0);
+ return result;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/TwoSecondIntersectionFinder.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/TwoSecondIntersectionFinder.java
new file mode 100644
index 0000000..88aa4ab
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/TwoSecondIntersectionFinder.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.builder;
+
+import com.coremedia.iso.boxes.TimeToSampleBox;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This <code>FragmentIntersectionFinder</code> cuts the input movie in 2 second
+ * snippets.
+ */
+public class TwoSecondIntersectionFinder implements FragmentIntersectionFinder {
+
+ protected long getDuration(Track track) {
+ long duration = 0;
+ for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
+ duration += entry.getCount() * entry.getDelta();
+ }
+ return duration;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long[] sampleNumbers(Track track, Movie movie) {
+ List<TimeToSampleBox.Entry> entries = track.getDecodingTimeEntries();
+
+ double trackLength = 0;
+ for (Track thisTrack : movie.getTracks()) {
+ double thisTracksLength = getDuration(thisTrack) / thisTrack.getTrackMetaData().getTimescale();
+ if (trackLength < thisTracksLength) {
+ trackLength = thisTracksLength;
+ }
+ }
+
+ int fragmentCount = (int)Math.ceil(trackLength / 2) - 1;
+ if (fragmentCount < 1) {
+ fragmentCount = 1;
+ }
+
+ long fragments[] = new long[fragmentCount];
+ Arrays.fill(fragments, -1);
+ fragments[0] = 1;
+
+ long time = 0;
+ int samples = 0;
+ for (TimeToSampleBox.Entry entry : entries) {
+ for (int i = 0; i < entry.getCount(); i++) {
+ int currentFragment = (int) (time / track.getTrackMetaData().getTimescale() / 2) + 1;
+ if (currentFragment >= fragments.length) {
+ break;
+ }
+ fragments[currentFragment] = samples++ + 1;
+ time += entry.getDelta();
+ }
+ }
+ long last = samples + 1;
+ // fill all -1 ones.
+ for (int i = fragments.length - 1; i >= 0; i--) {
+ if (fragments[i] == -1) {
+ fragments[i] = last ;
+ }
+ last = fragments[i];
+ }
+ return fragments;
+
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/.svn/all-wcprops
new file mode 100644
index 0000000..a7245be
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/418/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/.svn/entries
new file mode 100644
index 0000000..bde4d0e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-03-11T20:54:45.638478Z
+418
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+mp4
+dir
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/all-wcprops
new file mode 100644
index 0000000..ec2a4f9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/418/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4
+END
+MovieCreator.java
+K 25
+svn:wc:ra_dav:version-url
+V 114
+/svn/!svn/ver/418/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/MovieCreator.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/entries
new file mode 100644
index 0000000..d7d7f30
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-03-11T20:54:45.638478Z
+418
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+MovieCreator.java
+file
+
+
+
+
+2012-09-14T17:27:49.987212Z
+ecb22de8d79473683de67e40f494641d
+2012-03-11T20:54:45.638478Z
+418
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1404
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/text-base/MovieCreator.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/text-base/MovieCreator.java.svn-base
new file mode 100644
index 0000000..ed9d15f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/.svn/text-base/MovieCreator.java.svn-base
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.container.mp4;
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.boxes.TrackBox;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Mp4TrackImpl;
+
+import java.io.IOException;
+import java.nio.channels.ReadableByteChannel;
+import java.util.List;
+
+/**
+ * Shortcut to build a movie from an MP4 file.
+ */
+public class MovieCreator {
+ public static Movie build(ReadableByteChannel channel) throws IOException {
+ IsoFile isoFile = new IsoFile(channel);
+ Movie m = new Movie();
+ List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(TrackBox.class);
+ for (TrackBox trackBox : trackBoxes) {
+ m.addTrack(new Mp4TrackImpl(trackBox));
+ }
+ return m;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/MovieCreator.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/MovieCreator.java
new file mode 100644
index 0000000..ed9d15f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/container/mp4/MovieCreator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.container.mp4;
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.boxes.TrackBox;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Mp4TrackImpl;
+
+import java.io.IOException;
+import java.nio.channels.ReadableByteChannel;
+import java.util.List;
+
+/**
+ * Shortcut to build a movie from an MP4 file.
+ */
+public class MovieCreator {
+ public static Movie build(ReadableByteChannel channel) throws IOException {
+ IsoFile isoFile = new IsoFile(channel);
+ Movie m = new Movie();
+ List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(TrackBox.class);
+ for (TrackBox trackBox : trackBoxes) {
+ m.addTrack(new Mp4TrackImpl(trackBox));
+ }
+ return m;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/all-wcprops
new file mode 100644
index 0000000..496d7bb
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/all-wcprops
@@ -0,0 +1,89 @@
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks
+END
+DivideTimeScaleTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/DivideTimeScaleTrack.java
+END
+CroppedTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/CroppedTrack.java
+END
+ChangeTimeScaleTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ChangeTimeScaleTrack.java
+END
+EC3TrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/EC3TrackImpl.java
+END
+ReplaceSampleTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ReplaceSampleTrack.java
+END
+QuicktimeTextTrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/svn/!svn/ver/691/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/QuicktimeTextTrackImpl.java
+END
+Amf0Track.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/Amf0Track.java
+END
+SilenceTrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 111
+/svn/!svn/ver/698/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/SilenceTrackImpl.java
+END
+H264TrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/H264TrackImpl.java
+END
+TextTrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/684/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/TextTrackImpl.java
+END
+AACTrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java
+END
+MultiplyTimeScaleTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/MultiplyTimeScaleTrack.java
+END
+AppendTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 106
+/svn/!svn/ver/714/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AppendTrack.java
+END
+AC3TrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AC3TrackImpl.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/entries
new file mode 100644
index 0000000..dbe8ae3
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/entries
@@ -0,0 +1,504 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+DivideTimeScaleTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+6aa41cdb7489e16e879ddebd68b7ac52
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3893
+
+CroppedTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+5d9c65d9aac52a26372aa3fdf6f1b7ea
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6025
+
+ChangeTimeScaleTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+ba01d30ac4b7c4fa9a2c6538ee537594
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7152
+
+EC3TrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+1d568bfaa0f41c3771986a7790347072
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13623
+
+ReplaceSampleTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+f6846b7a262ab0e530da46f4d7e34850
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3139
+
+QuicktimeTextTrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+7ccd01a58545fb02b507b57892eb53e5
+2012-06-24T21:35:59.546504Z
+691
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5128
+
+Amf0Track.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+4718a34bc271adf4517de9829ac74a9d
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3858
+
+SilenceTrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+a897677e602dfa0d64af1b0d33a04ca8
+2012-06-26T08:37:32.910396Z
+698
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2623
+
+H264TrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+76abb4b21c13d11215a2ac4cb0c7e461
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+28816
+
+TextTrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+5a643c876754eb5c7bcf75b4b71114a1
+2012-06-24T14:45:45.932648Z
+684
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4963
+
+AACTrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+ece8d364a9a5aeabf6a281f6d428e3cf
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10097
+
+MultiplyTimeScaleTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+b0ea53239c124607a26a181f594f82a1
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4192
+
+AppendTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+e7814aebc4500724771fd0582455a7ca
+2012-07-18T23:22:45.506793Z
+714
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15783
+
+AC3TrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+fbd724739cd9a5f2b28f16b412b27309
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+19303
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AACTrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AACTrackImpl.java.svn-base
new file mode 100644
index 0000000..df51a1a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AACTrackImpl.java.svn-base
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2012 castLabs GmbH, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.AC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.*;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.*;
+
+/**
+ */
+public class AACTrackImpl extends AbstractTrack {
+ public static Map<Integer, Integer> samplingFrequencyIndexMap = new HashMap<Integer, Integer>();
+
+ static {
+ samplingFrequencyIndexMap.put(96000, 0);
+ samplingFrequencyIndexMap.put(88200, 1);
+ samplingFrequencyIndexMap.put(64000, 2);
+ samplingFrequencyIndexMap.put(48000, 3);
+ samplingFrequencyIndexMap.put(44100, 4);
+ samplingFrequencyIndexMap.put(32000, 5);
+ samplingFrequencyIndexMap.put(24000, 6);
+ samplingFrequencyIndexMap.put(22050, 7);
+ samplingFrequencyIndexMap.put(16000, 8);
+ samplingFrequencyIndexMap.put(12000, 9);
+ samplingFrequencyIndexMap.put(11025, 10);
+ samplingFrequencyIndexMap.put(8000, 11);
+ samplingFrequencyIndexMap.put(0x0, 96000);
+ samplingFrequencyIndexMap.put(0x1, 88200);
+ samplingFrequencyIndexMap.put(0x2, 64000);
+ samplingFrequencyIndexMap.put(0x3, 48000);
+ samplingFrequencyIndexMap.put(0x4, 44100);
+ samplingFrequencyIndexMap.put(0x5, 32000);
+ samplingFrequencyIndexMap.put(0x6, 24000);
+ samplingFrequencyIndexMap.put(0x7, 22050);
+ samplingFrequencyIndexMap.put(0x8, 16000);
+ samplingFrequencyIndexMap.put(0x9, 12000);
+ samplingFrequencyIndexMap.put(0xa, 11025);
+ samplingFrequencyIndexMap.put(0xb, 8000);
+ }
+
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int channelCount;
+ int channelconfig;
+
+ int bufferSizeDB;
+ long maxBitRate;
+ long avgBitRate;
+
+ private BufferedInputStream inputStream;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+ List<TimeToSampleBox.Entry> stts;
+ private String lang = "und";
+
+
+ public AACTrackImpl(InputStream inputStream, String lang) throws IOException {
+ this.lang = lang;
+ parse(inputStream);
+ }
+
+ public AACTrackImpl(InputStream inputStream) throws IOException {
+ parse(inputStream);
+ }
+
+ private void parse(InputStream inputStream) throws IOException {
+ this.inputStream = new BufferedInputStream(inputStream);
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+
+ double packetsPerSecond = (double)samplerate / 1024.0;
+ double duration = samples.size() / packetsPerSecond;
+
+ long dataSize = 0;
+ LinkedList<Integer> queue = new LinkedList<Integer>();
+ for (int i = 0; i < samples.size(); i++) {
+ int size = samples.get(i).capacity();
+ dataSize += size;
+ queue.add(size);
+ while (queue.size() > packetsPerSecond) {
+ queue.pop();
+ }
+ if (queue.size() == (int) packetsPerSecond) {
+ int currSize = 0;
+ for (int j = 0 ; j < queue.size(); j++) {
+ currSize += queue.get(j);
+ }
+ double currBitrate = 8.0 * currSize / queue.size() * packetsPerSecond;
+ if (currBitrate > maxBitRate) {
+ maxBitRate = (int)currBitrate;
+ }
+ }
+ }
+
+ avgBitRate = (int) (8 * dataSize / duration);
+
+ bufferSizeDB = 1536; /* TODO: Calcultate this somehow! */
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("mp4a");
+ audioSampleEntry.setChannelCount(2);
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+
+ ESDescriptorBox esds = new ESDescriptorBox();
+ ESDescriptor descriptor = new ESDescriptor();
+ descriptor.setEsId(0);
+
+ SLConfigDescriptor slConfigDescriptor = new SLConfigDescriptor();
+ slConfigDescriptor.setPredefined(2);
+ descriptor.setSlConfigDescriptor(slConfigDescriptor);
+
+ DecoderConfigDescriptor decoderConfigDescriptor = new DecoderConfigDescriptor();
+ decoderConfigDescriptor.setObjectTypeIndication(0x40);
+ decoderConfigDescriptor.setStreamType(5);
+ decoderConfigDescriptor.setBufferSizeDB(bufferSizeDB);
+ decoderConfigDescriptor.setMaxBitRate(maxBitRate);
+ decoderConfigDescriptor.setAvgBitRate(avgBitRate);
+
+ AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig();
+ audioSpecificConfig.setAudioObjectType(2); // AAC LC
+ audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get(samplerate));
+ audioSpecificConfig.setChannelConfiguration(channelconfig);
+ decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig);
+
+ descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor);
+
+ ByteBuffer data = descriptor.serialize();
+ esds.setData(data);
+ audioSampleEntry.addBox(esds);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "soun";
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public Box getMediaHeaderBox() {
+ return new SoundMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private boolean readVariables() throws IOException {
+ byte[] data = new byte[100];
+ inputStream.mark(100);
+ if (100 != inputStream.read(data, 0, 100)) {
+ return false;
+ }
+ inputStream.reset(); // Rewind
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(12);
+ if (syncword != 0xfff) {
+ return false;
+ }
+ int id = brb.readBits(1);
+ int layer = brb.readBits(2);
+ int protectionAbsent = brb.readBits(1);
+ int profile = brb.readBits(2);
+ samplerate = samplingFrequencyIndexMap.get(brb.readBits(4));
+ brb.readBits(1);
+ channelconfig = brb.readBits(3);
+ int original = brb.readBits(1);
+ int home = brb.readBits(1);
+ int emphasis = brb.readBits(2);
+
+ return true;
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+
+ readSamples = true;
+ byte[] header = new byte[15];
+ boolean ret = false;
+ inputStream.mark(15);
+ while (-1 != inputStream.read(header)) {
+ ret = true;
+ ByteBuffer bb = ByteBuffer.wrap(header);
+ inputStream.reset();
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(12);
+ if (syncword != 0xfff) {
+ return false;
+ }
+ brb.readBits(3);
+ int protectionAbsent = brb.readBits(1);
+ brb.readBits(14);
+ int frameSize = brb.readBits(13);
+ int bufferFullness = brb.readBits(11);
+ int noBlocks = brb.readBits(2);
+ int used = (int) Math.ceil(brb.getPosition() / 8.0);
+ if (protectionAbsent == 0) {
+ used += 2;
+ }
+ inputStream.skip(used);
+ frameSize -= used;
+// System.out.println("Size: " + frameSize + " fullness: " + bufferFullness + " no blocks: " + noBlocks);
+ byte[] data = new byte[frameSize];
+ inputStream.read(data);
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1024));
+ inputStream.mark(15);
+ }
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ return "AACTrackImpl{" +
+ "samplerate=" + samplerate +
+ ", bitrate=" + bitrate +
+ ", channelCount=" + channelCount +
+ ", channelconfig=" + channelconfig +
+ '}';
+ }
+}
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AC3TrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AC3TrackImpl.java.svn-base
new file mode 100644
index 0000000..5e5b2cd
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AC3TrackImpl.java.svn-base
@@ -0,0 +1,513 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.AC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class AC3TrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int channelCount;
+
+ int fscod;
+ int bsid;
+ int bsmod;
+ int acmod;
+ int lfeon;
+ int frmsizecod;
+
+ int frameSize;
+ int[][][][] bitRateAndFrameSizeTable;
+
+ private InputStream inputStream;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+ List<TimeToSampleBox.Entry> stts;
+ private String lang = "und";
+
+ public AC3TrackImpl(InputStream fin, String lang) throws IOException {
+ this.lang = lang;
+ parse(fin);
+ }
+
+ public AC3TrackImpl(InputStream fin) throws IOException {
+ parse(fin);
+ }
+
+ private void parse(InputStream fin) throws IOException {
+ inputStream = fin;
+ bitRateAndFrameSizeTable = new int[19][2][3][2];
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+ initBitRateAndFrameSizeTable();
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ac-3");
+ audioSampleEntry.setChannelCount(2); // According to ETSI TS 102 366 Annex F
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+ AC3SpecificBox ac3 = new AC3SpecificBox();
+ ac3.setAcmod(acmod);
+ ac3.setBitRateCode(frmsizecod >> 1);
+ ac3.setBsid(bsid);
+ ac3.setBsmod(bsmod);
+ ac3.setFscod(fscod);
+ ac3.setLfeon(lfeon);
+ ac3.setReserved(0);
+
+ audioSampleEntry.addBox(ac3);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "soun";
+ }
+
+ public Box getMediaHeaderBox() {
+ return new SoundMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private boolean readVariables() throws IOException {
+ byte[] data = new byte[100];
+ inputStream.mark(100);
+ if (100 != inputStream.read(data, 0, 100)) {
+ return false;
+ }
+ inputStream.reset(); // Rewind
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(16);
+ if (syncword != 0xb77) {
+ return false;
+ }
+ brb.readBits(16); // CRC-1
+ fscod = brb.readBits(2);
+
+ switch (fscod) {
+ case 0:
+ samplerate = 48000;
+ break;
+
+ case 1:
+ samplerate = 44100;
+ break;
+
+ case 2:
+ samplerate = 32000;
+ break;
+
+ case 3:
+ samplerate = 0;
+ break;
+
+ }
+ if (samplerate == 0) {
+ return false;
+ }
+
+ frmsizecod = brb.readBits(6);
+
+ if (!calcBitrateAndFrameSize(frmsizecod)) {
+ return false;
+ }
+
+ if (frameSize == 0) {
+ return false;
+ }
+ bsid = brb.readBits(5);
+ bsmod = brb.readBits(3);
+ acmod = brb.readBits(3);
+
+ if (bsid == 9) {
+ samplerate /= 2;
+ } else if (bsid != 8 && bsid != 6) {
+ return false;
+ }
+
+ if ((acmod != 1) && ((acmod & 1) == 1)) {
+ brb.readBits(2);
+ }
+
+ if (0 != (acmod & 4)) {
+ brb.readBits(2);
+ }
+
+ if (acmod == 2) {
+ brb.readBits(2);
+ }
+
+ switch (acmod) {
+ case 0:
+ channelCount = 2;
+ break;
+
+ case 1:
+ channelCount = 1;
+ break;
+
+ case 2:
+ channelCount = 2;
+ break;
+
+ case 3:
+ channelCount = 3;
+ break;
+
+ case 4:
+ channelCount = 3;
+ break;
+
+ case 5:
+ channelCount = 4;
+ break;
+
+ case 6:
+ channelCount = 4;
+ break;
+
+ case 7:
+ channelCount = 5;
+ break;
+
+ }
+
+ lfeon = brb.readBits(1);
+
+ if (lfeon == 1) {
+ channelCount++;
+ }
+ return true;
+ }
+
+ private boolean calcBitrateAndFrameSize(int code) {
+ int frmsizecode = code >>> 1;
+ int flag = code & 1;
+ if (frmsizecode > 18 || flag > 1 || fscod > 2) {
+ return false;
+ }
+ bitrate = bitRateAndFrameSizeTable[frmsizecode][flag][fscod][0];
+ frameSize = 2 * bitRateAndFrameSizeTable[frmsizecode][flag][fscod][1];
+ return true;
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+ readSamples = true;
+ byte[] header = new byte[5];
+ boolean ret = false;
+ inputStream.mark(5);
+ while (-1 != inputStream.read(header)) {
+ ret = true;
+ int frmsizecode = header[4] & 63;
+ calcBitrateAndFrameSize(frmsizecode);
+ inputStream.reset();
+ byte[] data = new byte[frameSize];
+ inputStream.read(data);
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1536));
+ inputStream.mark(5);
+ }
+ return ret;
+ }
+
+ private void initBitRateAndFrameSizeTable() {
+ // ETSI 102 366 Table 4.13, in frmsizecod, flag, fscod, bitrate/size order. Note that all sizes are in words, and all bitrates in kbps
+
+ // 48kHz
+ bitRateAndFrameSizeTable[0][0][0][0] = 32;
+ bitRateAndFrameSizeTable[0][1][0][0] = 32;
+ bitRateAndFrameSizeTable[0][0][0][1] = 64;
+ bitRateAndFrameSizeTable[0][1][0][1] = 64;
+ bitRateAndFrameSizeTable[1][0][0][0] = 40;
+ bitRateAndFrameSizeTable[1][1][0][0] = 40;
+ bitRateAndFrameSizeTable[1][0][0][1] = 80;
+ bitRateAndFrameSizeTable[1][1][0][1] = 80;
+ bitRateAndFrameSizeTable[2][0][0][0] = 48;
+ bitRateAndFrameSizeTable[2][1][0][0] = 48;
+ bitRateAndFrameSizeTable[2][0][0][1] = 96;
+ bitRateAndFrameSizeTable[2][1][0][1] = 96;
+ bitRateAndFrameSizeTable[3][0][0][0] = 56;
+ bitRateAndFrameSizeTable[3][1][0][0] = 56;
+ bitRateAndFrameSizeTable[3][0][0][1] = 112;
+ bitRateAndFrameSizeTable[3][1][0][1] = 112;
+ bitRateAndFrameSizeTable[4][0][0][0] = 64;
+ bitRateAndFrameSizeTable[4][1][0][0] = 64;
+ bitRateAndFrameSizeTable[4][0][0][1] = 128;
+ bitRateAndFrameSizeTable[4][1][0][1] = 128;
+ bitRateAndFrameSizeTable[5][0][0][0] = 80;
+ bitRateAndFrameSizeTable[5][1][0][0] = 80;
+ bitRateAndFrameSizeTable[5][0][0][1] = 160;
+ bitRateAndFrameSizeTable[5][1][0][1] = 160;
+ bitRateAndFrameSizeTable[6][0][0][0] = 96;
+ bitRateAndFrameSizeTable[6][1][0][0] = 96;
+ bitRateAndFrameSizeTable[6][0][0][1] = 192;
+ bitRateAndFrameSizeTable[6][1][0][1] = 192;
+ bitRateAndFrameSizeTable[7][0][0][0] = 112;
+ bitRateAndFrameSizeTable[7][1][0][0] = 112;
+ bitRateAndFrameSizeTable[7][0][0][1] = 224;
+ bitRateAndFrameSizeTable[7][1][0][1] = 224;
+ bitRateAndFrameSizeTable[8][0][0][0] = 128;
+ bitRateAndFrameSizeTable[8][1][0][0] = 128;
+ bitRateAndFrameSizeTable[8][0][0][1] = 256;
+ bitRateAndFrameSizeTable[8][1][0][1] = 256;
+ bitRateAndFrameSizeTable[9][0][0][0] = 160;
+ bitRateAndFrameSizeTable[9][1][0][0] = 160;
+ bitRateAndFrameSizeTable[9][0][0][1] = 320;
+ bitRateAndFrameSizeTable[9][1][0][1] = 320;
+ bitRateAndFrameSizeTable[10][0][0][0] = 192;
+ bitRateAndFrameSizeTable[10][1][0][0] = 192;
+ bitRateAndFrameSizeTable[10][0][0][1] = 384;
+ bitRateAndFrameSizeTable[10][1][0][1] = 384;
+ bitRateAndFrameSizeTable[11][0][0][0] = 224;
+ bitRateAndFrameSizeTable[11][1][0][0] = 224;
+ bitRateAndFrameSizeTable[11][0][0][1] = 448;
+ bitRateAndFrameSizeTable[11][1][0][1] = 448;
+ bitRateAndFrameSizeTable[12][0][0][0] = 256;
+ bitRateAndFrameSizeTable[12][1][0][0] = 256;
+ bitRateAndFrameSizeTable[12][0][0][1] = 512;
+ bitRateAndFrameSizeTable[12][1][0][1] = 512;
+ bitRateAndFrameSizeTable[13][0][0][0] = 320;
+ bitRateAndFrameSizeTable[13][1][0][0] = 320;
+ bitRateAndFrameSizeTable[13][0][0][1] = 640;
+ bitRateAndFrameSizeTable[13][1][0][1] = 640;
+ bitRateAndFrameSizeTable[14][0][0][0] = 384;
+ bitRateAndFrameSizeTable[14][1][0][0] = 384;
+ bitRateAndFrameSizeTable[14][0][0][1] = 768;
+ bitRateAndFrameSizeTable[14][1][0][1] = 768;
+ bitRateAndFrameSizeTable[15][0][0][0] = 448;
+ bitRateAndFrameSizeTable[15][1][0][0] = 448;
+ bitRateAndFrameSizeTable[15][0][0][1] = 896;
+ bitRateAndFrameSizeTable[15][1][0][1] = 896;
+ bitRateAndFrameSizeTable[16][0][0][0] = 512;
+ bitRateAndFrameSizeTable[16][1][0][0] = 512;
+ bitRateAndFrameSizeTable[16][0][0][1] = 1024;
+ bitRateAndFrameSizeTable[16][1][0][1] = 1024;
+ bitRateAndFrameSizeTable[17][0][0][0] = 576;
+ bitRateAndFrameSizeTable[17][1][0][0] = 576;
+ bitRateAndFrameSizeTable[17][0][0][1] = 1152;
+ bitRateAndFrameSizeTable[17][1][0][1] = 1152;
+ bitRateAndFrameSizeTable[18][0][0][0] = 640;
+ bitRateAndFrameSizeTable[18][1][0][0] = 640;
+ bitRateAndFrameSizeTable[18][0][0][1] = 1280;
+ bitRateAndFrameSizeTable[18][1][0][1] = 1280;
+
+ // 44.1 kHz
+ bitRateAndFrameSizeTable[0][0][1][0] = 32;
+ bitRateAndFrameSizeTable[0][1][1][0] = 32;
+ bitRateAndFrameSizeTable[0][0][1][1] = 69;
+ bitRateAndFrameSizeTable[0][1][1][1] = 70;
+ bitRateAndFrameSizeTable[1][0][1][0] = 40;
+ bitRateAndFrameSizeTable[1][1][1][0] = 40;
+ bitRateAndFrameSizeTable[1][0][1][1] = 87;
+ bitRateAndFrameSizeTable[1][1][1][1] = 88;
+ bitRateAndFrameSizeTable[2][0][1][0] = 48;
+ bitRateAndFrameSizeTable[2][1][1][0] = 48;
+ bitRateAndFrameSizeTable[2][0][1][1] = 104;
+ bitRateAndFrameSizeTable[2][1][1][1] = 105;
+ bitRateAndFrameSizeTable[3][0][1][0] = 56;
+ bitRateAndFrameSizeTable[3][1][1][0] = 56;
+ bitRateAndFrameSizeTable[3][0][1][1] = 121;
+ bitRateAndFrameSizeTable[3][1][1][1] = 122;
+ bitRateAndFrameSizeTable[4][0][1][0] = 64;
+ bitRateAndFrameSizeTable[4][1][1][0] = 64;
+ bitRateAndFrameSizeTable[4][0][1][1] = 139;
+ bitRateAndFrameSizeTable[4][1][1][1] = 140;
+ bitRateAndFrameSizeTable[5][0][1][0] = 80;
+ bitRateAndFrameSizeTable[5][1][1][0] = 80;
+ bitRateAndFrameSizeTable[5][0][1][1] = 174;
+ bitRateAndFrameSizeTable[5][1][1][1] = 175;
+ bitRateAndFrameSizeTable[6][0][1][0] = 96;
+ bitRateAndFrameSizeTable[6][1][1][0] = 96;
+ bitRateAndFrameSizeTable[6][0][1][1] = 208;
+ bitRateAndFrameSizeTable[6][1][1][1] = 209;
+ bitRateAndFrameSizeTable[7][0][1][0] = 112;
+ bitRateAndFrameSizeTable[7][1][1][0] = 112;
+ bitRateAndFrameSizeTable[7][0][1][1] = 243;
+ bitRateAndFrameSizeTable[7][1][1][1] = 244;
+ bitRateAndFrameSizeTable[8][0][1][0] = 128;
+ bitRateAndFrameSizeTable[8][1][1][0] = 128;
+ bitRateAndFrameSizeTable[8][0][1][1] = 278;
+ bitRateAndFrameSizeTable[8][1][1][1] = 279;
+ bitRateAndFrameSizeTable[9][0][1][0] = 160;
+ bitRateAndFrameSizeTable[9][1][1][0] = 160;
+ bitRateAndFrameSizeTable[9][0][1][1] = 348;
+ bitRateAndFrameSizeTable[9][1][1][1] = 349;
+ bitRateAndFrameSizeTable[10][0][1][0] = 192;
+ bitRateAndFrameSizeTable[10][1][1][0] = 192;
+ bitRateAndFrameSizeTable[10][0][1][1] = 417;
+ bitRateAndFrameSizeTable[10][1][1][1] = 418;
+ bitRateAndFrameSizeTable[11][0][1][0] = 224;
+ bitRateAndFrameSizeTable[11][1][1][0] = 224;
+ bitRateAndFrameSizeTable[11][0][1][1] = 487;
+ bitRateAndFrameSizeTable[11][1][1][1] = 488;
+ bitRateAndFrameSizeTable[12][0][1][0] = 256;
+ bitRateAndFrameSizeTable[12][1][1][0] = 256;
+ bitRateAndFrameSizeTable[12][0][1][1] = 557;
+ bitRateAndFrameSizeTable[12][1][1][1] = 558;
+ bitRateAndFrameSizeTable[13][0][1][0] = 320;
+ bitRateAndFrameSizeTable[13][1][1][0] = 320;
+ bitRateAndFrameSizeTable[13][0][1][1] = 696;
+ bitRateAndFrameSizeTable[13][1][1][1] = 697;
+ bitRateAndFrameSizeTable[14][0][1][0] = 384;
+ bitRateAndFrameSizeTable[14][1][1][0] = 384;
+ bitRateAndFrameSizeTable[14][0][1][1] = 835;
+ bitRateAndFrameSizeTable[14][1][1][1] = 836;
+ bitRateAndFrameSizeTable[15][0][1][0] = 448;
+ bitRateAndFrameSizeTable[15][1][1][0] = 448;
+ bitRateAndFrameSizeTable[15][0][1][1] = 975;
+ bitRateAndFrameSizeTable[15][1][1][1] = 975;
+ bitRateAndFrameSizeTable[16][0][1][0] = 512;
+ bitRateAndFrameSizeTable[16][1][1][0] = 512;
+ bitRateAndFrameSizeTable[16][0][1][1] = 1114;
+ bitRateAndFrameSizeTable[16][1][1][1] = 1115;
+ bitRateAndFrameSizeTable[17][0][1][0] = 576;
+ bitRateAndFrameSizeTable[17][1][1][0] = 576;
+ bitRateAndFrameSizeTable[17][0][1][1] = 1253;
+ bitRateAndFrameSizeTable[17][1][1][1] = 1254;
+ bitRateAndFrameSizeTable[18][0][1][0] = 640;
+ bitRateAndFrameSizeTable[18][1][1][0] = 640;
+ bitRateAndFrameSizeTable[18][0][1][1] = 1393;
+ bitRateAndFrameSizeTable[18][1][1][1] = 1394;
+
+ // 32kHz
+ bitRateAndFrameSizeTable[0][0][2][0] = 32;
+ bitRateAndFrameSizeTable[0][1][2][0] = 32;
+ bitRateAndFrameSizeTable[0][0][2][1] = 96;
+ bitRateAndFrameSizeTable[0][1][2][1] = 96;
+ bitRateAndFrameSizeTable[1][0][2][0] = 40;
+ bitRateAndFrameSizeTable[1][1][2][0] = 40;
+ bitRateAndFrameSizeTable[1][0][2][1] = 120;
+ bitRateAndFrameSizeTable[1][1][2][1] = 120;
+ bitRateAndFrameSizeTable[2][0][2][0] = 48;
+ bitRateAndFrameSizeTable[2][1][2][0] = 48;
+ bitRateAndFrameSizeTable[2][0][2][1] = 144;
+ bitRateAndFrameSizeTable[2][1][2][1] = 144;
+ bitRateAndFrameSizeTable[3][0][2][0] = 56;
+ bitRateAndFrameSizeTable[3][1][2][0] = 56;
+ bitRateAndFrameSizeTable[3][0][2][1] = 168;
+ bitRateAndFrameSizeTable[3][1][2][1] = 168;
+ bitRateAndFrameSizeTable[4][0][2][0] = 64;
+ bitRateAndFrameSizeTable[4][1][2][0] = 64;
+ bitRateAndFrameSizeTable[4][0][2][1] = 192;
+ bitRateAndFrameSizeTable[4][1][2][1] = 192;
+ bitRateAndFrameSizeTable[5][0][2][0] = 80;
+ bitRateAndFrameSizeTable[5][1][2][0] = 80;
+ bitRateAndFrameSizeTable[5][0][2][1] = 240;
+ bitRateAndFrameSizeTable[5][1][2][1] = 240;
+ bitRateAndFrameSizeTable[6][0][2][0] = 96;
+ bitRateAndFrameSizeTable[6][1][2][0] = 96;
+ bitRateAndFrameSizeTable[6][0][2][1] = 288;
+ bitRateAndFrameSizeTable[6][1][2][1] = 288;
+ bitRateAndFrameSizeTable[7][0][2][0] = 112;
+ bitRateAndFrameSizeTable[7][1][2][0] = 112;
+ bitRateAndFrameSizeTable[7][0][2][1] = 336;
+ bitRateAndFrameSizeTable[7][1][2][1] = 336;
+ bitRateAndFrameSizeTable[8][0][2][0] = 128;
+ bitRateAndFrameSizeTable[8][1][2][0] = 128;
+ bitRateAndFrameSizeTable[8][0][2][1] = 384;
+ bitRateAndFrameSizeTable[8][1][2][1] = 384;
+ bitRateAndFrameSizeTable[9][0][2][0] = 160;
+ bitRateAndFrameSizeTable[9][1][2][0] = 160;
+ bitRateAndFrameSizeTable[9][0][2][1] = 480;
+ bitRateAndFrameSizeTable[9][1][2][1] = 480;
+ bitRateAndFrameSizeTable[10][0][2][0] = 192;
+ bitRateAndFrameSizeTable[10][1][2][0] = 192;
+ bitRateAndFrameSizeTable[10][0][2][1] = 576;
+ bitRateAndFrameSizeTable[10][1][2][1] = 576;
+ bitRateAndFrameSizeTable[11][0][2][0] = 224;
+ bitRateAndFrameSizeTable[11][1][2][0] = 224;
+ bitRateAndFrameSizeTable[11][0][2][1] = 672;
+ bitRateAndFrameSizeTable[11][1][2][1] = 672;
+ bitRateAndFrameSizeTable[12][0][2][0] = 256;
+ bitRateAndFrameSizeTable[12][1][2][0] = 256;
+ bitRateAndFrameSizeTable[12][0][2][1] = 768;
+ bitRateAndFrameSizeTable[12][1][2][1] = 768;
+ bitRateAndFrameSizeTable[13][0][2][0] = 320;
+ bitRateAndFrameSizeTable[13][1][2][0] = 320;
+ bitRateAndFrameSizeTable[13][0][2][1] = 960;
+ bitRateAndFrameSizeTable[13][1][2][1] = 960;
+ bitRateAndFrameSizeTable[14][0][2][0] = 384;
+ bitRateAndFrameSizeTable[14][1][2][0] = 384;
+ bitRateAndFrameSizeTable[14][0][2][1] = 1152;
+ bitRateAndFrameSizeTable[14][1][2][1] = 1152;
+ bitRateAndFrameSizeTable[15][0][2][0] = 448;
+ bitRateAndFrameSizeTable[15][1][2][0] = 448;
+ bitRateAndFrameSizeTable[15][0][2][1] = 1344;
+ bitRateAndFrameSizeTable[15][1][2][1] = 1344;
+ bitRateAndFrameSizeTable[16][0][2][0] = 512;
+ bitRateAndFrameSizeTable[16][1][2][0] = 512;
+ bitRateAndFrameSizeTable[16][0][2][1] = 1536;
+ bitRateAndFrameSizeTable[16][1][2][1] = 1536;
+ bitRateAndFrameSizeTable[17][0][2][0] = 576;
+ bitRateAndFrameSizeTable[17][1][2][0] = 576;
+ bitRateAndFrameSizeTable[17][0][2][1] = 1728;
+ bitRateAndFrameSizeTable[17][1][2][1] = 1728;
+ bitRateAndFrameSizeTable[18][0][2][0] = 640;
+ bitRateAndFrameSizeTable[18][1][2][0] = 640;
+ bitRateAndFrameSizeTable[18][0][2][1] = 1920;
+ bitRateAndFrameSizeTable[18][1][2][1] = 1920;
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/Amf0Track.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/Amf0Track.java.svn-base
new file mode 100644
index 0000000..0917767
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/Amf0Track.java.svn-base
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.adobe.ActionMessageFormat0SampleEntryBox;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public class Amf0Track extends AbstractTrack {
+ SortedMap<Long, byte[]> rawSamples = new TreeMap<Long, byte[]>() {
+ };
+ private TrackMetaData trackMetaData = new TrackMetaData();
+
+
+ /**
+ * Creates a new AMF0 track from
+ *
+ * @param rawSamples
+ */
+ public Amf0Track(Map<Long, byte[]> rawSamples) {
+ this.rawSamples = new TreeMap<Long, byte[]>(rawSamples);
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000); // Text tracks use millieseconds
+ trackMetaData.setLanguage("eng");
+ }
+
+ public List<ByteBuffer> getSamples() {
+ LinkedList<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ for (byte[] bytes : rawSamples.values()) {
+ samples.add(ByteBuffer.wrap(bytes));
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ SampleDescriptionBox stsd = new SampleDescriptionBox();
+ ActionMessageFormat0SampleEntryBox amf0 = new ActionMessageFormat0SampleEntryBox();
+ amf0.setDataReferenceIndex(1);
+ stsd.addBox(amf0);
+ return stsd;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ LinkedList<TimeToSampleBox.Entry> timesToSample = new LinkedList<TimeToSampleBox.Entry>();
+ LinkedList<Long> keys = new LinkedList<Long>(rawSamples.keySet());
+ Collections.sort(keys);
+ long lastTimeStamp = 0;
+ for (Long key : keys) {
+ long delta = key - lastTimeStamp;
+ if (timesToSample.size() > 0 && timesToSample.peek().getDelta() == delta) {
+ timesToSample.peek().setCount(timesToSample.peek().getCount() + 1);
+ } else {
+ timesToSample.add(new TimeToSampleBox.Entry(1, delta));
+ }
+ lastTimeStamp = key;
+ }
+ return timesToSample;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ // AMF0 tracks do not have Composition Time
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ // AMF0 tracks do not have Sync Samples
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ // AMF0 tracks do not have Sample Dependencies
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public String getHandler() {
+ return "data";
+ }
+
+ public Box getMediaHeaderBox() {
+ return new NullMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AppendTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AppendTrack.java.svn-base
new file mode 100644
index 0000000..93ee0cd
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AppendTrack.java.svn-base
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.DecoderConfigDescriptor;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.util.*;
+
+/**
+ * Appends two or more <code>Tracks</code> of the same type. No only that the type must be equal
+ * also the decoder settings must be the same.
+ */
+public class AppendTrack extends AbstractTrack {
+ Track[] tracks;
+ SampleDescriptionBox stsd;
+
+ public AppendTrack(Track... tracks) throws IOException {
+ this.tracks = tracks;
+
+ for (Track track : tracks) {
+
+ if (stsd == null) {
+ stsd = track.getSampleDescriptionBox();
+ } else {
+ ByteArrayOutputStream curBaos = new ByteArrayOutputStream();
+ ByteArrayOutputStream refBaos = new ByteArrayOutputStream();
+ track.getSampleDescriptionBox().getBox(Channels.newChannel(curBaos));
+ stsd.getBox(Channels.newChannel(refBaos));
+ byte[] cur = curBaos.toByteArray();
+ byte[] ref = refBaos.toByteArray();
+
+ if (!Arrays.equals(ref, cur)) {
+ SampleDescriptionBox curStsd = track.getSampleDescriptionBox();
+ if (stsd.getBoxes().size() == 1 && curStsd.getBoxes().size() == 1) {
+ if (stsd.getBoxes().get(0) instanceof AudioSampleEntry && curStsd.getBoxes().get(0) instanceof AudioSampleEntry) {
+ AudioSampleEntry aseResult = mergeAudioSampleEntries((AudioSampleEntry) stsd.getBoxes().get(0), (AudioSampleEntry) curStsd.getBoxes().get(0));
+ if (aseResult != null) {
+ stsd.setBoxes(Collections.<Box>singletonList(aseResult));
+ return;
+ }
+ }
+ }
+ throw new IOException("Cannot append " + track + " to " + tracks[0] + " since their Sample Description Boxes differ: \n" + track.getSampleDescriptionBox() + "\n vs. \n" + tracks[0].getSampleDescriptionBox());
+ }
+ }
+ }
+ }
+
+ private AudioSampleEntry mergeAudioSampleEntries(AudioSampleEntry ase1, AudioSampleEntry ase2) throws IOException {
+ if (ase1.getType().equals(ase2.getType())) {
+ AudioSampleEntry ase = new AudioSampleEntry(ase2.getType());
+ if (ase1.getBytesPerFrame() == ase2.getBytesPerFrame()) {
+ ase.setBytesPerFrame(ase1.getBytesPerFrame());
+ } else {
+ return null;
+ }
+ if (ase1.getBytesPerPacket() == ase2.getBytesPerPacket()) {
+ ase.setBytesPerPacket(ase1.getBytesPerPacket());
+ } else {
+ return null;
+ }
+ if (ase1.getBytesPerSample() == ase2.getBytesPerSample()) {
+ ase.setBytesPerSample(ase1.getBytesPerSample());
+ } else {
+ return null;
+ }
+ if (ase1.getChannelCount() == ase2.getChannelCount()) {
+ ase.setChannelCount(ase1.getChannelCount());
+ } else {
+ return null;
+ }
+ if (ase1.getPacketSize() == ase2.getPacketSize()) {
+ ase.setPacketSize(ase1.getPacketSize());
+ } else {
+ return null;
+ }
+ if (ase1.getCompressionId() == ase2.getCompressionId()) {
+ ase.setCompressionId(ase1.getCompressionId());
+ } else {
+ return null;
+ }
+ if (ase1.getSampleRate() == ase2.getSampleRate()) {
+ ase.setSampleRate(ase1.getSampleRate());
+ } else {
+ return null;
+ }
+ if (ase1.getSampleSize() == ase2.getSampleSize()) {
+ ase.setSampleSize(ase1.getSampleSize());
+ } else {
+ return null;
+ }
+ if (ase1.getSamplesPerPacket() == ase2.getSamplesPerPacket()) {
+ ase.setSamplesPerPacket(ase1.getSamplesPerPacket());
+ } else {
+ return null;
+ }
+ if (ase1.getSoundVersion() == ase2.getSoundVersion()) {
+ ase.setSoundVersion(ase1.getSoundVersion());
+ } else {
+ return null;
+ }
+ if (Arrays.equals(ase1.getSoundVersion2Data(), ase2.getSoundVersion2Data())) {
+ ase.setSoundVersion2Data(ase1.getSoundVersion2Data());
+ } else {
+ return null;
+ }
+ if (ase1.getBoxes().size() == ase2.getBoxes().size()) {
+ Iterator<Box> bxs1 = ase1.getBoxes().iterator();
+ Iterator<Box> bxs2 = ase2.getBoxes().iterator();
+ while (bxs1.hasNext()) {
+ Box cur1 = bxs1.next();
+ Box cur2 = bxs2.next();
+ ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+ ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+ cur1.getBox(Channels.newChannel(baos1));
+ cur2.getBox(Channels.newChannel(baos2));
+ if (Arrays.equals(baos1.toByteArray(), baos2.toByteArray())) {
+ ase.addBox(cur1);
+ } else {
+ if (ESDescriptorBox.TYPE.equals(cur1.getType()) && ESDescriptorBox.TYPE.equals(cur2.getType())) {
+ ESDescriptorBox esdsBox1 = (ESDescriptorBox) cur1;
+ ESDescriptorBox esdsBox2 = (ESDescriptorBox) cur2;
+ ESDescriptor esds1 = esdsBox1.getEsDescriptor();
+ ESDescriptor esds2 = esdsBox2.getEsDescriptor();
+ if (esds1.getURLFlag() != esds2.getURLFlag()) {
+ return null;
+ }
+ if (esds1.getURLLength() != esds2.getURLLength()) {
+ return null;
+ }
+ if (esds1.getDependsOnEsId() != esds2.getDependsOnEsId()) {
+ return null;
+ }
+ if (esds1.getEsId() != esds2.getEsId()) {
+ return null;
+ }
+ if (esds1.getoCREsId() != esds2.getoCREsId()) {
+ return null;
+ }
+ if (esds1.getoCRstreamFlag() != esds2.getoCRstreamFlag()) {
+ return null;
+ }
+ if (esds1.getRemoteODFlag() != esds2.getRemoteODFlag()) {
+ return null;
+ }
+ if (esds1.getStreamDependenceFlag() != esds2.getStreamDependenceFlag()) {
+ return null;
+ }
+ if (esds1.getStreamPriority() != esds2.getStreamPriority()) {
+ return null;
+ }
+ if (esds1.getURLString() != null ? !esds1.getURLString().equals(esds2.getURLString()) : esds2.getURLString() != null) {
+ return null;
+ }
+ if (esds1.getDecoderConfigDescriptor() != null ? !esds1.getDecoderConfigDescriptor().equals(esds2.getDecoderConfigDescriptor()) : esds2.getDecoderConfigDescriptor() != null) {
+ DecoderConfigDescriptor dcd1 = esds1.getDecoderConfigDescriptor();
+ DecoderConfigDescriptor dcd2 = esds2.getDecoderConfigDescriptor();
+ if (!dcd1.getAudioSpecificInfo().equals(dcd2.getAudioSpecificInfo())) {
+ return null;
+ }
+ if (dcd1.getAvgBitRate() != dcd2.getAvgBitRate()) {
+ // I don't care
+ }
+ if (dcd1.getBufferSizeDB() != dcd2.getBufferSizeDB()) {
+ // I don't care
+ }
+
+ if (dcd1.getDecoderSpecificInfo() != null ? !dcd1.getDecoderSpecificInfo().equals(dcd2.getDecoderSpecificInfo()) : dcd2.getDecoderSpecificInfo() != null) {
+ return null;
+ }
+
+ if (dcd1.getMaxBitRate() != dcd2.getMaxBitRate()) {
+ // I don't care
+ }
+ if (!dcd1.getProfileLevelIndicationDescriptors().equals(dcd2.getProfileLevelIndicationDescriptors())) {
+ return null;
+ }
+
+ if (dcd1.getObjectTypeIndication() != dcd2.getObjectTypeIndication()) {
+ return null;
+ }
+ if (dcd1.getStreamType() != dcd2.getStreamType()) {
+ return null;
+ }
+ if (dcd1.getUpStream() != dcd2.getUpStream()) {
+ return null;
+ }
+
+
+ }
+ if (esds1.getOtherDescriptors() != null ? !esds1.getOtherDescriptors().equals(esds2.getOtherDescriptors()) : esds2.getOtherDescriptors() != null) {
+ return null;
+ }
+ if (esds1.getSlConfigDescriptor() != null ? !esds1.getSlConfigDescriptor().equals(esds2.getSlConfigDescriptor()) : esds2.getSlConfigDescriptor() != null) {
+ return null;
+ }
+ ase.addBox(cur1);
+ }
+ }
+ }
+ }
+ return ase;
+ } else {
+ return null;
+ }
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ ArrayList<ByteBuffer> lists = new ArrayList<ByteBuffer>();
+
+ for (Track track : tracks) {
+ lists.addAll(track.getSamples());
+ }
+
+ return lists;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return stsd;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ if (tracks[0].getDecodingTimeEntries() != null && !tracks[0].getDecodingTimeEntries().isEmpty()) {
+ List<long[]> lists = new LinkedList<long[]>();
+ for (Track track : tracks) {
+ lists.add(TimeToSampleBox.blowupTimeToSamples(track.getDecodingTimeEntries()));
+ }
+
+ LinkedList<TimeToSampleBox.Entry> returnDecodingEntries = new LinkedList<TimeToSampleBox.Entry>();
+ for (long[] list : lists) {
+ for (long nuDecodingTime : list) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getDelta() != nuDecodingTime) {
+ TimeToSampleBox.Entry e = new TimeToSampleBox.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ TimeToSampleBox.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ if (tracks[0].getCompositionTimeEntries() != null && !tracks[0].getCompositionTimeEntries().isEmpty()) {
+ List<int[]> lists = new LinkedList<int[]>();
+ for (Track track : tracks) {
+ lists.add(CompositionTimeToSample.blowupCompositionTimes(track.getCompositionTimeEntries()));
+ }
+ LinkedList<CompositionTimeToSample.Entry> compositionTimeEntries = new LinkedList<CompositionTimeToSample.Entry>();
+ for (int[] list : lists) {
+ for (int compositionTime : list) {
+ if (compositionTimeEntries.isEmpty() || compositionTimeEntries.getLast().getOffset() != compositionTime) {
+ CompositionTimeToSample.Entry e = new CompositionTimeToSample.Entry(1, compositionTime);
+ compositionTimeEntries.add(e);
+ } else {
+ CompositionTimeToSample.Entry e = compositionTimeEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ }
+ return compositionTimeEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public long[] getSyncSamples() {
+ if (tracks[0].getSyncSamples() != null && tracks[0].getSyncSamples().length > 0) {
+ int numSyncSamples = 0;
+ for (Track track : tracks) {
+ numSyncSamples += track.getSyncSamples().length;
+ }
+ long[] returnSyncSamples = new long[numSyncSamples];
+
+ int pos = 0;
+ long samplesBefore = 0;
+ for (Track track : tracks) {
+ for (long l : track.getSyncSamples()) {
+ returnSyncSamples[pos++] = samplesBefore + l;
+ }
+ samplesBefore += track.getSamples().size();
+ }
+ return returnSyncSamples;
+ } else {
+ return null;
+ }
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ if (tracks[0].getSampleDependencies() != null && !tracks[0].getSampleDependencies().isEmpty()) {
+ List<SampleDependencyTypeBox.Entry> list = new LinkedList<SampleDependencyTypeBox.Entry>();
+ for (Track track : tracks) {
+ list.addAll(track.getSampleDependencies());
+ }
+ return list;
+ } else {
+ return null;
+ }
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return tracks[0].getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return tracks[0].getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return tracks[0].getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return tracks[0].getSubsampleInformationBox();
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ChangeTimeScaleTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ChangeTimeScaleTrack.java.svn-base
new file mode 100644
index 0000000..50f76c2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ChangeTimeScaleTrack.java.svn-base
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.logging.Logger;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class ChangeTimeScaleTrack implements Track {
+ private static final Logger LOG = Logger.getLogger(ChangeTimeScaleTrack.class.getName());
+
+ Track source;
+ List<CompositionTimeToSample.Entry> ctts;
+ List<TimeToSampleBox.Entry> tts;
+ long timeScale;
+
+ /**
+ * Changes the time scale of the source track to the target time scale and makes sure
+ * that any rounding errors that may have summed are corrected exactly before the syncSamples.
+ *
+ * @param source the source track
+ * @param targetTimeScale the resulting time scale of this track.
+ * @param syncSamples at these sync points where rounding error are corrected.
+ */
+ public ChangeTimeScaleTrack(Track source, long targetTimeScale, long[] syncSamples) {
+ this.source = source;
+ this.timeScale = targetTimeScale;
+ double timeScaleFactor = (double) targetTimeScale / source.getTrackMetaData().getTimescale();
+ ctts = adjustCtts(source.getCompositionTimeEntries(), timeScaleFactor);
+ tts = adjustTts(source.getDecodingTimeEntries(), timeScaleFactor, syncSamples, getTimes(source, syncSamples, targetTimeScale));
+ }
+
+ private static long[] getTimes(Track track, long[] syncSamples, long targetTimeScale) {
+ long[] syncSampleTimes = new long[syncSamples.length];
+ Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries());
+
+ int currentSample = 1; // first syncsample is 1
+ long currentDuration = 0;
+ long currentDelta = 0;
+ int currentSyncSampleIndex = 0;
+ long left = 0;
+
+
+ while (currentSample <= syncSamples[syncSamples.length - 1]) {
+ if (currentSample++ == syncSamples[currentSyncSampleIndex]) {
+ syncSampleTimes[currentSyncSampleIndex++] = (currentDuration * targetTimeScale) / track.getTrackMetaData().getTimescale();
+ }
+ if (left-- == 0) {
+ TimeToSampleBox.Entry entry = timeQueue.poll();
+ left = entry.getCount() - 1;
+ currentDelta = entry.getDelta();
+ }
+ currentDuration += currentDelta;
+ }
+ return syncSampleTimes;
+
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return tts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return ctts;
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(timeScale);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ /**
+ * Adjusting the composition times is easy. Just scale it by the factor - that's it. There is no rounding
+ * error summing up.
+ *
+ * @param source
+ * @param timeScaleFactor
+ * @return
+ */
+ static List<CompositionTimeToSample.Entry> adjustCtts(List<CompositionTimeToSample.Entry> source, double timeScaleFactor) {
+ if (source != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(source.size());
+ for (CompositionTimeToSample.Entry entry : source) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), (int) Math.round(timeScaleFactor * entry.getOffset())));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ static List<TimeToSampleBox.Entry> adjustTts(List<TimeToSampleBox.Entry> source, double timeScaleFactor, long[] syncSample, long[] syncSampleTimes) {
+
+ long[] sourceArray = TimeToSampleBox.blowupTimeToSamples(source);
+ long summedDurations = 0;
+
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (int i = 1; i <= sourceArray.length; i++) {
+ long duration = sourceArray[i - 1];
+
+ long x = Math.round(timeScaleFactor * duration);
+
+
+ TimeToSampleBox.Entry last = entries2.peekLast();
+ int ssIndex;
+ if ((ssIndex = Arrays.binarySearch(syncSample, i + 1)) >= 0) {
+ // we are at the sample before sync point
+ if (syncSampleTimes[ssIndex] != summedDurations) {
+ long correction = syncSampleTimes[ssIndex] - (summedDurations + x);
+ LOG.finest(String.format("Sample %d %d / %d - correct by %d", i, summedDurations, syncSampleTimes[ssIndex], correction));
+ x += correction;
+ }
+ }
+ summedDurations += x;
+ if (last == null) {
+ entries2.add(new TimeToSampleBox.Entry(1, x));
+ } else if (last.getDelta() != x) {
+ entries2.add(new TimeToSampleBox.Entry(1, x));
+ } else {
+ last.setCount(last.getCount() + 1);
+ }
+
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "ChangeTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/CroppedTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/CroppedTrack.java.svn-base
new file mode 100644
index 0000000..2389961
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/CroppedTrack.java.svn-base
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Generates a Track that starts at fromSample and ends at toSample (exclusive). The user of this class
+ * has to make sure that the fromSample is a random access sample.
+ * <ul>
+ * <li>In AAC this is every single sample</li>
+ * <li>In H264 this is every sample that is marked in the SyncSampleBox</li>
+ * </ul>
+ */
+public class CroppedTrack extends AbstractTrack {
+ Track origTrack;
+ private int fromSample;
+ private int toSample;
+ private long[] syncSampleArray;
+
+ public CroppedTrack(Track origTrack, long fromSample, long toSample) {
+ this.origTrack = origTrack;
+ assert fromSample <= Integer.MAX_VALUE;
+ assert toSample <= Integer.MAX_VALUE;
+ this.fromSample = (int) fromSample;
+ this.toSample = (int) toSample;
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return origTrack.getSamples().subList(fromSample, toSample);
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return origTrack.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ if (origTrack.getDecodingTimeEntries() != null && !origTrack.getDecodingTimeEntries().isEmpty()) {
+ // todo optimize! too much long is allocated but then not used
+ long[] decodingTimes = TimeToSampleBox.blowupTimeToSamples(origTrack.getDecodingTimeEntries());
+ long[] nuDecodingTimes = new long[toSample - fromSample];
+ System.arraycopy(decodingTimes, fromSample, nuDecodingTimes, 0, toSample - fromSample);
+
+ LinkedList<TimeToSampleBox.Entry> returnDecodingEntries = new LinkedList<TimeToSampleBox.Entry>();
+
+ for (long nuDecodingTime : nuDecodingTimes) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getDelta() != nuDecodingTime) {
+ TimeToSampleBox.Entry e = new TimeToSampleBox.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ TimeToSampleBox.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ if (origTrack.getCompositionTimeEntries() != null && !origTrack.getCompositionTimeEntries().isEmpty()) {
+ int[] compositionTime = CompositionTimeToSample.blowupCompositionTimes(origTrack.getCompositionTimeEntries());
+ int[] nuCompositionTimes = new int[toSample - fromSample];
+ System.arraycopy(compositionTime, fromSample, nuCompositionTimes, 0, toSample - fromSample);
+
+ LinkedList<CompositionTimeToSample.Entry> returnDecodingEntries = new LinkedList<CompositionTimeToSample.Entry>();
+
+ for (int nuDecodingTime : nuCompositionTimes) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getOffset() != nuDecodingTime) {
+ CompositionTimeToSample.Entry e = new CompositionTimeToSample.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ CompositionTimeToSample.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ synchronized public long[] getSyncSamples() {
+ if (this.syncSampleArray == null) {
+ if (origTrack.getSyncSamples() != null && origTrack.getSyncSamples().length > 0) {
+ List<Long> syncSamples = new LinkedList<Long>();
+ for (long l : origTrack.getSyncSamples()) {
+ if (l >= fromSample && l < toSample) {
+ syncSamples.add(l - fromSample);
+ }
+ }
+ syncSampleArray = new long[syncSamples.size()];
+ for (int i = 0; i < syncSampleArray.length; i++) {
+ syncSampleArray[i] = syncSamples.get(i);
+
+ }
+ return syncSampleArray;
+ } else {
+ return null;
+ }
+ } else {
+ return this.syncSampleArray;
+ }
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ if (origTrack.getSampleDependencies() != null && !origTrack.getSampleDependencies().isEmpty()) {
+ return origTrack.getSampleDependencies().subList(fromSample, toSample);
+ } else {
+ return null;
+ }
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return origTrack.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return origTrack.getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return origTrack.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return origTrack.getSubsampleInformationBox();
+ }
+
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/DivideTimeScaleTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/DivideTimeScaleTrack.java.svn-base
new file mode 100644
index 0000000..c51e8e0
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/DivideTimeScaleTrack.java.svn-base
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class DivideTimeScaleTrack implements Track {
+ Track source;
+ private int timeScaleDivisor;
+
+ public DivideTimeScaleTrack(Track source, int timeScaleDivisor) {
+ this.source = source;
+ this.timeScaleDivisor = timeScaleDivisor;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return adjustTts();
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return adjustCtts();
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(source.getTrackMetaData().getTimescale() / this.timeScaleDivisor);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ List<CompositionTimeToSample.Entry> adjustCtts() {
+ List<CompositionTimeToSample.Entry> origCtts = this.source.getCompositionTimeEntries();
+ if (origCtts != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(origCtts.size());
+ for (CompositionTimeToSample.Entry entry : origCtts) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), entry.getOffset() / timeScaleDivisor));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ List<TimeToSampleBox.Entry> adjustTts() {
+ List<TimeToSampleBox.Entry> origTts = source.getDecodingTimeEntries();
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (TimeToSampleBox.Entry e : origTts) {
+ entries2.add(new TimeToSampleBox.Entry(e.getCount(), e.getDelta() / timeScaleDivisor));
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "MultiplyTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/EC3TrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/EC3TrackImpl.java.svn-base
new file mode 100644
index 0000000..d0b2d76
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/EC3TrackImpl.java.svn-base
@@ -0,0 +1,436 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.EC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: magnus
+ * Date: 2012-03-14
+ * Time: 10:39
+ * To change this template use File | Settings | File Templates.
+ */
+public class EC3TrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int frameSize;
+
+ List<BitStreamInfo> entries = new LinkedList<BitStreamInfo>();
+
+ private BufferedInputStream inputStream;
+ private List<ByteBuffer> samples;
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ private String lang = "und";
+
+ public EC3TrackImpl(InputStream fin, String lang) throws IOException {
+ this.lang = lang;
+ parse(fin);
+ }
+
+ public EC3TrackImpl(InputStream fin) throws IOException {
+ parse(fin);
+ }
+
+ private void parse(InputStream fin) throws IOException {
+ inputStream = new BufferedInputStream(fin);
+
+ boolean done = false;
+ inputStream.mark(10000);
+ while (!done) {
+ BitStreamInfo bsi = readVariables();
+ if (bsi == null) {
+ throw new IOException();
+ }
+ for (BitStreamInfo entry : entries) {
+ if (bsi.strmtyp != 1 && entry.substreamid == bsi.substreamid) {
+ done = true;
+ }
+ }
+ if (!done) {
+ entries.add(bsi);
+ long skipped = inputStream.skip(bsi.frameSize);
+ assert skipped == bsi.frameSize;
+ }
+ }
+
+ inputStream.reset();
+
+ if (entries.size() == 0) {
+ throw new IOException();
+ }
+ samplerate = entries.get(0).samplerate;
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ec-3");
+ audioSampleEntry.setChannelCount(2); // According to ETSI TS 102 366 Annex F
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+ EC3SpecificBox ec3 = new EC3SpecificBox();
+ int[] deps = new int[entries.size()];
+ int[] chan_locs = new int[entries.size()];
+ for (BitStreamInfo bsi : entries) {
+ if (bsi.strmtyp == 1) {
+ deps[bsi.substreamid]++;
+ chan_locs[bsi.substreamid] = ((bsi.chanmap >> 6) & 0x100) | ((bsi.chanmap >> 5) & 0xff);
+ }
+ }
+ for (BitStreamInfo bsi : entries) {
+ if (bsi.strmtyp != 1) {
+ EC3SpecificBox.Entry e = new EC3SpecificBox.Entry();
+ e.fscod = bsi.fscod;
+ e.bsid = bsi.bsid;
+ e.bsmod = bsi.bsmod;
+ e.acmod = bsi.acmod;
+ e.lfeon = bsi.lfeon;
+ e.reserved = 0;
+ e.num_dep_sub = deps[bsi.substreamid];
+ e.chan_loc = chan_locs[bsi.substreamid];
+ e.reserved2 = 0;
+ ec3.addEntry(e);
+ }
+ bitrate += bsi.bitrate;
+ frameSize += bsi.frameSize;
+ }
+
+ ec3.setDataRate(bitrate / 1000);
+ audioSampleEntry.addBox(ec3);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "soun";
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new SoundMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private BitStreamInfo readVariables() throws IOException {
+ byte[] data = new byte[200];
+ inputStream.mark(200);
+ if (200 != inputStream.read(data, 0, 200)) {
+ return null;
+ }
+ inputStream.reset(); // Rewind
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(16);
+ if (syncword != 0xb77) {
+ return null;
+ }
+
+ BitStreamInfo entry = new BitStreamInfo();
+
+ entry.strmtyp = brb.readBits(2);
+ entry.substreamid = brb.readBits(3);
+ int frmsiz = brb.readBits(11);
+ entry.frameSize = 2 * (frmsiz + 1);
+
+ entry.fscod = brb.readBits(2);
+ int fscod2 = -1;
+ int numblkscod;
+ if (entry.fscod == 3) {
+ fscod2 = brb.readBits(2);
+ numblkscod = 3;
+ } else {
+ numblkscod = brb.readBits(2);
+ }
+ int numberOfBlocksPerSyncFrame = 0;
+ switch (numblkscod) {
+ case 0:
+ numberOfBlocksPerSyncFrame = 1;
+ break;
+
+ case 1:
+ numberOfBlocksPerSyncFrame = 2;
+ break;
+
+ case 2:
+ numberOfBlocksPerSyncFrame = 3;
+ break;
+
+ case 3:
+ numberOfBlocksPerSyncFrame = 6;
+ break;
+
+ }
+ entry.frameSize *= (6 / numberOfBlocksPerSyncFrame);
+
+ entry.acmod = brb.readBits(3);
+ entry.lfeon = brb.readBits(1);
+ entry.bsid = brb.readBits(5);
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8); // compr
+ }
+ if (0 == entry.acmod) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8);
+ }
+ }
+ if (1 == entry.strmtyp) {
+ if (1 == brb.readBits(1)) {
+ entry.chanmap = brb.readBits(16);
+ }
+ }
+ if (1 == brb.readBits(1)) { // mixing metadata
+ if (entry.acmod > 2) {
+ brb.readBits(2);
+ }
+ if (1 == (entry.acmod & 1) && entry.acmod > 2) {
+ brb.readBits(3);
+ brb.readBits(3);
+ }
+ if (0 < (entry.acmod & 4)) {
+ brb.readBits(3);
+ brb.readBits(3);
+ }
+ if (1 == entry.lfeon) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ }
+ }
+ if (0 == entry.strmtyp) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ if (0 == entry.acmod) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ int mixdef = brb.readBits(2);
+ if (1 == mixdef) {
+ brb.readBits(5);
+ } else if (2 == mixdef) {
+ brb.readBits(12);
+ } else if (3 == mixdef) {
+ int mixdeflen = brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(7);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8);
+ }
+ }
+ }
+ for (int i = 0; i < (mixdeflen + 2); i++) {
+ brb.readBits(8);
+ }
+ brb.byteSync();
+ }
+ if (entry.acmod < 2) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(14);
+ }
+ if (0 == entry.acmod) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(14);
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ if (numblkscod == 0) {
+ brb.readBits(5);
+ } else {
+ for (int i = 0; i < numberOfBlocksPerSyncFrame; i++) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ if (1 == brb.readBits(1)) { // infomdate
+ entry.bsmod = brb.readBits(3);
+ }
+
+ switch (entry.fscod) {
+ case 0:
+ entry.samplerate = 48000;
+ break;
+
+ case 1:
+ entry.samplerate = 44100;
+ break;
+
+ case 2:
+ entry.samplerate = 32000;
+ break;
+
+ case 3: {
+ switch (fscod2) {
+ case 0:
+ entry.samplerate = 24000;
+ break;
+
+ case 1:
+ entry.samplerate = 22050;
+ break;
+
+ case 2:
+ entry.samplerate = 16000;
+ break;
+
+ case 3:
+ entry.samplerate = 0;
+ break;
+ }
+ break;
+ }
+
+ }
+ if (entry.samplerate == 0) {
+ return null;
+ }
+
+ entry.bitrate = (int) (((double) entry.samplerate) / 1536.0 * entry.frameSize * 8);
+
+ return entry;
+ }
+
+ private boolean readSamples() throws IOException {
+ int read = frameSize;
+ boolean ret = false;
+ while (frameSize == read) {
+ ret = true;
+ byte[] data = new byte[frameSize];
+ read = inputStream.read(data);
+ if (read == frameSize) {
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1536));
+ }
+ }
+ return ret;
+ }
+
+ public static class BitStreamInfo extends EC3SpecificBox.Entry {
+ public int frameSize;
+ public int substreamid;
+ public int bitrate;
+ public int samplerate;
+ public int strmtyp;
+ public int chanmap;
+
+ @Override
+ public String toString() {
+ return "BitStreamInfo{" +
+ "frameSize=" + frameSize +
+ ", substreamid=" + substreamid +
+ ", bitrate=" + bitrate +
+ ", samplerate=" + samplerate +
+ ", strmtyp=" + strmtyp +
+ ", chanmap=" + chanmap +
+ '}';
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "EC3TrackImpl{" +
+ "bitrate=" + bitrate +
+ ", samplerate=" + samplerate +
+ ", entries=" + entries +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/H264TrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/H264TrackImpl.java.svn-base
new file mode 100644
index 0000000..b3c1866
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/H264TrackImpl.java.svn-base
@@ -0,0 +1,740 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
+import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.h264.model.PictureParameterSet;
+import com.googlecode.mp4parser.h264.model.SeqParameterSet;
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * The <code>H264TrackImpl</code> creates a <code>Track</code> from an H.264
+ * Annex B file.
+ */
+public class H264TrackImpl extends AbstractTrack {
+ private static final Logger LOG = Logger.getLogger(H264TrackImpl.class.getName());
+
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ private ReaderWrapper reader;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+
+ List<TimeToSampleBox.Entry> stts;
+ List<CompositionTimeToSample.Entry> ctts;
+ List<SampleDependencyTypeBox.Entry> sdtp;
+ List<Integer> stss;
+
+ SeqParameterSet seqParameterSet = null;
+ PictureParameterSet pictureParameterSet = null;
+ LinkedList<byte[]> seqParameterSetList = new LinkedList<byte[]>();
+ LinkedList<byte[]> pictureParameterSetList = new LinkedList<byte[]>();
+
+ private int width;
+ private int height;
+ private int timescale;
+ private int frametick;
+ private int currentScSize;
+ private int prevScSize;
+
+ private SEIMessage seiMessage;
+ int frameNrInGop = 0;
+ private boolean determineFrameRate = true;
+ private String lang = "und";
+
+ public H264TrackImpl(InputStream inputStream, String lang, long timescale) throws IOException {
+ this.lang = lang;
+ if (timescale > 1000) {
+ timescale = timescale; //e.g. 23976
+ frametick = 1000;
+ determineFrameRate = false;
+ } else {
+ throw new IllegalArgumentException("Timescale must be specified in milliseconds!");
+ }
+ parse(inputStream);
+ }
+
+ public H264TrackImpl(InputStream inputStream, String lang) throws IOException {
+ this.lang = lang;
+ parse(inputStream);
+ }
+
+ public H264TrackImpl(InputStream inputStream) throws IOException {
+ parse(inputStream);
+ }
+
+ private void parse(InputStream inputStream) throws IOException {
+ this.reader = new ReaderWrapper(inputStream);
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+ ctts = new LinkedList<CompositionTimeToSample.Entry>();
+ sdtp = new LinkedList<SampleDependencyTypeBox.Entry>();
+ stss = new LinkedList<Integer>();
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ VisualSampleEntry visualSampleEntry = new VisualSampleEntry("avc1");
+ visualSampleEntry.setDataReferenceIndex(1);
+ visualSampleEntry.setDepth(24);
+ visualSampleEntry.setFrameCount(1);
+ visualSampleEntry.setHorizresolution(72);
+ visualSampleEntry.setVertresolution(72);
+ visualSampleEntry.setWidth(width);
+ visualSampleEntry.setHeight(height);
+ visualSampleEntry.setCompressorname("AVC Coding");
+
+ AvcConfigurationBox avcConfigurationBox = new AvcConfigurationBox();
+
+ avcConfigurationBox.setSequenceParameterSets(seqParameterSetList);
+ avcConfigurationBox.setPictureParameterSets(pictureParameterSetList);
+ avcConfigurationBox.setAvcLevelIndication(seqParameterSet.level_idc);
+ avcConfigurationBox.setAvcProfileIndication(seqParameterSet.profile_idc);
+ avcConfigurationBox.setBitDepthLumaMinus8(seqParameterSet.bit_depth_luma_minus8);
+ avcConfigurationBox.setBitDepthChromaMinus8(seqParameterSet.bit_depth_chroma_minus8);
+ avcConfigurationBox.setChromaFormat(seqParameterSet.chroma_format_idc.getId());
+ avcConfigurationBox.setConfigurationVersion(1);
+ avcConfigurationBox.setLengthSizeMinusOne(3);
+ avcConfigurationBox.setProfileCompatibility(seqParameterSetList.get(0)[1]);
+
+ visualSampleEntry.addBox(avcConfigurationBox);
+ sampleDescriptionBox.addBox(visualSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(timescale);
+ trackMetaData.setWidth(width);
+ trackMetaData.setHeight(height);
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return ctts;
+ }
+
+ public long[] getSyncSamples() {
+ long[] returns = new long[stss.size()];
+ for (int i = 0; i < stss.size(); i++) {
+ returns[i] = stss.get(i);
+ }
+ return returns;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return sdtp;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "vide";
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new VideoMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private boolean readVariables() {
+ width = (seqParameterSet.pic_width_in_mbs_minus1 + 1) * 16;
+ int mult = 2;
+ if (seqParameterSet.frame_mbs_only_flag) {
+ mult = 1;
+ }
+ height = 16 * (seqParameterSet.pic_height_in_map_units_minus1 + 1) * mult;
+ if (seqParameterSet.frame_cropping_flag) {
+ int chromaArrayType = 0;
+ if (seqParameterSet.residual_color_transform_flag == false) {
+ chromaArrayType = seqParameterSet.chroma_format_idc.getId();
+ }
+ int cropUnitX = 1;
+ int cropUnitY = mult;
+ if (chromaArrayType != 0) {
+ cropUnitX = seqParameterSet.chroma_format_idc.getSubWidth();
+ cropUnitY = seqParameterSet.chroma_format_idc.getSubHeight() * mult;
+ }
+
+ width -= cropUnitX * (seqParameterSet.frame_crop_left_offset + seqParameterSet.frame_crop_right_offset);
+ height -= cropUnitY * (seqParameterSet.frame_crop_top_offset + seqParameterSet.frame_crop_bottom_offset);
+ }
+ return true;
+ }
+
+ private boolean findNextStartcode() throws IOException {
+ byte[] test = new byte[]{-1, -1, -1, -1};
+
+ int c;
+ while ((c = reader.read()) != -1) {
+ test[0] = test[1];
+ test[1] = test[2];
+ test[2] = test[3];
+ test[3] = (byte) c;
+ if (test[0] == 0 && test[1] == 0 && test[2] == 0 && test[3] == 1) {
+ prevScSize = currentScSize;
+ currentScSize = 4;
+ return true;
+ }
+ if (test[0] == 0 && test[1] == 0 && test[2] == 1) {
+ prevScSize = currentScSize;
+ currentScSize = 3;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private enum NALActions {
+ IGNORE, BUFFER, STORE, END
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+
+ readSamples = true;
+
+
+ findNextStartcode();
+ reader.mark();
+ long pos = reader.getPos();
+
+ ArrayList<byte[]> buffered = new ArrayList<byte[]>();
+
+ int frameNr = 0;
+
+ while (findNextStartcode()) {
+ long newpos = reader.getPos();
+ int size = (int) (newpos - pos - prevScSize);
+ reader.reset();
+ byte[] data = new byte[size ];
+ reader.read(data);
+ int type = data[0];
+ int nal_ref_idc = (type >> 5) & 3;
+ int nal_unit_type = type & 0x1f;
+ LOG.fine("Found startcode at " + (pos -4) + " Type: " + nal_unit_type + " ref idc: " + nal_ref_idc + " (size " + size + ")");
+ NALActions action = handleNALUnit(nal_ref_idc, nal_unit_type, data);
+ switch (action) {
+ case IGNORE:
+ break;
+
+ case BUFFER:
+ buffered.add(data);
+ break;
+
+ case STORE:
+ int stdpValue = 22;
+ frameNr++;
+ buffered.add(data);
+ ByteBuffer bb = createSample(buffered);
+ boolean IdrPicFlag = false;
+ if (nal_unit_type == 5) {
+ stdpValue += 16;
+ IdrPicFlag = true;
+ }
+ ByteArrayInputStream bs = cleanBuffer(buffered.get(buffered.size() - 1));
+ SliceHeader sh = new SliceHeader(bs, seqParameterSet, pictureParameterSet, IdrPicFlag);
+ if (sh.slice_type == SliceHeader.SliceType.B) {
+ stdpValue += 4;
+ }
+ LOG.fine("Adding sample with size " + bb.capacity() + " and header " + sh);
+ buffered.clear();
+ samples.add(bb);
+ stts.add(new TimeToSampleBox.Entry(1, frametick));
+ if (nal_unit_type == 5) { // IDR Picture
+ stss.add(frameNr);
+ }
+ if (seiMessage.n_frames == 0) {
+ frameNrInGop = 0;
+ }
+ int offset = 0;
+ if (seiMessage.clock_timestamp_flag) {
+ offset = seiMessage.n_frames - frameNrInGop;
+ } else if (seiMessage.removal_delay_flag) {
+ offset = seiMessage.dpb_removal_delay / 2;
+ }
+ ctts.add(new CompositionTimeToSample.Entry(1, offset * frametick));
+ sdtp.add(new SampleDependencyTypeBox.Entry(stdpValue));
+ frameNrInGop++;
+ break;
+
+ case END:
+ return true;
+
+
+ }
+ pos = newpos;
+ reader.seek(currentScSize);
+ reader.mark();
+ }
+ return true;
+ }
+
+ private ByteBuffer createSample(List<byte[]> buffers) {
+ int outsize = 0;
+ for (int i = 0; i < buffers.size(); i++) {
+ outsize += buffers.get(i).length + 4;
+ }
+ byte[] output = new byte[outsize];
+
+ ByteBuffer bb = ByteBuffer.wrap(output);
+ for (int i = 0; i < buffers.size(); i++) {
+ bb.putInt(buffers.get(i).length);
+ bb.put(buffers.get(i));
+ }
+ bb.rewind();
+ return bb;
+ }
+
+ private ByteArrayInputStream cleanBuffer(byte[] data) {
+ byte[] output = new byte[data.length];
+ int inPos = 0;
+ int outPos = 0;
+ while (inPos < data.length) {
+ if (data[inPos] == 0 && data[inPos + 1] == 0 && data[inPos + 2] == 3) {
+ output[outPos] = 0;
+ output[outPos + 1] = 0;
+ inPos += 3;
+ outPos += 2;
+ } else {
+ output[outPos] = data[inPos];
+ inPos++;
+ outPos++;
+ }
+ }
+ return new ByteArrayInputStream(output, 0, outPos);
+ }
+
+ private NALActions handleNALUnit(int nal_ref_idc, int nal_unit_type, byte[] data) throws IOException {
+ NALActions action;
+ switch (nal_unit_type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ action = NALActions.STORE; // Will only work in single slice per frame mode!
+ break;
+
+ case 6:
+ seiMessage = new SEIMessage(cleanBuffer(data), seqParameterSet);
+ action = NALActions.BUFFER;
+ break;
+
+ case 9:
+// printAccessUnitDelimiter(data);
+ int type = data[1] >> 5;
+ LOG.fine("Access unit delimiter type: " + type);
+ action = NALActions.BUFFER;
+ break;
+
+
+ case 7:
+ if (seqParameterSet == null) {
+ ByteArrayInputStream is = cleanBuffer(data);
+ is.read();
+ seqParameterSet = SeqParameterSet.read(is);
+ seqParameterSetList.add(data);
+ configureFramerate();
+ }
+ action = NALActions.IGNORE;
+ break;
+
+ case 8:
+ if (pictureParameterSet == null) {
+ ByteArrayInputStream is = new ByteArrayInputStream(data);
+ is.read();
+ pictureParameterSet = PictureParameterSet.read(is);
+ pictureParameterSetList.add(data);
+ }
+ action = NALActions.IGNORE;
+ break;
+
+ case 10:
+ case 11:
+ action = NALActions.END;
+ break;
+
+ default:
+ System.err.println("Unknown NAL unit type: " + nal_unit_type);
+ action = NALActions.IGNORE;
+
+ }
+
+ return action;
+ }
+
+ private void configureFramerate() {
+ if (determineFrameRate) {
+ if (seqParameterSet.vuiParams != null) {
+ timescale = seqParameterSet.vuiParams.time_scale >> 1; // Not sure why, but I found this in several places, and it works...
+ frametick = seqParameterSet.vuiParams.num_units_in_tick;
+ if (timescale == 0 || frametick == 0) {
+ System.err.println("Warning: vuiParams contain invalid values: time_scale: " + timescale + " and frame_tick: " + frametick + ". Setting frame rate to 25fps");
+ timescale = 90000;
+ frametick = 3600;
+ }
+ } else {
+ System.err.println("Warning: Can't determine frame rate. Guessing 25 fps");
+ timescale = 90000;
+ frametick = 3600;
+ }
+ }
+ }
+
+ public void printAccessUnitDelimiter(byte[] data) {
+ LOG.fine("Access unit delimiter: " + (data[1] >> 5));
+ }
+
+ public static class SliceHeader {
+
+ public enum SliceType {
+ P, B, I, SP, SI
+ }
+
+ public int first_mb_in_slice;
+ public SliceType slice_type;
+ public int pic_parameter_set_id;
+ public int colour_plane_id;
+ public int frame_num;
+ public boolean field_pic_flag = false;
+ public boolean bottom_field_flag = false;
+ public int idr_pic_id;
+ public int pic_order_cnt_lsb;
+ public int delta_pic_order_cnt_bottom;
+
+ public SliceHeader(InputStream is, SeqParameterSet sps, PictureParameterSet pps, boolean IdrPicFlag) throws IOException {
+ is.read();
+ CAVLCReader reader = new CAVLCReader(is);
+ first_mb_in_slice = reader.readUE("SliceHeader: first_mb_in_slice");
+ switch (reader.readUE("SliceHeader: slice_type")) {
+ case 0:
+ case 5:
+ slice_type = SliceType.P;
+ break;
+
+ case 1:
+ case 6:
+ slice_type = SliceType.B;
+ break;
+
+ case 2:
+ case 7:
+ slice_type = SliceType.I;
+ break;
+
+ case 3:
+ case 8:
+ slice_type = SliceType.SP;
+ break;
+
+ case 4:
+ case 9:
+ slice_type = SliceType.SI;
+ break;
+
+ }
+ pic_parameter_set_id = reader.readUE("SliceHeader: pic_parameter_set_id");
+ if (sps.residual_color_transform_flag) {
+ colour_plane_id = reader.readU(2, "SliceHeader: colour_plane_id");
+ }
+ frame_num = reader.readU(sps.log2_max_frame_num_minus4 + 4, "SliceHeader: frame_num");
+
+ if (!sps.frame_mbs_only_flag) {
+ field_pic_flag = reader.readBool("SliceHeader: field_pic_flag");
+ if (field_pic_flag) {
+ bottom_field_flag = reader.readBool("SliceHeader: bottom_field_flag");
+ }
+ }
+ if (IdrPicFlag) {
+ idr_pic_id = reader.readUE("SliceHeader: idr_pic_id");
+ if (sps.pic_order_cnt_type == 0) {
+ pic_order_cnt_lsb = reader.readU(sps.log2_max_pic_order_cnt_lsb_minus4 + 4, "SliceHeader: pic_order_cnt_lsb");
+ if (pps.pic_order_present_flag && !field_pic_flag) {
+ delta_pic_order_cnt_bottom = reader.readSE("SliceHeader: delta_pic_order_cnt_bottom");
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SliceHeader{" +
+ "first_mb_in_slice=" + first_mb_in_slice +
+ ", slice_type=" + slice_type +
+ ", pic_parameter_set_id=" + pic_parameter_set_id +
+ ", colour_plane_id=" + colour_plane_id +
+ ", frame_num=" + frame_num +
+ ", field_pic_flag=" + field_pic_flag +
+ ", bottom_field_flag=" + bottom_field_flag +
+ ", idr_pic_id=" + idr_pic_id +
+ ", pic_order_cnt_lsb=" + pic_order_cnt_lsb +
+ ", delta_pic_order_cnt_bottom=" + delta_pic_order_cnt_bottom +
+ '}';
+ }
+ }
+
+ private class ReaderWrapper {
+ private InputStream inputStream;
+ private long pos = 0;
+
+ private long markPos = 0;
+
+
+ private ReaderWrapper(InputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ int read() throws IOException {
+ pos++;
+ return inputStream.read();
+ }
+
+ long read(byte[] data) throws IOException {
+ long read = inputStream.read(data);
+ pos += read;
+ return read;
+ }
+
+ long seek(int dist) throws IOException {
+ long seeked = inputStream.skip(dist);
+ pos += seeked;
+ return seeked;
+ }
+
+ public long getPos() {
+ return pos;
+ }
+
+ public void mark() {
+ int i = 1048576;
+ LOG.fine("Marking with " + i + " at " + pos);
+ inputStream.mark(i);
+ markPos = pos;
+ }
+
+
+ public void reset() throws IOException {
+ long diff = pos - markPos;
+ LOG.fine("Resetting to " + markPos + " (pos is " + pos + ") which makes the buffersize " + diff);
+ inputStream.reset();
+ pos = markPos;
+ }
+ }
+
+ public class SEIMessage {
+
+ int payloadType = 0;
+ int payloadSize = 0;
+
+ boolean removal_delay_flag;
+ int cpb_removal_delay;
+ int dpb_removal_delay;
+
+ boolean clock_timestamp_flag;
+ int pic_struct;
+ int ct_type;
+ int nuit_field_based_flag;
+ int counting_type;
+ int full_timestamp_flag;
+ int discontinuity_flag;
+ int cnt_dropped_flag;
+ int n_frames;
+ int seconds_value;
+ int minutes_value;
+ int hours_value;
+ int time_offset_length;
+ int time_offset;
+
+ SeqParameterSet sps;
+
+ public SEIMessage(InputStream is, SeqParameterSet sps) throws IOException {
+ this.sps = sps;
+ is.read();
+ int datasize = is.available();
+ int read = 0;
+ while (read < datasize) {
+ payloadType = 0;
+ payloadSize = 0;
+ int last_payload_type_bytes = is.read();
+ read++;
+ while (last_payload_type_bytes == 0xff) {
+ payloadType += last_payload_type_bytes;
+ last_payload_type_bytes = is.read();
+ read++;
+ }
+ payloadType += last_payload_type_bytes;
+ int last_payload_size_bytes = is.read();
+ read++;
+
+ while (last_payload_size_bytes == 0xff) {
+ payloadSize += last_payload_size_bytes;
+ last_payload_size_bytes = is.read();
+ read++;
+ }
+ payloadSize += last_payload_size_bytes;
+ if (datasize - read >= payloadSize) {
+ if (payloadType == 1) { // pic_timing is what we are interested in!
+ if (sps.vuiParams != null && (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null || sps.vuiParams.pic_struct_present_flag)) {
+ byte[] data = new byte[payloadSize];
+ is.read(data);
+ read += payloadSize;
+ CAVLCReader reader = new CAVLCReader(new ByteArrayInputStream(data));
+ if (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null) {
+ removal_delay_flag = true;
+ cpb_removal_delay = reader.readU(sps.vuiParams.nalHRDParams.cpb_removal_delay_length_minus1 + 1, "SEI: cpb_removal_delay");
+ dpb_removal_delay = reader.readU(sps.vuiParams.nalHRDParams.dpb_output_delay_length_minus1 + 1, "SEI: dpb_removal_delay");
+ } else {
+ removal_delay_flag = false;
+ }
+ if (sps.vuiParams.pic_struct_present_flag) {
+ pic_struct = reader.readU(4, "SEI: pic_struct");
+ int numClockTS;
+ switch (pic_struct) {
+ case 0:
+ case 1:
+ case 2:
+ default:
+ numClockTS = 1;
+ break;
+
+ case 3:
+ case 4:
+ case 7:
+ numClockTS = 2;
+ break;
+
+ case 5:
+ case 6:
+ case 8:
+ numClockTS = 3;
+ break;
+ }
+ for (int i = 0; i < numClockTS; i++) {
+ clock_timestamp_flag = reader.readBool("pic_timing SEI: clock_timestamp_flag[" + i + "]");
+ if (clock_timestamp_flag) {
+ ct_type = reader.readU(2, "pic_timing SEI: ct_type");
+ nuit_field_based_flag = reader.readU(1, "pic_timing SEI: nuit_field_based_flag");
+ counting_type = reader.readU(5, "pic_timing SEI: counting_type");
+ full_timestamp_flag = reader.readU(1, "pic_timing SEI: full_timestamp_flag");
+ discontinuity_flag = reader.readU(1, "pic_timing SEI: discontinuity_flag");
+ cnt_dropped_flag = reader.readU(1, "pic_timing SEI: cnt_dropped_flag");
+ n_frames = reader.readU(8, "pic_timing SEI: n_frames");
+ if (full_timestamp_flag == 1) {
+ seconds_value = reader.readU(6, "pic_timing SEI: seconds_value");
+ minutes_value = reader.readU(6, "pic_timing SEI: minutes_value");
+ hours_value = reader.readU(5, "pic_timing SEI: hours_value");
+ } else {
+ if (reader.readBool("pic_timing SEI: seconds_flag")) {
+ seconds_value = reader.readU(6, "pic_timing SEI: seconds_value");
+ if (reader.readBool("pic_timing SEI: minutes_flag")) {
+ minutes_value = reader.readU(6, "pic_timing SEI: minutes_value");
+ if (reader.readBool("pic_timing SEI: hours_flag")) {
+ hours_value = reader.readU(5, "pic_timing SEI: hours_value");
+ }
+ }
+ }
+ }
+ if (true) {
+ if (sps.vuiParams.nalHRDParams != null) {
+ time_offset_length = sps.vuiParams.nalHRDParams.time_offset_length;
+ } else if (sps.vuiParams.vclHRDParams != null) {
+ time_offset_length = sps.vuiParams.vclHRDParams.time_offset_length;
+ } else {
+ time_offset_length = 24;
+ }
+ time_offset = reader.readU(24, "pic_timing SEI: time_offset");
+ }
+ }
+ }
+ }
+
+ } else {
+ for (int i = 0; i < payloadSize; i++) {
+ is.read();
+ read++;
+ }
+ }
+ } else {
+ for (int i = 0; i < payloadSize; i++) {
+ is.read();
+ read++;
+ }
+ }
+ } else {
+ read = datasize;
+ }
+ LOG.fine(this.toString());
+ }
+ }
+
+ @Override
+ public String toString() {
+ String out = "SEIMessage{" +
+ "payloadType=" + payloadType +
+ ", payloadSize=" + payloadSize;
+ if (payloadType == 1) {
+ if (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null) {
+
+ out += ", cpb_removal_delay=" + cpb_removal_delay +
+ ", dpb_removal_delay=" + dpb_removal_delay;
+ }
+ if (sps.vuiParams.pic_struct_present_flag) {
+ out += ", pic_struct=" + pic_struct;
+ if (clock_timestamp_flag) {
+ out += ", ct_type=" + ct_type +
+ ", nuit_field_based_flag=" + nuit_field_based_flag +
+ ", counting_type=" + counting_type +
+ ", full_timestamp_flag=" + full_timestamp_flag +
+ ", discontinuity_flag=" + discontinuity_flag +
+ ", cnt_dropped_flag=" + cnt_dropped_flag +
+ ", n_frames=" + n_frames +
+ ", seconds_value=" + seconds_value +
+ ", minutes_value=" + minutes_value +
+ ", hours_value=" + hours_value +
+ ", time_offset_length=" + time_offset_length +
+ ", time_offset=" + time_offset;
+ }
+ }
+ }
+ out += '}';
+ return out;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/MultiplyTimeScaleTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/MultiplyTimeScaleTrack.java.svn-base
new file mode 100644
index 0000000..e9a90e4
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/MultiplyTimeScaleTrack.java.svn-base
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+import static com.googlecode.mp4parser.util.Math.gcd;
+import static com.googlecode.mp4parser.util.Math.lcm;
+import static java.lang.Math.round;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class MultiplyTimeScaleTrack implements Track {
+ Track source;
+ private int timeScaleFactor;
+
+ public MultiplyTimeScaleTrack(Track source, int timeScaleFactor) {
+ this.source = source;
+ this.timeScaleFactor = timeScaleFactor;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return adjustTts(source.getDecodingTimeEntries(), timeScaleFactor);
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return adjustCtts(source.getCompositionTimeEntries(), timeScaleFactor);
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(source.getTrackMetaData().getTimescale() * this.timeScaleFactor);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ static List<CompositionTimeToSample.Entry> adjustCtts(List<CompositionTimeToSample.Entry> source, int timeScaleFactor) {
+ if (source != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(source.size());
+ for (CompositionTimeToSample.Entry entry : source) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), timeScaleFactor * entry.getOffset()));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ static List<TimeToSampleBox.Entry> adjustTts(List<TimeToSampleBox.Entry> source, int timeScaleFactor) {
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (TimeToSampleBox.Entry e : source) {
+ entries2.add(new TimeToSampleBox.Entry(e.getCount(), timeScaleFactor * e.getDelta()));
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "MultiplyTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/QuicktimeTextTrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/QuicktimeTextTrackImpl.java.svn-base
new file mode 100644
index 0000000..8efa399
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/QuicktimeTextTrackImpl.java.svn-base
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.TextSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.apple.BaseMediaInfoAtom;
+import com.googlecode.mp4parser.boxes.apple.GenericMediaHeaderAtom;
+import com.googlecode.mp4parser.boxes.apple.GenericMediaHeaderTextAtom;
+import com.googlecode.mp4parser.boxes.apple.QuicktimeTextSampleEntry;
+import com.googlecode.mp4parser.boxes.threegpp26245.FontTableBox;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A Text track as Quicktime Pro would create.
+ */
+public class QuicktimeTextTrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+ List<Line> subs = new LinkedList<Line>();
+
+ public List<Line> getSubs() {
+ return subs;
+ }
+
+ public QuicktimeTextTrackImpl() {
+ sampleDescriptionBox = new SampleDescriptionBox();
+ QuicktimeTextSampleEntry textTrack = new QuicktimeTextSampleEntry();
+ textTrack.setDataReferenceIndex(1);
+ sampleDescriptionBox.addBox(textTrack);
+
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000);
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ samples.add(ByteBuffer.wrap(new byte[]{0, 0}));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ try {
+ dos.writeShort(sub.text.getBytes("UTF-8").length);
+ dos.write(sub.text.getBytes("UTF-8"));
+ dos.close();
+ } catch (IOException e) {
+ throw new Error("VM is broken. Does not support UTF-8");
+ }
+ samples.add(ByteBuffer.wrap(baos.toByteArray()));
+ lastEnd = sub.to;
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ stts.add(new TimeToSampleBox.Entry(1, silentTime));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ stts.add(new TimeToSampleBox.Entry(1, sub.to - sub.from));
+ lastEnd = sub.to;
+ }
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "text";
+ }
+
+
+ public static class Line {
+ long from;
+ long to;
+ String text;
+
+
+ public Line(long from, long to, String text) {
+ this.from = from;
+ this.to = to;
+ this.text = text;
+ }
+
+ public long getFrom() {
+ return from;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public long getTo() {
+ return to;
+ }
+ }
+
+ public Box getMediaHeaderBox() {
+ GenericMediaHeaderAtom ghmd = new GenericMediaHeaderAtom();
+ ghmd.addBox(new BaseMediaInfoAtom());
+ ghmd.addBox(new GenericMediaHeaderTextAtom());
+ return ghmd;
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ReplaceSampleTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ReplaceSampleTrack.java.svn-base
new file mode 100644
index 0000000..81a129d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ReplaceSampleTrack.java.svn-base
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.AbstractList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Generates a Track where a single sample has been replaced by a given <code>ByteBuffer</code>.
+ */
+
+public class ReplaceSampleTrack extends AbstractTrack {
+ Track origTrack;
+ private long sampleNumber;
+ private ByteBuffer sampleContent;
+ private List<ByteBuffer> samples;
+
+ public ReplaceSampleTrack(Track origTrack, long sampleNumber, ByteBuffer content) {
+ this.origTrack = origTrack;
+ this.sampleNumber = sampleNumber;
+ this.sampleContent = content;
+ this.samples = new ReplaceASingleEntryList();
+
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return origTrack.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return origTrack.getDecodingTimeEntries();
+
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return origTrack.getCompositionTimeEntries();
+
+ }
+
+ synchronized public long[] getSyncSamples() {
+ return origTrack.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return origTrack.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return origTrack.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return origTrack.getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return origTrack.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return origTrack.getSubsampleInformationBox();
+ }
+
+ private class ReplaceASingleEntryList extends AbstractList<ByteBuffer> {
+ @Override
+ public ByteBuffer get(int index) {
+ if (ReplaceSampleTrack.this.sampleNumber == index) {
+ return ReplaceSampleTrack.this.sampleContent;
+ } else {
+ return ReplaceSampleTrack.this.origTrack.getSamples().get(index);
+ }
+ }
+
+ @Override
+ public int size() {
+ return ReplaceSampleTrack.this.origTrack.getSamples().size();
+ }
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/SilenceTrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/SilenceTrackImpl.java.svn-base
new file mode 100644
index 0000000..f74ab3c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/SilenceTrackImpl.java.svn-base
@@ -0,0 +1,98 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Mp4TrackImpl;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This is just a basic idea how things could work but they don't.
+ */
+public class SilenceTrackImpl implements Track {
+ Track source;
+
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ TimeToSampleBox.Entry entry;
+
+ public SilenceTrackImpl(Track ofType, long ms) {
+ source = ofType;
+ if ("mp4a".equals(ofType.getSampleDescriptionBox().getSampleEntry().getType())) {
+ long numFrames = getTrackMetaData().getTimescale() * ms / 1000 / 1024;
+ long standZeit = getTrackMetaData().getTimescale() * ms / numFrames / 1000;
+ entry = new TimeToSampleBox.Entry(numFrames, standZeit);
+
+
+ while (numFrames-- > 0) {
+ samples.add((ByteBuffer) ByteBuffer.wrap(new byte[]{
+ 0x21, 0x10, 0x04, 0x60, (byte) 0x8c, 0x1c,
+ }).rewind());
+ }
+
+ } else {
+ throw new RuntimeException("Tracks of type " + ofType.getClass().getSimpleName() + " are not supported");
+ }
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return Collections.singletonList(entry);
+
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return source.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/TextTrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/TextTrackImpl.java.svn-base
new file mode 100644
index 0000000..3bae143
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/TextTrackImpl.java.svn-base
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.TextSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.threegpp26245.FontTableBox;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class TextTrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+ List<Line> subs = new LinkedList<Line>();
+
+ public List<Line> getSubs() {
+ return subs;
+ }
+
+ public TextTrackImpl() {
+ sampleDescriptionBox = new SampleDescriptionBox();
+ TextSampleEntry tx3g = new TextSampleEntry("tx3g");
+ tx3g.setDataReferenceIndex(1);
+ tx3g.setStyleRecord(new TextSampleEntry.StyleRecord());
+ tx3g.setBoxRecord(new TextSampleEntry.BoxRecord());
+ sampleDescriptionBox.addBox(tx3g);
+
+ FontTableBox ftab = new FontTableBox();
+ ftab.setEntries(Collections.singletonList(new FontTableBox.FontRecord(1, "Serif")));
+
+ tx3g.addBox(ftab);
+
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000); // Text tracks use millieseconds
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ samples.add(ByteBuffer.wrap(new byte[]{0, 0}));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ try {
+ dos.writeShort(sub.text.getBytes("UTF-8").length);
+ dos.write(sub.text.getBytes("UTF-8"));
+ dos.close();
+ } catch (IOException e) {
+ throw new Error("VM is broken. Does not support UTF-8");
+ }
+ samples.add(ByteBuffer.wrap(baos.toByteArray()));
+ lastEnd = sub.to;
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ stts.add(new TimeToSampleBox.Entry(1, silentTime));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ stts.add(new TimeToSampleBox.Entry(1, sub.to - sub.from));
+ lastEnd = sub.to;
+ }
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "sbtl";
+ }
+
+
+ public static class Line {
+ long from;
+ long to;
+ String text;
+
+
+ public Line(long from, long to, String text) {
+ this.from = from;
+ this.to = to;
+ this.text = text;
+ }
+
+ public long getFrom() {
+ return from;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public long getTo() {
+ return to;
+ }
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new NullMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java
new file mode 100644
index 0000000..df51a1a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2012 castLabs GmbH, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.AC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.*;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.*;
+
+/**
+ */
+public class AACTrackImpl extends AbstractTrack {
+ public static Map<Integer, Integer> samplingFrequencyIndexMap = new HashMap<Integer, Integer>();
+
+ static {
+ samplingFrequencyIndexMap.put(96000, 0);
+ samplingFrequencyIndexMap.put(88200, 1);
+ samplingFrequencyIndexMap.put(64000, 2);
+ samplingFrequencyIndexMap.put(48000, 3);
+ samplingFrequencyIndexMap.put(44100, 4);
+ samplingFrequencyIndexMap.put(32000, 5);
+ samplingFrequencyIndexMap.put(24000, 6);
+ samplingFrequencyIndexMap.put(22050, 7);
+ samplingFrequencyIndexMap.put(16000, 8);
+ samplingFrequencyIndexMap.put(12000, 9);
+ samplingFrequencyIndexMap.put(11025, 10);
+ samplingFrequencyIndexMap.put(8000, 11);
+ samplingFrequencyIndexMap.put(0x0, 96000);
+ samplingFrequencyIndexMap.put(0x1, 88200);
+ samplingFrequencyIndexMap.put(0x2, 64000);
+ samplingFrequencyIndexMap.put(0x3, 48000);
+ samplingFrequencyIndexMap.put(0x4, 44100);
+ samplingFrequencyIndexMap.put(0x5, 32000);
+ samplingFrequencyIndexMap.put(0x6, 24000);
+ samplingFrequencyIndexMap.put(0x7, 22050);
+ samplingFrequencyIndexMap.put(0x8, 16000);
+ samplingFrequencyIndexMap.put(0x9, 12000);
+ samplingFrequencyIndexMap.put(0xa, 11025);
+ samplingFrequencyIndexMap.put(0xb, 8000);
+ }
+
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int channelCount;
+ int channelconfig;
+
+ int bufferSizeDB;
+ long maxBitRate;
+ long avgBitRate;
+
+ private BufferedInputStream inputStream;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+ List<TimeToSampleBox.Entry> stts;
+ private String lang = "und";
+
+
+ public AACTrackImpl(InputStream inputStream, String lang) throws IOException {
+ this.lang = lang;
+ parse(inputStream);
+ }
+
+ public AACTrackImpl(InputStream inputStream) throws IOException {
+ parse(inputStream);
+ }
+
+ private void parse(InputStream inputStream) throws IOException {
+ this.inputStream = new BufferedInputStream(inputStream);
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+
+ double packetsPerSecond = (double)samplerate / 1024.0;
+ double duration = samples.size() / packetsPerSecond;
+
+ long dataSize = 0;
+ LinkedList<Integer> queue = new LinkedList<Integer>();
+ for (int i = 0; i < samples.size(); i++) {
+ int size = samples.get(i).capacity();
+ dataSize += size;
+ queue.add(size);
+ while (queue.size() > packetsPerSecond) {
+ queue.pop();
+ }
+ if (queue.size() == (int) packetsPerSecond) {
+ int currSize = 0;
+ for (int j = 0 ; j < queue.size(); j++) {
+ currSize += queue.get(j);
+ }
+ double currBitrate = 8.0 * currSize / queue.size() * packetsPerSecond;
+ if (currBitrate > maxBitRate) {
+ maxBitRate = (int)currBitrate;
+ }
+ }
+ }
+
+ avgBitRate = (int) (8 * dataSize / duration);
+
+ bufferSizeDB = 1536; /* TODO: Calcultate this somehow! */
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("mp4a");
+ audioSampleEntry.setChannelCount(2);
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+
+ ESDescriptorBox esds = new ESDescriptorBox();
+ ESDescriptor descriptor = new ESDescriptor();
+ descriptor.setEsId(0);
+
+ SLConfigDescriptor slConfigDescriptor = new SLConfigDescriptor();
+ slConfigDescriptor.setPredefined(2);
+ descriptor.setSlConfigDescriptor(slConfigDescriptor);
+
+ DecoderConfigDescriptor decoderConfigDescriptor = new DecoderConfigDescriptor();
+ decoderConfigDescriptor.setObjectTypeIndication(0x40);
+ decoderConfigDescriptor.setStreamType(5);
+ decoderConfigDescriptor.setBufferSizeDB(bufferSizeDB);
+ decoderConfigDescriptor.setMaxBitRate(maxBitRate);
+ decoderConfigDescriptor.setAvgBitRate(avgBitRate);
+
+ AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig();
+ audioSpecificConfig.setAudioObjectType(2); // AAC LC
+ audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get(samplerate));
+ audioSpecificConfig.setChannelConfiguration(channelconfig);
+ decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig);
+
+ descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor);
+
+ ByteBuffer data = descriptor.serialize();
+ esds.setData(data);
+ audioSampleEntry.addBox(esds);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "soun";
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public Box getMediaHeaderBox() {
+ return new SoundMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private boolean readVariables() throws IOException {
+ byte[] data = new byte[100];
+ inputStream.mark(100);
+ if (100 != inputStream.read(data, 0, 100)) {
+ return false;
+ }
+ inputStream.reset(); // Rewind
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(12);
+ if (syncword != 0xfff) {
+ return false;
+ }
+ int id = brb.readBits(1);
+ int layer = brb.readBits(2);
+ int protectionAbsent = brb.readBits(1);
+ int profile = brb.readBits(2);
+ samplerate = samplingFrequencyIndexMap.get(brb.readBits(4));
+ brb.readBits(1);
+ channelconfig = brb.readBits(3);
+ int original = brb.readBits(1);
+ int home = brb.readBits(1);
+ int emphasis = brb.readBits(2);
+
+ return true;
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+
+ readSamples = true;
+ byte[] header = new byte[15];
+ boolean ret = false;
+ inputStream.mark(15);
+ while (-1 != inputStream.read(header)) {
+ ret = true;
+ ByteBuffer bb = ByteBuffer.wrap(header);
+ inputStream.reset();
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(12);
+ if (syncword != 0xfff) {
+ return false;
+ }
+ brb.readBits(3);
+ int protectionAbsent = brb.readBits(1);
+ brb.readBits(14);
+ int frameSize = brb.readBits(13);
+ int bufferFullness = brb.readBits(11);
+ int noBlocks = brb.readBits(2);
+ int used = (int) Math.ceil(brb.getPosition() / 8.0);
+ if (protectionAbsent == 0) {
+ used += 2;
+ }
+ inputStream.skip(used);
+ frameSize -= used;
+// System.out.println("Size: " + frameSize + " fullness: " + bufferFullness + " no blocks: " + noBlocks);
+ byte[] data = new byte[frameSize];
+ inputStream.read(data);
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1024));
+ inputStream.mark(15);
+ }
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ return "AACTrackImpl{" +
+ "samplerate=" + samplerate +
+ ", bitrate=" + bitrate +
+ ", channelCount=" + channelCount +
+ ", channelconfig=" + channelconfig +
+ '}';
+ }
+}
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AC3TrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AC3TrackImpl.java
new file mode 100644
index 0000000..5e5b2cd
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AC3TrackImpl.java
@@ -0,0 +1,513 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.AC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class AC3TrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int channelCount;
+
+ int fscod;
+ int bsid;
+ int bsmod;
+ int acmod;
+ int lfeon;
+ int frmsizecod;
+
+ int frameSize;
+ int[][][][] bitRateAndFrameSizeTable;
+
+ private InputStream inputStream;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+ List<TimeToSampleBox.Entry> stts;
+ private String lang = "und";
+
+ public AC3TrackImpl(InputStream fin, String lang) throws IOException {
+ this.lang = lang;
+ parse(fin);
+ }
+
+ public AC3TrackImpl(InputStream fin) throws IOException {
+ parse(fin);
+ }
+
+ private void parse(InputStream fin) throws IOException {
+ inputStream = fin;
+ bitRateAndFrameSizeTable = new int[19][2][3][2];
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+ initBitRateAndFrameSizeTable();
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ac-3");
+ audioSampleEntry.setChannelCount(2); // According to ETSI TS 102 366 Annex F
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+ AC3SpecificBox ac3 = new AC3SpecificBox();
+ ac3.setAcmod(acmod);
+ ac3.setBitRateCode(frmsizecod >> 1);
+ ac3.setBsid(bsid);
+ ac3.setBsmod(bsmod);
+ ac3.setFscod(fscod);
+ ac3.setLfeon(lfeon);
+ ac3.setReserved(0);
+
+ audioSampleEntry.addBox(ac3);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "soun";
+ }
+
+ public Box getMediaHeaderBox() {
+ return new SoundMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private boolean readVariables() throws IOException {
+ byte[] data = new byte[100];
+ inputStream.mark(100);
+ if (100 != inputStream.read(data, 0, 100)) {
+ return false;
+ }
+ inputStream.reset(); // Rewind
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(16);
+ if (syncword != 0xb77) {
+ return false;
+ }
+ brb.readBits(16); // CRC-1
+ fscod = brb.readBits(2);
+
+ switch (fscod) {
+ case 0:
+ samplerate = 48000;
+ break;
+
+ case 1:
+ samplerate = 44100;
+ break;
+
+ case 2:
+ samplerate = 32000;
+ break;
+
+ case 3:
+ samplerate = 0;
+ break;
+
+ }
+ if (samplerate == 0) {
+ return false;
+ }
+
+ frmsizecod = brb.readBits(6);
+
+ if (!calcBitrateAndFrameSize(frmsizecod)) {
+ return false;
+ }
+
+ if (frameSize == 0) {
+ return false;
+ }
+ bsid = brb.readBits(5);
+ bsmod = brb.readBits(3);
+ acmod = brb.readBits(3);
+
+ if (bsid == 9) {
+ samplerate /= 2;
+ } else if (bsid != 8 && bsid != 6) {
+ return false;
+ }
+
+ if ((acmod != 1) && ((acmod & 1) == 1)) {
+ brb.readBits(2);
+ }
+
+ if (0 != (acmod & 4)) {
+ brb.readBits(2);
+ }
+
+ if (acmod == 2) {
+ brb.readBits(2);
+ }
+
+ switch (acmod) {
+ case 0:
+ channelCount = 2;
+ break;
+
+ case 1:
+ channelCount = 1;
+ break;
+
+ case 2:
+ channelCount = 2;
+ break;
+
+ case 3:
+ channelCount = 3;
+ break;
+
+ case 4:
+ channelCount = 3;
+ break;
+
+ case 5:
+ channelCount = 4;
+ break;
+
+ case 6:
+ channelCount = 4;
+ break;
+
+ case 7:
+ channelCount = 5;
+ break;
+
+ }
+
+ lfeon = brb.readBits(1);
+
+ if (lfeon == 1) {
+ channelCount++;
+ }
+ return true;
+ }
+
+ private boolean calcBitrateAndFrameSize(int code) {
+ int frmsizecode = code >>> 1;
+ int flag = code & 1;
+ if (frmsizecode > 18 || flag > 1 || fscod > 2) {
+ return false;
+ }
+ bitrate = bitRateAndFrameSizeTable[frmsizecode][flag][fscod][0];
+ frameSize = 2 * bitRateAndFrameSizeTable[frmsizecode][flag][fscod][1];
+ return true;
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+ readSamples = true;
+ byte[] header = new byte[5];
+ boolean ret = false;
+ inputStream.mark(5);
+ while (-1 != inputStream.read(header)) {
+ ret = true;
+ int frmsizecode = header[4] & 63;
+ calcBitrateAndFrameSize(frmsizecode);
+ inputStream.reset();
+ byte[] data = new byte[frameSize];
+ inputStream.read(data);
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1536));
+ inputStream.mark(5);
+ }
+ return ret;
+ }
+
+ private void initBitRateAndFrameSizeTable() {
+ // ETSI 102 366 Table 4.13, in frmsizecod, flag, fscod, bitrate/size order. Note that all sizes are in words, and all bitrates in kbps
+
+ // 48kHz
+ bitRateAndFrameSizeTable[0][0][0][0] = 32;
+ bitRateAndFrameSizeTable[0][1][0][0] = 32;
+ bitRateAndFrameSizeTable[0][0][0][1] = 64;
+ bitRateAndFrameSizeTable[0][1][0][1] = 64;
+ bitRateAndFrameSizeTable[1][0][0][0] = 40;
+ bitRateAndFrameSizeTable[1][1][0][0] = 40;
+ bitRateAndFrameSizeTable[1][0][0][1] = 80;
+ bitRateAndFrameSizeTable[1][1][0][1] = 80;
+ bitRateAndFrameSizeTable[2][0][0][0] = 48;
+ bitRateAndFrameSizeTable[2][1][0][0] = 48;
+ bitRateAndFrameSizeTable[2][0][0][1] = 96;
+ bitRateAndFrameSizeTable[2][1][0][1] = 96;
+ bitRateAndFrameSizeTable[3][0][0][0] = 56;
+ bitRateAndFrameSizeTable[3][1][0][0] = 56;
+ bitRateAndFrameSizeTable[3][0][0][1] = 112;
+ bitRateAndFrameSizeTable[3][1][0][1] = 112;
+ bitRateAndFrameSizeTable[4][0][0][0] = 64;
+ bitRateAndFrameSizeTable[4][1][0][0] = 64;
+ bitRateAndFrameSizeTable[4][0][0][1] = 128;
+ bitRateAndFrameSizeTable[4][1][0][1] = 128;
+ bitRateAndFrameSizeTable[5][0][0][0] = 80;
+ bitRateAndFrameSizeTable[5][1][0][0] = 80;
+ bitRateAndFrameSizeTable[5][0][0][1] = 160;
+ bitRateAndFrameSizeTable[5][1][0][1] = 160;
+ bitRateAndFrameSizeTable[6][0][0][0] = 96;
+ bitRateAndFrameSizeTable[6][1][0][0] = 96;
+ bitRateAndFrameSizeTable[6][0][0][1] = 192;
+ bitRateAndFrameSizeTable[6][1][0][1] = 192;
+ bitRateAndFrameSizeTable[7][0][0][0] = 112;
+ bitRateAndFrameSizeTable[7][1][0][0] = 112;
+ bitRateAndFrameSizeTable[7][0][0][1] = 224;
+ bitRateAndFrameSizeTable[7][1][0][1] = 224;
+ bitRateAndFrameSizeTable[8][0][0][0] = 128;
+ bitRateAndFrameSizeTable[8][1][0][0] = 128;
+ bitRateAndFrameSizeTable[8][0][0][1] = 256;
+ bitRateAndFrameSizeTable[8][1][0][1] = 256;
+ bitRateAndFrameSizeTable[9][0][0][0] = 160;
+ bitRateAndFrameSizeTable[9][1][0][0] = 160;
+ bitRateAndFrameSizeTable[9][0][0][1] = 320;
+ bitRateAndFrameSizeTable[9][1][0][1] = 320;
+ bitRateAndFrameSizeTable[10][0][0][0] = 192;
+ bitRateAndFrameSizeTable[10][1][0][0] = 192;
+ bitRateAndFrameSizeTable[10][0][0][1] = 384;
+ bitRateAndFrameSizeTable[10][1][0][1] = 384;
+ bitRateAndFrameSizeTable[11][0][0][0] = 224;
+ bitRateAndFrameSizeTable[11][1][0][0] = 224;
+ bitRateAndFrameSizeTable[11][0][0][1] = 448;
+ bitRateAndFrameSizeTable[11][1][0][1] = 448;
+ bitRateAndFrameSizeTable[12][0][0][0] = 256;
+ bitRateAndFrameSizeTable[12][1][0][0] = 256;
+ bitRateAndFrameSizeTable[12][0][0][1] = 512;
+ bitRateAndFrameSizeTable[12][1][0][1] = 512;
+ bitRateAndFrameSizeTable[13][0][0][0] = 320;
+ bitRateAndFrameSizeTable[13][1][0][0] = 320;
+ bitRateAndFrameSizeTable[13][0][0][1] = 640;
+ bitRateAndFrameSizeTable[13][1][0][1] = 640;
+ bitRateAndFrameSizeTable[14][0][0][0] = 384;
+ bitRateAndFrameSizeTable[14][1][0][0] = 384;
+ bitRateAndFrameSizeTable[14][0][0][1] = 768;
+ bitRateAndFrameSizeTable[14][1][0][1] = 768;
+ bitRateAndFrameSizeTable[15][0][0][0] = 448;
+ bitRateAndFrameSizeTable[15][1][0][0] = 448;
+ bitRateAndFrameSizeTable[15][0][0][1] = 896;
+ bitRateAndFrameSizeTable[15][1][0][1] = 896;
+ bitRateAndFrameSizeTable[16][0][0][0] = 512;
+ bitRateAndFrameSizeTable[16][1][0][0] = 512;
+ bitRateAndFrameSizeTable[16][0][0][1] = 1024;
+ bitRateAndFrameSizeTable[16][1][0][1] = 1024;
+ bitRateAndFrameSizeTable[17][0][0][0] = 576;
+ bitRateAndFrameSizeTable[17][1][0][0] = 576;
+ bitRateAndFrameSizeTable[17][0][0][1] = 1152;
+ bitRateAndFrameSizeTable[17][1][0][1] = 1152;
+ bitRateAndFrameSizeTable[18][0][0][0] = 640;
+ bitRateAndFrameSizeTable[18][1][0][0] = 640;
+ bitRateAndFrameSizeTable[18][0][0][1] = 1280;
+ bitRateAndFrameSizeTable[18][1][0][1] = 1280;
+
+ // 44.1 kHz
+ bitRateAndFrameSizeTable[0][0][1][0] = 32;
+ bitRateAndFrameSizeTable[0][1][1][0] = 32;
+ bitRateAndFrameSizeTable[0][0][1][1] = 69;
+ bitRateAndFrameSizeTable[0][1][1][1] = 70;
+ bitRateAndFrameSizeTable[1][0][1][0] = 40;
+ bitRateAndFrameSizeTable[1][1][1][0] = 40;
+ bitRateAndFrameSizeTable[1][0][1][1] = 87;
+ bitRateAndFrameSizeTable[1][1][1][1] = 88;
+ bitRateAndFrameSizeTable[2][0][1][0] = 48;
+ bitRateAndFrameSizeTable[2][1][1][0] = 48;
+ bitRateAndFrameSizeTable[2][0][1][1] = 104;
+ bitRateAndFrameSizeTable[2][1][1][1] = 105;
+ bitRateAndFrameSizeTable[3][0][1][0] = 56;
+ bitRateAndFrameSizeTable[3][1][1][0] = 56;
+ bitRateAndFrameSizeTable[3][0][1][1] = 121;
+ bitRateAndFrameSizeTable[3][1][1][1] = 122;
+ bitRateAndFrameSizeTable[4][0][1][0] = 64;
+ bitRateAndFrameSizeTable[4][1][1][0] = 64;
+ bitRateAndFrameSizeTable[4][0][1][1] = 139;
+ bitRateAndFrameSizeTable[4][1][1][1] = 140;
+ bitRateAndFrameSizeTable[5][0][1][0] = 80;
+ bitRateAndFrameSizeTable[5][1][1][0] = 80;
+ bitRateAndFrameSizeTable[5][0][1][1] = 174;
+ bitRateAndFrameSizeTable[5][1][1][1] = 175;
+ bitRateAndFrameSizeTable[6][0][1][0] = 96;
+ bitRateAndFrameSizeTable[6][1][1][0] = 96;
+ bitRateAndFrameSizeTable[6][0][1][1] = 208;
+ bitRateAndFrameSizeTable[6][1][1][1] = 209;
+ bitRateAndFrameSizeTable[7][0][1][0] = 112;
+ bitRateAndFrameSizeTable[7][1][1][0] = 112;
+ bitRateAndFrameSizeTable[7][0][1][1] = 243;
+ bitRateAndFrameSizeTable[7][1][1][1] = 244;
+ bitRateAndFrameSizeTable[8][0][1][0] = 128;
+ bitRateAndFrameSizeTable[8][1][1][0] = 128;
+ bitRateAndFrameSizeTable[8][0][1][1] = 278;
+ bitRateAndFrameSizeTable[8][1][1][1] = 279;
+ bitRateAndFrameSizeTable[9][0][1][0] = 160;
+ bitRateAndFrameSizeTable[9][1][1][0] = 160;
+ bitRateAndFrameSizeTable[9][0][1][1] = 348;
+ bitRateAndFrameSizeTable[9][1][1][1] = 349;
+ bitRateAndFrameSizeTable[10][0][1][0] = 192;
+ bitRateAndFrameSizeTable[10][1][1][0] = 192;
+ bitRateAndFrameSizeTable[10][0][1][1] = 417;
+ bitRateAndFrameSizeTable[10][1][1][1] = 418;
+ bitRateAndFrameSizeTable[11][0][1][0] = 224;
+ bitRateAndFrameSizeTable[11][1][1][0] = 224;
+ bitRateAndFrameSizeTable[11][0][1][1] = 487;
+ bitRateAndFrameSizeTable[11][1][1][1] = 488;
+ bitRateAndFrameSizeTable[12][0][1][0] = 256;
+ bitRateAndFrameSizeTable[12][1][1][0] = 256;
+ bitRateAndFrameSizeTable[12][0][1][1] = 557;
+ bitRateAndFrameSizeTable[12][1][1][1] = 558;
+ bitRateAndFrameSizeTable[13][0][1][0] = 320;
+ bitRateAndFrameSizeTable[13][1][1][0] = 320;
+ bitRateAndFrameSizeTable[13][0][1][1] = 696;
+ bitRateAndFrameSizeTable[13][1][1][1] = 697;
+ bitRateAndFrameSizeTable[14][0][1][0] = 384;
+ bitRateAndFrameSizeTable[14][1][1][0] = 384;
+ bitRateAndFrameSizeTable[14][0][1][1] = 835;
+ bitRateAndFrameSizeTable[14][1][1][1] = 836;
+ bitRateAndFrameSizeTable[15][0][1][0] = 448;
+ bitRateAndFrameSizeTable[15][1][1][0] = 448;
+ bitRateAndFrameSizeTable[15][0][1][1] = 975;
+ bitRateAndFrameSizeTable[15][1][1][1] = 975;
+ bitRateAndFrameSizeTable[16][0][1][0] = 512;
+ bitRateAndFrameSizeTable[16][1][1][0] = 512;
+ bitRateAndFrameSizeTable[16][0][1][1] = 1114;
+ bitRateAndFrameSizeTable[16][1][1][1] = 1115;
+ bitRateAndFrameSizeTable[17][0][1][0] = 576;
+ bitRateAndFrameSizeTable[17][1][1][0] = 576;
+ bitRateAndFrameSizeTable[17][0][1][1] = 1253;
+ bitRateAndFrameSizeTable[17][1][1][1] = 1254;
+ bitRateAndFrameSizeTable[18][0][1][0] = 640;
+ bitRateAndFrameSizeTable[18][1][1][0] = 640;
+ bitRateAndFrameSizeTable[18][0][1][1] = 1393;
+ bitRateAndFrameSizeTable[18][1][1][1] = 1394;
+
+ // 32kHz
+ bitRateAndFrameSizeTable[0][0][2][0] = 32;
+ bitRateAndFrameSizeTable[0][1][2][0] = 32;
+ bitRateAndFrameSizeTable[0][0][2][1] = 96;
+ bitRateAndFrameSizeTable[0][1][2][1] = 96;
+ bitRateAndFrameSizeTable[1][0][2][0] = 40;
+ bitRateAndFrameSizeTable[1][1][2][0] = 40;
+ bitRateAndFrameSizeTable[1][0][2][1] = 120;
+ bitRateAndFrameSizeTable[1][1][2][1] = 120;
+ bitRateAndFrameSizeTable[2][0][2][0] = 48;
+ bitRateAndFrameSizeTable[2][1][2][0] = 48;
+ bitRateAndFrameSizeTable[2][0][2][1] = 144;
+ bitRateAndFrameSizeTable[2][1][2][1] = 144;
+ bitRateAndFrameSizeTable[3][0][2][0] = 56;
+ bitRateAndFrameSizeTable[3][1][2][0] = 56;
+ bitRateAndFrameSizeTable[3][0][2][1] = 168;
+ bitRateAndFrameSizeTable[3][1][2][1] = 168;
+ bitRateAndFrameSizeTable[4][0][2][0] = 64;
+ bitRateAndFrameSizeTable[4][1][2][0] = 64;
+ bitRateAndFrameSizeTable[4][0][2][1] = 192;
+ bitRateAndFrameSizeTable[4][1][2][1] = 192;
+ bitRateAndFrameSizeTable[5][0][2][0] = 80;
+ bitRateAndFrameSizeTable[5][1][2][0] = 80;
+ bitRateAndFrameSizeTable[5][0][2][1] = 240;
+ bitRateAndFrameSizeTable[5][1][2][1] = 240;
+ bitRateAndFrameSizeTable[6][0][2][0] = 96;
+ bitRateAndFrameSizeTable[6][1][2][0] = 96;
+ bitRateAndFrameSizeTable[6][0][2][1] = 288;
+ bitRateAndFrameSizeTable[6][1][2][1] = 288;
+ bitRateAndFrameSizeTable[7][0][2][0] = 112;
+ bitRateAndFrameSizeTable[7][1][2][0] = 112;
+ bitRateAndFrameSizeTable[7][0][2][1] = 336;
+ bitRateAndFrameSizeTable[7][1][2][1] = 336;
+ bitRateAndFrameSizeTable[8][0][2][0] = 128;
+ bitRateAndFrameSizeTable[8][1][2][0] = 128;
+ bitRateAndFrameSizeTable[8][0][2][1] = 384;
+ bitRateAndFrameSizeTable[8][1][2][1] = 384;
+ bitRateAndFrameSizeTable[9][0][2][0] = 160;
+ bitRateAndFrameSizeTable[9][1][2][0] = 160;
+ bitRateAndFrameSizeTable[9][0][2][1] = 480;
+ bitRateAndFrameSizeTable[9][1][2][1] = 480;
+ bitRateAndFrameSizeTable[10][0][2][0] = 192;
+ bitRateAndFrameSizeTable[10][1][2][0] = 192;
+ bitRateAndFrameSizeTable[10][0][2][1] = 576;
+ bitRateAndFrameSizeTable[10][1][2][1] = 576;
+ bitRateAndFrameSizeTable[11][0][2][0] = 224;
+ bitRateAndFrameSizeTable[11][1][2][0] = 224;
+ bitRateAndFrameSizeTable[11][0][2][1] = 672;
+ bitRateAndFrameSizeTable[11][1][2][1] = 672;
+ bitRateAndFrameSizeTable[12][0][2][0] = 256;
+ bitRateAndFrameSizeTable[12][1][2][0] = 256;
+ bitRateAndFrameSizeTable[12][0][2][1] = 768;
+ bitRateAndFrameSizeTable[12][1][2][1] = 768;
+ bitRateAndFrameSizeTable[13][0][2][0] = 320;
+ bitRateAndFrameSizeTable[13][1][2][0] = 320;
+ bitRateAndFrameSizeTable[13][0][2][1] = 960;
+ bitRateAndFrameSizeTable[13][1][2][1] = 960;
+ bitRateAndFrameSizeTable[14][0][2][0] = 384;
+ bitRateAndFrameSizeTable[14][1][2][0] = 384;
+ bitRateAndFrameSizeTable[14][0][2][1] = 1152;
+ bitRateAndFrameSizeTable[14][1][2][1] = 1152;
+ bitRateAndFrameSizeTable[15][0][2][0] = 448;
+ bitRateAndFrameSizeTable[15][1][2][0] = 448;
+ bitRateAndFrameSizeTable[15][0][2][1] = 1344;
+ bitRateAndFrameSizeTable[15][1][2][1] = 1344;
+ bitRateAndFrameSizeTable[16][0][2][0] = 512;
+ bitRateAndFrameSizeTable[16][1][2][0] = 512;
+ bitRateAndFrameSizeTable[16][0][2][1] = 1536;
+ bitRateAndFrameSizeTable[16][1][2][1] = 1536;
+ bitRateAndFrameSizeTable[17][0][2][0] = 576;
+ bitRateAndFrameSizeTable[17][1][2][0] = 576;
+ bitRateAndFrameSizeTable[17][0][2][1] = 1728;
+ bitRateAndFrameSizeTable[17][1][2][1] = 1728;
+ bitRateAndFrameSizeTable[18][0][2][0] = 640;
+ bitRateAndFrameSizeTable[18][1][2][0] = 640;
+ bitRateAndFrameSizeTable[18][0][2][1] = 1920;
+ bitRateAndFrameSizeTable[18][1][2][1] = 1920;
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/Amf0Track.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/Amf0Track.java
new file mode 100644
index 0000000..0917767
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/Amf0Track.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.adobe.ActionMessageFormat0SampleEntryBox;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public class Amf0Track extends AbstractTrack {
+ SortedMap<Long, byte[]> rawSamples = new TreeMap<Long, byte[]>() {
+ };
+ private TrackMetaData trackMetaData = new TrackMetaData();
+
+
+ /**
+ * Creates a new AMF0 track from
+ *
+ * @param rawSamples
+ */
+ public Amf0Track(Map<Long, byte[]> rawSamples) {
+ this.rawSamples = new TreeMap<Long, byte[]>(rawSamples);
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000); // Text tracks use millieseconds
+ trackMetaData.setLanguage("eng");
+ }
+
+ public List<ByteBuffer> getSamples() {
+ LinkedList<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ for (byte[] bytes : rawSamples.values()) {
+ samples.add(ByteBuffer.wrap(bytes));
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ SampleDescriptionBox stsd = new SampleDescriptionBox();
+ ActionMessageFormat0SampleEntryBox amf0 = new ActionMessageFormat0SampleEntryBox();
+ amf0.setDataReferenceIndex(1);
+ stsd.addBox(amf0);
+ return stsd;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ LinkedList<TimeToSampleBox.Entry> timesToSample = new LinkedList<TimeToSampleBox.Entry>();
+ LinkedList<Long> keys = new LinkedList<Long>(rawSamples.keySet());
+ Collections.sort(keys);
+ long lastTimeStamp = 0;
+ for (Long key : keys) {
+ long delta = key - lastTimeStamp;
+ if (timesToSample.size() > 0 && timesToSample.peek().getDelta() == delta) {
+ timesToSample.peek().setCount(timesToSample.peek().getCount() + 1);
+ } else {
+ timesToSample.add(new TimeToSampleBox.Entry(1, delta));
+ }
+ lastTimeStamp = key;
+ }
+ return timesToSample;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ // AMF0 tracks do not have Composition Time
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ // AMF0 tracks do not have Sync Samples
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ // AMF0 tracks do not have Sample Dependencies
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public String getHandler() {
+ return "data";
+ }
+
+ public Box getMediaHeaderBox() {
+ return new NullMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AppendTrack.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AppendTrack.java
new file mode 100644
index 0000000..93ee0cd
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AppendTrack.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.DecoderConfigDescriptor;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.util.*;
+
+/**
+ * Appends two or more <code>Tracks</code> of the same type. No only that the type must be equal
+ * also the decoder settings must be the same.
+ */
+public class AppendTrack extends AbstractTrack {
+ Track[] tracks;
+ SampleDescriptionBox stsd;
+
+ public AppendTrack(Track... tracks) throws IOException {
+ this.tracks = tracks;
+
+ for (Track track : tracks) {
+
+ if (stsd == null) {
+ stsd = track.getSampleDescriptionBox();
+ } else {
+ ByteArrayOutputStream curBaos = new ByteArrayOutputStream();
+ ByteArrayOutputStream refBaos = new ByteArrayOutputStream();
+ track.getSampleDescriptionBox().getBox(Channels.newChannel(curBaos));
+ stsd.getBox(Channels.newChannel(refBaos));
+ byte[] cur = curBaos.toByteArray();
+ byte[] ref = refBaos.toByteArray();
+
+ if (!Arrays.equals(ref, cur)) {
+ SampleDescriptionBox curStsd = track.getSampleDescriptionBox();
+ if (stsd.getBoxes().size() == 1 && curStsd.getBoxes().size() == 1) {
+ if (stsd.getBoxes().get(0) instanceof AudioSampleEntry && curStsd.getBoxes().get(0) instanceof AudioSampleEntry) {
+ AudioSampleEntry aseResult = mergeAudioSampleEntries((AudioSampleEntry) stsd.getBoxes().get(0), (AudioSampleEntry) curStsd.getBoxes().get(0));
+ if (aseResult != null) {
+ stsd.setBoxes(Collections.<Box>singletonList(aseResult));
+ return;
+ }
+ }
+ }
+ throw new IOException("Cannot append " + track + " to " + tracks[0] + " since their Sample Description Boxes differ: \n" + track.getSampleDescriptionBox() + "\n vs. \n" + tracks[0].getSampleDescriptionBox());
+ }
+ }
+ }
+ }
+
+ private AudioSampleEntry mergeAudioSampleEntries(AudioSampleEntry ase1, AudioSampleEntry ase2) throws IOException {
+ if (ase1.getType().equals(ase2.getType())) {
+ AudioSampleEntry ase = new AudioSampleEntry(ase2.getType());
+ if (ase1.getBytesPerFrame() == ase2.getBytesPerFrame()) {
+ ase.setBytesPerFrame(ase1.getBytesPerFrame());
+ } else {
+ return null;
+ }
+ if (ase1.getBytesPerPacket() == ase2.getBytesPerPacket()) {
+ ase.setBytesPerPacket(ase1.getBytesPerPacket());
+ } else {
+ return null;
+ }
+ if (ase1.getBytesPerSample() == ase2.getBytesPerSample()) {
+ ase.setBytesPerSample(ase1.getBytesPerSample());
+ } else {
+ return null;
+ }
+ if (ase1.getChannelCount() == ase2.getChannelCount()) {
+ ase.setChannelCount(ase1.getChannelCount());
+ } else {
+ return null;
+ }
+ if (ase1.getPacketSize() == ase2.getPacketSize()) {
+ ase.setPacketSize(ase1.getPacketSize());
+ } else {
+ return null;
+ }
+ if (ase1.getCompressionId() == ase2.getCompressionId()) {
+ ase.setCompressionId(ase1.getCompressionId());
+ } else {
+ return null;
+ }
+ if (ase1.getSampleRate() == ase2.getSampleRate()) {
+ ase.setSampleRate(ase1.getSampleRate());
+ } else {
+ return null;
+ }
+ if (ase1.getSampleSize() == ase2.getSampleSize()) {
+ ase.setSampleSize(ase1.getSampleSize());
+ } else {
+ return null;
+ }
+ if (ase1.getSamplesPerPacket() == ase2.getSamplesPerPacket()) {
+ ase.setSamplesPerPacket(ase1.getSamplesPerPacket());
+ } else {
+ return null;
+ }
+ if (ase1.getSoundVersion() == ase2.getSoundVersion()) {
+ ase.setSoundVersion(ase1.getSoundVersion());
+ } else {
+ return null;
+ }
+ if (Arrays.equals(ase1.getSoundVersion2Data(), ase2.getSoundVersion2Data())) {
+ ase.setSoundVersion2Data(ase1.getSoundVersion2Data());
+ } else {
+ return null;
+ }
+ if (ase1.getBoxes().size() == ase2.getBoxes().size()) {
+ Iterator<Box> bxs1 = ase1.getBoxes().iterator();
+ Iterator<Box> bxs2 = ase2.getBoxes().iterator();
+ while (bxs1.hasNext()) {
+ Box cur1 = bxs1.next();
+ Box cur2 = bxs2.next();
+ ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+ ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+ cur1.getBox(Channels.newChannel(baos1));
+ cur2.getBox(Channels.newChannel(baos2));
+ if (Arrays.equals(baos1.toByteArray(), baos2.toByteArray())) {
+ ase.addBox(cur1);
+ } else {
+ if (ESDescriptorBox.TYPE.equals(cur1.getType()) && ESDescriptorBox.TYPE.equals(cur2.getType())) {
+ ESDescriptorBox esdsBox1 = (ESDescriptorBox) cur1;
+ ESDescriptorBox esdsBox2 = (ESDescriptorBox) cur2;
+ ESDescriptor esds1 = esdsBox1.getEsDescriptor();
+ ESDescriptor esds2 = esdsBox2.getEsDescriptor();
+ if (esds1.getURLFlag() != esds2.getURLFlag()) {
+ return null;
+ }
+ if (esds1.getURLLength() != esds2.getURLLength()) {
+ return null;
+ }
+ if (esds1.getDependsOnEsId() != esds2.getDependsOnEsId()) {
+ return null;
+ }
+ if (esds1.getEsId() != esds2.getEsId()) {
+ return null;
+ }
+ if (esds1.getoCREsId() != esds2.getoCREsId()) {
+ return null;
+ }
+ if (esds1.getoCRstreamFlag() != esds2.getoCRstreamFlag()) {
+ return null;
+ }
+ if (esds1.getRemoteODFlag() != esds2.getRemoteODFlag()) {
+ return null;
+ }
+ if (esds1.getStreamDependenceFlag() != esds2.getStreamDependenceFlag()) {
+ return null;
+ }
+ if (esds1.getStreamPriority() != esds2.getStreamPriority()) {
+ return null;
+ }
+ if (esds1.getURLString() != null ? !esds1.getURLString().equals(esds2.getURLString()) : esds2.getURLString() != null) {
+ return null;
+ }
+ if (esds1.getDecoderConfigDescriptor() != null ? !esds1.getDecoderConfigDescriptor().equals(esds2.getDecoderConfigDescriptor()) : esds2.getDecoderConfigDescriptor() != null) {
+ DecoderConfigDescriptor dcd1 = esds1.getDecoderConfigDescriptor();
+ DecoderConfigDescriptor dcd2 = esds2.getDecoderConfigDescriptor();
+ if (!dcd1.getAudioSpecificInfo().equals(dcd2.getAudioSpecificInfo())) {
+ return null;
+ }
+ if (dcd1.getAvgBitRate() != dcd2.getAvgBitRate()) {
+ // I don't care
+ }
+ if (dcd1.getBufferSizeDB() != dcd2.getBufferSizeDB()) {
+ // I don't care
+ }
+
+ if (dcd1.getDecoderSpecificInfo() != null ? !dcd1.getDecoderSpecificInfo().equals(dcd2.getDecoderSpecificInfo()) : dcd2.getDecoderSpecificInfo() != null) {
+ return null;
+ }
+
+ if (dcd1.getMaxBitRate() != dcd2.getMaxBitRate()) {
+ // I don't care
+ }
+ if (!dcd1.getProfileLevelIndicationDescriptors().equals(dcd2.getProfileLevelIndicationDescriptors())) {
+ return null;
+ }
+
+ if (dcd1.getObjectTypeIndication() != dcd2.getObjectTypeIndication()) {
+ return null;
+ }
+ if (dcd1.getStreamType() != dcd2.getStreamType()) {
+ return null;
+ }
+ if (dcd1.getUpStream() != dcd2.getUpStream()) {
+ return null;
+ }
+
+
+ }
+ if (esds1.getOtherDescriptors() != null ? !esds1.getOtherDescriptors().equals(esds2.getOtherDescriptors()) : esds2.getOtherDescriptors() != null) {
+ return null;
+ }
+ if (esds1.getSlConfigDescriptor() != null ? !esds1.getSlConfigDescriptor().equals(esds2.getSlConfigDescriptor()) : esds2.getSlConfigDescriptor() != null) {
+ return null;
+ }
+ ase.addBox(cur1);
+ }
+ }
+ }
+ }
+ return ase;
+ } else {
+ return null;
+ }
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ ArrayList<ByteBuffer> lists = new ArrayList<ByteBuffer>();
+
+ for (Track track : tracks) {
+ lists.addAll(track.getSamples());
+ }
+
+ return lists;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return stsd;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ if (tracks[0].getDecodingTimeEntries() != null && !tracks[0].getDecodingTimeEntries().isEmpty()) {
+ List<long[]> lists = new LinkedList<long[]>();
+ for (Track track : tracks) {
+ lists.add(TimeToSampleBox.blowupTimeToSamples(track.getDecodingTimeEntries()));
+ }
+
+ LinkedList<TimeToSampleBox.Entry> returnDecodingEntries = new LinkedList<TimeToSampleBox.Entry>();
+ for (long[] list : lists) {
+ for (long nuDecodingTime : list) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getDelta() != nuDecodingTime) {
+ TimeToSampleBox.Entry e = new TimeToSampleBox.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ TimeToSampleBox.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ if (tracks[0].getCompositionTimeEntries() != null && !tracks[0].getCompositionTimeEntries().isEmpty()) {
+ List<int[]> lists = new LinkedList<int[]>();
+ for (Track track : tracks) {
+ lists.add(CompositionTimeToSample.blowupCompositionTimes(track.getCompositionTimeEntries()));
+ }
+ LinkedList<CompositionTimeToSample.Entry> compositionTimeEntries = new LinkedList<CompositionTimeToSample.Entry>();
+ for (int[] list : lists) {
+ for (int compositionTime : list) {
+ if (compositionTimeEntries.isEmpty() || compositionTimeEntries.getLast().getOffset() != compositionTime) {
+ CompositionTimeToSample.Entry e = new CompositionTimeToSample.Entry(1, compositionTime);
+ compositionTimeEntries.add(e);
+ } else {
+ CompositionTimeToSample.Entry e = compositionTimeEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ }
+ return compositionTimeEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public long[] getSyncSamples() {
+ if (tracks[0].getSyncSamples() != null && tracks[0].getSyncSamples().length > 0) {
+ int numSyncSamples = 0;
+ for (Track track : tracks) {
+ numSyncSamples += track.getSyncSamples().length;
+ }
+ long[] returnSyncSamples = new long[numSyncSamples];
+
+ int pos = 0;
+ long samplesBefore = 0;
+ for (Track track : tracks) {
+ for (long l : track.getSyncSamples()) {
+ returnSyncSamples[pos++] = samplesBefore + l;
+ }
+ samplesBefore += track.getSamples().size();
+ }
+ return returnSyncSamples;
+ } else {
+ return null;
+ }
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ if (tracks[0].getSampleDependencies() != null && !tracks[0].getSampleDependencies().isEmpty()) {
+ List<SampleDependencyTypeBox.Entry> list = new LinkedList<SampleDependencyTypeBox.Entry>();
+ for (Track track : tracks) {
+ list.addAll(track.getSampleDependencies());
+ }
+ return list;
+ } else {
+ return null;
+ }
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return tracks[0].getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return tracks[0].getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return tracks[0].getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return tracks[0].getSubsampleInformationBox();
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ChangeTimeScaleTrack.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ChangeTimeScaleTrack.java
new file mode 100644
index 0000000..50f76c2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ChangeTimeScaleTrack.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.logging.Logger;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class ChangeTimeScaleTrack implements Track {
+ private static final Logger LOG = Logger.getLogger(ChangeTimeScaleTrack.class.getName());
+
+ Track source;
+ List<CompositionTimeToSample.Entry> ctts;
+ List<TimeToSampleBox.Entry> tts;
+ long timeScale;
+
+ /**
+ * Changes the time scale of the source track to the target time scale and makes sure
+ * that any rounding errors that may have summed are corrected exactly before the syncSamples.
+ *
+ * @param source the source track
+ * @param targetTimeScale the resulting time scale of this track.
+ * @param syncSamples at these sync points where rounding error are corrected.
+ */
+ public ChangeTimeScaleTrack(Track source, long targetTimeScale, long[] syncSamples) {
+ this.source = source;
+ this.timeScale = targetTimeScale;
+ double timeScaleFactor = (double) targetTimeScale / source.getTrackMetaData().getTimescale();
+ ctts = adjustCtts(source.getCompositionTimeEntries(), timeScaleFactor);
+ tts = adjustTts(source.getDecodingTimeEntries(), timeScaleFactor, syncSamples, getTimes(source, syncSamples, targetTimeScale));
+ }
+
+ private static long[] getTimes(Track track, long[] syncSamples, long targetTimeScale) {
+ long[] syncSampleTimes = new long[syncSamples.length];
+ Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries());
+
+ int currentSample = 1; // first syncsample is 1
+ long currentDuration = 0;
+ long currentDelta = 0;
+ int currentSyncSampleIndex = 0;
+ long left = 0;
+
+
+ while (currentSample <= syncSamples[syncSamples.length - 1]) {
+ if (currentSample++ == syncSamples[currentSyncSampleIndex]) {
+ syncSampleTimes[currentSyncSampleIndex++] = (currentDuration * targetTimeScale) / track.getTrackMetaData().getTimescale();
+ }
+ if (left-- == 0) {
+ TimeToSampleBox.Entry entry = timeQueue.poll();
+ left = entry.getCount() - 1;
+ currentDelta = entry.getDelta();
+ }
+ currentDuration += currentDelta;
+ }
+ return syncSampleTimes;
+
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return tts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return ctts;
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(timeScale);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ /**
+ * Adjusting the composition times is easy. Just scale it by the factor - that's it. There is no rounding
+ * error summing up.
+ *
+ * @param source
+ * @param timeScaleFactor
+ * @return
+ */
+ static List<CompositionTimeToSample.Entry> adjustCtts(List<CompositionTimeToSample.Entry> source, double timeScaleFactor) {
+ if (source != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(source.size());
+ for (CompositionTimeToSample.Entry entry : source) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), (int) Math.round(timeScaleFactor * entry.getOffset())));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ static List<TimeToSampleBox.Entry> adjustTts(List<TimeToSampleBox.Entry> source, double timeScaleFactor, long[] syncSample, long[] syncSampleTimes) {
+
+ long[] sourceArray = TimeToSampleBox.blowupTimeToSamples(source);
+ long summedDurations = 0;
+
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (int i = 1; i <= sourceArray.length; i++) {
+ long duration = sourceArray[i - 1];
+
+ long x = Math.round(timeScaleFactor * duration);
+
+
+ TimeToSampleBox.Entry last = entries2.peekLast();
+ int ssIndex;
+ if ((ssIndex = Arrays.binarySearch(syncSample, i + 1)) >= 0) {
+ // we are at the sample before sync point
+ if (syncSampleTimes[ssIndex] != summedDurations) {
+ long correction = syncSampleTimes[ssIndex] - (summedDurations + x);
+ LOG.finest(String.format("Sample %d %d / %d - correct by %d", i, summedDurations, syncSampleTimes[ssIndex], correction));
+ x += correction;
+ }
+ }
+ summedDurations += x;
+ if (last == null) {
+ entries2.add(new TimeToSampleBox.Entry(1, x));
+ } else if (last.getDelta() != x) {
+ entries2.add(new TimeToSampleBox.Entry(1, x));
+ } else {
+ last.setCount(last.getCount() + 1);
+ }
+
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "ChangeTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/CroppedTrack.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/CroppedTrack.java
new file mode 100644
index 0000000..2389961
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/CroppedTrack.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Generates a Track that starts at fromSample and ends at toSample (exclusive). The user of this class
+ * has to make sure that the fromSample is a random access sample.
+ * <ul>
+ * <li>In AAC this is every single sample</li>
+ * <li>In H264 this is every sample that is marked in the SyncSampleBox</li>
+ * </ul>
+ */
+public class CroppedTrack extends AbstractTrack {
+ Track origTrack;
+ private int fromSample;
+ private int toSample;
+ private long[] syncSampleArray;
+
+ public CroppedTrack(Track origTrack, long fromSample, long toSample) {
+ this.origTrack = origTrack;
+ assert fromSample <= Integer.MAX_VALUE;
+ assert toSample <= Integer.MAX_VALUE;
+ this.fromSample = (int) fromSample;
+ this.toSample = (int) toSample;
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return origTrack.getSamples().subList(fromSample, toSample);
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return origTrack.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ if (origTrack.getDecodingTimeEntries() != null && !origTrack.getDecodingTimeEntries().isEmpty()) {
+ // todo optimize! too much long is allocated but then not used
+ long[] decodingTimes = TimeToSampleBox.blowupTimeToSamples(origTrack.getDecodingTimeEntries());
+ long[] nuDecodingTimes = new long[toSample - fromSample];
+ System.arraycopy(decodingTimes, fromSample, nuDecodingTimes, 0, toSample - fromSample);
+
+ LinkedList<TimeToSampleBox.Entry> returnDecodingEntries = new LinkedList<TimeToSampleBox.Entry>();
+
+ for (long nuDecodingTime : nuDecodingTimes) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getDelta() != nuDecodingTime) {
+ TimeToSampleBox.Entry e = new TimeToSampleBox.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ TimeToSampleBox.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ if (origTrack.getCompositionTimeEntries() != null && !origTrack.getCompositionTimeEntries().isEmpty()) {
+ int[] compositionTime = CompositionTimeToSample.blowupCompositionTimes(origTrack.getCompositionTimeEntries());
+ int[] nuCompositionTimes = new int[toSample - fromSample];
+ System.arraycopy(compositionTime, fromSample, nuCompositionTimes, 0, toSample - fromSample);
+
+ LinkedList<CompositionTimeToSample.Entry> returnDecodingEntries = new LinkedList<CompositionTimeToSample.Entry>();
+
+ for (int nuDecodingTime : nuCompositionTimes) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getOffset() != nuDecodingTime) {
+ CompositionTimeToSample.Entry e = new CompositionTimeToSample.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ CompositionTimeToSample.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ synchronized public long[] getSyncSamples() {
+ if (this.syncSampleArray == null) {
+ if (origTrack.getSyncSamples() != null && origTrack.getSyncSamples().length > 0) {
+ List<Long> syncSamples = new LinkedList<Long>();
+ for (long l : origTrack.getSyncSamples()) {
+ if (l >= fromSample && l < toSample) {
+ syncSamples.add(l - fromSample);
+ }
+ }
+ syncSampleArray = new long[syncSamples.size()];
+ for (int i = 0; i < syncSampleArray.length; i++) {
+ syncSampleArray[i] = syncSamples.get(i);
+
+ }
+ return syncSampleArray;
+ } else {
+ return null;
+ }
+ } else {
+ return this.syncSampleArray;
+ }
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ if (origTrack.getSampleDependencies() != null && !origTrack.getSampleDependencies().isEmpty()) {
+ return origTrack.getSampleDependencies().subList(fromSample, toSample);
+ } else {
+ return null;
+ }
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return origTrack.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return origTrack.getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return origTrack.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return origTrack.getSubsampleInformationBox();
+ }
+
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/DivideTimeScaleTrack.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/DivideTimeScaleTrack.java
new file mode 100644
index 0000000..c51e8e0
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/DivideTimeScaleTrack.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class DivideTimeScaleTrack implements Track {
+ Track source;
+ private int timeScaleDivisor;
+
+ public DivideTimeScaleTrack(Track source, int timeScaleDivisor) {
+ this.source = source;
+ this.timeScaleDivisor = timeScaleDivisor;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return adjustTts();
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return adjustCtts();
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(source.getTrackMetaData().getTimescale() / this.timeScaleDivisor);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ List<CompositionTimeToSample.Entry> adjustCtts() {
+ List<CompositionTimeToSample.Entry> origCtts = this.source.getCompositionTimeEntries();
+ if (origCtts != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(origCtts.size());
+ for (CompositionTimeToSample.Entry entry : origCtts) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), entry.getOffset() / timeScaleDivisor));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ List<TimeToSampleBox.Entry> adjustTts() {
+ List<TimeToSampleBox.Entry> origTts = source.getDecodingTimeEntries();
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (TimeToSampleBox.Entry e : origTts) {
+ entries2.add(new TimeToSampleBox.Entry(e.getCount(), e.getDelta() / timeScaleDivisor));
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "MultiplyTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/EC3TrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/EC3TrackImpl.java
new file mode 100644
index 0000000..d0b2d76
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/EC3TrackImpl.java
@@ -0,0 +1,436 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.EC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: magnus
+ * Date: 2012-03-14
+ * Time: 10:39
+ * To change this template use File | Settings | File Templates.
+ */
+public class EC3TrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int frameSize;
+
+ List<BitStreamInfo> entries = new LinkedList<BitStreamInfo>();
+
+ private BufferedInputStream inputStream;
+ private List<ByteBuffer> samples;
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ private String lang = "und";
+
+ public EC3TrackImpl(InputStream fin, String lang) throws IOException {
+ this.lang = lang;
+ parse(fin);
+ }
+
+ public EC3TrackImpl(InputStream fin) throws IOException {
+ parse(fin);
+ }
+
+ private void parse(InputStream fin) throws IOException {
+ inputStream = new BufferedInputStream(fin);
+
+ boolean done = false;
+ inputStream.mark(10000);
+ while (!done) {
+ BitStreamInfo bsi = readVariables();
+ if (bsi == null) {
+ throw new IOException();
+ }
+ for (BitStreamInfo entry : entries) {
+ if (bsi.strmtyp != 1 && entry.substreamid == bsi.substreamid) {
+ done = true;
+ }
+ }
+ if (!done) {
+ entries.add(bsi);
+ long skipped = inputStream.skip(bsi.frameSize);
+ assert skipped == bsi.frameSize;
+ }
+ }
+
+ inputStream.reset();
+
+ if (entries.size() == 0) {
+ throw new IOException();
+ }
+ samplerate = entries.get(0).samplerate;
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ec-3");
+ audioSampleEntry.setChannelCount(2); // According to ETSI TS 102 366 Annex F
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+ EC3SpecificBox ec3 = new EC3SpecificBox();
+ int[] deps = new int[entries.size()];
+ int[] chan_locs = new int[entries.size()];
+ for (BitStreamInfo bsi : entries) {
+ if (bsi.strmtyp == 1) {
+ deps[bsi.substreamid]++;
+ chan_locs[bsi.substreamid] = ((bsi.chanmap >> 6) & 0x100) | ((bsi.chanmap >> 5) & 0xff);
+ }
+ }
+ for (BitStreamInfo bsi : entries) {
+ if (bsi.strmtyp != 1) {
+ EC3SpecificBox.Entry e = new EC3SpecificBox.Entry();
+ e.fscod = bsi.fscod;
+ e.bsid = bsi.bsid;
+ e.bsmod = bsi.bsmod;
+ e.acmod = bsi.acmod;
+ e.lfeon = bsi.lfeon;
+ e.reserved = 0;
+ e.num_dep_sub = deps[bsi.substreamid];
+ e.chan_loc = chan_locs[bsi.substreamid];
+ e.reserved2 = 0;
+ ec3.addEntry(e);
+ }
+ bitrate += bsi.bitrate;
+ frameSize += bsi.frameSize;
+ }
+
+ ec3.setDataRate(bitrate / 1000);
+ audioSampleEntry.addBox(ec3);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "soun";
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new SoundMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private BitStreamInfo readVariables() throws IOException {
+ byte[] data = new byte[200];
+ inputStream.mark(200);
+ if (200 != inputStream.read(data, 0, 200)) {
+ return null;
+ }
+ inputStream.reset(); // Rewind
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(16);
+ if (syncword != 0xb77) {
+ return null;
+ }
+
+ BitStreamInfo entry = new BitStreamInfo();
+
+ entry.strmtyp = brb.readBits(2);
+ entry.substreamid = brb.readBits(3);
+ int frmsiz = brb.readBits(11);
+ entry.frameSize = 2 * (frmsiz + 1);
+
+ entry.fscod = brb.readBits(2);
+ int fscod2 = -1;
+ int numblkscod;
+ if (entry.fscod == 3) {
+ fscod2 = brb.readBits(2);
+ numblkscod = 3;
+ } else {
+ numblkscod = brb.readBits(2);
+ }
+ int numberOfBlocksPerSyncFrame = 0;
+ switch (numblkscod) {
+ case 0:
+ numberOfBlocksPerSyncFrame = 1;
+ break;
+
+ case 1:
+ numberOfBlocksPerSyncFrame = 2;
+ break;
+
+ case 2:
+ numberOfBlocksPerSyncFrame = 3;
+ break;
+
+ case 3:
+ numberOfBlocksPerSyncFrame = 6;
+ break;
+
+ }
+ entry.frameSize *= (6 / numberOfBlocksPerSyncFrame);
+
+ entry.acmod = brb.readBits(3);
+ entry.lfeon = brb.readBits(1);
+ entry.bsid = brb.readBits(5);
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8); // compr
+ }
+ if (0 == entry.acmod) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8);
+ }
+ }
+ if (1 == entry.strmtyp) {
+ if (1 == brb.readBits(1)) {
+ entry.chanmap = brb.readBits(16);
+ }
+ }
+ if (1 == brb.readBits(1)) { // mixing metadata
+ if (entry.acmod > 2) {
+ brb.readBits(2);
+ }
+ if (1 == (entry.acmod & 1) && entry.acmod > 2) {
+ brb.readBits(3);
+ brb.readBits(3);
+ }
+ if (0 < (entry.acmod & 4)) {
+ brb.readBits(3);
+ brb.readBits(3);
+ }
+ if (1 == entry.lfeon) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ }
+ }
+ if (0 == entry.strmtyp) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ if (0 == entry.acmod) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ int mixdef = brb.readBits(2);
+ if (1 == mixdef) {
+ brb.readBits(5);
+ } else if (2 == mixdef) {
+ brb.readBits(12);
+ } else if (3 == mixdef) {
+ int mixdeflen = brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(7);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8);
+ }
+ }
+ }
+ for (int i = 0; i < (mixdeflen + 2); i++) {
+ brb.readBits(8);
+ }
+ brb.byteSync();
+ }
+ if (entry.acmod < 2) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(14);
+ }
+ if (0 == entry.acmod) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(14);
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ if (numblkscod == 0) {
+ brb.readBits(5);
+ } else {
+ for (int i = 0; i < numberOfBlocksPerSyncFrame; i++) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ if (1 == brb.readBits(1)) { // infomdate
+ entry.bsmod = brb.readBits(3);
+ }
+
+ switch (entry.fscod) {
+ case 0:
+ entry.samplerate = 48000;
+ break;
+
+ case 1:
+ entry.samplerate = 44100;
+ break;
+
+ case 2:
+ entry.samplerate = 32000;
+ break;
+
+ case 3: {
+ switch (fscod2) {
+ case 0:
+ entry.samplerate = 24000;
+ break;
+
+ case 1:
+ entry.samplerate = 22050;
+ break;
+
+ case 2:
+ entry.samplerate = 16000;
+ break;
+
+ case 3:
+ entry.samplerate = 0;
+ break;
+ }
+ break;
+ }
+
+ }
+ if (entry.samplerate == 0) {
+ return null;
+ }
+
+ entry.bitrate = (int) (((double) entry.samplerate) / 1536.0 * entry.frameSize * 8);
+
+ return entry;
+ }
+
+ private boolean readSamples() throws IOException {
+ int read = frameSize;
+ boolean ret = false;
+ while (frameSize == read) {
+ ret = true;
+ byte[] data = new byte[frameSize];
+ read = inputStream.read(data);
+ if (read == frameSize) {
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1536));
+ }
+ }
+ return ret;
+ }
+
+ public static class BitStreamInfo extends EC3SpecificBox.Entry {
+ public int frameSize;
+ public int substreamid;
+ public int bitrate;
+ public int samplerate;
+ public int strmtyp;
+ public int chanmap;
+
+ @Override
+ public String toString() {
+ return "BitStreamInfo{" +
+ "frameSize=" + frameSize +
+ ", substreamid=" + substreamid +
+ ", bitrate=" + bitrate +
+ ", samplerate=" + samplerate +
+ ", strmtyp=" + strmtyp +
+ ", chanmap=" + chanmap +
+ '}';
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "EC3TrackImpl{" +
+ "bitrate=" + bitrate +
+ ", samplerate=" + samplerate +
+ ", entries=" + entries +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/H264TrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/H264TrackImpl.java
new file mode 100644
index 0000000..b3c1866
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/H264TrackImpl.java
@@ -0,0 +1,740 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
+import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.h264.model.PictureParameterSet;
+import com.googlecode.mp4parser.h264.model.SeqParameterSet;
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * The <code>H264TrackImpl</code> creates a <code>Track</code> from an H.264
+ * Annex B file.
+ */
+public class H264TrackImpl extends AbstractTrack {
+ private static final Logger LOG = Logger.getLogger(H264TrackImpl.class.getName());
+
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ private ReaderWrapper reader;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+
+ List<TimeToSampleBox.Entry> stts;
+ List<CompositionTimeToSample.Entry> ctts;
+ List<SampleDependencyTypeBox.Entry> sdtp;
+ List<Integer> stss;
+
+ SeqParameterSet seqParameterSet = null;
+ PictureParameterSet pictureParameterSet = null;
+ LinkedList<byte[]> seqParameterSetList = new LinkedList<byte[]>();
+ LinkedList<byte[]> pictureParameterSetList = new LinkedList<byte[]>();
+
+ private int width;
+ private int height;
+ private int timescale;
+ private int frametick;
+ private int currentScSize;
+ private int prevScSize;
+
+ private SEIMessage seiMessage;
+ int frameNrInGop = 0;
+ private boolean determineFrameRate = true;
+ private String lang = "und";
+
+ public H264TrackImpl(InputStream inputStream, String lang, long timescale) throws IOException {
+ this.lang = lang;
+ if (timescale > 1000) {
+ timescale = timescale; //e.g. 23976
+ frametick = 1000;
+ determineFrameRate = false;
+ } else {
+ throw new IllegalArgumentException("Timescale must be specified in milliseconds!");
+ }
+ parse(inputStream);
+ }
+
+ public H264TrackImpl(InputStream inputStream, String lang) throws IOException {
+ this.lang = lang;
+ parse(inputStream);
+ }
+
+ public H264TrackImpl(InputStream inputStream) throws IOException {
+ parse(inputStream);
+ }
+
+ private void parse(InputStream inputStream) throws IOException {
+ this.reader = new ReaderWrapper(inputStream);
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+ ctts = new LinkedList<CompositionTimeToSample.Entry>();
+ sdtp = new LinkedList<SampleDependencyTypeBox.Entry>();
+ stss = new LinkedList<Integer>();
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ VisualSampleEntry visualSampleEntry = new VisualSampleEntry("avc1");
+ visualSampleEntry.setDataReferenceIndex(1);
+ visualSampleEntry.setDepth(24);
+ visualSampleEntry.setFrameCount(1);
+ visualSampleEntry.setHorizresolution(72);
+ visualSampleEntry.setVertresolution(72);
+ visualSampleEntry.setWidth(width);
+ visualSampleEntry.setHeight(height);
+ visualSampleEntry.setCompressorname("AVC Coding");
+
+ AvcConfigurationBox avcConfigurationBox = new AvcConfigurationBox();
+
+ avcConfigurationBox.setSequenceParameterSets(seqParameterSetList);
+ avcConfigurationBox.setPictureParameterSets(pictureParameterSetList);
+ avcConfigurationBox.setAvcLevelIndication(seqParameterSet.level_idc);
+ avcConfigurationBox.setAvcProfileIndication(seqParameterSet.profile_idc);
+ avcConfigurationBox.setBitDepthLumaMinus8(seqParameterSet.bit_depth_luma_minus8);
+ avcConfigurationBox.setBitDepthChromaMinus8(seqParameterSet.bit_depth_chroma_minus8);
+ avcConfigurationBox.setChromaFormat(seqParameterSet.chroma_format_idc.getId());
+ avcConfigurationBox.setConfigurationVersion(1);
+ avcConfigurationBox.setLengthSizeMinusOne(3);
+ avcConfigurationBox.setProfileCompatibility(seqParameterSetList.get(0)[1]);
+
+ visualSampleEntry.addBox(avcConfigurationBox);
+ sampleDescriptionBox.addBox(visualSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(timescale);
+ trackMetaData.setWidth(width);
+ trackMetaData.setHeight(height);
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return ctts;
+ }
+
+ public long[] getSyncSamples() {
+ long[] returns = new long[stss.size()];
+ for (int i = 0; i < stss.size(); i++) {
+ returns[i] = stss.get(i);
+ }
+ return returns;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return sdtp;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "vide";
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new VideoMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private boolean readVariables() {
+ width = (seqParameterSet.pic_width_in_mbs_minus1 + 1) * 16;
+ int mult = 2;
+ if (seqParameterSet.frame_mbs_only_flag) {
+ mult = 1;
+ }
+ height = 16 * (seqParameterSet.pic_height_in_map_units_minus1 + 1) * mult;
+ if (seqParameterSet.frame_cropping_flag) {
+ int chromaArrayType = 0;
+ if (seqParameterSet.residual_color_transform_flag == false) {
+ chromaArrayType = seqParameterSet.chroma_format_idc.getId();
+ }
+ int cropUnitX = 1;
+ int cropUnitY = mult;
+ if (chromaArrayType != 0) {
+ cropUnitX = seqParameterSet.chroma_format_idc.getSubWidth();
+ cropUnitY = seqParameterSet.chroma_format_idc.getSubHeight() * mult;
+ }
+
+ width -= cropUnitX * (seqParameterSet.frame_crop_left_offset + seqParameterSet.frame_crop_right_offset);
+ height -= cropUnitY * (seqParameterSet.frame_crop_top_offset + seqParameterSet.frame_crop_bottom_offset);
+ }
+ return true;
+ }
+
+ private boolean findNextStartcode() throws IOException {
+ byte[] test = new byte[]{-1, -1, -1, -1};
+
+ int c;
+ while ((c = reader.read()) != -1) {
+ test[0] = test[1];
+ test[1] = test[2];
+ test[2] = test[3];
+ test[3] = (byte) c;
+ if (test[0] == 0 && test[1] == 0 && test[2] == 0 && test[3] == 1) {
+ prevScSize = currentScSize;
+ currentScSize = 4;
+ return true;
+ }
+ if (test[0] == 0 && test[1] == 0 && test[2] == 1) {
+ prevScSize = currentScSize;
+ currentScSize = 3;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private enum NALActions {
+ IGNORE, BUFFER, STORE, END
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+
+ readSamples = true;
+
+
+ findNextStartcode();
+ reader.mark();
+ long pos = reader.getPos();
+
+ ArrayList<byte[]> buffered = new ArrayList<byte[]>();
+
+ int frameNr = 0;
+
+ while (findNextStartcode()) {
+ long newpos = reader.getPos();
+ int size = (int) (newpos - pos - prevScSize);
+ reader.reset();
+ byte[] data = new byte[size ];
+ reader.read(data);
+ int type = data[0];
+ int nal_ref_idc = (type >> 5) & 3;
+ int nal_unit_type = type & 0x1f;
+ LOG.fine("Found startcode at " + (pos -4) + " Type: " + nal_unit_type + " ref idc: " + nal_ref_idc + " (size " + size + ")");
+ NALActions action = handleNALUnit(nal_ref_idc, nal_unit_type, data);
+ switch (action) {
+ case IGNORE:
+ break;
+
+ case BUFFER:
+ buffered.add(data);
+ break;
+
+ case STORE:
+ int stdpValue = 22;
+ frameNr++;
+ buffered.add(data);
+ ByteBuffer bb = createSample(buffered);
+ boolean IdrPicFlag = false;
+ if (nal_unit_type == 5) {
+ stdpValue += 16;
+ IdrPicFlag = true;
+ }
+ ByteArrayInputStream bs = cleanBuffer(buffered.get(buffered.size() - 1));
+ SliceHeader sh = new SliceHeader(bs, seqParameterSet, pictureParameterSet, IdrPicFlag);
+ if (sh.slice_type == SliceHeader.SliceType.B) {
+ stdpValue += 4;
+ }
+ LOG.fine("Adding sample with size " + bb.capacity() + " and header " + sh);
+ buffered.clear();
+ samples.add(bb);
+ stts.add(new TimeToSampleBox.Entry(1, frametick));
+ if (nal_unit_type == 5) { // IDR Picture
+ stss.add(frameNr);
+ }
+ if (seiMessage.n_frames == 0) {
+ frameNrInGop = 0;
+ }
+ int offset = 0;
+ if (seiMessage.clock_timestamp_flag) {
+ offset = seiMessage.n_frames - frameNrInGop;
+ } else if (seiMessage.removal_delay_flag) {
+ offset = seiMessage.dpb_removal_delay / 2;
+ }
+ ctts.add(new CompositionTimeToSample.Entry(1, offset * frametick));
+ sdtp.add(new SampleDependencyTypeBox.Entry(stdpValue));
+ frameNrInGop++;
+ break;
+
+ case END:
+ return true;
+
+
+ }
+ pos = newpos;
+ reader.seek(currentScSize);
+ reader.mark();
+ }
+ return true;
+ }
+
+ private ByteBuffer createSample(List<byte[]> buffers) {
+ int outsize = 0;
+ for (int i = 0; i < buffers.size(); i++) {
+ outsize += buffers.get(i).length + 4;
+ }
+ byte[] output = new byte[outsize];
+
+ ByteBuffer bb = ByteBuffer.wrap(output);
+ for (int i = 0; i < buffers.size(); i++) {
+ bb.putInt(buffers.get(i).length);
+ bb.put(buffers.get(i));
+ }
+ bb.rewind();
+ return bb;
+ }
+
+ private ByteArrayInputStream cleanBuffer(byte[] data) {
+ byte[] output = new byte[data.length];
+ int inPos = 0;
+ int outPos = 0;
+ while (inPos < data.length) {
+ if (data[inPos] == 0 && data[inPos + 1] == 0 && data[inPos + 2] == 3) {
+ output[outPos] = 0;
+ output[outPos + 1] = 0;
+ inPos += 3;
+ outPos += 2;
+ } else {
+ output[outPos] = data[inPos];
+ inPos++;
+ outPos++;
+ }
+ }
+ return new ByteArrayInputStream(output, 0, outPos);
+ }
+
+ private NALActions handleNALUnit(int nal_ref_idc, int nal_unit_type, byte[] data) throws IOException {
+ NALActions action;
+ switch (nal_unit_type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ action = NALActions.STORE; // Will only work in single slice per frame mode!
+ break;
+
+ case 6:
+ seiMessage = new SEIMessage(cleanBuffer(data), seqParameterSet);
+ action = NALActions.BUFFER;
+ break;
+
+ case 9:
+// printAccessUnitDelimiter(data);
+ int type = data[1] >> 5;
+ LOG.fine("Access unit delimiter type: " + type);
+ action = NALActions.BUFFER;
+ break;
+
+
+ case 7:
+ if (seqParameterSet == null) {
+ ByteArrayInputStream is = cleanBuffer(data);
+ is.read();
+ seqParameterSet = SeqParameterSet.read(is);
+ seqParameterSetList.add(data);
+ configureFramerate();
+ }
+ action = NALActions.IGNORE;
+ break;
+
+ case 8:
+ if (pictureParameterSet == null) {
+ ByteArrayInputStream is = new ByteArrayInputStream(data);
+ is.read();
+ pictureParameterSet = PictureParameterSet.read(is);
+ pictureParameterSetList.add(data);
+ }
+ action = NALActions.IGNORE;
+ break;
+
+ case 10:
+ case 11:
+ action = NALActions.END;
+ break;
+
+ default:
+ System.err.println("Unknown NAL unit type: " + nal_unit_type);
+ action = NALActions.IGNORE;
+
+ }
+
+ return action;
+ }
+
+ private void configureFramerate() {
+ if (determineFrameRate) {
+ if (seqParameterSet.vuiParams != null) {
+ timescale = seqParameterSet.vuiParams.time_scale >> 1; // Not sure why, but I found this in several places, and it works...
+ frametick = seqParameterSet.vuiParams.num_units_in_tick;
+ if (timescale == 0 || frametick == 0) {
+ System.err.println("Warning: vuiParams contain invalid values: time_scale: " + timescale + " and frame_tick: " + frametick + ". Setting frame rate to 25fps");
+ timescale = 90000;
+ frametick = 3600;
+ }
+ } else {
+ System.err.println("Warning: Can't determine frame rate. Guessing 25 fps");
+ timescale = 90000;
+ frametick = 3600;
+ }
+ }
+ }
+
+ public void printAccessUnitDelimiter(byte[] data) {
+ LOG.fine("Access unit delimiter: " + (data[1] >> 5));
+ }
+
+ public static class SliceHeader {
+
+ public enum SliceType {
+ P, B, I, SP, SI
+ }
+
+ public int first_mb_in_slice;
+ public SliceType slice_type;
+ public int pic_parameter_set_id;
+ public int colour_plane_id;
+ public int frame_num;
+ public boolean field_pic_flag = false;
+ public boolean bottom_field_flag = false;
+ public int idr_pic_id;
+ public int pic_order_cnt_lsb;
+ public int delta_pic_order_cnt_bottom;
+
+ public SliceHeader(InputStream is, SeqParameterSet sps, PictureParameterSet pps, boolean IdrPicFlag) throws IOException {
+ is.read();
+ CAVLCReader reader = new CAVLCReader(is);
+ first_mb_in_slice = reader.readUE("SliceHeader: first_mb_in_slice");
+ switch (reader.readUE("SliceHeader: slice_type")) {
+ case 0:
+ case 5:
+ slice_type = SliceType.P;
+ break;
+
+ case 1:
+ case 6:
+ slice_type = SliceType.B;
+ break;
+
+ case 2:
+ case 7:
+ slice_type = SliceType.I;
+ break;
+
+ case 3:
+ case 8:
+ slice_type = SliceType.SP;
+ break;
+
+ case 4:
+ case 9:
+ slice_type = SliceType.SI;
+ break;
+
+ }
+ pic_parameter_set_id = reader.readUE("SliceHeader: pic_parameter_set_id");
+ if (sps.residual_color_transform_flag) {
+ colour_plane_id = reader.readU(2, "SliceHeader: colour_plane_id");
+ }
+ frame_num = reader.readU(sps.log2_max_frame_num_minus4 + 4, "SliceHeader: frame_num");
+
+ if (!sps.frame_mbs_only_flag) {
+ field_pic_flag = reader.readBool("SliceHeader: field_pic_flag");
+ if (field_pic_flag) {
+ bottom_field_flag = reader.readBool("SliceHeader: bottom_field_flag");
+ }
+ }
+ if (IdrPicFlag) {
+ idr_pic_id = reader.readUE("SliceHeader: idr_pic_id");
+ if (sps.pic_order_cnt_type == 0) {
+ pic_order_cnt_lsb = reader.readU(sps.log2_max_pic_order_cnt_lsb_minus4 + 4, "SliceHeader: pic_order_cnt_lsb");
+ if (pps.pic_order_present_flag && !field_pic_flag) {
+ delta_pic_order_cnt_bottom = reader.readSE("SliceHeader: delta_pic_order_cnt_bottom");
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SliceHeader{" +
+ "first_mb_in_slice=" + first_mb_in_slice +
+ ", slice_type=" + slice_type +
+ ", pic_parameter_set_id=" + pic_parameter_set_id +
+ ", colour_plane_id=" + colour_plane_id +
+ ", frame_num=" + frame_num +
+ ", field_pic_flag=" + field_pic_flag +
+ ", bottom_field_flag=" + bottom_field_flag +
+ ", idr_pic_id=" + idr_pic_id +
+ ", pic_order_cnt_lsb=" + pic_order_cnt_lsb +
+ ", delta_pic_order_cnt_bottom=" + delta_pic_order_cnt_bottom +
+ '}';
+ }
+ }
+
+ private class ReaderWrapper {
+ private InputStream inputStream;
+ private long pos = 0;
+
+ private long markPos = 0;
+
+
+ private ReaderWrapper(InputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ int read() throws IOException {
+ pos++;
+ return inputStream.read();
+ }
+
+ long read(byte[] data) throws IOException {
+ long read = inputStream.read(data);
+ pos += read;
+ return read;
+ }
+
+ long seek(int dist) throws IOException {
+ long seeked = inputStream.skip(dist);
+ pos += seeked;
+ return seeked;
+ }
+
+ public long getPos() {
+ return pos;
+ }
+
+ public void mark() {
+ int i = 1048576;
+ LOG.fine("Marking with " + i + " at " + pos);
+ inputStream.mark(i);
+ markPos = pos;
+ }
+
+
+ public void reset() throws IOException {
+ long diff = pos - markPos;
+ LOG.fine("Resetting to " + markPos + " (pos is " + pos + ") which makes the buffersize " + diff);
+ inputStream.reset();
+ pos = markPos;
+ }
+ }
+
+ public class SEIMessage {
+
+ int payloadType = 0;
+ int payloadSize = 0;
+
+ boolean removal_delay_flag;
+ int cpb_removal_delay;
+ int dpb_removal_delay;
+
+ boolean clock_timestamp_flag;
+ int pic_struct;
+ int ct_type;
+ int nuit_field_based_flag;
+ int counting_type;
+ int full_timestamp_flag;
+ int discontinuity_flag;
+ int cnt_dropped_flag;
+ int n_frames;
+ int seconds_value;
+ int minutes_value;
+ int hours_value;
+ int time_offset_length;
+ int time_offset;
+
+ SeqParameterSet sps;
+
+ public SEIMessage(InputStream is, SeqParameterSet sps) throws IOException {
+ this.sps = sps;
+ is.read();
+ int datasize = is.available();
+ int read = 0;
+ while (read < datasize) {
+ payloadType = 0;
+ payloadSize = 0;
+ int last_payload_type_bytes = is.read();
+ read++;
+ while (last_payload_type_bytes == 0xff) {
+ payloadType += last_payload_type_bytes;
+ last_payload_type_bytes = is.read();
+ read++;
+ }
+ payloadType += last_payload_type_bytes;
+ int last_payload_size_bytes = is.read();
+ read++;
+
+ while (last_payload_size_bytes == 0xff) {
+ payloadSize += last_payload_size_bytes;
+ last_payload_size_bytes = is.read();
+ read++;
+ }
+ payloadSize += last_payload_size_bytes;
+ if (datasize - read >= payloadSize) {
+ if (payloadType == 1) { // pic_timing is what we are interested in!
+ if (sps.vuiParams != null && (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null || sps.vuiParams.pic_struct_present_flag)) {
+ byte[] data = new byte[payloadSize];
+ is.read(data);
+ read += payloadSize;
+ CAVLCReader reader = new CAVLCReader(new ByteArrayInputStream(data));
+ if (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null) {
+ removal_delay_flag = true;
+ cpb_removal_delay = reader.readU(sps.vuiParams.nalHRDParams.cpb_removal_delay_length_minus1 + 1, "SEI: cpb_removal_delay");
+ dpb_removal_delay = reader.readU(sps.vuiParams.nalHRDParams.dpb_output_delay_length_minus1 + 1, "SEI: dpb_removal_delay");
+ } else {
+ removal_delay_flag = false;
+ }
+ if (sps.vuiParams.pic_struct_present_flag) {
+ pic_struct = reader.readU(4, "SEI: pic_struct");
+ int numClockTS;
+ switch (pic_struct) {
+ case 0:
+ case 1:
+ case 2:
+ default:
+ numClockTS = 1;
+ break;
+
+ case 3:
+ case 4:
+ case 7:
+ numClockTS = 2;
+ break;
+
+ case 5:
+ case 6:
+ case 8:
+ numClockTS = 3;
+ break;
+ }
+ for (int i = 0; i < numClockTS; i++) {
+ clock_timestamp_flag = reader.readBool("pic_timing SEI: clock_timestamp_flag[" + i + "]");
+ if (clock_timestamp_flag) {
+ ct_type = reader.readU(2, "pic_timing SEI: ct_type");
+ nuit_field_based_flag = reader.readU(1, "pic_timing SEI: nuit_field_based_flag");
+ counting_type = reader.readU(5, "pic_timing SEI: counting_type");
+ full_timestamp_flag = reader.readU(1, "pic_timing SEI: full_timestamp_flag");
+ discontinuity_flag = reader.readU(1, "pic_timing SEI: discontinuity_flag");
+ cnt_dropped_flag = reader.readU(1, "pic_timing SEI: cnt_dropped_flag");
+ n_frames = reader.readU(8, "pic_timing SEI: n_frames");
+ if (full_timestamp_flag == 1) {
+ seconds_value = reader.readU(6, "pic_timing SEI: seconds_value");
+ minutes_value = reader.readU(6, "pic_timing SEI: minutes_value");
+ hours_value = reader.readU(5, "pic_timing SEI: hours_value");
+ } else {
+ if (reader.readBool("pic_timing SEI: seconds_flag")) {
+ seconds_value = reader.readU(6, "pic_timing SEI: seconds_value");
+ if (reader.readBool("pic_timing SEI: minutes_flag")) {
+ minutes_value = reader.readU(6, "pic_timing SEI: minutes_value");
+ if (reader.readBool("pic_timing SEI: hours_flag")) {
+ hours_value = reader.readU(5, "pic_timing SEI: hours_value");
+ }
+ }
+ }
+ }
+ if (true) {
+ if (sps.vuiParams.nalHRDParams != null) {
+ time_offset_length = sps.vuiParams.nalHRDParams.time_offset_length;
+ } else if (sps.vuiParams.vclHRDParams != null) {
+ time_offset_length = sps.vuiParams.vclHRDParams.time_offset_length;
+ } else {
+ time_offset_length = 24;
+ }
+ time_offset = reader.readU(24, "pic_timing SEI: time_offset");
+ }
+ }
+ }
+ }
+
+ } else {
+ for (int i = 0; i < payloadSize; i++) {
+ is.read();
+ read++;
+ }
+ }
+ } else {
+ for (int i = 0; i < payloadSize; i++) {
+ is.read();
+ read++;
+ }
+ }
+ } else {
+ read = datasize;
+ }
+ LOG.fine(this.toString());
+ }
+ }
+
+ @Override
+ public String toString() {
+ String out = "SEIMessage{" +
+ "payloadType=" + payloadType +
+ ", payloadSize=" + payloadSize;
+ if (payloadType == 1) {
+ if (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null) {
+
+ out += ", cpb_removal_delay=" + cpb_removal_delay +
+ ", dpb_removal_delay=" + dpb_removal_delay;
+ }
+ if (sps.vuiParams.pic_struct_present_flag) {
+ out += ", pic_struct=" + pic_struct;
+ if (clock_timestamp_flag) {
+ out += ", ct_type=" + ct_type +
+ ", nuit_field_based_flag=" + nuit_field_based_flag +
+ ", counting_type=" + counting_type +
+ ", full_timestamp_flag=" + full_timestamp_flag +
+ ", discontinuity_flag=" + discontinuity_flag +
+ ", cnt_dropped_flag=" + cnt_dropped_flag +
+ ", n_frames=" + n_frames +
+ ", seconds_value=" + seconds_value +
+ ", minutes_value=" + minutes_value +
+ ", hours_value=" + hours_value +
+ ", time_offset_length=" + time_offset_length +
+ ", time_offset=" + time_offset;
+ }
+ }
+ }
+ out += '}';
+ return out;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/MultiplyTimeScaleTrack.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/MultiplyTimeScaleTrack.java
new file mode 100644
index 0000000..e9a90e4
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/MultiplyTimeScaleTrack.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+import static com.googlecode.mp4parser.util.Math.gcd;
+import static com.googlecode.mp4parser.util.Math.lcm;
+import static java.lang.Math.round;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class MultiplyTimeScaleTrack implements Track {
+ Track source;
+ private int timeScaleFactor;
+
+ public MultiplyTimeScaleTrack(Track source, int timeScaleFactor) {
+ this.source = source;
+ this.timeScaleFactor = timeScaleFactor;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return adjustTts(source.getDecodingTimeEntries(), timeScaleFactor);
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return adjustCtts(source.getCompositionTimeEntries(), timeScaleFactor);
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(source.getTrackMetaData().getTimescale() * this.timeScaleFactor);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ static List<CompositionTimeToSample.Entry> adjustCtts(List<CompositionTimeToSample.Entry> source, int timeScaleFactor) {
+ if (source != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(source.size());
+ for (CompositionTimeToSample.Entry entry : source) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), timeScaleFactor * entry.getOffset()));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ static List<TimeToSampleBox.Entry> adjustTts(List<TimeToSampleBox.Entry> source, int timeScaleFactor) {
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (TimeToSampleBox.Entry e : source) {
+ entries2.add(new TimeToSampleBox.Entry(e.getCount(), timeScaleFactor * e.getDelta()));
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "MultiplyTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/QuicktimeTextTrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/QuicktimeTextTrackImpl.java
new file mode 100644
index 0000000..8efa399
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/QuicktimeTextTrackImpl.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.TextSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.apple.BaseMediaInfoAtom;
+import com.googlecode.mp4parser.boxes.apple.GenericMediaHeaderAtom;
+import com.googlecode.mp4parser.boxes.apple.GenericMediaHeaderTextAtom;
+import com.googlecode.mp4parser.boxes.apple.QuicktimeTextSampleEntry;
+import com.googlecode.mp4parser.boxes.threegpp26245.FontTableBox;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A Text track as Quicktime Pro would create.
+ */
+public class QuicktimeTextTrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+ List<Line> subs = new LinkedList<Line>();
+
+ public List<Line> getSubs() {
+ return subs;
+ }
+
+ public QuicktimeTextTrackImpl() {
+ sampleDescriptionBox = new SampleDescriptionBox();
+ QuicktimeTextSampleEntry textTrack = new QuicktimeTextSampleEntry();
+ textTrack.setDataReferenceIndex(1);
+ sampleDescriptionBox.addBox(textTrack);
+
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000);
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ samples.add(ByteBuffer.wrap(new byte[]{0, 0}));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ try {
+ dos.writeShort(sub.text.getBytes("UTF-8").length);
+ dos.write(sub.text.getBytes("UTF-8"));
+ dos.close();
+ } catch (IOException e) {
+ throw new Error("VM is broken. Does not support UTF-8");
+ }
+ samples.add(ByteBuffer.wrap(baos.toByteArray()));
+ lastEnd = sub.to;
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ stts.add(new TimeToSampleBox.Entry(1, silentTime));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ stts.add(new TimeToSampleBox.Entry(1, sub.to - sub.from));
+ lastEnd = sub.to;
+ }
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "text";
+ }
+
+
+ public static class Line {
+ long from;
+ long to;
+ String text;
+
+
+ public Line(long from, long to, String text) {
+ this.from = from;
+ this.to = to;
+ this.text = text;
+ }
+
+ public long getFrom() {
+ return from;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public long getTo() {
+ return to;
+ }
+ }
+
+ public Box getMediaHeaderBox() {
+ GenericMediaHeaderAtom ghmd = new GenericMediaHeaderAtom();
+ ghmd.addBox(new BaseMediaInfoAtom());
+ ghmd.addBox(new GenericMediaHeaderTextAtom());
+ return ghmd;
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ReplaceSampleTrack.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ReplaceSampleTrack.java
new file mode 100644
index 0000000..81a129d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ReplaceSampleTrack.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.AbstractList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Generates a Track where a single sample has been replaced by a given <code>ByteBuffer</code>.
+ */
+
+public class ReplaceSampleTrack extends AbstractTrack {
+ Track origTrack;
+ private long sampleNumber;
+ private ByteBuffer sampleContent;
+ private List<ByteBuffer> samples;
+
+ public ReplaceSampleTrack(Track origTrack, long sampleNumber, ByteBuffer content) {
+ this.origTrack = origTrack;
+ this.sampleNumber = sampleNumber;
+ this.sampleContent = content;
+ this.samples = new ReplaceASingleEntryList();
+
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return origTrack.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return origTrack.getDecodingTimeEntries();
+
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return origTrack.getCompositionTimeEntries();
+
+ }
+
+ synchronized public long[] getSyncSamples() {
+ return origTrack.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return origTrack.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return origTrack.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return origTrack.getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return origTrack.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return origTrack.getSubsampleInformationBox();
+ }
+
+ private class ReplaceASingleEntryList extends AbstractList<ByteBuffer> {
+ @Override
+ public ByteBuffer get(int index) {
+ if (ReplaceSampleTrack.this.sampleNumber == index) {
+ return ReplaceSampleTrack.this.sampleContent;
+ } else {
+ return ReplaceSampleTrack.this.origTrack.getSamples().get(index);
+ }
+ }
+
+ @Override
+ public int size() {
+ return ReplaceSampleTrack.this.origTrack.getSamples().size();
+ }
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/SilenceTrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/SilenceTrackImpl.java
new file mode 100644
index 0000000..f74ab3c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/SilenceTrackImpl.java
@@ -0,0 +1,98 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Mp4TrackImpl;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This is just a basic idea how things could work but they don't.
+ */
+public class SilenceTrackImpl implements Track {
+ Track source;
+
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ TimeToSampleBox.Entry entry;
+
+ public SilenceTrackImpl(Track ofType, long ms) {
+ source = ofType;
+ if ("mp4a".equals(ofType.getSampleDescriptionBox().getSampleEntry().getType())) {
+ long numFrames = getTrackMetaData().getTimescale() * ms / 1000 / 1024;
+ long standZeit = getTrackMetaData().getTimescale() * ms / numFrames / 1000;
+ entry = new TimeToSampleBox.Entry(numFrames, standZeit);
+
+
+ while (numFrames-- > 0) {
+ samples.add((ByteBuffer) ByteBuffer.wrap(new byte[]{
+ 0x21, 0x10, 0x04, 0x60, (byte) 0x8c, 0x1c,
+ }).rewind());
+ }
+
+ } else {
+ throw new RuntimeException("Tracks of type " + ofType.getClass().getSimpleName() + " are not supported");
+ }
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return Collections.singletonList(entry);
+
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return source.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/TextTrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/TextTrackImpl.java
new file mode 100644
index 0000000..3bae143
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/TextTrackImpl.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.TextSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.threegpp26245.FontTableBox;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class TextTrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+ List<Line> subs = new LinkedList<Line>();
+
+ public List<Line> getSubs() {
+ return subs;
+ }
+
+ public TextTrackImpl() {
+ sampleDescriptionBox = new SampleDescriptionBox();
+ TextSampleEntry tx3g = new TextSampleEntry("tx3g");
+ tx3g.setDataReferenceIndex(1);
+ tx3g.setStyleRecord(new TextSampleEntry.StyleRecord());
+ tx3g.setBoxRecord(new TextSampleEntry.BoxRecord());
+ sampleDescriptionBox.addBox(tx3g);
+
+ FontTableBox ftab = new FontTableBox();
+ ftab.setEntries(Collections.singletonList(new FontTableBox.FontRecord(1, "Serif")));
+
+ tx3g.addBox(ftab);
+
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000); // Text tracks use millieseconds
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ samples.add(ByteBuffer.wrap(new byte[]{0, 0}));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ try {
+ dos.writeShort(sub.text.getBytes("UTF-8").length);
+ dos.write(sub.text.getBytes("UTF-8"));
+ dos.close();
+ } catch (IOException e) {
+ throw new Error("VM is broken. Does not support UTF-8");
+ }
+ samples.add(ByteBuffer.wrap(baos.toByteArray()));
+ lastEnd = sub.to;
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ stts.add(new TimeToSampleBox.Entry(1, silentTime));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ stts.add(new TimeToSampleBox.Entry(1, sub.to - sub.from));
+ lastEnd = sub.to;
+ }
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "sbtl";
+ }
+
+
+ public static class Line {
+ long from;
+ long to;
+ String text;
+
+
+ public Line(long from, long to, String text) {
+ this.from = from;
+ this.to = to;
+ this.text = text;
+ }
+
+ public long getFrom() {
+ return from;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public long getTo() {
+ return to;
+ }
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new NullMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/all-wcprops
new file mode 100644
index 0000000..b39616d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 78
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes
+END
+mp4-boxes.zip
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4-boxes.zip
+END
+AC3SpecificBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AC3SpecificBox.java
+END
+MLPSpecificBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/MLPSpecificBox.java
+END
+DTSSpecificBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/DTSSpecificBox.java
+END
+EC3SpecificBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/755/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/EC3SpecificBox.java
+END
+AbstractSampleEncryptionBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 111
+/svn/!svn/ver/744/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractSampleEncryptionBox.java
+END
+AbstractTrackEncryptionBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 110
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractTrackEncryptionBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/entries
new file mode 100644
index 0000000..46c357c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/entries
@@ -0,0 +1,293 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+mp4-boxes.zip
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+03585a21ffb96d2fec7af185e1a9d11b
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+62700
+
+threegpp26244
+dir
+
+threegpp26245
+dir
+
+basemediaformat
+dir
+
+piff
+dir
+
+MLPSpecificBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+f98e12a419f1111e27ea55eca285239b
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1868
+
+DTSSpecificBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+7e647845ba17be37607c41420f886df0
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5730
+
+AbstractTrackEncryptionBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+4621f8e06000ce397b5aae869fce1857
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2588
+
+mp4
+dir
+
+apple
+dir
+
+AC3SpecificBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+3f4c28d22ec46abb5263d756387a4d5f
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2661
+
+cenc
+dir
+
+EC3SpecificBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+80bcdd2db3972a22acdfa12a536dd1a5
+2012-08-17T01:13:17.213046Z
+755
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3860
+
+AbstractSampleEncryptionBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+b729f787e78562982c5f5cf998c58e06
+2012-08-14T13:54:34.186978Z
+744
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10292
+
+ultraviolet
+dir
+
+adobe
+dir
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/prop-base/mp4-boxes.zip.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/prop-base/mp4-boxes.zip.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/prop-base/mp4-boxes.zip.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AC3SpecificBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AC3SpecificBox.java.svn-base
new file mode 100644
index 0000000..a3006cd
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AC3SpecificBox.java.svn-base
@@ -0,0 +1,119 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.googlecode.mp4parser.AbstractBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+
+public class AC3SpecificBox extends AbstractBox {
+ int fscod;
+ int bsid;
+ int bsmod;
+ int acmod;
+ int lfeon;
+ int bitRateCode;
+ int reserved;
+
+ public AC3SpecificBox() {
+ super("dac3");
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 3;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ BitReaderBuffer brb = new BitReaderBuffer(content);
+ fscod = brb.readBits(2);
+ bsid = brb.readBits(5);
+ bsmod = brb.readBits(3);
+ acmod = brb.readBits(3);
+ lfeon = brb.readBits(1);
+ bitRateCode = brb.readBits(5);
+ reserved = brb.readBits(5);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer);
+ bwb.writeBits(fscod, 2);
+ bwb.writeBits(bsid, 5);
+ bwb.writeBits(bsmod, 3);
+ bwb.writeBits(acmod, 3);
+ bwb.writeBits(lfeon, 1);
+ bwb.writeBits(bitRateCode, 5);
+ bwb.writeBits(reserved, 5);
+ }
+
+ public int getFscod() {
+ return fscod;
+ }
+
+ public void setFscod(int fscod) {
+ this.fscod = fscod;
+ }
+
+ public int getBsid() {
+ return bsid;
+ }
+
+ public void setBsid(int bsid) {
+ this.bsid = bsid;
+ }
+
+ public int getBsmod() {
+ return bsmod;
+ }
+
+ public void setBsmod(int bsmod) {
+ this.bsmod = bsmod;
+ }
+
+ public int getAcmod() {
+ return acmod;
+ }
+
+ public void setAcmod(int acmod) {
+ this.acmod = acmod;
+ }
+
+ public int getLfeon() {
+ return lfeon;
+ }
+
+ public void setLfeon(int lfeon) {
+ this.lfeon = lfeon;
+ }
+
+ public int getBitRateCode() {
+ return bitRateCode;
+ }
+
+ public void setBitRateCode(int bitRateCode) {
+ this.bitRateCode = bitRateCode;
+ }
+
+ public int getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(int reserved) {
+ this.reserved = reserved;
+ }
+
+ @Override
+ public String toString() {
+ return "AC3SpecificBox{" +
+ "fscod=" + fscod +
+ ", bsid=" + bsid +
+ ", bsmod=" + bsmod +
+ ", acmod=" + acmod +
+ ", lfeon=" + lfeon +
+ ", bitRateCode=" + bitRateCode +
+ ", reserved=" + reserved +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AbstractSampleEncryptionBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AbstractSampleEncryptionBox.java.svn-base
new file mode 100644
index 0000000..7ac4bba
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AbstractSampleEncryptionBox.java.svn-base
@@ -0,0 +1,350 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.TrackHeaderBox;
+import com.coremedia.iso.boxes.fragment.TrackFragmentHeaderBox;
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.boxes.basemediaformat.TrackEncryptionBox;
+import com.googlecode.mp4parser.util.Path;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+
+public abstract class AbstractSampleEncryptionBox extends AbstractFullBox {
+ int algorithmId = -1;
+ int ivSize = -1;
+ byte[] kid = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ List<Entry> entries = new LinkedList<Entry>();
+
+ protected AbstractSampleEncryptionBox(String type) {
+ super(type);
+ }
+
+ public int getOffsetToFirstIV() {
+ int offset = (getSize() > (1l << 32) ? 16 : 8);
+ offset += isOverrideTrackEncryptionBoxParameters() ? 20 : 0;
+ offset += 4; //num entries
+ return offset;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ int useThisIvSize = -1;
+ if ((getFlags() & 0x1) > 0) {
+ algorithmId = IsoTypeReader.readUInt24(content);
+ ivSize = IsoTypeReader.readUInt8(content);
+ useThisIvSize = ivSize;
+ kid = new byte[16];
+ content.get(kid);
+ } else {
+ List<Box> tkhds = Path.getPaths(this, "/moov[0]/trak/tkhd");
+ for (Box tkhd : tkhds) {
+ if (((TrackHeaderBox) tkhd).getTrackId() == this.getParent().getBoxes(TrackFragmentHeaderBox.class).get(0).getTrackId()) {
+ AbstractTrackEncryptionBox tenc = (AbstractTrackEncryptionBox) Path.getPath(tkhd, "../mdia[0]/minf[0]/stbl[0]/stsd[0]/enc.[0]/sinf[0]/schi[0]/tenc[0]");
+ if (tenc == null) {
+ tenc = (AbstractTrackEncryptionBox) Path.getPath(tkhd, "../mdia[0]/minf[0]/stbl[0]/stsd[0]/enc.[0]/sinf[0]/schi[0]/uuid[0]");
+ }
+ useThisIvSize = tenc.getDefaultIvSize();
+ }
+ }
+ }
+ long numOfEntries = IsoTypeReader.readUInt32(content);
+
+ while (numOfEntries-- > 0) {
+ Entry e = new Entry();
+ e.iv = new byte[useThisIvSize < 0 ? 8 : useThisIvSize]; // default to 8
+ content.get(e.iv);
+ if ((getFlags() & 0x2) > 0) {
+ int numOfPairs = IsoTypeReader.readUInt16(content);
+ e.pairs = new LinkedList<Entry.Pair>();
+ while (numOfPairs-- > 0) {
+ e.pairs.add(e.createPair(IsoTypeReader.readUInt16(content), IsoTypeReader.readUInt32(content)));
+ }
+ }
+ entries.add(e);
+
+ }
+ }
+
+
+ public int getSampleCount() {
+ return entries.size();
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public int getAlgorithmId() {
+ return algorithmId;
+ }
+
+ public void setAlgorithmId(int algorithmId) {
+ this.algorithmId = algorithmId;
+ }
+
+ public int getIvSize() {
+ return ivSize;
+ }
+
+ public void setIvSize(int ivSize) {
+ this.ivSize = ivSize;
+ }
+
+ public byte[] getKid() {
+ return kid;
+ }
+
+ public void setKid(byte[] kid) {
+ this.kid = kid;
+ }
+
+
+ public boolean isSubSampleEncryption() {
+ return (getFlags() & 0x2) > 0;
+ }
+
+ public boolean isOverrideTrackEncryptionBoxParameters() {
+ return (getFlags() & 0x1) > 0;
+ }
+
+ public void setSubSampleEncryption(boolean b) {
+ if (b) {
+ setFlags(getFlags() | 0x2);
+ } else {
+ setFlags(getFlags() & (0xffffff ^ 0x2));
+ }
+ }
+
+ public void setOverrideTrackEncryptionBoxParameters(boolean b) {
+ if (b) {
+ setFlags(getFlags() | 0x1);
+ } else {
+ setFlags(getFlags() & (0xffffff ^ 0x1));
+ }
+ }
+
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ if (isOverrideTrackEncryptionBoxParameters()) {
+ IsoTypeWriter.writeUInt24(byteBuffer, algorithmId);
+ IsoTypeWriter.writeUInt8(byteBuffer, ivSize);
+ byteBuffer.put(kid);
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
+ for (Entry entry : entries) {
+ if (isOverrideTrackEncryptionBoxParameters()) {
+ byte[] ivFull = new byte[ivSize];
+ System.arraycopy(entry.iv, 0, ivFull, ivSize - entry.iv.length, entry.iv.length);
+ byteBuffer.put(ivFull);
+ } else {
+ // just put the iv - i don't know any better
+ byteBuffer.put(entry.iv);
+ }
+ if (isSubSampleEncryption()) {
+ IsoTypeWriter.writeUInt16(byteBuffer, entry.pairs.size());
+ for (Entry.Pair pair : entry.pairs) {
+ IsoTypeWriter.writeUInt16(byteBuffer, pair.clear);
+ IsoTypeWriter.writeUInt32(byteBuffer, pair.encrypted);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected long getContentSize() {
+ long contentSize = 4;
+ if (isOverrideTrackEncryptionBoxParameters()) {
+ contentSize += 4;
+ contentSize += kid.length;
+ }
+ contentSize += 4;
+ for (Entry entry : entries) {
+ contentSize += entry.getSize();
+ }
+ return contentSize;
+ }
+
+ @Override
+ public void getBox(WritableByteChannel os) throws IOException {
+ super.getBox(os);
+ }
+
+ public Entry createEntry() {
+ return new Entry();
+ }
+
+ public class Entry {
+ public byte[] iv;
+ public List<Pair> pairs = new LinkedList<Pair>();
+
+ public int getSize() {
+ int size = 0;
+ if (isOverrideTrackEncryptionBoxParameters()) {
+ size = ivSize;
+ } else {
+ size = iv.length;
+ }
+
+
+ if (isSubSampleEncryption()) {
+ size += 2;
+ for (Entry.Pair pair : pairs) {
+ size += 6;
+ }
+ }
+ return size;
+ }
+
+ public Pair createPair(int clear, long encrypted) {
+ return new Pair(clear, encrypted);
+ }
+
+
+ public class Pair {
+ public int clear;
+ public long encrypted;
+
+ public Pair(int clear, long encrypted) {
+ this.clear = clear;
+ this.encrypted = encrypted;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Pair pair = (Pair) o;
+
+ if (clear != pair.clear) {
+ return false;
+ }
+ if (encrypted != pair.encrypted) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clear;
+ result = 31 * result + (int) (encrypted ^ (encrypted >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "clr:" + clear + " enc:" + encrypted;
+ }
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (!new BigInteger(iv).equals(new BigInteger(entry.iv))) {
+ return false;
+ }
+ if (pairs != null ? !pairs.equals(entry.pairs) : entry.pairs != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = iv != null ? Arrays.hashCode(iv) : 0;
+ result = 31 * result + (pairs != null ? pairs.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "iv=" + Hex.encodeHex(iv) +
+ ", pairs=" + pairs +
+ '}';
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ AbstractSampleEncryptionBox that = (AbstractSampleEncryptionBox) o;
+
+ if (algorithmId != that.algorithmId) {
+ return false;
+ }
+ if (ivSize != that.ivSize) {
+ return false;
+ }
+ if (entries != null ? !entries.equals(that.entries) : that.entries != null) {
+ return false;
+ }
+ if (!Arrays.equals(kid, that.kid)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = algorithmId;
+ result = 31 * result + ivSize;
+ result = 31 * result + (kid != null ? Arrays.hashCode(kid) : 0);
+ result = 31 * result + (entries != null ? entries.hashCode() : 0);
+ return result;
+ }
+
+ public List<Short> getEntrySizes() {
+ List<Short> entrySizes = new ArrayList<Short>(entries.size());
+ for (Entry entry : entries) {
+ short size = (short) entry.iv.length;
+ if (isSubSampleEncryption()) {
+ size += 2; //numPairs
+ size += entry.pairs.size() * 6;
+ }
+ entrySizes.add(size);
+ }
+ return entrySizes;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AbstractTrackEncryptionBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AbstractTrackEncryptionBox.java.svn-base
new file mode 100644
index 0000000..fe35fde
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/AbstractTrackEncryptionBox.java.svn-base
@@ -0,0 +1,93 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.UUID;
+
+/**
+ *
+ */
+public abstract class AbstractTrackEncryptionBox extends AbstractFullBox {
+ int defaultAlgorithmId;
+ int defaultIvSize;
+ byte[] default_KID;
+
+ protected AbstractTrackEncryptionBox(String type) {
+ super(type);
+ }
+
+ public int getDefaultAlgorithmId() {
+ return defaultAlgorithmId;
+ }
+
+ public void setDefaultAlgorithmId(int defaultAlgorithmId) {
+ this.defaultAlgorithmId = defaultAlgorithmId;
+ }
+
+ public int getDefaultIvSize() {
+ return defaultIvSize;
+ }
+
+ public void setDefaultIvSize(int defaultIvSize) {
+ this.defaultIvSize = defaultIvSize;
+ }
+
+ public String getDefault_KID() {
+ ByteBuffer b = ByteBuffer.wrap(default_KID);
+ b.order(ByteOrder.BIG_ENDIAN);
+ return new UUID(b.getLong(), b.getLong()).toString();
+ }
+
+ public void setDefault_KID(byte[] default_KID) {
+ this.default_KID = default_KID;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ defaultAlgorithmId = IsoTypeReader.readUInt24(content);
+ defaultIvSize = IsoTypeReader.readUInt8(content);
+ default_KID = new byte[16];
+ content.get(default_KID);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ IsoTypeWriter.writeUInt24(byteBuffer, defaultAlgorithmId);
+ IsoTypeWriter.writeUInt8(byteBuffer, defaultIvSize);
+ byteBuffer.put(default_KID);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 24;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AbstractTrackEncryptionBox that = (AbstractTrackEncryptionBox) o;
+
+ if (defaultAlgorithmId != that.defaultAlgorithmId) return false;
+ if (defaultIvSize != that.defaultIvSize) return false;
+ if (!Arrays.equals(default_KID, that.default_KID)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = defaultAlgorithmId;
+ result = 31 * result + defaultIvSize;
+ result = 31 * result + (default_KID != null ? Arrays.hashCode(default_KID) : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/DTSSpecificBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/DTSSpecificBox.java.svn-base
new file mode 100644
index 0000000..a4cb346
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/DTSSpecificBox.java.svn-base
@@ -0,0 +1,217 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: magnus
+ * Date: 2012-03-09
+ * Time: 16:11
+ * To change this template use File | Settings | File Templates.
+ */
+public class DTSSpecificBox extends AbstractBox {
+
+ long DTSSamplingFrequency;
+ long maxBitRate;
+ long avgBitRate;
+ int pcmSampleDepth;
+ int frameDuration;
+ int streamConstruction;
+ int coreLFEPresent;
+ int coreLayout;
+ int coreSize;
+ int stereoDownmix;
+ int representationType;
+ int channelLayout;
+ int multiAssetFlag;
+ int LBRDurationMod;
+ int reservedBoxPresent;
+ int reserved;
+
+ public DTSSpecificBox() {
+ super("ddts");
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 20;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ DTSSamplingFrequency = IsoTypeReader.readUInt32(content);
+ maxBitRate = IsoTypeReader.readUInt32(content);
+ avgBitRate = IsoTypeReader.readUInt32(content);
+ pcmSampleDepth = IsoTypeReader.readUInt8(content);
+ BitReaderBuffer brb = new BitReaderBuffer(content);
+ frameDuration = brb.readBits(2);
+ streamConstruction = brb.readBits(5);
+ coreLFEPresent = brb.readBits(1);
+ coreLayout = brb.readBits(6);
+ coreSize = brb.readBits(14);
+ stereoDownmix = brb.readBits(1);
+ representationType = brb.readBits(3);
+ channelLayout = brb.readBits(16);
+ multiAssetFlag = brb.readBits(1);
+ LBRDurationMod = brb.readBits(1);
+ reservedBoxPresent = brb.readBits(1);
+ reserved = brb.readBits(5);
+
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ IsoTypeWriter.writeUInt32(byteBuffer, DTSSamplingFrequency);
+ IsoTypeWriter.writeUInt32(byteBuffer, maxBitRate);
+ IsoTypeWriter.writeUInt32(byteBuffer, avgBitRate);
+ IsoTypeWriter.writeUInt8(byteBuffer, pcmSampleDepth);
+ BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer);
+ bwb.writeBits(frameDuration, 2);
+ bwb.writeBits(streamConstruction, 5);
+ bwb.writeBits(coreLFEPresent, 1);
+ bwb.writeBits(coreLayout, 6);
+ bwb.writeBits(coreSize, 14);
+ bwb.writeBits(stereoDownmix, 1);
+ bwb.writeBits(representationType, 3);
+ bwb.writeBits(channelLayout, 16);
+ bwb.writeBits(multiAssetFlag, 1);
+ bwb.writeBits(LBRDurationMod, 1);
+ bwb.writeBits(reservedBoxPresent, 1);
+ bwb.writeBits(reserved, 5);
+
+ }
+
+ public long getAvgBitRate() {
+ return avgBitRate;
+ }
+
+ public void setAvgBitRate(long avgBitRate) {
+ this.avgBitRate = avgBitRate;
+ }
+
+ public long getDTSSamplingFrequency() {
+ return DTSSamplingFrequency;
+ }
+
+ public void setDTSSamplingFrequency(long DTSSamplingFrequency) {
+ this.DTSSamplingFrequency = DTSSamplingFrequency;
+ }
+
+ public long getMaxBitRate() {
+ return maxBitRate;
+ }
+
+ public void setMaxBitRate(long maxBitRate) {
+ this.maxBitRate = maxBitRate;
+ }
+
+ public int getPcmSampleDepth() {
+ return pcmSampleDepth;
+ }
+
+ public void setPcmSampleDepth(int pcmSampleDepth) {
+ this.pcmSampleDepth = pcmSampleDepth;
+ }
+
+ public int getFrameDuration() {
+ return frameDuration;
+ }
+
+ public void setFrameDuration(int frameDuration) {
+ this.frameDuration = frameDuration;
+ }
+
+ public int getStreamConstruction() {
+ return streamConstruction;
+ }
+
+ public void setStreamConstruction(int streamConstruction) {
+ this.streamConstruction = streamConstruction;
+ }
+
+ public int getCoreLFEPresent() {
+ return coreLFEPresent;
+ }
+
+ public void setCoreLFEPresent(int coreLFEPresent) {
+ this.coreLFEPresent = coreLFEPresent;
+ }
+
+ public int getCoreLayout() {
+ return coreLayout;
+ }
+
+ public void setCoreLayout(int coreLayout) {
+ this.coreLayout = coreLayout;
+ }
+
+ public int getCoreSize() {
+ return coreSize;
+ }
+
+ public void setCoreSize(int coreSize) {
+ this.coreSize = coreSize;
+ }
+
+ public int getStereoDownmix() {
+ return stereoDownmix;
+ }
+
+ public void setStereoDownmix(int stereoDownmix) {
+ this.stereoDownmix = stereoDownmix;
+ }
+
+ public int getRepresentationType() {
+ return representationType;
+ }
+
+ public void setRepresentationType(int representationType) {
+ this.representationType = representationType;
+ }
+
+ public int getChannelLayout() {
+ return channelLayout;
+ }
+
+ public void setChannelLayout(int channelLayout) {
+ this.channelLayout = channelLayout;
+ }
+
+ public int getMultiAssetFlag() {
+ return multiAssetFlag;
+ }
+
+ public void setMultiAssetFlag(int multiAssetFlag) {
+ this.multiAssetFlag = multiAssetFlag;
+ }
+
+ public int getLBRDurationMod() {
+ return LBRDurationMod;
+ }
+
+ public void setLBRDurationMod(int LBRDurationMod) {
+ this.LBRDurationMod = LBRDurationMod;
+ }
+
+ public int getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(int reserved) {
+ this.reserved = reserved;
+ }
+
+ public int getReservedBoxPresent() {
+ return reservedBoxPresent;
+ }
+
+ public void setReservedBoxPresent(int reservedBoxPresent) {
+ this.reservedBoxPresent = reservedBoxPresent;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/EC3SpecificBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/EC3SpecificBox.java.svn-base
new file mode 100644
index 0000000..412db04
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/EC3SpecificBox.java.svn-base
@@ -0,0 +1,140 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.googlecode.mp4parser.AbstractBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class EC3SpecificBox extends AbstractBox {
+ List<Entry> entries = new LinkedList<Entry>();
+ int dataRate;
+ int numIndSub;
+
+ public EC3SpecificBox() {
+ super("dec3");
+ }
+
+ @Override
+ public long getContentSize() {
+ long size = 2;
+ for (Entry entry : entries) {
+ if (entry.num_dep_sub > 0) {
+ size += 4;
+ } else {
+ size += 3;
+ }
+ }
+ return size;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ BitReaderBuffer brb = new BitReaderBuffer(content);
+ dataRate = brb.readBits(13);
+ numIndSub = brb.readBits(3) + 1;
+ // This field indicates the number of independent substreams that are present in the Enhanced AC-3 bitstream. The value
+ // of this field is one less than the number of independent substreams present.
+
+
+ for (int i = 0; i < numIndSub; i++) {
+ Entry e = new Entry();
+ e.fscod = brb.readBits(2);
+ e.bsid = brb.readBits(5);
+ e.bsmod = brb.readBits(5);
+ e.acmod = brb.readBits(3);
+ e.lfeon = brb.readBits(1);
+ e.reserved = brb.readBits(3);
+ e.num_dep_sub = brb.readBits(4);
+ if (e.num_dep_sub > 0) {
+ e.chan_loc = brb.readBits(9);
+ } else {
+ e.reserved2 = brb.readBits(1);
+ }
+ entries.add(e);
+ }
+ }
+
+ @Override
+ public void getContent(ByteBuffer byteBuffer) {
+ BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer);
+ bwb.writeBits(dataRate, 13);
+ bwb.writeBits(entries.size() - 1, 3);
+ for (Entry e : entries) {
+ bwb.writeBits(e.fscod, 2);
+ bwb.writeBits(e.bsid, 5);
+ bwb.writeBits(e.bsmod, 5);
+ bwb.writeBits(e.acmod, 3);
+ bwb.writeBits(e.lfeon, 1);
+ bwb.writeBits(e.reserved, 3);
+ bwb.writeBits(e.num_dep_sub, 4);
+ if (e.num_dep_sub > 0) {
+ bwb.writeBits(e.chan_loc, 9);
+ } else {
+ bwb.writeBits(e.reserved2, 1);
+ }
+ }
+ }
+
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public void addEntry(Entry entry) {
+ this.entries.add(entry);
+ }
+
+ public int getDataRate() {
+ return dataRate;
+ }
+
+ public void setDataRate(int dataRate) {
+ this.dataRate = dataRate;
+ }
+
+ public int getNumIndSub() {
+ return numIndSub;
+ }
+
+ public void setNumIndSub(int numIndSub) {
+ this.numIndSub = numIndSub;
+ }
+
+ public static class Entry {
+ public int fscod;
+ public int bsid;
+ public int bsmod;
+ public int acmod;
+ public int lfeon;
+ public int reserved;
+ public int num_dep_sub;
+ public int chan_loc;
+ public int reserved2;
+
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "fscod=" + fscod +
+ ", bsid=" + bsid +
+ ", bsmod=" + bsmod +
+ ", acmod=" + acmod +
+ ", lfeon=" + lfeon +
+ ", reserved=" + reserved +
+ ", num_dep_sub=" + num_dep_sub +
+ ", chan_loc=" + chan_loc +
+ ", reserved2=" + reserved2 +
+ '}';
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/MLPSpecificBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/MLPSpecificBox.java.svn-base
new file mode 100644
index 0000000..fe6f0d8
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/MLPSpecificBox.java.svn-base
@@ -0,0 +1,76 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.googlecode.mp4parser.AbstractBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+
+
+public class MLPSpecificBox extends AbstractBox {
+
+ int format_info;
+ int peak_data_rate;
+ int reserved;
+ int reserved2;
+
+ public MLPSpecificBox() {
+ super("dmlp");
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 10;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ BitReaderBuffer brb = new BitReaderBuffer(content);
+ format_info = brb.readBits(32);
+ peak_data_rate = brb.readBits(15);
+ reserved = brb.readBits(1);
+ reserved2 = brb.readBits(32);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer);
+ bwb.writeBits(format_info, 32);
+ bwb.writeBits(peak_data_rate, 15);
+ bwb.writeBits(reserved, 1);
+ bwb.writeBits(reserved2, 32);
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getFormat_info() {
+ return format_info;
+ }
+
+ public void setFormat_info(int format_info) {
+ this.format_info = format_info;
+ }
+
+ public int getPeak_data_rate() {
+ return peak_data_rate;
+ }
+
+ public void setPeak_data_rate(int peak_data_rate) {
+ this.peak_data_rate = peak_data_rate;
+ }
+
+ public int getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(int reserved) {
+ this.reserved = reserved;
+ }
+
+ public int getReserved2() {
+ return reserved2;
+ }
+
+ public void setReserved2(int reserved2) {
+ this.reserved2 = reserved2;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/mp4-boxes.zip.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/mp4-boxes.zip.svn-base
new file mode 100644
index 0000000..c9bcf8b
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/.svn/text-base/mp4-boxes.zip.svn-base
Binary files differ
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AC3SpecificBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AC3SpecificBox.java
new file mode 100644
index 0000000..a3006cd
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AC3SpecificBox.java
@@ -0,0 +1,119 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.googlecode.mp4parser.AbstractBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+
+public class AC3SpecificBox extends AbstractBox {
+ int fscod;
+ int bsid;
+ int bsmod;
+ int acmod;
+ int lfeon;
+ int bitRateCode;
+ int reserved;
+
+ public AC3SpecificBox() {
+ super("dac3");
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 3;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ BitReaderBuffer brb = new BitReaderBuffer(content);
+ fscod = brb.readBits(2);
+ bsid = brb.readBits(5);
+ bsmod = brb.readBits(3);
+ acmod = brb.readBits(3);
+ lfeon = brb.readBits(1);
+ bitRateCode = brb.readBits(5);
+ reserved = brb.readBits(5);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer);
+ bwb.writeBits(fscod, 2);
+ bwb.writeBits(bsid, 5);
+ bwb.writeBits(bsmod, 3);
+ bwb.writeBits(acmod, 3);
+ bwb.writeBits(lfeon, 1);
+ bwb.writeBits(bitRateCode, 5);
+ bwb.writeBits(reserved, 5);
+ }
+
+ public int getFscod() {
+ return fscod;
+ }
+
+ public void setFscod(int fscod) {
+ this.fscod = fscod;
+ }
+
+ public int getBsid() {
+ return bsid;
+ }
+
+ public void setBsid(int bsid) {
+ this.bsid = bsid;
+ }
+
+ public int getBsmod() {
+ return bsmod;
+ }
+
+ public void setBsmod(int bsmod) {
+ this.bsmod = bsmod;
+ }
+
+ public int getAcmod() {
+ return acmod;
+ }
+
+ public void setAcmod(int acmod) {
+ this.acmod = acmod;
+ }
+
+ public int getLfeon() {
+ return lfeon;
+ }
+
+ public void setLfeon(int lfeon) {
+ this.lfeon = lfeon;
+ }
+
+ public int getBitRateCode() {
+ return bitRateCode;
+ }
+
+ public void setBitRateCode(int bitRateCode) {
+ this.bitRateCode = bitRateCode;
+ }
+
+ public int getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(int reserved) {
+ this.reserved = reserved;
+ }
+
+ @Override
+ public String toString() {
+ return "AC3SpecificBox{" +
+ "fscod=" + fscod +
+ ", bsid=" + bsid +
+ ", bsmod=" + bsmod +
+ ", acmod=" + acmod +
+ ", lfeon=" + lfeon +
+ ", bitRateCode=" + bitRateCode +
+ ", reserved=" + reserved +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractSampleEncryptionBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractSampleEncryptionBox.java
new file mode 100644
index 0000000..7ac4bba
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractSampleEncryptionBox.java
@@ -0,0 +1,350 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.TrackHeaderBox;
+import com.coremedia.iso.boxes.fragment.TrackFragmentHeaderBox;
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.boxes.basemediaformat.TrackEncryptionBox;
+import com.googlecode.mp4parser.util.Path;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+
+public abstract class AbstractSampleEncryptionBox extends AbstractFullBox {
+ int algorithmId = -1;
+ int ivSize = -1;
+ byte[] kid = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ List<Entry> entries = new LinkedList<Entry>();
+
+ protected AbstractSampleEncryptionBox(String type) {
+ super(type);
+ }
+
+ public int getOffsetToFirstIV() {
+ int offset = (getSize() > (1l << 32) ? 16 : 8);
+ offset += isOverrideTrackEncryptionBoxParameters() ? 20 : 0;
+ offset += 4; //num entries
+ return offset;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ int useThisIvSize = -1;
+ if ((getFlags() & 0x1) > 0) {
+ algorithmId = IsoTypeReader.readUInt24(content);
+ ivSize = IsoTypeReader.readUInt8(content);
+ useThisIvSize = ivSize;
+ kid = new byte[16];
+ content.get(kid);
+ } else {
+ List<Box> tkhds = Path.getPaths(this, "/moov[0]/trak/tkhd");
+ for (Box tkhd : tkhds) {
+ if (((TrackHeaderBox) tkhd).getTrackId() == this.getParent().getBoxes(TrackFragmentHeaderBox.class).get(0).getTrackId()) {
+ AbstractTrackEncryptionBox tenc = (AbstractTrackEncryptionBox) Path.getPath(tkhd, "../mdia[0]/minf[0]/stbl[0]/stsd[0]/enc.[0]/sinf[0]/schi[0]/tenc[0]");
+ if (tenc == null) {
+ tenc = (AbstractTrackEncryptionBox) Path.getPath(tkhd, "../mdia[0]/minf[0]/stbl[0]/stsd[0]/enc.[0]/sinf[0]/schi[0]/uuid[0]");
+ }
+ useThisIvSize = tenc.getDefaultIvSize();
+ }
+ }
+ }
+ long numOfEntries = IsoTypeReader.readUInt32(content);
+
+ while (numOfEntries-- > 0) {
+ Entry e = new Entry();
+ e.iv = new byte[useThisIvSize < 0 ? 8 : useThisIvSize]; // default to 8
+ content.get(e.iv);
+ if ((getFlags() & 0x2) > 0) {
+ int numOfPairs = IsoTypeReader.readUInt16(content);
+ e.pairs = new LinkedList<Entry.Pair>();
+ while (numOfPairs-- > 0) {
+ e.pairs.add(e.createPair(IsoTypeReader.readUInt16(content), IsoTypeReader.readUInt32(content)));
+ }
+ }
+ entries.add(e);
+
+ }
+ }
+
+
+ public int getSampleCount() {
+ return entries.size();
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public int getAlgorithmId() {
+ return algorithmId;
+ }
+
+ public void setAlgorithmId(int algorithmId) {
+ this.algorithmId = algorithmId;
+ }
+
+ public int getIvSize() {
+ return ivSize;
+ }
+
+ public void setIvSize(int ivSize) {
+ this.ivSize = ivSize;
+ }
+
+ public byte[] getKid() {
+ return kid;
+ }
+
+ public void setKid(byte[] kid) {
+ this.kid = kid;
+ }
+
+
+ public boolean isSubSampleEncryption() {
+ return (getFlags() & 0x2) > 0;
+ }
+
+ public boolean isOverrideTrackEncryptionBoxParameters() {
+ return (getFlags() & 0x1) > 0;
+ }
+
+ public void setSubSampleEncryption(boolean b) {
+ if (b) {
+ setFlags(getFlags() | 0x2);
+ } else {
+ setFlags(getFlags() & (0xffffff ^ 0x2));
+ }
+ }
+
+ public void setOverrideTrackEncryptionBoxParameters(boolean b) {
+ if (b) {
+ setFlags(getFlags() | 0x1);
+ } else {
+ setFlags(getFlags() & (0xffffff ^ 0x1));
+ }
+ }
+
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ if (isOverrideTrackEncryptionBoxParameters()) {
+ IsoTypeWriter.writeUInt24(byteBuffer, algorithmId);
+ IsoTypeWriter.writeUInt8(byteBuffer, ivSize);
+ byteBuffer.put(kid);
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
+ for (Entry entry : entries) {
+ if (isOverrideTrackEncryptionBoxParameters()) {
+ byte[] ivFull = new byte[ivSize];
+ System.arraycopy(entry.iv, 0, ivFull, ivSize - entry.iv.length, entry.iv.length);
+ byteBuffer.put(ivFull);
+ } else {
+ // just put the iv - i don't know any better
+ byteBuffer.put(entry.iv);
+ }
+ if (isSubSampleEncryption()) {
+ IsoTypeWriter.writeUInt16(byteBuffer, entry.pairs.size());
+ for (Entry.Pair pair : entry.pairs) {
+ IsoTypeWriter.writeUInt16(byteBuffer, pair.clear);
+ IsoTypeWriter.writeUInt32(byteBuffer, pair.encrypted);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected long getContentSize() {
+ long contentSize = 4;
+ if (isOverrideTrackEncryptionBoxParameters()) {
+ contentSize += 4;
+ contentSize += kid.length;
+ }
+ contentSize += 4;
+ for (Entry entry : entries) {
+ contentSize += entry.getSize();
+ }
+ return contentSize;
+ }
+
+ @Override
+ public void getBox(WritableByteChannel os) throws IOException {
+ super.getBox(os);
+ }
+
+ public Entry createEntry() {
+ return new Entry();
+ }
+
+ public class Entry {
+ public byte[] iv;
+ public List<Pair> pairs = new LinkedList<Pair>();
+
+ public int getSize() {
+ int size = 0;
+ if (isOverrideTrackEncryptionBoxParameters()) {
+ size = ivSize;
+ } else {
+ size = iv.length;
+ }
+
+
+ if (isSubSampleEncryption()) {
+ size += 2;
+ for (Entry.Pair pair : pairs) {
+ size += 6;
+ }
+ }
+ return size;
+ }
+
+ public Pair createPair(int clear, long encrypted) {
+ return new Pair(clear, encrypted);
+ }
+
+
+ public class Pair {
+ public int clear;
+ public long encrypted;
+
+ public Pair(int clear, long encrypted) {
+ this.clear = clear;
+ this.encrypted = encrypted;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Pair pair = (Pair) o;
+
+ if (clear != pair.clear) {
+ return false;
+ }
+ if (encrypted != pair.encrypted) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clear;
+ result = 31 * result + (int) (encrypted ^ (encrypted >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "clr:" + clear + " enc:" + encrypted;
+ }
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (!new BigInteger(iv).equals(new BigInteger(entry.iv))) {
+ return false;
+ }
+ if (pairs != null ? !pairs.equals(entry.pairs) : entry.pairs != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = iv != null ? Arrays.hashCode(iv) : 0;
+ result = 31 * result + (pairs != null ? pairs.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "iv=" + Hex.encodeHex(iv) +
+ ", pairs=" + pairs +
+ '}';
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ AbstractSampleEncryptionBox that = (AbstractSampleEncryptionBox) o;
+
+ if (algorithmId != that.algorithmId) {
+ return false;
+ }
+ if (ivSize != that.ivSize) {
+ return false;
+ }
+ if (entries != null ? !entries.equals(that.entries) : that.entries != null) {
+ return false;
+ }
+ if (!Arrays.equals(kid, that.kid)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = algorithmId;
+ result = 31 * result + ivSize;
+ result = 31 * result + (kid != null ? Arrays.hashCode(kid) : 0);
+ result = 31 * result + (entries != null ? entries.hashCode() : 0);
+ return result;
+ }
+
+ public List<Short> getEntrySizes() {
+ List<Short> entrySizes = new ArrayList<Short>(entries.size());
+ for (Entry entry : entries) {
+ short size = (short) entry.iv.length;
+ if (isSubSampleEncryption()) {
+ size += 2; //numPairs
+ size += entry.pairs.size() * 6;
+ }
+ entrySizes.add(size);
+ }
+ return entrySizes;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractTrackEncryptionBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractTrackEncryptionBox.java
new file mode 100644
index 0000000..fe35fde
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/AbstractTrackEncryptionBox.java
@@ -0,0 +1,93 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.UUID;
+
+/**
+ *
+ */
+public abstract class AbstractTrackEncryptionBox extends AbstractFullBox {
+ int defaultAlgorithmId;
+ int defaultIvSize;
+ byte[] default_KID;
+
+ protected AbstractTrackEncryptionBox(String type) {
+ super(type);
+ }
+
+ public int getDefaultAlgorithmId() {
+ return defaultAlgorithmId;
+ }
+
+ public void setDefaultAlgorithmId(int defaultAlgorithmId) {
+ this.defaultAlgorithmId = defaultAlgorithmId;
+ }
+
+ public int getDefaultIvSize() {
+ return defaultIvSize;
+ }
+
+ public void setDefaultIvSize(int defaultIvSize) {
+ this.defaultIvSize = defaultIvSize;
+ }
+
+ public String getDefault_KID() {
+ ByteBuffer b = ByteBuffer.wrap(default_KID);
+ b.order(ByteOrder.BIG_ENDIAN);
+ return new UUID(b.getLong(), b.getLong()).toString();
+ }
+
+ public void setDefault_KID(byte[] default_KID) {
+ this.default_KID = default_KID;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ defaultAlgorithmId = IsoTypeReader.readUInt24(content);
+ defaultIvSize = IsoTypeReader.readUInt8(content);
+ default_KID = new byte[16];
+ content.get(default_KID);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ IsoTypeWriter.writeUInt24(byteBuffer, defaultAlgorithmId);
+ IsoTypeWriter.writeUInt8(byteBuffer, defaultIvSize);
+ byteBuffer.put(default_KID);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 24;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AbstractTrackEncryptionBox that = (AbstractTrackEncryptionBox) o;
+
+ if (defaultAlgorithmId != that.defaultAlgorithmId) return false;
+ if (defaultIvSize != that.defaultIvSize) return false;
+ if (!Arrays.equals(default_KID, that.default_KID)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = defaultAlgorithmId;
+ result = 31 * result + defaultIvSize;
+ result = 31 * result + (default_KID != null ? Arrays.hashCode(default_KID) : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/DTSSpecificBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/DTSSpecificBox.java
new file mode 100644
index 0000000..a4cb346
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/DTSSpecificBox.java
@@ -0,0 +1,217 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: magnus
+ * Date: 2012-03-09
+ * Time: 16:11
+ * To change this template use File | Settings | File Templates.
+ */
+public class DTSSpecificBox extends AbstractBox {
+
+ long DTSSamplingFrequency;
+ long maxBitRate;
+ long avgBitRate;
+ int pcmSampleDepth;
+ int frameDuration;
+ int streamConstruction;
+ int coreLFEPresent;
+ int coreLayout;
+ int coreSize;
+ int stereoDownmix;
+ int representationType;
+ int channelLayout;
+ int multiAssetFlag;
+ int LBRDurationMod;
+ int reservedBoxPresent;
+ int reserved;
+
+ public DTSSpecificBox() {
+ super("ddts");
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 20;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ DTSSamplingFrequency = IsoTypeReader.readUInt32(content);
+ maxBitRate = IsoTypeReader.readUInt32(content);
+ avgBitRate = IsoTypeReader.readUInt32(content);
+ pcmSampleDepth = IsoTypeReader.readUInt8(content);
+ BitReaderBuffer brb = new BitReaderBuffer(content);
+ frameDuration = brb.readBits(2);
+ streamConstruction = brb.readBits(5);
+ coreLFEPresent = brb.readBits(1);
+ coreLayout = brb.readBits(6);
+ coreSize = brb.readBits(14);
+ stereoDownmix = brb.readBits(1);
+ representationType = brb.readBits(3);
+ channelLayout = brb.readBits(16);
+ multiAssetFlag = brb.readBits(1);
+ LBRDurationMod = brb.readBits(1);
+ reservedBoxPresent = brb.readBits(1);
+ reserved = brb.readBits(5);
+
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ IsoTypeWriter.writeUInt32(byteBuffer, DTSSamplingFrequency);
+ IsoTypeWriter.writeUInt32(byteBuffer, maxBitRate);
+ IsoTypeWriter.writeUInt32(byteBuffer, avgBitRate);
+ IsoTypeWriter.writeUInt8(byteBuffer, pcmSampleDepth);
+ BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer);
+ bwb.writeBits(frameDuration, 2);
+ bwb.writeBits(streamConstruction, 5);
+ bwb.writeBits(coreLFEPresent, 1);
+ bwb.writeBits(coreLayout, 6);
+ bwb.writeBits(coreSize, 14);
+ bwb.writeBits(stereoDownmix, 1);
+ bwb.writeBits(representationType, 3);
+ bwb.writeBits(channelLayout, 16);
+ bwb.writeBits(multiAssetFlag, 1);
+ bwb.writeBits(LBRDurationMod, 1);
+ bwb.writeBits(reservedBoxPresent, 1);
+ bwb.writeBits(reserved, 5);
+
+ }
+
+ public long getAvgBitRate() {
+ return avgBitRate;
+ }
+
+ public void setAvgBitRate(long avgBitRate) {
+ this.avgBitRate = avgBitRate;
+ }
+
+ public long getDTSSamplingFrequency() {
+ return DTSSamplingFrequency;
+ }
+
+ public void setDTSSamplingFrequency(long DTSSamplingFrequency) {
+ this.DTSSamplingFrequency = DTSSamplingFrequency;
+ }
+
+ public long getMaxBitRate() {
+ return maxBitRate;
+ }
+
+ public void setMaxBitRate(long maxBitRate) {
+ this.maxBitRate = maxBitRate;
+ }
+
+ public int getPcmSampleDepth() {
+ return pcmSampleDepth;
+ }
+
+ public void setPcmSampleDepth(int pcmSampleDepth) {
+ this.pcmSampleDepth = pcmSampleDepth;
+ }
+
+ public int getFrameDuration() {
+ return frameDuration;
+ }
+
+ public void setFrameDuration(int frameDuration) {
+ this.frameDuration = frameDuration;
+ }
+
+ public int getStreamConstruction() {
+ return streamConstruction;
+ }
+
+ public void setStreamConstruction(int streamConstruction) {
+ this.streamConstruction = streamConstruction;
+ }
+
+ public int getCoreLFEPresent() {
+ return coreLFEPresent;
+ }
+
+ public void setCoreLFEPresent(int coreLFEPresent) {
+ this.coreLFEPresent = coreLFEPresent;
+ }
+
+ public int getCoreLayout() {
+ return coreLayout;
+ }
+
+ public void setCoreLayout(int coreLayout) {
+ this.coreLayout = coreLayout;
+ }
+
+ public int getCoreSize() {
+ return coreSize;
+ }
+
+ public void setCoreSize(int coreSize) {
+ this.coreSize = coreSize;
+ }
+
+ public int getStereoDownmix() {
+ return stereoDownmix;
+ }
+
+ public void setStereoDownmix(int stereoDownmix) {
+ this.stereoDownmix = stereoDownmix;
+ }
+
+ public int getRepresentationType() {
+ return representationType;
+ }
+
+ public void setRepresentationType(int representationType) {
+ this.representationType = representationType;
+ }
+
+ public int getChannelLayout() {
+ return channelLayout;
+ }
+
+ public void setChannelLayout(int channelLayout) {
+ this.channelLayout = channelLayout;
+ }
+
+ public int getMultiAssetFlag() {
+ return multiAssetFlag;
+ }
+
+ public void setMultiAssetFlag(int multiAssetFlag) {
+ this.multiAssetFlag = multiAssetFlag;
+ }
+
+ public int getLBRDurationMod() {
+ return LBRDurationMod;
+ }
+
+ public void setLBRDurationMod(int LBRDurationMod) {
+ this.LBRDurationMod = LBRDurationMod;
+ }
+
+ public int getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(int reserved) {
+ this.reserved = reserved;
+ }
+
+ public int getReservedBoxPresent() {
+ return reservedBoxPresent;
+ }
+
+ public void setReservedBoxPresent(int reservedBoxPresent) {
+ this.reservedBoxPresent = reservedBoxPresent;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/EC3SpecificBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/EC3SpecificBox.java
new file mode 100644
index 0000000..412db04
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/EC3SpecificBox.java
@@ -0,0 +1,140 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.googlecode.mp4parser.AbstractBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class EC3SpecificBox extends AbstractBox {
+ List<Entry> entries = new LinkedList<Entry>();
+ int dataRate;
+ int numIndSub;
+
+ public EC3SpecificBox() {
+ super("dec3");
+ }
+
+ @Override
+ public long getContentSize() {
+ long size = 2;
+ for (Entry entry : entries) {
+ if (entry.num_dep_sub > 0) {
+ size += 4;
+ } else {
+ size += 3;
+ }
+ }
+ return size;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ BitReaderBuffer brb = new BitReaderBuffer(content);
+ dataRate = brb.readBits(13);
+ numIndSub = brb.readBits(3) + 1;
+ // This field indicates the number of independent substreams that are present in the Enhanced AC-3 bitstream. The value
+ // of this field is one less than the number of independent substreams present.
+
+
+ for (int i = 0; i < numIndSub; i++) {
+ Entry e = new Entry();
+ e.fscod = brb.readBits(2);
+ e.bsid = brb.readBits(5);
+ e.bsmod = brb.readBits(5);
+ e.acmod = brb.readBits(3);
+ e.lfeon = brb.readBits(1);
+ e.reserved = brb.readBits(3);
+ e.num_dep_sub = brb.readBits(4);
+ if (e.num_dep_sub > 0) {
+ e.chan_loc = brb.readBits(9);
+ } else {
+ e.reserved2 = brb.readBits(1);
+ }
+ entries.add(e);
+ }
+ }
+
+ @Override
+ public void getContent(ByteBuffer byteBuffer) {
+ BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer);
+ bwb.writeBits(dataRate, 13);
+ bwb.writeBits(entries.size() - 1, 3);
+ for (Entry e : entries) {
+ bwb.writeBits(e.fscod, 2);
+ bwb.writeBits(e.bsid, 5);
+ bwb.writeBits(e.bsmod, 5);
+ bwb.writeBits(e.acmod, 3);
+ bwb.writeBits(e.lfeon, 1);
+ bwb.writeBits(e.reserved, 3);
+ bwb.writeBits(e.num_dep_sub, 4);
+ if (e.num_dep_sub > 0) {
+ bwb.writeBits(e.chan_loc, 9);
+ } else {
+ bwb.writeBits(e.reserved2, 1);
+ }
+ }
+ }
+
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public void addEntry(Entry entry) {
+ this.entries.add(entry);
+ }
+
+ public int getDataRate() {
+ return dataRate;
+ }
+
+ public void setDataRate(int dataRate) {
+ this.dataRate = dataRate;
+ }
+
+ public int getNumIndSub() {
+ return numIndSub;
+ }
+
+ public void setNumIndSub(int numIndSub) {
+ this.numIndSub = numIndSub;
+ }
+
+ public static class Entry {
+ public int fscod;
+ public int bsid;
+ public int bsmod;
+ public int acmod;
+ public int lfeon;
+ public int reserved;
+ public int num_dep_sub;
+ public int chan_loc;
+ public int reserved2;
+
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "fscod=" + fscod +
+ ", bsid=" + bsid +
+ ", bsmod=" + bsmod +
+ ", acmod=" + acmod +
+ ", lfeon=" + lfeon +
+ ", reserved=" + reserved +
+ ", num_dep_sub=" + num_dep_sub +
+ ", chan_loc=" + chan_loc +
+ ", reserved2=" + reserved2 +
+ '}';
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/MLPSpecificBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/MLPSpecificBox.java
new file mode 100644
index 0000000..fe6f0d8
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/MLPSpecificBox.java
@@ -0,0 +1,76 @@
+package com.googlecode.mp4parser.boxes;
+
+import com.googlecode.mp4parser.AbstractBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+
+
+public class MLPSpecificBox extends AbstractBox {
+
+ int format_info;
+ int peak_data_rate;
+ int reserved;
+ int reserved2;
+
+ public MLPSpecificBox() {
+ super("dmlp");
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 10;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ BitReaderBuffer brb = new BitReaderBuffer(content);
+ format_info = brb.readBits(32);
+ peak_data_rate = brb.readBits(15);
+ reserved = brb.readBits(1);
+ reserved2 = brb.readBits(32);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer);
+ bwb.writeBits(format_info, 32);
+ bwb.writeBits(peak_data_rate, 15);
+ bwb.writeBits(reserved, 1);
+ bwb.writeBits(reserved2, 32);
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getFormat_info() {
+ return format_info;
+ }
+
+ public void setFormat_info(int format_info) {
+ this.format_info = format_info;
+ }
+
+ public int getPeak_data_rate() {
+ return peak_data_rate;
+ }
+
+ public void setPeak_data_rate(int peak_data_rate) {
+ this.peak_data_rate = peak_data_rate;
+ }
+
+ public int getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(int reserved) {
+ this.reserved = reserved;
+ }
+
+ public int getReserved2() {
+ return reserved2;
+ }
+
+ public void setReserved2(int reserved2) {
+ this.reserved2 = reserved2;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/all-wcprops
new file mode 100644
index 0000000..d4678d2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe
+END
+ActionMessageFormat0SampleEntryBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 124
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/ActionMessageFormat0SampleEntryBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/entries
new file mode 100644
index 0000000..c291f28
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+ActionMessageFormat0SampleEntryBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.297230Z
+cf327bd01b68a2d3801d58c67ba3a500
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+901
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/text-base/ActionMessageFormat0SampleEntryBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/text-base/ActionMessageFormat0SampleEntryBox.java.svn-base
new file mode 100644
index 0000000..483dd8f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/.svn/text-base/ActionMessageFormat0SampleEntryBox.java.svn-base
@@ -0,0 +1,38 @@
+package com.googlecode.mp4parser.boxes.adobe;
+
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.sampleentry.SampleEntry;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Sample Entry as used for Action Message Format tracks.
+ */
+public class ActionMessageFormat0SampleEntryBox extends SampleEntry {
+ public ActionMessageFormat0SampleEntryBox() {
+ super("amf0");
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 8;
+ for (Box box : boxes) {
+ size += box.getSize();
+ }
+
+ return size;
+ }
+
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ _parseReservedAndDataReferenceIndex(content);
+ _parseChildBoxes(content);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ _writeReservedAndDataReferenceIndex(byteBuffer);
+ _writeChildBoxes(byteBuffer);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/ActionMessageFormat0SampleEntryBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/ActionMessageFormat0SampleEntryBox.java
new file mode 100644
index 0000000..483dd8f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/adobe/ActionMessageFormat0SampleEntryBox.java
@@ -0,0 +1,38 @@
+package com.googlecode.mp4parser.boxes.adobe;
+
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.sampleentry.SampleEntry;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Sample Entry as used for Action Message Format tracks.
+ */
+public class ActionMessageFormat0SampleEntryBox extends SampleEntry {
+ public ActionMessageFormat0SampleEntryBox() {
+ super("amf0");
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 8;
+ for (Box box : boxes) {
+ size += box.getSize();
+ }
+
+ return size;
+ }
+
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ _parseReservedAndDataReferenceIndex(content);
+ _parseChildBoxes(content);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ _writeReservedAndDataReferenceIndex(byteBuffer);
+ _writeChildBoxes(byteBuffer);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/all-wcprops
new file mode 100644
index 0000000..458d104
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/727/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple
+END
+TimeCodeBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/684/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TimeCodeBox.java
+END
+QuicktimeTextSampleEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 114
+/svn/!svn/ver/690/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/QuicktimeTextSampleEntry.java
+END
+GenericMediaHeaderAtom.java
+K 25
+svn:wc:ra_dav:version-url
+V 112
+/svn/!svn/ver/684/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderAtom.java
+END
+BaseMediaInfoAtom.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/687/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/BaseMediaInfoAtom.java
+END
+TaptAtom.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/727/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TaptAtom.java
+END
+GenericMediaHeaderTextAtom.java
+K 25
+svn:wc:ra_dav:version-url
+V 116
+/svn/!svn/ver/685/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderTextAtom.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/entries
new file mode 100644
index 0000000..ad474c2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/entries
@@ -0,0 +1,232 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-08-08T07:05:08.133759Z
+727
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+TimeCodeBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.197229Z
+c584657a6b97bbddc67e006ea6425bb6
+2012-06-24T14:45:45.932648Z
+684
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1498
+
+QuicktimeTextSampleEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.197229Z
+9fb53b7189ae88149477c073fb987599
+2012-06-24T21:27:52.519961Z
+690
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6202
+
+GenericMediaHeaderAtom.java
+file
+
+
+
+
+2012-09-14T17:27:51.207229Z
+2eba5114788056352adb0e5e7d8cee33
+2012-06-24T14:45:45.932648Z
+684
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+890
+
+BaseMediaInfoAtom.java
+file
+
+
+
+
+2012-09-14T17:27:51.207229Z
+6119ba316b09d48ed85824b96f2b68b1
+2012-06-24T19:53:06.650023Z
+687
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2695
+
+TaptAtom.java
+file
+
+
+
+
+2012-09-14T17:27:51.207229Z
+9eed1655d1a9f0c187071c0bf6ff61bc
+2012-08-08T07:05:08.133759Z
+727
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+327
+
+GenericMediaHeaderTextAtom.java
+file
+
+
+
+
+2012-09-14T17:27:51.207229Z
+b06b279065c7b8475ade9558fa8227c9
+2012-06-24T15:08:14.651658Z
+685
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2866
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/BaseMediaInfoAtom.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/BaseMediaInfoAtom.java.svn-base
new file mode 100644
index 0000000..706569e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/BaseMediaInfoAtom.java.svn-base
@@ -0,0 +1,110 @@
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+
+public class BaseMediaInfoAtom extends AbstractFullBox {
+ public static final String TYPE = "gmin";
+
+ short graphicsMode = 64;
+ int opColorR = 32768;
+ int opColorG = 32768;
+ int opColorB = 32768;
+ short balance;
+ short reserved;
+
+ public BaseMediaInfoAtom() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 16;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.putShort(graphicsMode);
+ IsoTypeWriter.writeUInt16(byteBuffer, opColorR);
+ IsoTypeWriter.writeUInt16(byteBuffer,opColorG );
+ IsoTypeWriter.writeUInt16(byteBuffer,opColorB );
+ byteBuffer.putShort(balance);
+ byteBuffer.putShort(reserved);
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ graphicsMode = content.getShort();
+ opColorR = IsoTypeReader.readUInt16(content);
+ opColorG = IsoTypeReader.readUInt16(content);
+ opColorB = IsoTypeReader.readUInt16(content);
+ balance = content.getShort();
+ reserved = content.getShort();
+
+ }
+
+ public short getGraphicsMode() {
+ return graphicsMode;
+ }
+
+ public void setGraphicsMode(short graphicsMode) {
+ this.graphicsMode = graphicsMode;
+ }
+
+ public int getOpColorR() {
+ return opColorR;
+ }
+
+ public void setOpColorR(int opColorR) {
+ this.opColorR = opColorR;
+ }
+
+ public int getOpColorG() {
+ return opColorG;
+ }
+
+ public void setOpColorG(int opColorG) {
+ this.opColorG = opColorG;
+ }
+
+ public int getOpColorB() {
+ return opColorB;
+ }
+
+ public void setOpColorB(int opColorB) {
+ this.opColorB = opColorB;
+ }
+
+ public short getBalance() {
+ return balance;
+ }
+
+ public void setBalance(short balance) {
+ this.balance = balance;
+ }
+
+ public short getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(short reserved) {
+ this.reserved = reserved;
+ }
+
+ @Override
+ public String toString() {
+ return "BaseMediaInfoAtom{" +
+ "graphicsMode=" + graphicsMode +
+ ", opColorR=" + opColorR +
+ ", opColorG=" + opColorG +
+ ", opColorB=" + opColorB +
+ ", balance=" + balance +
+ ", reserved=" + reserved +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/GenericMediaHeaderAtom.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/GenericMediaHeaderAtom.java.svn-base
new file mode 100644
index 0000000..ac2033f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/GenericMediaHeaderAtom.java.svn-base
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.googlecode.mp4parser.AbstractContainerBox;
+
+public class GenericMediaHeaderAtom extends AbstractContainerBox {
+
+ public static final String TYPE = "gmhd";
+
+ public GenericMediaHeaderAtom() {
+ super(TYPE);
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/GenericMediaHeaderTextAtom.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/GenericMediaHeaderTextAtom.java.svn-base
new file mode 100644
index 0000000..fd52dc9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/GenericMediaHeaderTextAtom.java.svn-base
@@ -0,0 +1,130 @@
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.googlecode.mp4parser.AbstractBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Undocumented atom in the gmhd atom of text tracks.
+ */
+public class GenericMediaHeaderTextAtom extends AbstractBox {
+
+ public static final String TYPE = "text";
+
+ int unknown_1 = 65536;
+ int unknown_2;
+ int unknown_3;
+ int unknown_4;
+ int unknown_5 = 65536;
+ int unknown_6;
+ int unknown_7;
+ int unknown_8;
+ int unknown_9 = 1073741824;
+
+ public GenericMediaHeaderTextAtom() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 36;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ byteBuffer.putInt(unknown_1);
+ byteBuffer.putInt(unknown_2);
+ byteBuffer.putInt(unknown_3);
+ byteBuffer.putInt(unknown_4);
+ byteBuffer.putInt(unknown_5);
+ byteBuffer.putInt(unknown_6);
+ byteBuffer.putInt(unknown_7);
+ byteBuffer.putInt(unknown_8);
+ byteBuffer.putInt(unknown_9);
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ unknown_1 = content.getInt();
+ unknown_2 = content.getInt();
+ unknown_3 = content.getInt();
+ unknown_4 = content.getInt();
+ unknown_5 = content.getInt();
+ unknown_6 = content.getInt();
+ unknown_7 = content.getInt();
+ unknown_8 = content.getInt();
+ unknown_9 = content.getInt();
+ }
+
+ public int getUnknown_1() {
+ return unknown_1;
+ }
+
+ public void setUnknown_1(int unknown_1) {
+ this.unknown_1 = unknown_1;
+ }
+
+ public int getUnknown_2() {
+ return unknown_2;
+ }
+
+ public void setUnknown_2(int unknown_2) {
+ this.unknown_2 = unknown_2;
+ }
+
+ public int getUnknown_3() {
+ return unknown_3;
+ }
+
+ public void setUnknown_3(int unknown_3) {
+ this.unknown_3 = unknown_3;
+ }
+
+ public int getUnknown_4() {
+ return unknown_4;
+ }
+
+ public void setUnknown_4(int unknown_4) {
+ this.unknown_4 = unknown_4;
+ }
+
+ public int getUnknown_5() {
+ return unknown_5;
+ }
+
+ public void setUnknown_5(int unknown_5) {
+ this.unknown_5 = unknown_5;
+ }
+
+ public int getUnknown_6() {
+ return unknown_6;
+ }
+
+ public void setUnknown_6(int unknown_6) {
+ this.unknown_6 = unknown_6;
+ }
+
+ public int getUnknown_7() {
+ return unknown_7;
+ }
+
+ public void setUnknown_7(int unknown_7) {
+ this.unknown_7 = unknown_7;
+ }
+
+ public int getUnknown_8() {
+ return unknown_8;
+ }
+
+ public void setUnknown_8(int unknown_8) {
+ this.unknown_8 = unknown_8;
+ }
+
+ public int getUnknown_9() {
+ return unknown_9;
+ }
+
+ public void setUnknown_9(int unknown_9) {
+ this.unknown_9 = unknown_9;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/QuicktimeTextSampleEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/QuicktimeTextSampleEntry.java.svn-base
new file mode 100644
index 0000000..8784fc6
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/QuicktimeTextSampleEntry.java.svn-base
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.sampleentry.SampleEntry;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Entry type for timed text samples defined in the timed text specification (ISO/IEC 14496-17).
+ */
+public class QuicktimeTextSampleEntry extends SampleEntry {
+
+ public static final String TYPE = "text";
+
+ int displayFlags;
+ int textJustification;
+
+ int backgroundR;
+ int backgroundG;
+ int backgroundB;
+
+ long defaultTextBox;
+ long reserved1;
+
+ short fontNumber;
+ short fontFace;
+ byte reserved2;
+ short reserved3;
+
+ int foregroundR = 65535;
+ int foregroundG = 65535;
+ int foregroundB = 65535;
+
+ String fontName = "";
+
+ public QuicktimeTextSampleEntry() {
+ super(TYPE);
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ _parseReservedAndDataReferenceIndex(content);
+
+ displayFlags = content.getInt();
+ textJustification = content.getInt();
+ backgroundR = IsoTypeReader.readUInt16(content);
+ backgroundG = IsoTypeReader.readUInt16(content);
+ backgroundB = IsoTypeReader.readUInt16(content);
+ defaultTextBox = IsoTypeReader.readUInt64(content);
+ reserved1 = IsoTypeReader.readUInt64(content);
+ fontNumber = content.getShort();
+ fontFace = content.getShort();
+ reserved2 = content.get();
+ reserved3 = content.getShort();
+ foregroundR = IsoTypeReader.readUInt16(content);
+ foregroundG = IsoTypeReader.readUInt16(content);
+ foregroundB = IsoTypeReader.readUInt16(content);
+
+ if (content.remaining() > 0) {
+ int length = IsoTypeReader.readUInt8(content);
+ byte[] myFontName = new byte[length];
+ content.get(myFontName);
+ fontName = new String(myFontName);
+ } else {
+ fontName = null;
+ }
+ }
+
+
+ protected long getContentSize() {
+ return 52 + (fontName != null ? fontName.length() : 0);
+ }
+
+
+ public int getDisplayFlags() {
+ return displayFlags;
+ }
+
+ public void setDisplayFlags(int displayFlags) {
+ this.displayFlags = displayFlags;
+ }
+
+ public int getTextJustification() {
+ return textJustification;
+ }
+
+ public void setTextJustification(int textJustification) {
+ this.textJustification = textJustification;
+ }
+
+ public int getBackgroundR() {
+ return backgroundR;
+ }
+
+ public void setBackgroundR(int backgroundR) {
+ this.backgroundR = backgroundR;
+ }
+
+ public int getBackgroundG() {
+ return backgroundG;
+ }
+
+ public void setBackgroundG(int backgroundG) {
+ this.backgroundG = backgroundG;
+ }
+
+ public int getBackgroundB() {
+ return backgroundB;
+ }
+
+ public void setBackgroundB(int backgroundB) {
+ this.backgroundB = backgroundB;
+ }
+
+ public long getDefaultTextBox() {
+ return defaultTextBox;
+ }
+
+ public void setDefaultTextBox(long defaultTextBox) {
+ this.defaultTextBox = defaultTextBox;
+ }
+
+ public long getReserved1() {
+ return reserved1;
+ }
+
+ public void setReserved1(long reserved1) {
+ this.reserved1 = reserved1;
+ }
+
+ public short getFontNumber() {
+ return fontNumber;
+ }
+
+ public void setFontNumber(short fontNumber) {
+ this.fontNumber = fontNumber;
+ }
+
+ public short getFontFace() {
+ return fontFace;
+ }
+
+ public void setFontFace(short fontFace) {
+ this.fontFace = fontFace;
+ }
+
+ public byte getReserved2() {
+ return reserved2;
+ }
+
+ public void setReserved2(byte reserved2) {
+ this.reserved2 = reserved2;
+ }
+
+ public short getReserved3() {
+ return reserved3;
+ }
+
+ public void setReserved3(short reserved3) {
+ this.reserved3 = reserved3;
+ }
+
+ public int getForegroundR() {
+ return foregroundR;
+ }
+
+ public void setForegroundR(int foregroundR) {
+ this.foregroundR = foregroundR;
+ }
+
+ public int getForegroundG() {
+ return foregroundG;
+ }
+
+ public void setForegroundG(int foregroundG) {
+ this.foregroundG = foregroundG;
+ }
+
+ public int getForegroundB() {
+ return foregroundB;
+ }
+
+ public void setForegroundB(int foregroundB) {
+ this.foregroundB = foregroundB;
+ }
+
+ public String getFontName() {
+ return fontName;
+ }
+
+ public void setFontName(String fontName) {
+ this.fontName = fontName;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ _writeReservedAndDataReferenceIndex(byteBuffer);
+ byteBuffer.putInt(displayFlags);
+ byteBuffer.putInt(textJustification);
+ IsoTypeWriter.writeUInt16(byteBuffer, backgroundR);
+ IsoTypeWriter.writeUInt16(byteBuffer, backgroundG);
+ IsoTypeWriter.writeUInt16(byteBuffer, backgroundB);
+ IsoTypeWriter.writeUInt64(byteBuffer, defaultTextBox);
+ IsoTypeWriter.writeUInt64(byteBuffer, reserved1);
+ byteBuffer.putShort(fontNumber);
+ byteBuffer.putShort(fontFace);
+ byteBuffer.put(reserved2);
+ byteBuffer.putShort(reserved3);
+
+ IsoTypeWriter.writeUInt16(byteBuffer, foregroundR);
+ IsoTypeWriter.writeUInt16(byteBuffer, foregroundG);
+ IsoTypeWriter.writeUInt16(byteBuffer, foregroundB);
+ if (fontName != null) {
+ IsoTypeWriter.writeUInt8(byteBuffer, fontName.length());
+ byteBuffer.put(fontName.getBytes());
+ }
+
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/TaptAtom.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/TaptAtom.java.svn-base
new file mode 100644
index 0000000..4fcea56
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/TaptAtom.java.svn-base
@@ -0,0 +1,16 @@
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.googlecode.mp4parser.AbstractContainerBox;
+
+/**
+ * Don't know what it is but it is obviously a container box.
+ */
+public class TaptAtom extends AbstractContainerBox {
+ public static final String TYPE = "tapt";
+
+ public TaptAtom() {
+ super(TYPE);
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/TimeCodeBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/TimeCodeBox.java.svn-base
new file mode 100644
index 0000000..e15c9f0
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/.svn/text-base/TimeCodeBox.java.svn-base
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.sampleentry.SampleEntry;
+
+import java.nio.ByteBuffer;
+
+public class TimeCodeBox extends SampleEntry {
+ byte[] data;
+
+
+ public TimeCodeBox() {
+ super("tmcd");
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 26;
+ for (Box box : boxes) {
+ size += box.getSize();
+ }
+ return size;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ _parseReservedAndDataReferenceIndex(content);
+ data = new byte[18];
+ content.get(data);
+ _parseChildBoxes(content);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ _writeReservedAndDataReferenceIndex(byteBuffer);
+ byteBuffer.put(data);
+ _writeChildBoxes(byteBuffer);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/BaseMediaInfoAtom.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/BaseMediaInfoAtom.java
new file mode 100644
index 0000000..706569e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/BaseMediaInfoAtom.java
@@ -0,0 +1,110 @@
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+
+public class BaseMediaInfoAtom extends AbstractFullBox {
+ public static final String TYPE = "gmin";
+
+ short graphicsMode = 64;
+ int opColorR = 32768;
+ int opColorG = 32768;
+ int opColorB = 32768;
+ short balance;
+ short reserved;
+
+ public BaseMediaInfoAtom() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 16;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.putShort(graphicsMode);
+ IsoTypeWriter.writeUInt16(byteBuffer, opColorR);
+ IsoTypeWriter.writeUInt16(byteBuffer,opColorG );
+ IsoTypeWriter.writeUInt16(byteBuffer,opColorB );
+ byteBuffer.putShort(balance);
+ byteBuffer.putShort(reserved);
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ graphicsMode = content.getShort();
+ opColorR = IsoTypeReader.readUInt16(content);
+ opColorG = IsoTypeReader.readUInt16(content);
+ opColorB = IsoTypeReader.readUInt16(content);
+ balance = content.getShort();
+ reserved = content.getShort();
+
+ }
+
+ public short getGraphicsMode() {
+ return graphicsMode;
+ }
+
+ public void setGraphicsMode(short graphicsMode) {
+ this.graphicsMode = graphicsMode;
+ }
+
+ public int getOpColorR() {
+ return opColorR;
+ }
+
+ public void setOpColorR(int opColorR) {
+ this.opColorR = opColorR;
+ }
+
+ public int getOpColorG() {
+ return opColorG;
+ }
+
+ public void setOpColorG(int opColorG) {
+ this.opColorG = opColorG;
+ }
+
+ public int getOpColorB() {
+ return opColorB;
+ }
+
+ public void setOpColorB(int opColorB) {
+ this.opColorB = opColorB;
+ }
+
+ public short getBalance() {
+ return balance;
+ }
+
+ public void setBalance(short balance) {
+ this.balance = balance;
+ }
+
+ public short getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(short reserved) {
+ this.reserved = reserved;
+ }
+
+ @Override
+ public String toString() {
+ return "BaseMediaInfoAtom{" +
+ "graphicsMode=" + graphicsMode +
+ ", opColorR=" + opColorR +
+ ", opColorG=" + opColorG +
+ ", opColorB=" + opColorB +
+ ", balance=" + balance +
+ ", reserved=" + reserved +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderAtom.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderAtom.java
new file mode 100644
index 0000000..ac2033f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderAtom.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.googlecode.mp4parser.AbstractContainerBox;
+
+public class GenericMediaHeaderAtom extends AbstractContainerBox {
+
+ public static final String TYPE = "gmhd";
+
+ public GenericMediaHeaderAtom() {
+ super(TYPE);
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderTextAtom.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderTextAtom.java
new file mode 100644
index 0000000..fd52dc9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/GenericMediaHeaderTextAtom.java
@@ -0,0 +1,130 @@
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.googlecode.mp4parser.AbstractBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Undocumented atom in the gmhd atom of text tracks.
+ */
+public class GenericMediaHeaderTextAtom extends AbstractBox {
+
+ public static final String TYPE = "text";
+
+ int unknown_1 = 65536;
+ int unknown_2;
+ int unknown_3;
+ int unknown_4;
+ int unknown_5 = 65536;
+ int unknown_6;
+ int unknown_7;
+ int unknown_8;
+ int unknown_9 = 1073741824;
+
+ public GenericMediaHeaderTextAtom() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 36;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ byteBuffer.putInt(unknown_1);
+ byteBuffer.putInt(unknown_2);
+ byteBuffer.putInt(unknown_3);
+ byteBuffer.putInt(unknown_4);
+ byteBuffer.putInt(unknown_5);
+ byteBuffer.putInt(unknown_6);
+ byteBuffer.putInt(unknown_7);
+ byteBuffer.putInt(unknown_8);
+ byteBuffer.putInt(unknown_9);
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ unknown_1 = content.getInt();
+ unknown_2 = content.getInt();
+ unknown_3 = content.getInt();
+ unknown_4 = content.getInt();
+ unknown_5 = content.getInt();
+ unknown_6 = content.getInt();
+ unknown_7 = content.getInt();
+ unknown_8 = content.getInt();
+ unknown_9 = content.getInt();
+ }
+
+ public int getUnknown_1() {
+ return unknown_1;
+ }
+
+ public void setUnknown_1(int unknown_1) {
+ this.unknown_1 = unknown_1;
+ }
+
+ public int getUnknown_2() {
+ return unknown_2;
+ }
+
+ public void setUnknown_2(int unknown_2) {
+ this.unknown_2 = unknown_2;
+ }
+
+ public int getUnknown_3() {
+ return unknown_3;
+ }
+
+ public void setUnknown_3(int unknown_3) {
+ this.unknown_3 = unknown_3;
+ }
+
+ public int getUnknown_4() {
+ return unknown_4;
+ }
+
+ public void setUnknown_4(int unknown_4) {
+ this.unknown_4 = unknown_4;
+ }
+
+ public int getUnknown_5() {
+ return unknown_5;
+ }
+
+ public void setUnknown_5(int unknown_5) {
+ this.unknown_5 = unknown_5;
+ }
+
+ public int getUnknown_6() {
+ return unknown_6;
+ }
+
+ public void setUnknown_6(int unknown_6) {
+ this.unknown_6 = unknown_6;
+ }
+
+ public int getUnknown_7() {
+ return unknown_7;
+ }
+
+ public void setUnknown_7(int unknown_7) {
+ this.unknown_7 = unknown_7;
+ }
+
+ public int getUnknown_8() {
+ return unknown_8;
+ }
+
+ public void setUnknown_8(int unknown_8) {
+ this.unknown_8 = unknown_8;
+ }
+
+ public int getUnknown_9() {
+ return unknown_9;
+ }
+
+ public void setUnknown_9(int unknown_9) {
+ this.unknown_9 = unknown_9;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/QuicktimeTextSampleEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/QuicktimeTextSampleEntry.java
new file mode 100644
index 0000000..8784fc6
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/QuicktimeTextSampleEntry.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.sampleentry.SampleEntry;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Entry type for timed text samples defined in the timed text specification (ISO/IEC 14496-17).
+ */
+public class QuicktimeTextSampleEntry extends SampleEntry {
+
+ public static final String TYPE = "text";
+
+ int displayFlags;
+ int textJustification;
+
+ int backgroundR;
+ int backgroundG;
+ int backgroundB;
+
+ long defaultTextBox;
+ long reserved1;
+
+ short fontNumber;
+ short fontFace;
+ byte reserved2;
+ short reserved3;
+
+ int foregroundR = 65535;
+ int foregroundG = 65535;
+ int foregroundB = 65535;
+
+ String fontName = "";
+
+ public QuicktimeTextSampleEntry() {
+ super(TYPE);
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ _parseReservedAndDataReferenceIndex(content);
+
+ displayFlags = content.getInt();
+ textJustification = content.getInt();
+ backgroundR = IsoTypeReader.readUInt16(content);
+ backgroundG = IsoTypeReader.readUInt16(content);
+ backgroundB = IsoTypeReader.readUInt16(content);
+ defaultTextBox = IsoTypeReader.readUInt64(content);
+ reserved1 = IsoTypeReader.readUInt64(content);
+ fontNumber = content.getShort();
+ fontFace = content.getShort();
+ reserved2 = content.get();
+ reserved3 = content.getShort();
+ foregroundR = IsoTypeReader.readUInt16(content);
+ foregroundG = IsoTypeReader.readUInt16(content);
+ foregroundB = IsoTypeReader.readUInt16(content);
+
+ if (content.remaining() > 0) {
+ int length = IsoTypeReader.readUInt8(content);
+ byte[] myFontName = new byte[length];
+ content.get(myFontName);
+ fontName = new String(myFontName);
+ } else {
+ fontName = null;
+ }
+ }
+
+
+ protected long getContentSize() {
+ return 52 + (fontName != null ? fontName.length() : 0);
+ }
+
+
+ public int getDisplayFlags() {
+ return displayFlags;
+ }
+
+ public void setDisplayFlags(int displayFlags) {
+ this.displayFlags = displayFlags;
+ }
+
+ public int getTextJustification() {
+ return textJustification;
+ }
+
+ public void setTextJustification(int textJustification) {
+ this.textJustification = textJustification;
+ }
+
+ public int getBackgroundR() {
+ return backgroundR;
+ }
+
+ public void setBackgroundR(int backgroundR) {
+ this.backgroundR = backgroundR;
+ }
+
+ public int getBackgroundG() {
+ return backgroundG;
+ }
+
+ public void setBackgroundG(int backgroundG) {
+ this.backgroundG = backgroundG;
+ }
+
+ public int getBackgroundB() {
+ return backgroundB;
+ }
+
+ public void setBackgroundB(int backgroundB) {
+ this.backgroundB = backgroundB;
+ }
+
+ public long getDefaultTextBox() {
+ return defaultTextBox;
+ }
+
+ public void setDefaultTextBox(long defaultTextBox) {
+ this.defaultTextBox = defaultTextBox;
+ }
+
+ public long getReserved1() {
+ return reserved1;
+ }
+
+ public void setReserved1(long reserved1) {
+ this.reserved1 = reserved1;
+ }
+
+ public short getFontNumber() {
+ return fontNumber;
+ }
+
+ public void setFontNumber(short fontNumber) {
+ this.fontNumber = fontNumber;
+ }
+
+ public short getFontFace() {
+ return fontFace;
+ }
+
+ public void setFontFace(short fontFace) {
+ this.fontFace = fontFace;
+ }
+
+ public byte getReserved2() {
+ return reserved2;
+ }
+
+ public void setReserved2(byte reserved2) {
+ this.reserved2 = reserved2;
+ }
+
+ public short getReserved3() {
+ return reserved3;
+ }
+
+ public void setReserved3(short reserved3) {
+ this.reserved3 = reserved3;
+ }
+
+ public int getForegroundR() {
+ return foregroundR;
+ }
+
+ public void setForegroundR(int foregroundR) {
+ this.foregroundR = foregroundR;
+ }
+
+ public int getForegroundG() {
+ return foregroundG;
+ }
+
+ public void setForegroundG(int foregroundG) {
+ this.foregroundG = foregroundG;
+ }
+
+ public int getForegroundB() {
+ return foregroundB;
+ }
+
+ public void setForegroundB(int foregroundB) {
+ this.foregroundB = foregroundB;
+ }
+
+ public String getFontName() {
+ return fontName;
+ }
+
+ public void setFontName(String fontName) {
+ this.fontName = fontName;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ _writeReservedAndDataReferenceIndex(byteBuffer);
+ byteBuffer.putInt(displayFlags);
+ byteBuffer.putInt(textJustification);
+ IsoTypeWriter.writeUInt16(byteBuffer, backgroundR);
+ IsoTypeWriter.writeUInt16(byteBuffer, backgroundG);
+ IsoTypeWriter.writeUInt16(byteBuffer, backgroundB);
+ IsoTypeWriter.writeUInt64(byteBuffer, defaultTextBox);
+ IsoTypeWriter.writeUInt64(byteBuffer, reserved1);
+ byteBuffer.putShort(fontNumber);
+ byteBuffer.putShort(fontFace);
+ byteBuffer.put(reserved2);
+ byteBuffer.putShort(reserved3);
+
+ IsoTypeWriter.writeUInt16(byteBuffer, foregroundR);
+ IsoTypeWriter.writeUInt16(byteBuffer, foregroundG);
+ IsoTypeWriter.writeUInt16(byteBuffer, foregroundB);
+ if (fontName != null) {
+ IsoTypeWriter.writeUInt8(byteBuffer, fontName.length());
+ byteBuffer.put(fontName.getBytes());
+ }
+
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TaptAtom.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TaptAtom.java
new file mode 100644
index 0000000..4fcea56
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TaptAtom.java
@@ -0,0 +1,16 @@
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.googlecode.mp4parser.AbstractContainerBox;
+
+/**
+ * Don't know what it is but it is obviously a container box.
+ */
+public class TaptAtom extends AbstractContainerBox {
+ public static final String TYPE = "tapt";
+
+ public TaptAtom() {
+ super(TYPE);
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TimeCodeBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TimeCodeBox.java
new file mode 100644
index 0000000..e15c9f0
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/apple/TimeCodeBox.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.boxes.apple;
+
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.sampleentry.SampleEntry;
+
+import java.nio.ByteBuffer;
+
+public class TimeCodeBox extends SampleEntry {
+ byte[] data;
+
+
+ public TimeCodeBox() {
+ super("tmcd");
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 26;
+ for (Box box : boxes) {
+ size += box.getSize();
+ }
+ return size;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ _parseReservedAndDataReferenceIndex(content);
+ data = new byte[18];
+ content.get(data);
+ _parseChildBoxes(content);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ _writeReservedAndDataReferenceIndex(byteBuffer);
+ byteBuffer.put(data);
+ _writeChildBoxes(byteBuffer);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/all-wcprops
new file mode 100644
index 0000000..f4b1c1b
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/671/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat
+END
+AvcNalUnitStorageBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/svn/!svn/ver/671/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/AvcNalUnitStorageBox.java
+END
+SampleEncryptionBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/SampleEncryptionBox.java
+END
+TrackEncryptionBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/svn/!svn/ver/276/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/TrackEncryptionBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/entries
new file mode 100644
index 0000000..6af5ffa
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-06-10T18:50:38.971172Z
+671
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+AvcNalUnitStorageBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.667221Z
+fa9f9cda733943c6af5e690156163dcb
+2012-06-10T18:50:38.971172Z
+671
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3290
+
+SampleEncryptionBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.667221Z
+b451a1521659345d7712cbb874eec98f
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+618
+
+TrackEncryptionBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.667221Z
+9ab827bee01e8abfe68167428df756dc
+2011-11-22T18:13:22.290919Z
+276
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+269
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/AvcNalUnitStorageBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/AvcNalUnitStorageBox.java.svn-base
new file mode 100644
index 0000000..7182eca
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/AvcNalUnitStorageBox.java.svn-base
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.basemediaformat;
+
+import com.googlecode.mp4parser.AbstractBox;
+import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * The AVC NAL Unit Storage Box SHALL contain an AVCDecoderConfigurationRecord,
+ * as defined in section 5.2.4.1 of the ISO 14496-12.
+ */
+public class AvcNalUnitStorageBox extends AbstractBox {
+ AvcConfigurationBox.AVCDecoderConfigurationRecord avcDecoderConfigurationRecord;
+
+ public AvcNalUnitStorageBox() {
+ super("avcn");
+ }
+
+ public AvcNalUnitStorageBox(AvcConfigurationBox avcConfigurationBox) {
+ super("avcn");
+ this.avcDecoderConfigurationRecord = avcConfigurationBox.getavcDecoderConfigurationRecord();
+ }
+
+ public AvcConfigurationBox.AVCDecoderConfigurationRecord getAvcDecoderConfigurationRecord() {
+ return avcDecoderConfigurationRecord;
+ }
+
+ // just to display sps in isoviewer no practical use
+ public int getLengthSizeMinusOne() {
+ return avcDecoderConfigurationRecord.lengthSizeMinusOne;
+ }
+
+ public String[] getSPS() {
+ return avcDecoderConfigurationRecord.getSPS();
+ }
+
+ public String[] getPPS() {
+ return avcDecoderConfigurationRecord.getPPS();
+ }
+
+ public List<String> getSequenceParameterSetsAsStrings() {
+ return avcDecoderConfigurationRecord.getSequenceParameterSetsAsStrings();
+ }
+
+ public List<String> getSequenceParameterSetExtsAsStrings() {
+ return avcDecoderConfigurationRecord.getSequenceParameterSetExtsAsStrings();
+ }
+
+ public List<String> getPictureParameterSetsAsStrings() {
+ return avcDecoderConfigurationRecord.getPictureParameterSetsAsStrings();
+ }
+
+ @Override
+ protected long getContentSize() {
+ return avcDecoderConfigurationRecord.getContentSize();
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ this.avcDecoderConfigurationRecord = new AvcConfigurationBox.AVCDecoderConfigurationRecord(content);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ this.avcDecoderConfigurationRecord.getContent(byteBuffer);
+ }
+
+ @Override
+ public String toString() {
+ return "AvcNalUnitStorageBox{" +
+ "SPS=" + avcDecoderConfigurationRecord.getSequenceParameterSetsAsStrings() +
+ ",PPS=" + avcDecoderConfigurationRecord.getPictureParameterSetsAsStrings() +
+ ",lengthSize=" + (avcDecoderConfigurationRecord.lengthSizeMinusOne + 1) +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/SampleEncryptionBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/SampleEncryptionBox.java.svn-base
new file mode 100644
index 0000000..3430818
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/SampleEncryptionBox.java.svn-base
@@ -0,0 +1,24 @@
+package com.googlecode.mp4parser.boxes.basemediaformat;
+
+import com.googlecode.mp4parser.boxes.AbstractSampleEncryptionBox;
+
+/**
+ * aligned(8) class AbstractSampleEncryptionBox extends FullBox(‘uuid’, extended_type= 0xA2394F52-5A9B-4f14-A244-6C427C648DF4, version=0, flags=0)
+ * {
+ * <p/>
+ * unsigned int (32) sample_count;
+ * {
+ * unsigned int(16) InitializationVector;
+ * }[ sample_count ]
+ * }
+ */
+public class SampleEncryptionBox extends AbstractSampleEncryptionBox {
+
+ /**
+ * Creates a SampleEncryptionBox for non-h264 tracks.
+ */
+ public SampleEncryptionBox() {
+ super("senc");
+
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/TrackEncryptionBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/TrackEncryptionBox.java.svn-base
new file mode 100644
index 0000000..dd93a12
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/.svn/text-base/TrackEncryptionBox.java.svn-base
@@ -0,0 +1,12 @@
+package com.googlecode.mp4parser.boxes.basemediaformat;
+
+import com.googlecode.mp4parser.boxes.AbstractTrackEncryptionBox;
+
+/**
+ *
+ */
+public class TrackEncryptionBox extends AbstractTrackEncryptionBox {
+ public TrackEncryptionBox() {
+ super("tenc");
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/AvcNalUnitStorageBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/AvcNalUnitStorageBox.java
new file mode 100644
index 0000000..7182eca
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/AvcNalUnitStorageBox.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.basemediaformat;
+
+import com.googlecode.mp4parser.AbstractBox;
+import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * The AVC NAL Unit Storage Box SHALL contain an AVCDecoderConfigurationRecord,
+ * as defined in section 5.2.4.1 of the ISO 14496-12.
+ */
+public class AvcNalUnitStorageBox extends AbstractBox {
+ AvcConfigurationBox.AVCDecoderConfigurationRecord avcDecoderConfigurationRecord;
+
+ public AvcNalUnitStorageBox() {
+ super("avcn");
+ }
+
+ public AvcNalUnitStorageBox(AvcConfigurationBox avcConfigurationBox) {
+ super("avcn");
+ this.avcDecoderConfigurationRecord = avcConfigurationBox.getavcDecoderConfigurationRecord();
+ }
+
+ public AvcConfigurationBox.AVCDecoderConfigurationRecord getAvcDecoderConfigurationRecord() {
+ return avcDecoderConfigurationRecord;
+ }
+
+ // just to display sps in isoviewer no practical use
+ public int getLengthSizeMinusOne() {
+ return avcDecoderConfigurationRecord.lengthSizeMinusOne;
+ }
+
+ public String[] getSPS() {
+ return avcDecoderConfigurationRecord.getSPS();
+ }
+
+ public String[] getPPS() {
+ return avcDecoderConfigurationRecord.getPPS();
+ }
+
+ public List<String> getSequenceParameterSetsAsStrings() {
+ return avcDecoderConfigurationRecord.getSequenceParameterSetsAsStrings();
+ }
+
+ public List<String> getSequenceParameterSetExtsAsStrings() {
+ return avcDecoderConfigurationRecord.getSequenceParameterSetExtsAsStrings();
+ }
+
+ public List<String> getPictureParameterSetsAsStrings() {
+ return avcDecoderConfigurationRecord.getPictureParameterSetsAsStrings();
+ }
+
+ @Override
+ protected long getContentSize() {
+ return avcDecoderConfigurationRecord.getContentSize();
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ this.avcDecoderConfigurationRecord = new AvcConfigurationBox.AVCDecoderConfigurationRecord(content);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ this.avcDecoderConfigurationRecord.getContent(byteBuffer);
+ }
+
+ @Override
+ public String toString() {
+ return "AvcNalUnitStorageBox{" +
+ "SPS=" + avcDecoderConfigurationRecord.getSequenceParameterSetsAsStrings() +
+ ",PPS=" + avcDecoderConfigurationRecord.getPictureParameterSetsAsStrings() +
+ ",lengthSize=" + (avcDecoderConfigurationRecord.lengthSizeMinusOne + 1) +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/SampleEncryptionBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/SampleEncryptionBox.java
new file mode 100644
index 0000000..3430818
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/SampleEncryptionBox.java
@@ -0,0 +1,24 @@
+package com.googlecode.mp4parser.boxes.basemediaformat;
+
+import com.googlecode.mp4parser.boxes.AbstractSampleEncryptionBox;
+
+/**
+ * aligned(8) class AbstractSampleEncryptionBox extends FullBox(‘uuid’, extended_type= 0xA2394F52-5A9B-4f14-A244-6C427C648DF4, version=0, flags=0)
+ * {
+ * <p/>
+ * unsigned int (32) sample_count;
+ * {
+ * unsigned int(16) InitializationVector;
+ * }[ sample_count ]
+ * }
+ */
+public class SampleEncryptionBox extends AbstractSampleEncryptionBox {
+
+ /**
+ * Creates a SampleEncryptionBox for non-h264 tracks.
+ */
+ public SampleEncryptionBox() {
+ super("senc");
+
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/TrackEncryptionBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/TrackEncryptionBox.java
new file mode 100644
index 0000000..dd93a12
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/basemediaformat/TrackEncryptionBox.java
@@ -0,0 +1,12 @@
+package com.googlecode.mp4parser.boxes.basemediaformat;
+
+import com.googlecode.mp4parser.boxes.AbstractTrackEncryptionBox;
+
+/**
+ *
+ */
+public class TrackEncryptionBox extends AbstractTrackEncryptionBox {
+ public TrackEncryptionBox() {
+ super("tenc");
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/all-wcprops
new file mode 100644
index 0000000..54b03df
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/728/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc
+END
+ProtectionSystemSpecificHeaderBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 122
+/svn/!svn/ver/728/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/ProtectionSystemSpecificHeaderBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/entries
new file mode 100644
index 0000000..c96b178
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-08-08T11:39:22.997932Z
+728
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+ProtectionSystemSpecificHeaderBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.237229Z
+5722926350b47a524ba4c38adeafe5bd
+2012-08-08T11:39:22.997932Z
+728
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3203
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/text-base/ProtectionSystemSpecificHeaderBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/text-base/ProtectionSystemSpecificHeaderBox.java.svn-base
new file mode 100644
index 0000000..d6b653c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/.svn/text-base/ProtectionSystemSpecificHeaderBox.java.svn-base
@@ -0,0 +1,89 @@
+package com.googlecode.mp4parser.boxes.cenc;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.Box;
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.util.UUIDConverter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+
+
+/**
+ * This box contains information needed by a Content Protection System to play back the content. The
+ * data format is specified by the system identified by the ‘pssh’ parameter SystemID, and is considered
+ * opaque for the purposes of this specification.
+ * <p/>
+ * The data encapsulated in the Data field may be read by the identified Content Protection System to
+ * enable decryption key acquisition and decryption of media data. For license/rights-based systems, the
+ * header information may include data such as the URL of license server(s) or rights issuer(s) used,
+ * embedded licenses/rights, and/or other protection system specific metadata.
+ * <p/>
+ * A single file may be constructed to be playable by multiple key and digital rights management (DRM)
+ * systems, by including one Protection System-Specific Header box for each system supported. Readers
+ * that process such presentations must match the SystemID field in this box to the SystemID(s) of the
+ * DRM System(s) they support, and select or create the matching Protection System-Specific Header
+ * box(es) for storage and retrieval of Protection-Specific information interpreted or created by that DRM
+ * system.
+ */
+public class ProtectionSystemSpecificHeaderBox extends AbstractFullBox {
+ public static final String TYPE = "pssh";
+
+ public static byte[] OMA2_SYSTEM_ID = UUIDConverter.convert(UUID.fromString("A2B55680-6F43-11E0-9A3F-0002A5D5C51B"));
+ public static byte[] PLAYREADY_SYSTEM_ID = UUIDConverter.convert(UUID.fromString("9A04F079-9840-4286-AB92-E65BE0885F95"));
+
+ byte[] content;
+ byte[] systemId;
+
+
+ public byte[] getSystemId() {
+ return systemId;
+ }
+
+ public void setSystemId(byte[] systemId) {
+ assert systemId.length == 16;
+ this.systemId = systemId;
+ }
+
+ public byte[] getContent() {
+ return content;
+ }
+
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
+
+ public ProtectionSystemSpecificHeaderBox() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 24 + content.length;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ assert systemId.length == 16;
+ byteBuffer.put(systemId, 0, 16);
+ IsoTypeWriter.writeUInt32(byteBuffer, content.length);
+ byteBuffer.put(content);
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ systemId = new byte[16];
+ content.get(systemId);
+ long length = IsoTypeReader.readUInt32(content);
+ this.content = new byte[content.remaining()];
+ content.get(this.content);
+ assert length == this.content.length;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/ProtectionSystemSpecificHeaderBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/ProtectionSystemSpecificHeaderBox.java
new file mode 100644
index 0000000..d6b653c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/cenc/ProtectionSystemSpecificHeaderBox.java
@@ -0,0 +1,89 @@
+package com.googlecode.mp4parser.boxes.cenc;
+
+import com.coremedia.iso.BoxParser;
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.boxes.Box;
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.util.UUIDConverter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+
+
+/**
+ * This box contains information needed by a Content Protection System to play back the content. The
+ * data format is specified by the system identified by the ‘pssh’ parameter SystemID, and is considered
+ * opaque for the purposes of this specification.
+ * <p/>
+ * The data encapsulated in the Data field may be read by the identified Content Protection System to
+ * enable decryption key acquisition and decryption of media data. For license/rights-based systems, the
+ * header information may include data such as the URL of license server(s) or rights issuer(s) used,
+ * embedded licenses/rights, and/or other protection system specific metadata.
+ * <p/>
+ * A single file may be constructed to be playable by multiple key and digital rights management (DRM)
+ * systems, by including one Protection System-Specific Header box for each system supported. Readers
+ * that process such presentations must match the SystemID field in this box to the SystemID(s) of the
+ * DRM System(s) they support, and select or create the matching Protection System-Specific Header
+ * box(es) for storage and retrieval of Protection-Specific information interpreted or created by that DRM
+ * system.
+ */
+public class ProtectionSystemSpecificHeaderBox extends AbstractFullBox {
+ public static final String TYPE = "pssh";
+
+ public static byte[] OMA2_SYSTEM_ID = UUIDConverter.convert(UUID.fromString("A2B55680-6F43-11E0-9A3F-0002A5D5C51B"));
+ public static byte[] PLAYREADY_SYSTEM_ID = UUIDConverter.convert(UUID.fromString("9A04F079-9840-4286-AB92-E65BE0885F95"));
+
+ byte[] content;
+ byte[] systemId;
+
+
+ public byte[] getSystemId() {
+ return systemId;
+ }
+
+ public void setSystemId(byte[] systemId) {
+ assert systemId.length == 16;
+ this.systemId = systemId;
+ }
+
+ public byte[] getContent() {
+ return content;
+ }
+
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
+
+ public ProtectionSystemSpecificHeaderBox() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 24 + content.length;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ assert systemId.length == 16;
+ byteBuffer.put(systemId, 0, 16);
+ IsoTypeWriter.writeUInt32(byteBuffer, content.length);
+ byteBuffer.put(content);
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ systemId = new byte[16];
+ content.get(systemId);
+ long length = IsoTypeReader.readUInt32(content);
+ this.content = new byte[content.remaining()];
+ content.get(this.content);
+ assert length == this.content.length;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4-boxes.zip b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4-boxes.zip
new file mode 100644
index 0000000..c9bcf8b
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4-boxes.zip
Binary files differ
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/all-wcprops
new file mode 100644
index 0000000..b552219
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4
+END
+ESDescriptorBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ESDescriptorBox.java
+END
+AbstractDescriptorBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 109
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/AbstractDescriptorBox.java
+END
+ObjectDescriptorBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ObjectDescriptorBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/entries
new file mode 100644
index 0000000..be8f9d8
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/entries
@@ -0,0 +1,136 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+ESDescriptorBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+3938742e971cd377b37592461870d872
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1021
+
+objectdescriptors
+dir
+
+AbstractDescriptorBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+3361124b1437f844ab4727c8dea81e3d
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2487
+
+samplegrouping
+dir
+
+ObjectDescriptorBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+b81e2d3876ef2af7ba17bf2d8bd6e504
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1983
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/AbstractDescriptorBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/AbstractDescriptorBox.java.svn-base
new file mode 100644
index 0000000..7d614e2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/AbstractDescriptorBox.java.svn-base
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4;
+
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BaseDescriptor;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ObjectDescriptorFactory;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * ES Descriptor Box.
+ */
+public class AbstractDescriptorBox extends AbstractFullBox {
+ private static Logger log = Logger.getLogger(AbstractDescriptorBox.class.getName());
+
+
+ public BaseDescriptor descriptor;
+ public ByteBuffer data;
+
+ public AbstractDescriptorBox(String type) {
+ super(type);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ data.rewind(); // has been fforwarded by parsing
+ byteBuffer.put(data);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 4 + data.limit();
+ }
+
+ public BaseDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ public String getDescriptorAsString() {
+ return descriptor.toString();
+ }
+
+ public void setData(ByteBuffer data) {
+ this.data = data;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ data = content.slice();
+ content.position(content.position() + content.remaining());
+ try {
+ data.rewind();
+ descriptor = ObjectDescriptorFactory.createFrom(-1, data);
+ } catch (IOException e) {
+ log.log(Level.WARNING, "Error parsing ObjectDescriptor", e);
+ //that's why we copied it ;)
+ } catch (IndexOutOfBoundsException e) {
+ log.log(Level.WARNING, "Error parsing ObjectDescriptor", e);
+ //that's why we copied it ;)
+ }
+
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/ESDescriptorBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/ESDescriptorBox.java.svn-base
new file mode 100644
index 0000000..5d9d591
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/ESDescriptorBox.java.svn-base
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4;
+
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor;
+
+/**
+ * ES Descriptor Box.
+ */
+public class ESDescriptorBox extends AbstractDescriptorBox {
+ public static final String TYPE = "esds";
+
+ public ESDescriptorBox() {
+ super(TYPE);
+ }
+
+ public ESDescriptor getEsDescriptor() {
+ return (ESDescriptor) super.getDescriptor();
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/ObjectDescriptorBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/ObjectDescriptorBox.java.svn-base
new file mode 100644
index 0000000..c9e7493
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/.svn/text-base/ObjectDescriptorBox.java.svn-base
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4;
+
+/**
+ * This object contains an Object Descriptor or an Initial Object Descriptor.
+ * There are a number of possible file types based on usage, depending on the descriptor:
+ * <ul>
+ * <li>Presentation, contains IOD which contains a BIFS stream (MP4 file);
+ * <li>Sub-part of a presentation, contains an IOD without a BIFS stream (MP4 file);</li>
+ * <li>Sub-part of a presentation, contains an OD (MP4 file);</li>
+ * <li>Free-form file, referenced by MP4 data references (free-format);</li>
+ * <li>Sub-part of a presentation, referenced by an ES URL.</li>
+ * </ul>
+ * NOTE: <br/>
+ * The first three are MP4 files, a file referenced by a data reference is not necessarily an MP4 file, as it is
+ * free-format. Files referenced by ES URLs, by data references, or intended as input to an editing process, need not have
+ * an Object Descriptor Box. <br/>
+ * An OD URL may point to an MP4 file. Implicitly, the target of such a URL is the OD/IOD located in the 'iods'
+ * atom in that file.</br/>
+ * If an MP4 file contains several object descriptors, only the OD/IOD in the 'iods' atom can be addressed using
+ * an OD URL from a remote MPEG-4 presentation.
+ */
+public class ObjectDescriptorBox extends AbstractDescriptorBox {
+ public static final String TYPE = "iods";
+
+ public ObjectDescriptorBox() {
+ super(TYPE);
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/AbstractDescriptorBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/AbstractDescriptorBox.java
new file mode 100644
index 0000000..7d614e2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/AbstractDescriptorBox.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4;
+
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BaseDescriptor;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ObjectDescriptorFactory;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * ES Descriptor Box.
+ */
+public class AbstractDescriptorBox extends AbstractFullBox {
+ private static Logger log = Logger.getLogger(AbstractDescriptorBox.class.getName());
+
+
+ public BaseDescriptor descriptor;
+ public ByteBuffer data;
+
+ public AbstractDescriptorBox(String type) {
+ super(type);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ data.rewind(); // has been fforwarded by parsing
+ byteBuffer.put(data);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 4 + data.limit();
+ }
+
+ public BaseDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ public String getDescriptorAsString() {
+ return descriptor.toString();
+ }
+
+ public void setData(ByteBuffer data) {
+ this.data = data;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ data = content.slice();
+ content.position(content.position() + content.remaining());
+ try {
+ data.rewind();
+ descriptor = ObjectDescriptorFactory.createFrom(-1, data);
+ } catch (IOException e) {
+ log.log(Level.WARNING, "Error parsing ObjectDescriptor", e);
+ //that's why we copied it ;)
+ } catch (IndexOutOfBoundsException e) {
+ log.log(Level.WARNING, "Error parsing ObjectDescriptor", e);
+ //that's why we copied it ;)
+ }
+
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ESDescriptorBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ESDescriptorBox.java
new file mode 100644
index 0000000..5d9d591
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ESDescriptorBox.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4;
+
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor;
+
+/**
+ * ES Descriptor Box.
+ */
+public class ESDescriptorBox extends AbstractDescriptorBox {
+ public static final String TYPE = "esds";
+
+ public ESDescriptorBox() {
+ super(TYPE);
+ }
+
+ public ESDescriptor getEsDescriptor() {
+ return (ESDescriptor) super.getDescriptor();
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ObjectDescriptorBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ObjectDescriptorBox.java
new file mode 100644
index 0000000..c9e7493
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/ObjectDescriptorBox.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4;
+
+/**
+ * This object contains an Object Descriptor or an Initial Object Descriptor.
+ * There are a number of possible file types based on usage, depending on the descriptor:
+ * <ul>
+ * <li>Presentation, contains IOD which contains a BIFS stream (MP4 file);
+ * <li>Sub-part of a presentation, contains an IOD without a BIFS stream (MP4 file);</li>
+ * <li>Sub-part of a presentation, contains an OD (MP4 file);</li>
+ * <li>Free-form file, referenced by MP4 data references (free-format);</li>
+ * <li>Sub-part of a presentation, referenced by an ES URL.</li>
+ * </ul>
+ * NOTE: <br/>
+ * The first three are MP4 files, a file referenced by a data reference is not necessarily an MP4 file, as it is
+ * free-format. Files referenced by ES URLs, by data references, or intended as input to an editing process, need not have
+ * an Object Descriptor Box. <br/>
+ * An OD URL may point to an MP4 file. Implicitly, the target of such a URL is the OD/IOD located in the 'iods'
+ * atom in that file.</br/>
+ * If an MP4 file contains several object descriptors, only the OD/IOD in the 'iods' atom can be addressed using
+ * an OD URL from a remote MPEG-4 presentation.
+ */
+public class ObjectDescriptorBox extends AbstractDescriptorBox {
+ public static final String TYPE = "iods";
+
+ public ObjectDescriptorBox() {
+ super(TYPE);
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/all-wcprops
new file mode 100644
index 0000000..5e1798f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/all-wcprops
@@ -0,0 +1,107 @@
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/712/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors
+END
+ObjectDescriptorBase.java
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorBase.java
+END
+SLConfigDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 124
+/svn/!svn/ver/712/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/SLConfigDescriptor.java
+END
+BitWriterBuffer.java
+K 25
+svn:wc:ra_dav:version-url
+V 121
+/svn/!svn/ver/683/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitWriterBuffer.java
+END
+ESDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/svn/!svn/ver/712/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ESDescriptor.java
+END
+BaseDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BaseDescriptor.java
+END
+ExtensionDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionDescriptor.java
+END
+Descriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 116
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/Descriptor.java
+END
+InitialObjectDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 129
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/InitialObjectDescriptor.java
+END
+ObjectDescriptor.java_bak
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptor.java_bak
+END
+ExtensionProfileLevelDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 137
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionProfileLevelDescriptor.java
+END
+DecoderSpecificInfo.java
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/svn/!svn/ver/712/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderSpecificInfo.java
+END
+ProfileLevelIndicationDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 138
+/svn/!svn/ver/712/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ProfileLevelIndicationDescriptor.java
+END
+DecoderConfigDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 129
+/svn/!svn/ver/501/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderConfigDescriptor.java
+END
+BitReaderBuffer.java
+K 25
+svn:wc:ra_dav:version-url
+V 121
+/svn/!svn/ver/501/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitReaderBuffer.java
+END
+AudioSpecificConfig.java
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/svn/!svn/ver/712/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/AudioSpecificConfig.java
+END
+ObjectDescriptorFactory.java
+K 25
+svn:wc:ra_dav:version-url
+V 129
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorFactory.java
+END
+UnknownDescriptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 123
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/UnknownDescriptor.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/entries
new file mode 100644
index 0000000..b150321
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/entries
@@ -0,0 +1,606 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-07-12T22:44:00.389345Z
+712
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+ObjectDescriptorBase.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+830afc294b0cc9e0fec5d733e021863c
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+961
+
+SLConfigDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+a0907a19df5c0cc9764689f9c6e3baa3
+2012-07-12T22:44:00.389345Z
+712
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3338
+
+BitWriterBuffer.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+771f8d900c6ff640d51fbb5dd4e933af
+2012-06-23T08:51:59.024275Z
+683
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1304
+
+ESDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+98f2c06a445b6b1d9836a6d3017da884
+2012-07-12T22:44:00.389345Z
+712
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+12845
+
+BaseDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+c74bfdd283a75b2a57aa1b7ebc71c178
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2811
+
+ExtensionDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+907c329cba88c885e4b4b4a751facc6d
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2973
+
+Descriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+ac97c8117fd734f9dd22172d9a9a3a42
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1228
+
+InitialObjectDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+9f15d54d46b6644e1beb981b4d8fcee6
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5237
+
+ObjectDescriptor.java_bak
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+e77953ffb3ced634e03955f8eb0db522
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3666
+
+ExtensionProfileLevelDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+e7e0a38947f89d9285ddfdef6205c843
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1626
+
+DecoderSpecificInfo.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+f1faa43de84eaf3219495512761b4521
+2012-07-12T22:44:00.389345Z
+712
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2292
+
+ProfileLevelIndicationDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+6c76abf5466eb8aa9dded6ea17e66588
+2012-07-12T22:44:00.389345Z
+712
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2143
+
+DecoderConfigDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+ead55e14a62664da9869cb3a092d4fc2
+2012-04-20T14:18:38.758800Z
+501
+hoemmagnus@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9508
+
+BitReaderBuffer.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+9a0bad29e63d974899749007d890b027
+2012-04-20T14:18:38.758800Z
+501
+hoemmagnus@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1241
+
+ObjectDescriptorFactory.java
+file
+
+
+
+
+2012-09-14T17:27:50.997226Z
+cd9f2bc6eb85a79c6148d91099bbe300
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7011
+
+AudioSpecificConfig.java
+file
+
+
+
+
+2012-09-14T17:27:51.007226Z
+79095f48595f6573c76ec69e5717cdfe
+2012-07-12T22:44:00.389345Z
+712
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+40856
+
+UnknownDescriptor.java
+file
+
+
+
+
+2012-09-14T17:27:51.007226Z
+66e3ecf5b31e50a1a4f80d08e4643af9
+2012-02-27T13:49:22.148513Z
+351
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1467
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/AudioSpecificConfig.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/AudioSpecificConfig.java.svn-base
new file mode 100644
index 0000000..86e319e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/AudioSpecificConfig.java.svn-base
@@ -0,0 +1,1176 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+//
+//GetAudioObjectType()
+//{
+//audioObjectType; 5 uimsbf
+//if (audioObjectType == 31) {
+//audioObjectType = 32 + audioObjectTypeExt; 6 uimsbf
+//}
+//return audioObjectType;
+//}
+//AudioSpecificConfig ()
+//{
+//audioObjectType = GetAudioObjectType();
+//samplingFrequencyIndex; 4 bslbf
+//if ( samplingFrequencyIndex == 0xf ) {
+//samplingFrequency; 24 uimsbf
+//}
+//channelConfiguration; 4 bslbf
+//sbrPresentFlag = -1;
+//psPresentFlag = -1;
+//if ( audioObjectType == 5 ||
+//audioObjectType == 29 ) {
+//extensionAudioObjectType = 5;
+//sbrPresentFlag = 1;
+//if ( audioObjectType == 29 ) {
+//psPresentFlag = 1;
+//}
+//extensionSamplingFrequencyIndex; 4 uimsbf
+//if ( extensionSamplingFrequencyIndex == 0xf )
+//extensionSamplingFrequency; 24 uimsbf
+//audioObjectType = GetAudioObjectType();
+//if ( audioObjectType == 22 )
+//extensionChannelConfiguration; 4 uimsbf
+//}
+//else {
+//extensionAudioObjectType = 0;
+//}
+//switch (audioObjectType) {
+//case 1:
+//case 2:
+//case 3:
+//case 4:
+//case 6:
+//case 7:
+//case 17:
+//case 19:
+//case 20:
+//case 21:
+//case 22:
+//case 23:
+//GASpecificConfig();
+//break:
+//case 8:
+//CelpSpecificConfig();
+//break;
+//case 9:
+//HvxcSpecificConfig();
+//break:
+//case 12:
+//TTSSpecificConfig();
+//break;
+//case 13:
+//case 14:
+//case 15:
+//case 16:
+//StructuredAudioSpecificConfig();
+//break;
+//case 24:
+//ErrorResilientCelpSpecificConfig();
+//break;
+//case 25:
+//ErrorResilientHvxcSpecificConfig();
+//break;
+//case 26:
+//case 27:
+//ParametricSpecificConfig();
+//break;
+// case 28:
+//SSCSpecificConfig();
+//break;
+//case 30:
+//sacPayloadEmbedding; 1 uimsbf
+//SpatialSpecificConfig();
+//break;
+//case 32:
+//case 33:
+//case 34:
+//MPEG_1_2_SpecificConfig();
+//break;
+//case 35:
+//DSTSpecificConfig();
+//break;
+//case 36:
+//fillBits; 5 bslbf
+//ALSSpecificConfig();
+//break;
+//case 37:
+//case 38:
+//SLSSpecificConfig();
+//break;
+//case 39:
+//ELDSpecificConfig(channelConfiguration);
+//break:
+//case 40:
+//case 41:
+//SymbolicMusicSpecificConfig();
+//break;
+//default:
+///* reserved */
+//}
+//switch (audioObjectType) {
+//case 17:
+//case 19:
+//case 20:
+//case 21:
+//case 22:
+//case 23:
+//case 24:
+//case 25:
+//case 26:
+//case 27:
+//case 39:
+//epConfig; 2 bslbf
+//if ( epConfig == 2 || epConfig == 3 ) {
+//ErrorProtectionSpecificConfig();
+//}
+//if ( epConfig == 3 ) {
+//directMapping; 1 bslbf
+//if ( ! directMapping ) {
+///* tbd */
+//}
+//}
+//}
+//if ( extensionAudioObjectType != 5 && bits_to_decode() >= 16 ) {
+//syncExtensionType; 11 bslbf
+//if (syncExtensionType == 0x2b7) {
+// extensionAudioObjectType = GetAudioObjectType();
+//if ( extensionAudioObjectType == 5 ) {
+//sbrPresentFlag; 1 uimsbf
+//if (sbrPresentFlag == 1) {
+//extensionSamplingFrequencyIndex; 4 uimsbf
+//if ( extensionSamplingFrequencyIndex == 0xf ) {
+//extensionSamplingFrequency; 24 uimsbf
+//}
+//if ( bits_to_decode() >= 12 ) {
+//syncExtensionType; 11 bslbf
+//if (syncExtesionType == 0x548) {
+//psPresentFlag; 1 uimsbf
+//}
+//}
+//}
+//}
+//if ( extensionAudioObjectType == 22 ) {
+//sbrPresentFlag; 1 uimsbf
+//if (sbrPresentFlag == 1) {
+//extensionSamplingFrequencyIndex; 4 uimsbf
+//if ( extensionSamplingFrequencyIndex == 0xf ) {
+//extensionSamplingFrequency; 24 uimsbf
+//}
+//}
+//extensionChannelConfiguration; 4 uimsbf
+//}
+//}
+//}
+//}
+// }
+//
+// TFCodingType
+//0x0 AAC scaleable
+//0x1 BSAC
+//0x2 TwinVQ
+//0x3 AAC non scaleable (i.e. multichannel)
+//
+// class TFSpecificConfig( uint(4) samplingFrequencyIndex, uint(4) channelConfiguration ) {
+//uint(2) TFCodingType;
+//uint(1) frameLength;
+//uint(1) dependsOnCoreCoder;
+//if (dependsOnCoreCoder == 1){
+//uint(14)coreCoderDelay
+//}
+//if (TFCodingType==BSAC) {
+//uint(11) lslayer_length
+//}
+//uint (1) extensionFlag;
+//if (channelConfiguration == 0 ){
+//program_config_element();
+//}
+//if (extensionFlag==1){
+//<to be defined in mpeg4 phase 2>
+//}
+//}
+//
+//program_config_element()
+//{
+//element_instance_tag 4 uimsbf
+//profile 2 uimsbf
+//sampling_frequency_index 4 uimsbf
+//num_front_channel_elements 4 uimsbf
+//num_side_channel_elements 4 uimsbf
+//num_back_channel_elements 4 uimsbf
+// num_lfe_channel_elements 2 uimsbf
+//num_assoc_data_elements 3 uimsbf
+//num_valid_cc_elements 4 uimsbf
+//mono_mixdown_present 1 uimsbf
+//if ( mono_mixdown_present == 1 )
+//mono_mixdown_element_number 4 uimsbf
+//stereo_mixdown_present 1 uimsbf
+//if ( stereo_mixdown_present == 1 )
+//stereo_mixdown_element_number 4 uimsbf
+//matrix_mixdown_idx_present 1 uimsbf
+//if ( matrix_mixdown_idx_present == 1 ) {
+//matrix_mixdown_idx 2 uimsbf
+//pseudo_surround_enable 1 uimsbf
+//}
+//for ( i = 0; i < num_front_channel_elements; i++) {
+//front_element_is_cpe[i]; 1 bslbf
+//front_element_tag_select[i]; 4 uimsbf
+//}
+//for ( i = 0; i < num_side_channel_elements; i++) {
+//side_element_is_cpe[i]; 1 bslbf
+//side_element_tag_select[i]; 4 uimsbf
+//}
+//for ( i = 0; i < num_back_channel_elements; i++) {
+//back_element_is_cpe[i]; 1 bslbf
+//back_element_tag_select[i]; 4 uimsbf
+//}
+//for ( i = 0; i < num_lfe_channel_elements; i++)
+//lfe_element_tag_select[i]; 4 uimsbf
+//for ( i = 0; i < num_assoc_data_elements; i++)
+//assoc_data_element_tag_select[i]; 4 uimsbf
+//for ( i = 0; i < num_valid_cc_elements; i++) {
+//cc_element_is_ind_sw[i]; 1 uimsbf
+//valid_cc_element_tag_select[i]; 4 uimsbf
+//}
+//byte_alignment()
+//comment_field_bytes 8 uimsbf
+//for ( i = 0; i < comment_field_bytes; i++)
+//comment_field_data[i]; 8 uimsbf
+//}
+
+@Descriptor(tags = 0x5, objectTypeIndication = 0x40)
+public class AudioSpecificConfig extends BaseDescriptor {
+ byte[] configBytes;
+
+ public static Map<Integer, Integer> samplingFrequencyIndexMap = new HashMap<Integer, Integer>();
+ public static Map<Integer, String> audioObjectTypeMap = new HashMap<Integer, String>();
+ int audioObjectType;
+ int samplingFrequencyIndex;
+ int samplingFrequency;
+ int channelConfiguration;
+ int extensionAudioObjectType;
+ int sbrPresentFlag;
+ int psPresentFlag;
+ int extensionSamplingFrequencyIndex;
+ int extensionSamplingFrequency;
+ int extensionChannelConfiguration;
+ int sacPayloadEmbedding;
+ int fillBits;
+ int epConfig;
+ int directMapping;
+ int syncExtensionType;
+
+ //GASpecificConfig
+ int frameLengthFlag;
+ int dependsOnCoreCoder;
+ int coreCoderDelay;
+ int extensionFlag;
+ int layerNr;
+ int numOfSubFrame;
+ int layer_length;
+ int aacSectionDataResilienceFlag;
+ int aacScalefactorDataResilienceFlag;
+ int aacSpectralDataResilienceFlag;
+ int extensionFlag3;
+ boolean gaSpecificConfig;
+
+ //ParametricSpecificConfig
+ int isBaseLayer;
+ int paraMode;
+ int paraExtensionFlag;
+ int hvxcVarMode;
+ int hvxcRateMode;
+ int erHvxcExtensionFlag;
+ int var_ScalableFlag;
+ int hilnQuantMode;
+ int hilnMaxNumLine;
+ int hilnSampleRateCode;
+ int hilnFrameLength;
+ int hilnContMode;
+ int hilnEnhaLayer;
+ int hilnEnhaQuantMode;
+ boolean parametricSpecificConfig;
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ ByteBuffer configBytes = bb.slice();
+ configBytes.limit(sizeOfInstance);
+ bb.position(bb.position() + sizeOfInstance);
+
+ //copy original bytes to internal array for constructing codec config strings (todo until writing of the config is supported)
+ this.configBytes = new byte[sizeOfInstance];
+ configBytes.get(this.configBytes);
+ configBytes.rewind();
+
+ BitReaderBuffer bitReaderBuffer = new BitReaderBuffer(configBytes);
+ audioObjectType = getAudioObjectType(bitReaderBuffer);
+ samplingFrequencyIndex = bitReaderBuffer.readBits(4);
+
+ if (samplingFrequencyIndex == 0xf) {
+ samplingFrequency = bitReaderBuffer.readBits(24);
+ }
+
+ channelConfiguration = bitReaderBuffer.readBits(4);
+
+ if (audioObjectType == 5 ||
+ audioObjectType == 29) {
+ extensionAudioObjectType = 5;
+ sbrPresentFlag = 1;
+ if (audioObjectType == 29) {
+ psPresentFlag = 1;
+ }
+ extensionSamplingFrequencyIndex = bitReaderBuffer.readBits(4);
+ if (extensionSamplingFrequencyIndex == 0xf)
+ extensionSamplingFrequency = bitReaderBuffer.readBits(24);
+ audioObjectType = getAudioObjectType(bitReaderBuffer);
+ if (audioObjectType == 22)
+ extensionChannelConfiguration = bitReaderBuffer.readBits(4);
+ } else {
+ extensionAudioObjectType = 0;
+ }
+
+ switch (audioObjectType) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 6:
+ case 7:
+ case 17:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ parseGaSpecificConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, bitReaderBuffer);
+ //GASpecificConfig();
+ break;
+ case 8:
+ throw new UnsupportedOperationException("can't parse CelpSpecificConfig yet");
+ //CelpSpecificConfig();
+ //break;
+ case 9:
+ throw new UnsupportedOperationException("can't parse HvxcSpecificConfig yet");
+ //HvxcSpecificConfig();
+ //break;
+ case 12:
+ throw new UnsupportedOperationException("can't parse TTSSpecificConfig yet");
+ //TTSSpecificConfig();
+ //break;
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ throw new UnsupportedOperationException("can't parse StructuredAudioSpecificConfig yet");
+ //StructuredAudioSpecificConfig();
+ //break;
+ case 24:
+ throw new UnsupportedOperationException("can't parse ErrorResilientCelpSpecificConfig yet");
+ //ErrorResilientCelpSpecificConfig();
+ //break;
+ case 25:
+ throw new UnsupportedOperationException("can't parse ErrorResilientHvxcSpecificConfig yet");
+ //ErrorResilientHvxcSpecificConfig();
+ //break;
+ case 26:
+ case 27:
+ parseParametricSpecificConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, bitReaderBuffer);
+ //ParametricSpecificConfig();
+ break;
+ case 28:
+ throw new UnsupportedOperationException("can't parse SSCSpecificConfig yet");
+ //SSCSpecificConfig();
+ //break;
+ case 30:
+ sacPayloadEmbedding = bitReaderBuffer.readBits(1);
+ throw new UnsupportedOperationException("can't parse SpatialSpecificConfig yet");
+ //SpatialSpecificConfig();
+ //break;
+ case 32:
+ case 33:
+ case 34:
+ throw new UnsupportedOperationException("can't parse MPEG_1_2_SpecificConfig yet");
+ //MPEG_1_2_SpecificConfig();
+ //break;
+ case 35:
+ throw new UnsupportedOperationException("can't parse DSTSpecificConfig yet");
+ //DSTSpecificConfig();
+ //break;
+ case 36:
+ fillBits = bitReaderBuffer.readBits(5);
+ throw new UnsupportedOperationException("can't parse ALSSpecificConfig yet");
+ //ALSSpecificConfig();
+ //break;
+ case 37:
+ case 38:
+ throw new UnsupportedOperationException("can't parse SLSSpecificConfig yet");
+ //SLSSpecificConfig();
+ //break;
+ case 39:
+ throw new UnsupportedOperationException("can't parse ELDSpecificConfig yet");
+ //ELDSpecificConfig(channelConfiguration);
+ //break;
+ case 40:
+ case 41:
+ throw new UnsupportedOperationException("can't parse SymbolicMusicSpecificConfig yet");
+ //SymbolicMusicSpecificConfig();
+ //break;
+ default:
+ /* reserved */
+ }
+
+ switch (audioObjectType) {
+ case 17:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 39:
+ epConfig = bitReaderBuffer.readBits(2);
+ if (epConfig == 2 || epConfig == 3) {
+ throw new UnsupportedOperationException("can't parse ErrorProtectionSpecificConfig yet");
+ //ErrorProtectionSpecificConfig();
+ }
+ if (epConfig == 3) {
+ directMapping = bitReaderBuffer.readBits(1);
+ if (directMapping == 0) {
+ /* tbd */
+ throw new RuntimeException("not implemented");
+ }
+ }
+ }
+
+ if (extensionAudioObjectType != 5 && bitReaderBuffer.remainingBits() >= 16) {
+ syncExtensionType = bitReaderBuffer.readBits(11);
+ if (syncExtensionType == 0x2b7) {
+ extensionAudioObjectType = getAudioObjectType(bitReaderBuffer);
+ if (extensionAudioObjectType == 5) {
+ sbrPresentFlag = bitReaderBuffer.readBits(1);
+ if (sbrPresentFlag == 1) {
+ extensionSamplingFrequencyIndex = bitReaderBuffer.readBits(4);
+ if (extensionSamplingFrequencyIndex == 0xf) {
+ extensionSamplingFrequency = bitReaderBuffer.readBits(24);
+ }
+ if (bitReaderBuffer.remainingBits() >= 12) {
+ syncExtensionType = bitReaderBuffer.readBits(11); //10101001000
+ if (syncExtensionType == 0x548) {
+ psPresentFlag = bitReaderBuffer.readBits(1);
+ }
+ }
+ }
+ }
+ if (extensionAudioObjectType == 22) {
+ sbrPresentFlag = bitReaderBuffer.readBits(1);
+ if (sbrPresentFlag == 1) {
+ extensionSamplingFrequencyIndex = bitReaderBuffer.readBits(4);
+ if (extensionSamplingFrequencyIndex == 0xf) {
+ extensionSamplingFrequency = bitReaderBuffer.readBits(24);
+ }
+ }
+ extensionChannelConfiguration = bitReaderBuffer.readBits(4);
+ }
+ }
+ }
+ }
+
+ private int gaSpecificConfigSize() {
+ return 0;
+ }
+
+ public int serializedSize() {
+ int out = 4;
+ if (audioObjectType == 2) {
+ out += gaSpecificConfigSize();
+ } else {
+ throw new UnsupportedOperationException("can't serialize that yet");
+ }
+ return out;
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.allocate(serializedSize());
+ IsoTypeWriter.writeUInt8(out, 5);
+ IsoTypeWriter.writeUInt8(out, serializedSize() - 2);
+ BitWriterBuffer bwb = new BitWriterBuffer(out);
+ bwb.writeBits(audioObjectType, 5);
+ bwb.writeBits(samplingFrequencyIndex, 4);
+ if (samplingFrequencyIndex == 0xf) {
+ throw new UnsupportedOperationException("can't serialize that yet");
+ }
+ bwb.writeBits(channelConfiguration, 4);
+
+ // Don't support any extensions, unusual GASpecificConfig other than the default or anything...
+
+ return out;
+ }
+
+ private int getAudioObjectType(BitReaderBuffer in) throws IOException {
+ int audioObjectType = in.readBits(5);
+ if (audioObjectType == 31) {
+ audioObjectType = 32 + in.readBits(6);
+ }
+ return audioObjectType;
+ }
+
+ private void parseGaSpecificConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+// GASpecificConfig (samplingFrequencyIndex,
+// channelConfiguration,
+// audioObjectType)
+// {
+ frameLengthFlag = in.readBits(1);
+ dependsOnCoreCoder = in.readBits(1);
+ if (dependsOnCoreCoder == 1) {
+ coreCoderDelay = in.readBits(14);
+ }
+ extensionFlag = in.readBits(1);
+ if (channelConfiguration == 0) {
+ throw new UnsupportedOperationException("can't parse program_config_element yet");
+ //program_config_element ();
+ }
+ if ((audioObjectType == 6) || (audioObjectType == 20)) {
+ layerNr = in.readBits(3);
+ }
+ if (extensionFlag == 1) {
+ if (audioObjectType == 22) {
+ numOfSubFrame = in.readBits(5);
+ layer_length = in.readBits(11);
+ }
+ if (audioObjectType == 17 || audioObjectType == 19 ||
+ audioObjectType == 20 || audioObjectType == 23) {
+ aacSectionDataResilienceFlag = in.readBits(1);
+ aacScalefactorDataResilienceFlag = in.readBits(1);
+ aacSpectralDataResilienceFlag = in.readBits(1);
+ }
+ extensionFlag3 = in.readBits(1);
+ if (extensionFlag3 == 1) {
+ /* tbd in version 3 */
+ }
+ }
+// }
+ gaSpecificConfig = true;
+ }
+
+ private void parseParametricSpecificConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ ParametricSpecificConfig() {
+ isBaseLayer; 1 uimsbf
+ if (isBaseLayer) {
+ PARAconfig();
+ } else {
+ HILNenexConfig();
+ }
+ }
+ */
+ isBaseLayer = in.readBits(1);
+ if (isBaseLayer == 1) {
+ parseParaConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, in);
+ } else {
+ parseHilnEnexConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, in);
+ }
+ }
+
+ private void parseParaConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ PARAconfig()
+ {
+ PARAmode; 2 uimsbf
+ if (PARAmode != 1) {
+ ErHVXCconfig();
+ }
+ if (PARAmode != 0) {
+ HILNconfig();
+ }
+ PARAextensionFlag; 1 uimsbf
+ if (PARAextensionFlag) {
+ // to be defined in MPEG-4 Phase 3
+ }
+ }
+ */
+ paraMode = in.readBits(2);
+
+ if (paraMode != 1) {
+ parseErHvxcConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, in);
+ }
+ if (paraMode != 0) {
+ parseHilnConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, in);
+ }
+
+ paraExtensionFlag = in.readBits(1);
+ parametricSpecificConfig = true;
+ }
+
+ private void parseErHvxcConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ ErHVXCconfig()
+ {
+ HVXCvarMode; 1 uimsbf
+ HVXCrateMode; 2 uimsbf
+ extensionFlag; 1 uimsbf
+ if (extensionFlag) {
+ var_ScalableFlag; 1 uimsbf
+ }
+ }
+ */
+ hvxcVarMode = in.readBits(1);
+ hvxcRateMode = in.readBits(2);
+ erHvxcExtensionFlag = in.readBits(1);
+
+ if (erHvxcExtensionFlag == 1) {
+ var_ScalableFlag = in.readBits(1);
+ }
+ }
+
+ private void parseHilnConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ HILNconfig()
+ {
+ HILNquantMode; 1 uimsbf
+ HILNmaxNumLine; 8 uimsbf
+ HILNsampleRateCode; 4 uimsbf
+ HILNframeLength; 12 uimsbf
+ HILNcontMode; 2 uimsbf
+ }
+ */
+ hilnQuantMode = in.readBits(1);
+ hilnMaxNumLine = in.readBits(8);
+ hilnSampleRateCode = in.readBits(4);
+ hilnFrameLength = in.readBits(12);
+ hilnContMode = in.readBits(2);
+ }
+
+ private void parseHilnEnexConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ HILNenexConfig()
+ {
+ HILNenhaLayer; 1 uimsbf
+ if (HILNenhaLayer) {
+ HILNenhaQuantMode; 2 uimsbf
+ }
+ }
+ */
+ hilnEnhaLayer = in.readBits(1);
+ if (hilnEnhaLayer == 1) {
+ hilnEnhaQuantMode = in.readBits(2);
+ }
+ }
+
+ public byte[] getConfigBytes() {
+ return configBytes;
+ }
+
+ public int getAudioObjectType() {
+ return audioObjectType;
+ }
+
+ public int getExtensionAudioObjectType() {
+ return extensionAudioObjectType;
+ }
+
+ public int getSbrPresentFlag() {
+ return sbrPresentFlag;
+ }
+
+ public int getPsPresentFlag() {
+ return psPresentFlag;
+ }
+
+ public void setAudioObjectType(int audioObjectType) {
+ this.audioObjectType = audioObjectType;
+ }
+
+ public void setSamplingFrequencyIndex(int samplingFrequencyIndex) {
+ this.samplingFrequencyIndex = samplingFrequencyIndex;
+ }
+
+ public void setSamplingFrequency(int samplingFrequency) {
+ this.samplingFrequency = samplingFrequency;
+ }
+
+ public void setChannelConfiguration(int channelConfiguration) {
+ this.channelConfiguration = channelConfiguration;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("AudioSpecificConfig");
+ sb.append("{configBytes=").append(Hex.encodeHex(configBytes));
+ sb.append(", audioObjectType=").append(audioObjectType).append(" (").append(audioObjectTypeMap.get(audioObjectType)).append(")");
+ sb.append(", samplingFrequencyIndex=").append(samplingFrequencyIndex).append(" (").append(samplingFrequencyIndexMap.get(samplingFrequencyIndex)).append(")");
+ sb.append(", samplingFrequency=").append(samplingFrequency);
+ sb.append(", channelConfiguration=").append(channelConfiguration);
+ if (extensionAudioObjectType > 0) {
+ sb.append(", extensionAudioObjectType=").append(extensionAudioObjectType).append(" (").append(audioObjectTypeMap.get(extensionAudioObjectType)).append(")");
+ sb.append(", sbrPresentFlag=").append(sbrPresentFlag);
+ sb.append(", psPresentFlag=").append(psPresentFlag);
+ sb.append(", extensionSamplingFrequencyIndex=").append(extensionSamplingFrequencyIndex).append(" (").append(samplingFrequencyIndexMap.get(extensionSamplingFrequencyIndex)).append(")");
+ sb.append(", extensionSamplingFrequency=").append(extensionSamplingFrequency);
+ sb.append(", extensionChannelConfiguration=").append(extensionChannelConfiguration);
+ }
+// sb.append(", sacPayloadEmbedding=").append(sacPayloadEmbedding);
+// sb.append(", fillBits=").append(fillBits);
+// sb.append(", epConfig=").append(epConfig);
+// sb.append(", directMapping=").append(directMapping);
+ sb.append(", syncExtensionType=").append(syncExtensionType);
+ if (gaSpecificConfig) {
+ sb.append(", frameLengthFlag=").append(frameLengthFlag);
+ sb.append(", dependsOnCoreCoder=").append(dependsOnCoreCoder);
+ sb.append(", coreCoderDelay=").append(coreCoderDelay);
+ sb.append(", extensionFlag=").append(extensionFlag);
+ sb.append(", layerNr=").append(layerNr);
+ sb.append(", numOfSubFrame=").append(numOfSubFrame);
+ sb.append(", layer_length=").append(layer_length);
+ sb.append(", aacSectionDataResilienceFlag=").append(aacSectionDataResilienceFlag);
+ sb.append(", aacScalefactorDataResilienceFlag=").append(aacScalefactorDataResilienceFlag);
+ sb.append(", aacSpectralDataResilienceFlag=").append(aacSpectralDataResilienceFlag);
+ sb.append(", extensionFlag3=").append(extensionFlag3);
+ }
+ if (parametricSpecificConfig) {
+ sb.append(", isBaseLayer=").append(isBaseLayer);
+ sb.append(", paraMode=").append(paraMode);
+ sb.append(", paraExtensionFlag=").append(paraExtensionFlag);
+ sb.append(", hvxcVarMode=").append(hvxcVarMode);
+ sb.append(", hvxcRateMode=").append(hvxcRateMode);
+ sb.append(", erHvxcExtensionFlag=").append(erHvxcExtensionFlag);
+ sb.append(", var_ScalableFlag=").append(var_ScalableFlag);
+ sb.append(", hilnQuantMode=").append(hilnQuantMode);
+ sb.append(", hilnMaxNumLine=").append(hilnMaxNumLine);
+ sb.append(", hilnSampleRateCode=").append(hilnSampleRateCode);
+ sb.append(", hilnFrameLength=").append(hilnFrameLength);
+ sb.append(", hilnContMode=").append(hilnContMode);
+ sb.append(", hilnEnhaLayer=").append(hilnEnhaLayer);
+ sb.append(", hilnEnhaQuantMode=").append(hilnEnhaQuantMode);
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ static {
+ // sampling_frequency_index sampling frequeny
+//0x0 96000
+//0x1 88200
+//0x2 64000
+//0x3 48000
+//0x4 44100
+//0x5 32000
+//0x6 24000
+//0x7 22050
+//0x8 16000
+//0x9 12000
+//0xa 11025
+//0xb 8000
+//0xc reserved
+//0xd reserved
+//0xe reserved
+//0xf reserved
+ samplingFrequencyIndexMap.put(0x0, 96000);
+ samplingFrequencyIndexMap.put(0x1, 88200);
+ samplingFrequencyIndexMap.put(0x2, 64000);
+ samplingFrequencyIndexMap.put(0x3, 48000);
+ samplingFrequencyIndexMap.put(0x4, 44100);
+ samplingFrequencyIndexMap.put(0x5, 32000);
+ samplingFrequencyIndexMap.put(0x6, 24000);
+ samplingFrequencyIndexMap.put(0x7, 22050);
+ samplingFrequencyIndexMap.put(0x8, 16000);
+ samplingFrequencyIndexMap.put(0x9, 12000);
+ samplingFrequencyIndexMap.put(0xa, 11025);
+ samplingFrequencyIndexMap.put(0xb, 8000);
+
+ /* audioObjectType IDs
+ 0 Null
+ 1 AAC main X X
+ 2 AAC LC X X X X X X X
+ 3 AAC SSR X X
+ 4 AAC LTP X X X X
+ 5 SBR X X
+ 6 AAC Scalable X X X X
+ 7 TwinVQ X X X
+ 8 CELP X X X X X X
+ 9 HVXC X X X X X
+ 10 (reserved)
+ 11 (reserved)
+ 12 TTSI X X X X X X
+ 13 Main synthetic X X
+ 14 Wavetable synthesis X* X*
+ 15 General MIDI X* X*
+ 16 Algorithmic Synthesis and Audio FX X* X*
+ 17 ER AAC LC X X X
+ 18 (reserved)
+ 19 ER AAC LTP X X
+ 20 ER AAC Scalable X X X
+ 21 ER TwinVQ X X
+ 22 ER BSAC X X
+ 23 ER AAC LD X X X X
+ 24 ER CELP X X X
+ 25 ER HVXC X X
+ 26 ER HILN X
+ 27 ER Parametric X
+ 28 SSC
+ 29 PS X
+ 30 MPEG Surround
+ 31 (escape)
+ 32 Layer-1
+ 33 Layer-2
+ 34 Layer-3
+ 35 DST
+ 36 ALS
+ 37 SLS
+ 38 SLS non-core
+ 39 ER AAC ELD
+ 40 SMR Simple
+ 41 SMR Main
+ */
+ audioObjectTypeMap.put(1, "AAC main");
+ audioObjectTypeMap.put(2, "AAC LC");
+ audioObjectTypeMap.put(3, "AAC SSR");
+ audioObjectTypeMap.put(4, "AAC LTP");
+ audioObjectTypeMap.put(5, "SBR");
+ audioObjectTypeMap.put(6, "AAC Scalable");
+ audioObjectTypeMap.put(7, "TwinVQ");
+ audioObjectTypeMap.put(8, "CELP");
+ audioObjectTypeMap.put(9, "HVXC");
+ audioObjectTypeMap.put(10, "(reserved)");
+ audioObjectTypeMap.put(11, "(reserved)");
+ audioObjectTypeMap.put(12, "TTSI");
+ audioObjectTypeMap.put(13, "Main synthetic");
+ audioObjectTypeMap.put(14, "Wavetable synthesis");
+ audioObjectTypeMap.put(15, "General MIDI");
+ audioObjectTypeMap.put(16, "Algorithmic Synthesis and Audio FX");
+ audioObjectTypeMap.put(17, "ER AAC LC");
+ audioObjectTypeMap.put(18, "(reserved)");
+ audioObjectTypeMap.put(19, "ER AAC LTP");
+ audioObjectTypeMap.put(20, "ER AAC Scalable");
+ audioObjectTypeMap.put(21, "ER TwinVQ");
+ audioObjectTypeMap.put(22, "ER BSAC");
+ audioObjectTypeMap.put(23, "ER AAC LD");
+ audioObjectTypeMap.put(24, "ER CELP");
+ audioObjectTypeMap.put(25, "ER HVXC");
+ audioObjectTypeMap.put(26, "ER HILN");
+ audioObjectTypeMap.put(27, "ER Parametric");
+ audioObjectTypeMap.put(28, "SSC");
+ audioObjectTypeMap.put(29, "PS");
+ audioObjectTypeMap.put(30, "MPEG Surround");
+ audioObjectTypeMap.put(31, "(escape)");
+ audioObjectTypeMap.put(32, "Layer-1");
+ audioObjectTypeMap.put(33, "Layer-2");
+ audioObjectTypeMap.put(34, "Layer-3");
+ audioObjectTypeMap.put(35, "DST");
+ audioObjectTypeMap.put(36, "ALS");
+ audioObjectTypeMap.put(37, "SLS");
+ audioObjectTypeMap.put(38, "SLS non-core");
+ audioObjectTypeMap.put(39, "ER AAC ELD");
+ audioObjectTypeMap.put(40, "SMR Simple");
+ audioObjectTypeMap.put(41, "SMR Main");
+
+ /* profileLevelIds
+ 0x00 Reserved for ISO use -
+ 0x01 Main Audio Profile L1
+ 0x02 Main Audio Profile L2
+ 0x03 Main Audio Profile L3
+ 0x04 Main Audio Profile L4
+ 0x05 Scalable Audio Profile L1
+ 0x06 Scalable Audio Profile L2
+ 0x07 Scalable Audio Profile L3
+ 0x08 Scalable Audio Profile L4
+ 0x09 Speech Audio Profile L1
+ 0x0A Speech Audio Profile L2
+ 0x0B Synthetic Audio Profile L1
+ 0x0C Synthetic Audio Profile L2
+ 0x0D Synthetic Audio Profile L3
+ 0x0E High Quality Audio Profile L1
+ 0x0F High Quality Audio Profile L2
+ 0x10 High Quality Audio Profile L3
+ 0x11 High Quality Audio Profile L4
+ 0x12 High Quality Audio Profile L5
+ 0x13 High Quality Audio Profile L6
+ 0x14 High Quality Audio Profile L7
+ 0x15 High Quality Audio Profile L8
+ 0x16 Low Delay Audio Profile L1
+ 0x17 Low Delay Audio Profile L2
+ 0x18 Low Delay Audio Profile L3
+ 0x19 Low Delay Audio Profile L4
+ 0x1A Low Delay Audio Profile L5
+ 0x1B Low Delay Audio Profile L6
+ 0x1C Low Delay Audio Profile L7
+ 0x1D Low Delay Audio Profile L8
+ 0x1E Natural Audio Profile L1
+ 0x1F Natural Audio Profile L2
+ 0x20 Natural Audio Profile L3
+ 0x21 Natural Audio Profile L4
+ 0x22 Mobile Audio Internetworking Profile L1
+ 0x23 Mobile Audio Internetworking Profile L2
+ 0x24 Mobile Audio Internetworking Profile L3
+ 0x25 Mobile Audio Internetworking Profile L4
+ 0x26 Mobile Audio Internetworking Profile L5
+ 0x27 Mobile Audio Internetworking Profile L6
+ 0x28 AAC Profile L1
+ 0x29 AAC Profile L2
+ 0x2A AAC Profile L4
+ 0x2B AAC Profile L5
+ 0x2C High Efficiency AAC Profile L2
+ 0x2D High Efficiency AAC Profile L3
+ 0x2E High Efficiency AAC Profile L4
+ 0x2F High Efficiency AAC Profile L5
+ 0x30 High Efficiency AAC v2 Profile L2
+ 0x31 High Efficiency AAC v2 Profile L3
+ 0x32 High Efficiency AAC v2 Profile L4
+ 0x33 High Efficiency AAC v2 Profile L5
+ 0x34 Low Delay AAC Profile L1
+ 0x35 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L1
+ 0x36 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L2
+ 0x37 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L3
+ 0x38 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L4
+ 0c39 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L5
+ 0x3A Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L6
+ 0x3B - 0x7F reserved for ISO use -
+ 0x80 - 0xFD user private -
+ 0xFE no audio profile specified -
+ 0xFF no audio capability required -
+
+ */
+ }
+
+
+ public int getSamplingFrequency() {
+ return samplingFrequencyIndex == 0xf ? samplingFrequency : samplingFrequencyIndexMap.get(samplingFrequencyIndex);
+ }
+
+ public int getChannelConfiguration() {
+ return channelConfiguration;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ AudioSpecificConfig that = (AudioSpecificConfig) o;
+
+ if (aacScalefactorDataResilienceFlag != that.aacScalefactorDataResilienceFlag) {
+ return false;
+ }
+ if (aacSectionDataResilienceFlag != that.aacSectionDataResilienceFlag) {
+ return false;
+ }
+ if (aacSpectralDataResilienceFlag != that.aacSpectralDataResilienceFlag) {
+ return false;
+ }
+ if (audioObjectType != that.audioObjectType) {
+ return false;
+ }
+ if (channelConfiguration != that.channelConfiguration) {
+ return false;
+ }
+ if (coreCoderDelay != that.coreCoderDelay) {
+ return false;
+ }
+ if (dependsOnCoreCoder != that.dependsOnCoreCoder) {
+ return false;
+ }
+ if (directMapping != that.directMapping) {
+ return false;
+ }
+ if (epConfig != that.epConfig) {
+ return false;
+ }
+ if (erHvxcExtensionFlag != that.erHvxcExtensionFlag) {
+ return false;
+ }
+ if (extensionAudioObjectType != that.extensionAudioObjectType) {
+ return false;
+ }
+ if (extensionChannelConfiguration != that.extensionChannelConfiguration) {
+ return false;
+ }
+ if (extensionFlag != that.extensionFlag) {
+ return false;
+ }
+ if (extensionFlag3 != that.extensionFlag3) {
+ return false;
+ }
+ if (extensionSamplingFrequency != that.extensionSamplingFrequency) {
+ return false;
+ }
+ if (extensionSamplingFrequencyIndex != that.extensionSamplingFrequencyIndex) {
+ return false;
+ }
+ if (fillBits != that.fillBits) {
+ return false;
+ }
+ if (frameLengthFlag != that.frameLengthFlag) {
+ return false;
+ }
+ if (gaSpecificConfig != that.gaSpecificConfig) {
+ return false;
+ }
+ if (hilnContMode != that.hilnContMode) {
+ return false;
+ }
+ if (hilnEnhaLayer != that.hilnEnhaLayer) {
+ return false;
+ }
+ if (hilnEnhaQuantMode != that.hilnEnhaQuantMode) {
+ return false;
+ }
+ if (hilnFrameLength != that.hilnFrameLength) {
+ return false;
+ }
+ if (hilnMaxNumLine != that.hilnMaxNumLine) {
+ return false;
+ }
+ if (hilnQuantMode != that.hilnQuantMode) {
+ return false;
+ }
+ if (hilnSampleRateCode != that.hilnSampleRateCode) {
+ return false;
+ }
+ if (hvxcRateMode != that.hvxcRateMode) {
+ return false;
+ }
+ if (hvxcVarMode != that.hvxcVarMode) {
+ return false;
+ }
+ if (isBaseLayer != that.isBaseLayer) {
+ return false;
+ }
+ if (layerNr != that.layerNr) {
+ return false;
+ }
+ if (layer_length != that.layer_length) {
+ return false;
+ }
+ if (numOfSubFrame != that.numOfSubFrame) {
+ return false;
+ }
+ if (paraExtensionFlag != that.paraExtensionFlag) {
+ return false;
+ }
+ if (paraMode != that.paraMode) {
+ return false;
+ }
+ if (parametricSpecificConfig != that.parametricSpecificConfig) {
+ return false;
+ }
+ if (psPresentFlag != that.psPresentFlag) {
+ return false;
+ }
+ if (sacPayloadEmbedding != that.sacPayloadEmbedding) {
+ return false;
+ }
+ if (samplingFrequency != that.samplingFrequency) {
+ return false;
+ }
+ if (samplingFrequencyIndex != that.samplingFrequencyIndex) {
+ return false;
+ }
+ if (sbrPresentFlag != that.sbrPresentFlag) {
+ return false;
+ }
+ if (syncExtensionType != that.syncExtensionType) {
+ return false;
+ }
+ if (var_ScalableFlag != that.var_ScalableFlag) {
+ return false;
+ }
+ if (!Arrays.equals(configBytes, that.configBytes)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = configBytes != null ? Arrays.hashCode(configBytes) : 0;
+ result = 31 * result + audioObjectType;
+ result = 31 * result + samplingFrequencyIndex;
+ result = 31 * result + samplingFrequency;
+ result = 31 * result + channelConfiguration;
+ result = 31 * result + extensionAudioObjectType;
+ result = 31 * result + sbrPresentFlag;
+ result = 31 * result + psPresentFlag;
+ result = 31 * result + extensionSamplingFrequencyIndex;
+ result = 31 * result + extensionSamplingFrequency;
+ result = 31 * result + extensionChannelConfiguration;
+ result = 31 * result + sacPayloadEmbedding;
+ result = 31 * result + fillBits;
+ result = 31 * result + epConfig;
+ result = 31 * result + directMapping;
+ result = 31 * result + syncExtensionType;
+ result = 31 * result + frameLengthFlag;
+ result = 31 * result + dependsOnCoreCoder;
+ result = 31 * result + coreCoderDelay;
+ result = 31 * result + extensionFlag;
+ result = 31 * result + layerNr;
+ result = 31 * result + numOfSubFrame;
+ result = 31 * result + layer_length;
+ result = 31 * result + aacSectionDataResilienceFlag;
+ result = 31 * result + aacScalefactorDataResilienceFlag;
+ result = 31 * result + aacSpectralDataResilienceFlag;
+ result = 31 * result + extensionFlag3;
+ result = 31 * result + (gaSpecificConfig ? 1 : 0);
+ result = 31 * result + isBaseLayer;
+ result = 31 * result + paraMode;
+ result = 31 * result + paraExtensionFlag;
+ result = 31 * result + hvxcVarMode;
+ result = 31 * result + hvxcRateMode;
+ result = 31 * result + erHvxcExtensionFlag;
+ result = 31 * result + var_ScalableFlag;
+ result = 31 * result + hilnQuantMode;
+ result = 31 * result + hilnMaxNumLine;
+ result = 31 * result + hilnSampleRateCode;
+ result = 31 * result + hilnFrameLength;
+ result = 31 * result + hilnContMode;
+ result = 31 * result + hilnEnhaLayer;
+ result = 31 * result + hilnEnhaQuantMode;
+ result = 31 * result + (parametricSpecificConfig ? 1 : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BaseDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BaseDescriptor.java.svn-base
new file mode 100644
index 0000000..6d94680
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BaseDescriptor.java.svn-base
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/*
+abstract aligned(8) expandable(228-1) class BaseDescriptor : bit(8) tag=0 {
+// empty. To be filled by classes extending this class.
+}
+
+int sizeOfInstance = 0;
+bit(1) nextByte;
+bit(7) sizeOfInstance;
+while(nextByte) {
+bit(1) nextByte;
+bit(7) sizeByte;
+sizeOfInstance = sizeOfInstance<<7 | sizeByte;
+}
+ */
+@Descriptor(tags = 0x00)
+public abstract class BaseDescriptor {
+ int tag;
+ int sizeOfInstance;
+ int sizeBytes;
+
+ public BaseDescriptor() {
+ }
+
+ public int getTag() {
+ return tag;
+ }
+
+ public int getSize() {
+ return sizeOfInstance
+ + 1//1 for the tag
+ + sizeBytes;
+ }
+
+ public int getSizeOfInstance() {
+ return sizeOfInstance;
+ }
+
+ public int getSizeBytes() {
+ return sizeBytes;
+ }
+
+ public final void parse(int tag, ByteBuffer bb) throws IOException {
+ this.tag = tag;
+
+ int i = 0;
+ int tmp = IsoTypeReader.readUInt8(bb);
+ i++;
+ sizeOfInstance = tmp & 0x7f;
+ while (tmp >>> 7 == 1) { //nextbyte indicator bit
+ tmp = IsoTypeReader.readUInt8(bb);
+ i++;
+ //sizeOfInstance = sizeOfInstance<<7 | sizeByte;
+ sizeOfInstance = sizeOfInstance << 7 | tmp & 0x7f;
+ }
+ sizeBytes = i;
+ ByteBuffer detailSource = bb.slice();
+ detailSource.limit(sizeOfInstance);
+ parseDetail(detailSource);
+ assert detailSource.remaining() == 0: this.getClass().getSimpleName() + " has not been fully parsed";
+ bb.position(bb.position() + sizeOfInstance);
+ }
+
+ public abstract void parseDetail(ByteBuffer bb) throws IOException;
+
+
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("BaseDescriptor");
+ sb.append("{tag=").append(tag);
+ sb.append(", sizeOfInstance=").append(sizeOfInstance);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BitReaderBuffer.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BitReaderBuffer.java.svn-base
new file mode 100644
index 0000000..7221503
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BitReaderBuffer.java.svn-base
@@ -0,0 +1,51 @@
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import java.nio.ByteBuffer;
+
+public class BitReaderBuffer {
+
+ private ByteBuffer buffer;
+ int initialPos;
+ int position;
+
+ public BitReaderBuffer(ByteBuffer buffer) {
+ this.buffer = buffer;
+ initialPos = buffer.position();
+ }
+
+ public int readBits(int i) {
+ byte b = buffer.get(initialPos + position / 8);
+ int v = b < 0 ? b + 256 : b;
+ int left = 8 - position % 8;
+ int rc;
+ if (i <= left) {
+ rc = (v << (position % 8) & 0xFF) >> ((position % 8) + (left - i));
+ position += i;
+ } else {
+ int now = left;
+ int then = i - left;
+ rc = readBits(now);
+ rc = rc << then;
+ rc += readBits(then);
+ }
+ buffer.position(initialPos + (int) Math.ceil((double) position / 8));
+ return rc;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public int byteSync() {
+ int left = 8 - position % 8;
+ if (left == 8) {
+ left = 0;
+ }
+ readBits(left);
+ return left;
+ }
+
+ public int remainingBits() {
+ return buffer.limit() * 8 - position;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BitWriterBuffer.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BitWriterBuffer.java.svn-base
new file mode 100644
index 0000000..e6ea67f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/BitWriterBuffer.java.svn-base
@@ -0,0 +1,36 @@
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import java.nio.ByteBuffer;
+
+public class BitWriterBuffer {
+
+ private ByteBuffer buffer;
+ int initialPos;
+ int position = 0;
+
+ public BitWriterBuffer(ByteBuffer buffer) {
+ this.buffer = buffer;
+ this.initialPos = buffer.position();
+ }
+
+ public void writeBits(int i, int numBits) {
+ assert i <= ((1 << numBits)-1): String.format("Trying to write a value bigger (%s) than the number bits (%s) allows. " +
+ "Please mask the value before writing it and make your code is really working as intended.", i, (1<<numBits)-1);
+
+ int left = 8 - position % 8;
+ if (numBits <= left) {
+ int current = (buffer.get(initialPos + position / 8));
+ current = current < 0 ? current + 256 : current;
+ current += i << (left - numBits);
+ buffer.put(initialPos + position / 8, (byte) (current > 127 ? current - 256 : current));
+ position += numBits;
+ } else {
+ int bitsSecondWrite = numBits - left;
+ writeBits(i >> bitsSecondWrite, left);
+ writeBits(i & (1 << bitsSecondWrite) - 1, bitsSecondWrite);
+ }
+ buffer.position(initialPos + position / 8 + ((position % 8 > 0) ? 1 : 0));
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/DecoderConfigDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/DecoderConfigDescriptor.java.svn-base
new file mode 100644
index 0000000..69d603a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/DecoderConfigDescriptor.java.svn-base
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * class DecoderConfigDescriptor extends BaseDescriptor : bit(8)
+ * tag=DecoderConfigDescrTag {
+ * bit(8) objectTypeIndication;
+ * bit(6) streamType;
+ * bit(1) upStream;
+ * const bit(1) reserved=1;
+ * bit(24) bufferSizeDB;
+ * bit(32) maxBitrate;
+ * bit(32) avgBitrate;
+ * DecoderSpecificInfo decSpecificInfo[0 .. 1];
+ * profileLevelIndicationIndexDescriptor profileLevelIndicationIndexDescr
+ * [0..255];
+ * }
+ */
+@Descriptor(tags = {0x04})
+public class DecoderConfigDescriptor extends BaseDescriptor {
+ private static Logger log = Logger.getLogger(DecoderConfigDescriptor.class.getName());
+
+
+ int objectTypeIndication;
+ int streamType;
+ int upStream;
+ int bufferSizeDB;
+ long maxBitRate;
+ long avgBitRate;
+
+ DecoderSpecificInfo decoderSpecificInfo;
+ AudioSpecificConfig audioSpecificInfo;
+ List<ProfileLevelIndicationDescriptor> profileLevelIndicationDescriptors = new ArrayList<ProfileLevelIndicationDescriptor>();
+ byte[] configDescriptorDeadBytes;
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ objectTypeIndication = IsoTypeReader.readUInt8(bb);
+
+ int data = IsoTypeReader.readUInt8(bb);
+ streamType = data >>> 2;
+ upStream = (data >> 1) & 0x1;
+
+ bufferSizeDB = IsoTypeReader.readUInt24(bb);
+ maxBitRate = IsoTypeReader.readUInt32(bb);
+ avgBitRate = IsoTypeReader.readUInt32(bb);
+
+
+
+ BaseDescriptor descriptor;
+ if (bb.remaining() > 2) { //1byte tag + at least 1byte size
+ final int begin = bb.position();
+ descriptor = ObjectDescriptorFactory.createFrom(objectTypeIndication, bb);
+ final int read = bb.position() - begin;
+ log.finer(descriptor + " - DecoderConfigDescr1 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor != null) {
+ final int size = descriptor.getSize();
+ if (read < size) {
+ //skip
+ configDescriptorDeadBytes = new byte[size - read];
+ bb.get(configDescriptorDeadBytes);
+ }
+ }
+ if (descriptor instanceof DecoderSpecificInfo) {
+ decoderSpecificInfo = (DecoderSpecificInfo) descriptor;
+ }
+ if (descriptor instanceof AudioSpecificConfig) {
+ audioSpecificInfo = (AudioSpecificConfig) descriptor;
+ }
+ }
+
+ while (bb.remaining() > 2) {
+ final long begin = bb.position();
+ descriptor = ObjectDescriptorFactory.createFrom(objectTypeIndication, bb);
+ final long read = bb.position() - begin;
+ log.finer(descriptor + " - DecoderConfigDescr2 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor instanceof ProfileLevelIndicationDescriptor) {
+ profileLevelIndicationDescriptors.add((ProfileLevelIndicationDescriptor) descriptor);
+ }
+ }
+ }
+ public int serializedSize() {
+ return 15 + audioSpecificInfo.serializedSize();
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.allocate(serializedSize());
+ IsoTypeWriter.writeUInt8(out, 4);
+ IsoTypeWriter.writeUInt8(out, serializedSize() - 2);
+ IsoTypeWriter.writeUInt8(out, objectTypeIndication);
+ int flags = (streamType << 2) | (upStream << 1) | 1;
+ IsoTypeWriter.writeUInt8(out, flags);
+ IsoTypeWriter.writeUInt24(out, bufferSizeDB);
+ IsoTypeWriter.writeUInt32(out, maxBitRate);
+ IsoTypeWriter.writeUInt32(out, avgBitRate);
+ out.put(audioSpecificInfo.serialize().array());
+ return out;
+ }
+
+ public DecoderSpecificInfo getDecoderSpecificInfo() {
+ return decoderSpecificInfo;
+ }
+
+ public AudioSpecificConfig getAudioSpecificInfo() {
+ return audioSpecificInfo;
+ }
+
+ public void setAudioSpecificInfo(AudioSpecificConfig audioSpecificInfo) {
+ this.audioSpecificInfo = audioSpecificInfo;
+ }
+
+ public List<ProfileLevelIndicationDescriptor> getProfileLevelIndicationDescriptors() {
+ return profileLevelIndicationDescriptors;
+ }
+
+ public int getObjectTypeIndication() {
+ return objectTypeIndication;
+ }
+
+ public void setObjectTypeIndication(int objectTypeIndication) {
+ this.objectTypeIndication = objectTypeIndication;
+ }
+
+ public int getStreamType() {
+ return streamType;
+ }
+
+ public void setStreamType(int streamType) {
+ this.streamType = streamType;
+ }
+
+ public int getUpStream() {
+ return upStream;
+ }
+
+ public void setUpStream(int upStream) {
+ this.upStream = upStream;
+ }
+
+ public int getBufferSizeDB() {
+ return bufferSizeDB;
+ }
+
+ public void setBufferSizeDB(int bufferSizeDB) {
+ this.bufferSizeDB = bufferSizeDB;
+ }
+
+ public long getMaxBitRate() {
+ return maxBitRate;
+ }
+
+ public void setMaxBitRate(long maxBitRate) {
+ this.maxBitRate = maxBitRate;
+ }
+
+ public long getAvgBitRate() {
+ return avgBitRate;
+ }
+
+ public void setAvgBitRate(long avgBitRate) {
+ this.avgBitRate = avgBitRate;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DecoderConfigDescriptor");
+ sb.append("{objectTypeIndication=").append(objectTypeIndication);
+ sb.append(", streamType=").append(streamType);
+ sb.append(", upStream=").append(upStream);
+ sb.append(", bufferSizeDB=").append(bufferSizeDB);
+ sb.append(", maxBitRate=").append(maxBitRate);
+ sb.append(", avgBitRate=").append(avgBitRate);
+ sb.append(", decoderSpecificInfo=").append(decoderSpecificInfo);
+ sb.append(", audioSpecificInfo=").append(audioSpecificInfo);
+ sb.append(", configDescriptorDeadBytes=").append(Hex.encodeHex(configDescriptorDeadBytes != null ? configDescriptorDeadBytes : new byte[]{}));
+ sb.append(", profileLevelIndicationDescriptors=").append(profileLevelIndicationDescriptors == null ? "null" : Arrays.asList(profileLevelIndicationDescriptors).toString());
+ sb.append('}');
+ return sb.toString();
+ }
+ /*objectTypeIndication values
+ 0x00 Forbidden
+ 0x01 Systems ISO/IEC 14496-1 a
+ 0x02 Systems ISO/IEC 14496-1 b
+ 0x03 Interaction Stream
+ 0x04 Systems ISO/IEC 14496-1 Extended BIFS Configuration c
+ 0x05 Systems ISO/IEC 14496-1 AFX d
+ 0x06 Font Data Stream
+ 0x07 Synthesized Texture Stream
+ 0x08 Streaming Text Stream
+ 0x09-0x1F reserved for ISO use
+ 0x20 Visual ISO/IEC 14496-2 e
+ 0x21 Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10 f
+ 0x22 Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10 f
+ 0x23-0x3F reserved for ISO use
+ 0x40 Audio ISO/IEC 14496-3 g
+ 0x41-0x5F reserved for ISO use
+ 0x60 Visual ISO/IEC 13818-2 Simple Profile
+ 0x61 Visual ISO/IEC 13818-2 Main Profile
+ 0x62 Visual ISO/IEC 13818-2 SNR Profile
+ 0x63 Visual ISO/IEC 13818-2 Spatial Profile
+ 0x64 Visual ISO/IEC 13818-2 High Profile
+ 0x65 Visual ISO/IEC 13818-2 422 Profile
+ 0x66 Audio ISO/IEC 13818-7 Main Profile
+ 0x67 Audio ISO/IEC 13818-7 LowComplexity Profile
+ 0x68 Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile
+ 0x69 Audio ISO/IEC 13818-3
+ 0x6A Visual ISO/IEC 11172-2
+ 0x6B Audio ISO/IEC 11172-3
+ 0x6C Visual ISO/IEC 10918-1
+ 0x6D reserved for registration authority i
+ 0x6E Visual ISO/IEC 15444-1
+ 0x6F - 0x9F reserved for ISO use
+ 0xA0 - 0xBF reserved for registration authority i
+ 0xC0 - 0xE0 user private
+ 0xE1 reserved for registration authority i
+ 0xE2 - 0xFE user private
+ 0xFF no object type specified h
+ */
+ /* streamType values
+ 0x00 Forbidden
+ 0x01 ObjectDescriptorStream (see 7.2.5)
+ 0x02 ClockReferenceStream (see 7.3.2.5)
+ 0x03 SceneDescriptionStream (see ISO/IEC 14496-11)
+ 0x04 VisualStream
+ 0x05 AudioStream
+ 0x06 MPEG7Stream
+ 0x07 IPMPStream (see 7.2.3.2)
+ 0x08 ObjectContentInfoStream (see 7.2.4.2)
+ 0x09 MPEGJStream
+ 0x0A Interaction Stream
+ 0x0B IPMPToolStream (see [ISO/IEC 14496-13])
+ 0x0C - 0x1F reserved for ISO use
+ 0x20 - 0x3F user private
+ */
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/DecoderSpecificInfo.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/DecoderSpecificInfo.java.svn-base
new file mode 100644
index 0000000..574943c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/DecoderSpecificInfo.java.svn-base
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * abstract class DecoderSpecificInfo extends BaseDescriptor : bit(8)
+ * tag=DecSpecificInfoTag
+ * {
+ * // empty. To be filled by classes extending this class.
+ * }
+ */
+@Descriptor(tags = 0x05)
+public class DecoderSpecificInfo extends BaseDescriptor {
+ byte[] bytes;
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ if (sizeOfInstance > 0) {
+ bytes = new byte[sizeOfInstance];
+ bb.get(bytes);
+ }
+ }
+
+ public int serializedSize() {
+ return bytes.length;
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.wrap(bytes);
+
+ return out;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DecoderSpecificInfo");
+ sb.append("{bytes=").append(bytes == null ? "null" : Hex.encodeHex(bytes));
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DecoderSpecificInfo that = (DecoderSpecificInfo) o;
+
+ if (!Arrays.equals(bytes, that.bytes)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return bytes != null ? Arrays.hashCode(bytes) : 0;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/Descriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/Descriptor.java.svn-base
new file mode 100644
index 0000000..11020c7
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/Descriptor.java.svn-base
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: mstattma
+ * Date: 06.08.2010
+ * Time: 06:54:58
+ * To change this template use File | Settings | File Templates.
+ */
+@Documented
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Descriptor {
+ int[] tags();
+
+ int objectTypeIndication() default -1;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ESDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ESDescriptor.java.svn-base
new file mode 100644
index 0000000..3bb4821
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ESDescriptor.java.svn-base
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/*
+class ES_Descriptor extends BaseDescriptor : bit(8) tag=ES_DescrTag {
+bit(16) ES_ID;
+bit(1) streamDependenceFlag;
+bit(1) URL_Flag;
+bit(1) OCRstreamFlag;
+bit(5) streamPriority;
+if (streamDependenceFlag)
+bit(16) dependsOn_ES_ID;
+if (URL_Flag) {
+bit(8) URLlength;
+bit(8) URLstring[URLlength];
+}
+if (OCRstreamFlag)
+bit(16) OCR_ES_Id;
+DecoderConfigDescriptor decConfigDescr;
+if (ODProfileLevelIndication==0x01) //no SL extension.
+{
+SLConfigDescriptor slConfigDescr;
+}
+else // SL extension is possible.
+{
+SLConfigDescriptor slConfigDescr;
+}
+IPI_DescrPointer ipiPtr[0 .. 1];
+IP_IdentificationDataSet ipIDS[0 .. 255];
+IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255];
+LanguageDescriptor langDescr[0 .. 255];
+QoS_Descriptor qosDescr[0 .. 1];
+RegistrationDescriptor regDescr[0 .. 1];
+ExtensionDescriptor extDescr[0 .. 255];
+}
+ */
+@Descriptor(tags = {0x03})
+public class ESDescriptor extends BaseDescriptor {
+ private static Logger log = Logger.getLogger(ESDescriptor.class.getName());
+
+ int esId;
+ int streamDependenceFlag;
+ int URLFlag;
+ int oCRstreamFlag;
+ int streamPriority;
+
+
+ int URLLength = 0;
+ String URLString;
+ int remoteODFlag;
+
+ int dependsOnEsId;
+ int oCREsId;
+
+ DecoderConfigDescriptor decoderConfigDescriptor;
+ SLConfigDescriptor slConfigDescriptor;
+ List<BaseDescriptor> otherDescriptors = new ArrayList<BaseDescriptor>();
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ esId = IsoTypeReader.readUInt16(bb);
+
+ int data = IsoTypeReader.readUInt8(bb);
+ streamDependenceFlag = data >>> 7;
+ URLFlag = (data >>> 6) & 0x1;
+ oCRstreamFlag = (data >>> 5) & 0x1;
+ streamPriority = data & 0x1f;
+
+ if (streamDependenceFlag == 1) {
+ dependsOnEsId = IsoTypeReader.readUInt16(bb);
+ }
+ if (URLFlag == 1) {
+ URLLength = IsoTypeReader.readUInt8(bb);
+ URLString = IsoTypeReader.readString(bb, URLLength);
+ }
+ if (oCRstreamFlag == 1) {
+ oCREsId = IsoTypeReader.readUInt16(bb);
+ }
+
+ int baseSize = 1 /*tag*/ + getSizeBytes() + 2 + 1 + (streamDependenceFlag == 1 ? 2 : 0) + (URLFlag == 1 ? 1 + URLLength : 0) + (oCRstreamFlag == 1 ? 2 : 0);
+
+ int begin = bb.position();
+ if (getSize() > baseSize + 2) {
+ BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ final long read = bb.position() - begin;
+ log.finer(descriptor + " - ESDescriptor1 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor != null) {
+ final int size = descriptor.getSize();
+ bb.position(begin + size);
+ baseSize += size;
+ } else {
+ baseSize += read;
+ }
+ if (descriptor instanceof DecoderConfigDescriptor) {
+ decoderConfigDescriptor = (DecoderConfigDescriptor) descriptor;
+ }
+ }
+
+ begin = bb.position();
+ if (getSize() > baseSize + 2) {
+ BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ final long read = bb.position() - begin;
+ log.finer(descriptor + " - ESDescriptor2 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor != null) {
+ final int size = descriptor.getSize();
+ bb.position(begin + size);
+ baseSize += size;
+ } else {
+ baseSize += read;
+ }
+ if (descriptor instanceof SLConfigDescriptor) {
+ slConfigDescriptor = (SLConfigDescriptor) descriptor;
+ }
+ } else {
+ log.warning("SLConfigDescriptor is missing!");
+ }
+
+ while (getSize() - baseSize > 2) {
+ begin = bb.position();
+ BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ final long read = bb.position() - begin;
+ log.finer(descriptor + " - ESDescriptor3 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor != null) {
+ final int size = descriptor.getSize();
+ bb.position(begin + size);
+ baseSize += size;
+ } else {
+ baseSize += read;
+ }
+ otherDescriptors.add(descriptor);
+ }
+ }
+ public int serializedSize() {
+ int out = 5;
+ if (streamDependenceFlag > 0) {
+ out += 2;
+ }
+ if (URLFlag > 0) {
+ out += 1 + URLLength;
+ }
+ if (oCRstreamFlag > 0) {
+ out += 2;
+ }
+
+ out += decoderConfigDescriptor.serializedSize();
+ out += slConfigDescriptor.serializedSize();
+
+ // Doesn't handle other descriptors yet
+
+ return out;
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.allocate(serializedSize()); // Usually is around 30 bytes, so 200 should be enough...
+ IsoTypeWriter.writeUInt8(out, 3);
+ IsoTypeWriter.writeUInt8(out, serializedSize() - 2); // Not OK for longer sizes!
+ IsoTypeWriter.writeUInt16(out, esId);
+ int flags = (streamDependenceFlag << 7) | (URLFlag << 6) | (oCRstreamFlag << 5) | (streamPriority & 0x1f);
+ IsoTypeWriter.writeUInt8(out, flags);
+ if (streamDependenceFlag > 0) {
+ IsoTypeWriter.writeUInt16(out, dependsOnEsId);
+ }
+ if (URLFlag > 0) {
+ IsoTypeWriter.writeUInt8(out, URLLength);
+ IsoTypeWriter.writeUtf8String(out, URLString);
+ }
+ if (oCRstreamFlag > 0) {
+ IsoTypeWriter.writeUInt16(out, oCREsId);
+ }
+
+ ByteBuffer dec = decoderConfigDescriptor.serialize();
+ ByteBuffer sl = slConfigDescriptor.serialize();
+ out.put(dec.array());
+ out.put(sl.array());
+
+ // Doesn't handle other descriptors yet
+
+ return out;
+ }
+
+// @Override
+// public int getSize() {
+// return 3 + (streamDependenceFlag == 1 ? 2 : 0) +
+// (URLFlag == 1 ? 1 + 8 * URLLength : 0) +
+// (oCRstreamFlag == 1 ? 2 : 0);
+// }
+
+ public DecoderConfigDescriptor getDecoderConfigDescriptor() {
+ return decoderConfigDescriptor;
+ }
+
+ public SLConfigDescriptor getSlConfigDescriptor() {
+ return slConfigDescriptor;
+ }
+
+ public void setDecoderConfigDescriptor(DecoderConfigDescriptor decoderConfigDescriptor) {
+ this.decoderConfigDescriptor = decoderConfigDescriptor;
+ }
+
+ public void setSlConfigDescriptor(SLConfigDescriptor slConfigDescriptor) {
+ this.slConfigDescriptor = slConfigDescriptor;
+ }
+
+ public List<BaseDescriptor> getOtherDescriptors() {
+ return otherDescriptors;
+ }
+
+ public int getoCREsId() {
+ return oCREsId;
+ }
+
+ public void setoCREsId(int oCREsId) {
+ this.oCREsId = oCREsId;
+ }
+
+ public int getEsId() {
+ return esId;
+ }
+
+ public void setEsId(int esId) {
+ this.esId = esId;
+ }
+
+ public int getStreamDependenceFlag() {
+ return streamDependenceFlag;
+ }
+
+ public void setStreamDependenceFlag(int streamDependenceFlag) {
+ this.streamDependenceFlag = streamDependenceFlag;
+ }
+
+ public int getURLFlag() {
+ return URLFlag;
+ }
+
+ public void setURLFlag(int URLFlag) {
+ this.URLFlag = URLFlag;
+ }
+
+ public int getoCRstreamFlag() {
+ return oCRstreamFlag;
+ }
+
+ public void setoCRstreamFlag(int oCRstreamFlag) {
+ this.oCRstreamFlag = oCRstreamFlag;
+ }
+
+ public int getStreamPriority() {
+ return streamPriority;
+ }
+
+ public void setStreamPriority(int streamPriority) {
+ this.streamPriority = streamPriority;
+ }
+
+ public int getURLLength() {
+ return URLLength;
+ }
+
+ public void setURLLength(int URLLength) {
+ this.URLLength = URLLength;
+ }
+
+ public String getURLString() {
+ return URLString;
+ }
+
+ public void setURLString(String URLString) {
+ this.URLString = URLString;
+ }
+
+ public int getRemoteODFlag() {
+ return remoteODFlag;
+ }
+
+ public void setRemoteODFlag(int remoteODFlag) {
+ this.remoteODFlag = remoteODFlag;
+ }
+
+ public int getDependsOnEsId() {
+ return dependsOnEsId;
+ }
+
+ public void setDependsOnEsId(int dependsOnEsId) {
+ this.dependsOnEsId = dependsOnEsId;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ESDescriptor");
+ sb.append("{esId=").append(esId);
+ sb.append(", streamDependenceFlag=").append(streamDependenceFlag);
+ sb.append(", URLFlag=").append(URLFlag);
+ sb.append(", oCRstreamFlag=").append(oCRstreamFlag);
+ sb.append(", streamPriority=").append(streamPriority);
+ sb.append(", URLLength=").append(URLLength);
+ sb.append(", URLString='").append(URLString).append('\'');
+ sb.append(", remoteODFlag=").append(remoteODFlag);
+ sb.append(", dependsOnEsId=").append(dependsOnEsId);
+ sb.append(", oCREsId=").append(oCREsId);
+ sb.append(", decoderConfigDescriptor=").append(decoderConfigDescriptor);
+ sb.append(", slConfigDescriptor=").append(slConfigDescriptor);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ESDescriptor that = (ESDescriptor) o;
+
+ if (URLFlag != that.URLFlag) return false;
+ if (URLLength != that.URLLength) return false;
+ if (dependsOnEsId != that.dependsOnEsId) return false;
+ if (esId != that.esId) return false;
+ if (oCREsId != that.oCREsId) return false;
+ if (oCRstreamFlag != that.oCRstreamFlag) return false;
+ if (remoteODFlag != that.remoteODFlag) return false;
+ if (streamDependenceFlag != that.streamDependenceFlag) return false;
+ if (streamPriority != that.streamPriority) return false;
+ if (URLString != null ? !URLString.equals(that.URLString) : that.URLString != null) return false;
+ if (decoderConfigDescriptor != null ? !decoderConfigDescriptor.equals(that.decoderConfigDescriptor) : that.decoderConfigDescriptor != null)
+ return false;
+ if (otherDescriptors != null ? !otherDescriptors.equals(that.otherDescriptors) : that.otherDescriptors != null)
+ return false;
+ if (slConfigDescriptor != null ? !slConfigDescriptor.equals(that.slConfigDescriptor) : that.slConfigDescriptor != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = esId;
+ result = 31 * result + streamDependenceFlag;
+ result = 31 * result + URLFlag;
+ result = 31 * result + oCRstreamFlag;
+ result = 31 * result + streamPriority;
+ result = 31 * result + URLLength;
+ result = 31 * result + (URLString != null ? URLString.hashCode() : 0);
+ result = 31 * result + remoteODFlag;
+ result = 31 * result + dependsOnEsId;
+ result = 31 * result + oCREsId;
+ result = 31 * result + (decoderConfigDescriptor != null ? decoderConfigDescriptor.hashCode() : 0);
+ result = 31 * result + (slConfigDescriptor != null ? slConfigDescriptor.hashCode() : 0);
+ result = 31 * result + (otherDescriptors != null ? otherDescriptors.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ExtensionDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ExtensionDescriptor.java.svn-base
new file mode 100644
index 0000000..7933f5a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ExtensionDescriptor.java.svn-base
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Logger;
+
+/**
+ * abstract class ExtensionDescriptor extends BaseDescriptor
+ * : bit(8) tag = ExtensionProfileLevelDescrTag, ExtDescrTagStartRange ..
+ * ExtDescrTagEndRange {
+ * // empty. To be filled by classes extending this class.
+ * }
+ */
+@Descriptor(tags = {0x13, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253})
+public class ExtensionDescriptor extends BaseDescriptor {
+ private static Logger log = Logger.getLogger(ExtensionDescriptor.class.getName());
+
+ byte[] bytes;
+
+
+ //todo: add this better to the tags list?
+ //14496-1:2010 p.20:
+ //0x6A-0xBF Reserved for ISO use
+ //0xC0-0xFE User private
+ //
+ //ExtDescrTagStartRange = 0x6A
+ //ExtDescrTagEndRange = 0xFE
+ static int[] allTags() {
+ int[] ints = new int[0xFE - 0x6A];
+
+ for (int i = 0x6A; i < 0xFE; i++) {
+ final int pos = i - 0x6A;
+ log.finest("pos:" + pos);
+ ints[pos] = i;
+ }
+ return ints;
+ }
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ if (getSize() > 0) {
+ bytes = new byte[sizeOfInstance];
+ bb.get(bytes);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ExtensionDescriptor");
+ sb.append("{bytes=").append(bytes == null ? "null" : Hex.encodeHex(bytes));
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ExtensionProfileLevelDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ExtensionProfileLevelDescriptor.java.svn-base
new file mode 100644
index 0000000..0cf4915
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ExtensionProfileLevelDescriptor.java.svn-base
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * abstract class ExtensionDescriptor extends BaseDescriptor
+ * : bit(8) tag = ExtensionProfileLevelDescrTag, ExtDescrTagStartRange ..
+ * ExtDescrTagEndRange {
+ * // empty. To be filled by classes extending this class.
+ * }
+ */
+@Descriptor(tags = {0x13})
+public class ExtensionProfileLevelDescriptor extends BaseDescriptor {
+ byte[] bytes;
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ if (getSize() > 0) {
+ bytes = new byte[getSize()];
+ bb.get(bytes);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ExtensionDescriptor");
+ sb.append("{bytes=").append(bytes == null ? "null" : Hex.encodeHex(bytes));
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/InitialObjectDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/InitialObjectDescriptor.java.svn-base
new file mode 100644
index 0000000..7a1f094
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/InitialObjectDescriptor.java.svn-base
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+class InitialObjectDescriptor extends ObjectDescriptorBase : bit(8)
+tag=InitialObjectDescrTag {
+bit(10) ObjectDescriptorID;
+bit(1) URL_Flag;
+bit(1) includeInlineProfileLevelFlag;
+const bit(4) reserved=0b1111;
+if (URL_Flag) {
+bit(8) URLlength;
+bit(8) URLstring[URLlength];
+} else {
+bit(8) ODProfileLevelIndication;
+bit(8) sceneProfileLevelIndication;
+bit(8) audioProfileLevelIndication;
+bit(8) visualProfileLevelIndication;
+bit(8) graphicsProfileLevelIndication;
+ES_Descriptor esDescr[1 .. 255];
+OCI_Descriptor ociDescr[0 .. 255];
+IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255];
+IPMP_Descriptor ipmpDescr [0 .. 255];
+IPMP_ToolListDescriptor toolListDescr[0 .. 1];
+}
+ExtensionDescriptor extDescr[0 .. 255];
+}
+*/
+//@Descriptor(tags = {0x02, 0x10})
+public class InitialObjectDescriptor extends ObjectDescriptorBase {
+ private int objectDescriptorId;
+ int urlFlag;
+ int includeInlineProfileLevelFlag;
+
+ int urlLength;
+ String urlString;
+
+ int oDProfileLevelIndication;
+ int sceneProfileLevelIndication;
+ int audioProfileLevelIndication;
+ int visualProfileLevelIndication;
+ int graphicsProfileLevelIndication;
+
+ List<ESDescriptor> esDescriptors = new ArrayList<ESDescriptor>();
+
+ List<ExtensionDescriptor> extensionDescriptors = new ArrayList<ExtensionDescriptor>();
+
+ List<BaseDescriptor> unknownDescriptors = new ArrayList<BaseDescriptor>();
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ int data = IsoTypeReader.readUInt16(bb);
+ objectDescriptorId = (data & 0xFFC0) >> 6;
+
+ urlFlag = (data & 0x3F) >> 5;
+ includeInlineProfileLevelFlag = (data & 0x1F) >> 4;
+
+ int sizeLeft = getSize() - 2;
+ if (urlFlag == 1) {
+ urlLength = IsoTypeReader.readUInt8(bb);
+ urlString = IsoTypeReader.readString(bb, urlLength);
+ sizeLeft = sizeLeft - (1 + urlLength);
+ } else {
+ oDProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+ sceneProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+ audioProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+ visualProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+ graphicsProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+
+ sizeLeft = sizeLeft - 5;
+
+ if (sizeLeft > 2) {
+ final BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ sizeLeft = sizeLeft - descriptor.getSize();
+ if (descriptor instanceof ESDescriptor) {
+ esDescriptors.add((ESDescriptor) descriptor);
+ } else {
+ unknownDescriptors.add(descriptor);
+ }
+ }
+ }
+
+ if (sizeLeft > 2) {
+ final BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ if (descriptor instanceof ExtensionDescriptor) {
+ extensionDescriptors.add((ExtensionDescriptor) descriptor);
+ } else {
+ unknownDescriptors.add(descriptor);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("InitialObjectDescriptor");
+ sb.append("{objectDescriptorId=").append(objectDescriptorId);
+ sb.append(", urlFlag=").append(urlFlag);
+ sb.append(", includeInlineProfileLevelFlag=").append(includeInlineProfileLevelFlag);
+ sb.append(", urlLength=").append(urlLength);
+ sb.append(", urlString='").append(urlString).append('\'');
+ sb.append(", oDProfileLevelIndication=").append(oDProfileLevelIndication);
+ sb.append(", sceneProfileLevelIndication=").append(sceneProfileLevelIndication);
+ sb.append(", audioProfileLevelIndication=").append(audioProfileLevelIndication);
+ sb.append(", visualProfileLevelIndication=").append(visualProfileLevelIndication);
+ sb.append(", graphicsProfileLevelIndication=").append(graphicsProfileLevelIndication);
+ sb.append(", esDescriptors=").append(esDescriptors);
+ sb.append(", extensionDescriptors=").append(extensionDescriptors);
+ sb.append(", unknownDescriptors=").append(unknownDescriptors);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptor.java_bak.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptor.java_bak.svn-base
new file mode 100644
index 0000000..c5cb586
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptor.java_bak.svn-base
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+class ObjectDescriptor extends ObjectDescriptorBase : bit(8) tag=ObjectDescrTag {
+bit(10) ObjectDescriptorID;
+bit(1) URL_Flag;
+const bit(5) reserved=0b1111.1;
+if (URL_Flag) {
+bit(8) URLlength;
+bit(8) URLstring[URLlength];
+} else {
+ES_Descriptor esDescr[1 .. 255];
+OCI_Descriptor ociDescr[0 .. 255];
+IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255];
+IPMP_Descriptor ipmpDescr [0 .. 255];
+}
+ExtensionDescriptor extDescr[0 .. 255];
+}
+*/
+@Descriptor(tags = {0x01, 0x11})
+public class ObjectDescriptor extends ObjectDescriptorBase {
+ private int objectDescriptorId;
+ int objectDescriptorUrlFlag;
+ int objectDescriptorUrlLength;
+ String objectDescriptorUrlString;
+
+
+ private int streamCount;
+ private int extensionFlag;
+ private List<ESDescriptor> esDescriptors = new ArrayList<ESDescriptor>();
+
+ private int descriptorLength;
+ private List<ExtensionDescriptor> extensionDescriptors = new ArrayList<ExtensionDescriptor>();
+
+ public static ObjectDescriptor createFrom(ByteBuffer in) throws IOException {
+/*
+ tmp = in.readUInt16();
+ esDescriptor.objectDescriptorId = tmp & 0x3f;
+ esDescriptor.objectDescriptorUrlFlag = (tmp >> 5) & 0x1;
+ if (esDescriptor.objectDescriptorUrlFlag == 1) {
+ esDescriptor.objectDescriptorUrlLength = in.readUInt8();
+ esDescriptor.objectDescriptorUrlString = new String(in.read(esDescriptor.objectDescriptorUrlLength));
+ }
+ */
+
+ ObjectDescriptor objectDescriptor = new ObjectDescriptor();
+
+ int data = IsoTypeReader.readUInt16(in);
+
+ objectDescriptor.objectDescriptorId = data & 0xFFC0;
+ objectDescriptor.streamCount = data & 0x3E;
+ objectDescriptor.extensionFlag = data & 0x1;
+
+// for (int i = 0; i < objectDescriptor.streamCount; i++) {
+// objectDescriptor.esDescriptors.add(ESDescriptor.createFrom(in));
+// }
+//
+// if (objectDescriptor.extensionFlag == 1) {
+// objectDescriptor.descriptorLength = in.readUInt8();
+// for (int i = 0; i < objectDescriptor.descriptorLength;) {
+// ExtensionDescriptor extensionDescriptor = ExtensionDescriptor.createFrom(in);
+// objectDescriptor.extensionDescriptors.add(extensionDescriptor);
+// i = i + extensionDescriptor.descriptorDataLength + 1;
+// }
+// }
+
+ return objectDescriptor;
+ }
+
+ @Override
+ public String toString() {
+ return "ObjectDescriptor{" +
+ "objectDescriptorId=" + objectDescriptorId +
+ ", streamCount=" + streamCount +
+ ", extensionFlag=" + extensionFlag +
+ ", esDescriptors=" + esDescriptors +
+ ", descriptorLength=" + descriptorLength +
+ ", extensionDescriptors=" + extensionDescriptors +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptorBase.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptorBase.java.svn-base
new file mode 100644
index 0000000..69a8684
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptorBase.java.svn-base
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+/*
+abstract class ObjectDescriptorBase extends BaseDescriptor : bit(8)
+tag=[ObjectDescrTag..InitialObjectDescrTag] {
+// empty. To be filled by classes extending this class.
+}
+ */
+@Descriptor(tags = 0x00)
+public abstract class ObjectDescriptorBase extends BaseDescriptor {
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptorFactory.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptorFactory.java.svn-base
new file mode 100644
index 0000000..6afba55
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ObjectDescriptorFactory.java.svn-base
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/* class tag values of 14496-1
+0x00 Forbidden
+0x01 ObjectDescrTag
+0x02 InitialObjectDescrTag
+0x03 ES_DescrTag
+0x04 DecoderConfigDescrTag
+0x05 DecSpecificInfoTag
+0x06 SLConfigDescrTag
+0x07 ContentIdentDescrTag
+0x08 SupplContentIdentDescrTag
+0x09 IPI_DescrPointerTag
+0x0A IPMP_DescrPointerTag
+0x0B IPMP_DescrTag
+0x0C QoS_DescrTag
+0x0D RegistrationDescrTag
+0x0E ES_ID_IncTag
+0x0F ES_ID_RefTag
+0x10 MP4_IOD_Tag
+0x11 MP4_OD_Tag
+0x12 IPL_DescrPointerRefTag
+0x13 ExtensionProfileLevelDescrTag
+0x14 profileLevelIndicationIndexDescrTag
+0x15-0x3F Reserved for ISO use
+0x40 ContentClassificationDescrTag
+0x41 KeyWordDescrTag
+0x42 RatingDescrTag
+0x43 LanguageDescrTag
+0x44 ShortTextualDescrTag
+0x45 ExpandedTextualDescrTag
+0x46 ContentCreatorNameDescrTag
+0x47 ContentCreationDateDescrTag
+0x48 OCICreatorNameDescrTag
+0x49 OCICreationDateDescrTag
+0x4A SmpteCameraPositionDescrTag
+0x4B SegmentDescrTag
+0x4C MediaTimeDescrTag
+0x4D-0x5F Reserved for ISO use (OCI extensions)
+0x60 IPMP_ToolsListDescrTag
+0x61 IPMP_ToolTag
+0x62 M4MuxTimingDescrTag
+0x63 M4MuxCodeTableDescrTag
+0x64 ExtSLConfigDescrTag
+0x65 M4MuxBufferSizeDescrTag
+0x66 M4MuxIdentDescrTag
+0x67 DependencyPointerTag
+0x68 DependencyMarkerTag
+0x69 M4MuxChannelDescrTag
+0x6A-0xBF Reserved for ISO use
+0xC0-0xFE User private
+0xFF Forbidden
+ */
+
+/* objectTypeIndication as of 14496-1
+0x00 Forbidden
+0x01 Systems ISO/IEC 14496-1 a
+0x02 Systems ISO/IEC 14496-1 b
+0x03 Interaction Stream
+0x04 Systems ISO/IEC 14496-1 Extended BIFS Configuration c
+0x05 Systems ISO/IEC 14496-1 AFX d
+0x06 Font Data Stream
+0x07 Synthesized Texture Stream
+0x08 Streaming Text Stream
+0x09-0x1F reserved for ISO use
+0x20 Visual ISO/IEC 14496-2 e
+0x21 Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10 f
+0x22 Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10 f
+0x23-0x3F reserved for ISO use
+0x40 Audio ISO/IEC 14496-3 g
+0x41-0x5F reserved for ISO use
+0x60 Visual ISO/IEC 13818-2 Simple Profile
+0x61 Visual ISO/IEC 13818-2 Main Profile
+0x62 Visual ISO/IEC 13818-2 SNR Profile
+0x63 Visual ISO/IEC 13818-2 Spatial Profile
+0x64 Visual ISO/IEC 13818-2 High Profile
+0x65 Visual ISO/IEC 13818-2 422 Profile
+0x66 Audio ISO/IEC 13818-7 Main Profile
+0x67 Audio ISO/IEC 13818-7 LowComplexity Profile
+0x68 Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile
+0x69 Audio ISO/IEC 13818-3
+0x6A Visual ISO/IEC 11172-2
+0x6B Audio ISO/IEC 11172-3
+0x6C Visual ISO/IEC 10918-1
+0x6D reserved for registration authority
+0x6E Visual ISO/IEC 15444-1
+0x6F - 0x9F reserved for ISO use
+0xA0 - 0xBF reserved for registration authority i
+0xC0 - 0xE0 user private
+0xE1 reserved for registration authority i
+0xE2 - 0xFE user private
+0xFF no object type specified h
+ */
+public class ObjectDescriptorFactory {
+ protected static Logger log = Logger.getLogger(ObjectDescriptorFactory.class.getName());
+
+ protected static Map<Integer, Map<Integer, Class<? extends BaseDescriptor>>> descriptorRegistry = new HashMap<Integer, Map<Integer, Class<? extends BaseDescriptor>>>();
+
+ static {
+ Set<Class<? extends BaseDescriptor>> annotated = new HashSet<Class<? extends BaseDescriptor>>();
+
+ annotated.add(DecoderSpecificInfo.class);
+ annotated.add(SLConfigDescriptor.class);
+ annotated.add(BaseDescriptor.class);
+ annotated.add(ExtensionDescriptor.class);
+ annotated.add(ObjectDescriptorBase.class);
+ annotated.add(ProfileLevelIndicationDescriptor.class);
+ annotated.add(AudioSpecificConfig.class);
+ annotated.add(ExtensionProfileLevelDescriptor.class);
+ annotated.add(ESDescriptor.class);
+ annotated.add(DecoderConfigDescriptor.class);
+ //annotated.add(ObjectDescriptor.class);
+
+ for (Class<? extends BaseDescriptor> clazz : annotated) {
+ final Descriptor descriptor = clazz.getAnnotation(Descriptor.class);
+ final int[] tags = descriptor.tags();
+ final int objectTypeInd = descriptor.objectTypeIndication();
+
+ Map<Integer, Class<? extends BaseDescriptor>> tagMap = descriptorRegistry.get(objectTypeInd);
+ if (tagMap == null) {
+ tagMap = new HashMap<Integer, Class<? extends BaseDescriptor>>();
+ }
+ for (int tag : tags) {
+ tagMap.put(tag, clazz);
+ }
+ descriptorRegistry.put(objectTypeInd, tagMap);
+ }
+ }
+
+ public static BaseDescriptor createFrom(int objectTypeIndication, ByteBuffer bb) throws IOException {
+ int tag = IsoTypeReader.readUInt8(bb);
+
+ Map<Integer, Class<? extends BaseDescriptor>> tagMap = descriptorRegistry.get(objectTypeIndication);
+ if (tagMap == null) {
+ tagMap = descriptorRegistry.get(-1);
+ }
+ Class<? extends BaseDescriptor> aClass = tagMap.get(tag);
+
+// if (tag == 0x00) {
+// log.warning("Found illegal tag 0x00! objectTypeIndication " + Integer.toHexString(objectTypeIndication) +
+// " and tag " + Integer.toHexString(tag) + " using: " + aClass);
+// aClass = BaseDescriptor.class;
+// }
+
+ BaseDescriptor baseDescriptor;
+ if (aClass == null || aClass.isInterface() || Modifier.isAbstract(aClass.getModifiers())) {
+ log.warning("No ObjectDescriptor found for objectTypeIndication " + Integer.toHexString(objectTypeIndication) +
+ " and tag " + Integer.toHexString(tag) + " found: " + aClass);
+ baseDescriptor = new UnknownDescriptor();
+ } else {
+ try {
+ baseDescriptor = aClass.newInstance();
+ } catch (Exception e) {
+ log.log(Level.SEVERE, "Couldn't instantiate BaseDescriptor class " + aClass + " for objectTypeIndication " + objectTypeIndication + " and tag " + tag, e);
+ throw new RuntimeException(e);
+ }
+ }
+ baseDescriptor.parse(tag, bb);
+ return baseDescriptor;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ProfileLevelIndicationDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ProfileLevelIndicationDescriptor.java.svn-base
new file mode 100644
index 0000000..625277e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/ProfileLevelIndicationDescriptor.java.svn-base
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * class ProfileLevelIndicationIndexDescriptor () extends BaseDescriptor
+ * : bit(8) ProfileLevelIndicationIndexDescrTag {
+ * bit(8) profileLevelIndicationIndex;
+ * }
+ */
+@Descriptor(tags = 0x14)
+public class ProfileLevelIndicationDescriptor extends BaseDescriptor {
+ int profileLevelIndicationIndex;
+
+ @Override
+ public void parseDetail( ByteBuffer bb) throws IOException {
+ profileLevelIndicationIndex = IsoTypeReader.readUInt8(bb);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ProfileLevelIndicationDescriptor");
+ sb.append("{profileLevelIndicationIndex=").append(Integer.toHexString(profileLevelIndicationIndex));
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ProfileLevelIndicationDescriptor that = (ProfileLevelIndicationDescriptor) o;
+
+ if (profileLevelIndicationIndex != that.profileLevelIndicationIndex) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return profileLevelIndicationIndex;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/SLConfigDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/SLConfigDescriptor.java.svn-base
new file mode 100644
index 0000000..70a58e6
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/SLConfigDescriptor.java.svn-base
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * class SLConfigDescriptor extends BaseDescriptor : bit(8) tag=SLConfigDescrTag {
+ * bit(8) predefined;
+ * if (predefined==0) {
+ * bit(1) useAccessUnitStartFlag;
+ * bit(1) useAccessUnitEndFlag;
+ * bit(1) useRandomAccessPointFlag;
+ * bit(1) hasRandomAccessUnitsOnlyFlag;
+ * bit(1) usePaddingFlag;
+ * bit(1) useTimeStampsFlag;
+ * bit(1) useIdleFlag;
+ * bit(1) durationFlag;
+ * bit(32) timeStampResolution;
+ * bit(32) OCRResolution;
+ * bit(8) timeStampLength; // must be ≤ 64
+ * bit(8) OCRLength; // must be ≤ 64
+ * bit(8) AU_Length; // must be ≤ 32
+ * bit(8) instantBitrateLength;
+ * bit(4) degradationPriorityLength;
+ * bit(5) AU_seqNumLength; // must be ≤ 16
+ * bit(5) packetSeqNumLength; // must be ≤ 16
+ * bit(2) reserved=0b11;
+ * }
+ * if (durationFlag) {
+ * bit(32) timeScale;
+ * bit(16) accessUnitDuration;
+ * bit(16) compositionUnitDuration;
+ * }
+ * if (!useTimeStampsFlag) {
+ * bit(timeStampLength) startDecodingTimeStamp;
+ * bit(timeStampLength) startCompositionTimeStamp;
+ * }
+ * }
+ */
+@Descriptor(tags = {0x06})
+public class SLConfigDescriptor extends BaseDescriptor {
+ int predefined;
+
+ public int getPredefined() {
+ return predefined;
+ }
+
+ public void setPredefined(int predefined) {
+ this.predefined = predefined;
+ }
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ predefined = IsoTypeReader.readUInt8(bb);
+ }
+
+ public int serializedSize() {
+ return 3;
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.allocate(3);
+ IsoTypeWriter.writeUInt8(out, 6);
+ IsoTypeWriter.writeUInt8(out, 1);
+ IsoTypeWriter.writeUInt8(out, predefined);
+ return out;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("SLConfigDescriptor");
+ sb.append("{predefined=").append(predefined);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ SLConfigDescriptor that = (SLConfigDescriptor) o;
+
+ if (predefined != that.predefined) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return predefined;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/UnknownDescriptor.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/UnknownDescriptor.java.svn-base
new file mode 100644
index 0000000..dd75a0f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/.svn/text-base/UnknownDescriptor.java.svn-base
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Logger;
+
+public class UnknownDescriptor extends BaseDescriptor {
+ private ByteBuffer data;
+ private static Logger log = Logger.getLogger(UnknownDescriptor.class.getName());
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ data = (ByteBuffer) bb.slice().limit(this.getSizeOfInstance());
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("UnknownDescriptor");
+ sb.append("{tag=").append(tag);
+ sb.append(", sizeOfInstance=").append(sizeOfInstance);
+ sb.append(", data=").append(data);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/AudioSpecificConfig.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/AudioSpecificConfig.java
new file mode 100644
index 0000000..86e319e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/AudioSpecificConfig.java
@@ -0,0 +1,1176 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+//
+//GetAudioObjectType()
+//{
+//audioObjectType; 5 uimsbf
+//if (audioObjectType == 31) {
+//audioObjectType = 32 + audioObjectTypeExt; 6 uimsbf
+//}
+//return audioObjectType;
+//}
+//AudioSpecificConfig ()
+//{
+//audioObjectType = GetAudioObjectType();
+//samplingFrequencyIndex; 4 bslbf
+//if ( samplingFrequencyIndex == 0xf ) {
+//samplingFrequency; 24 uimsbf
+//}
+//channelConfiguration; 4 bslbf
+//sbrPresentFlag = -1;
+//psPresentFlag = -1;
+//if ( audioObjectType == 5 ||
+//audioObjectType == 29 ) {
+//extensionAudioObjectType = 5;
+//sbrPresentFlag = 1;
+//if ( audioObjectType == 29 ) {
+//psPresentFlag = 1;
+//}
+//extensionSamplingFrequencyIndex; 4 uimsbf
+//if ( extensionSamplingFrequencyIndex == 0xf )
+//extensionSamplingFrequency; 24 uimsbf
+//audioObjectType = GetAudioObjectType();
+//if ( audioObjectType == 22 )
+//extensionChannelConfiguration; 4 uimsbf
+//}
+//else {
+//extensionAudioObjectType = 0;
+//}
+//switch (audioObjectType) {
+//case 1:
+//case 2:
+//case 3:
+//case 4:
+//case 6:
+//case 7:
+//case 17:
+//case 19:
+//case 20:
+//case 21:
+//case 22:
+//case 23:
+//GASpecificConfig();
+//break:
+//case 8:
+//CelpSpecificConfig();
+//break;
+//case 9:
+//HvxcSpecificConfig();
+//break:
+//case 12:
+//TTSSpecificConfig();
+//break;
+//case 13:
+//case 14:
+//case 15:
+//case 16:
+//StructuredAudioSpecificConfig();
+//break;
+//case 24:
+//ErrorResilientCelpSpecificConfig();
+//break;
+//case 25:
+//ErrorResilientHvxcSpecificConfig();
+//break;
+//case 26:
+//case 27:
+//ParametricSpecificConfig();
+//break;
+// case 28:
+//SSCSpecificConfig();
+//break;
+//case 30:
+//sacPayloadEmbedding; 1 uimsbf
+//SpatialSpecificConfig();
+//break;
+//case 32:
+//case 33:
+//case 34:
+//MPEG_1_2_SpecificConfig();
+//break;
+//case 35:
+//DSTSpecificConfig();
+//break;
+//case 36:
+//fillBits; 5 bslbf
+//ALSSpecificConfig();
+//break;
+//case 37:
+//case 38:
+//SLSSpecificConfig();
+//break;
+//case 39:
+//ELDSpecificConfig(channelConfiguration);
+//break:
+//case 40:
+//case 41:
+//SymbolicMusicSpecificConfig();
+//break;
+//default:
+///* reserved */
+//}
+//switch (audioObjectType) {
+//case 17:
+//case 19:
+//case 20:
+//case 21:
+//case 22:
+//case 23:
+//case 24:
+//case 25:
+//case 26:
+//case 27:
+//case 39:
+//epConfig; 2 bslbf
+//if ( epConfig == 2 || epConfig == 3 ) {
+//ErrorProtectionSpecificConfig();
+//}
+//if ( epConfig == 3 ) {
+//directMapping; 1 bslbf
+//if ( ! directMapping ) {
+///* tbd */
+//}
+//}
+//}
+//if ( extensionAudioObjectType != 5 && bits_to_decode() >= 16 ) {
+//syncExtensionType; 11 bslbf
+//if (syncExtensionType == 0x2b7) {
+// extensionAudioObjectType = GetAudioObjectType();
+//if ( extensionAudioObjectType == 5 ) {
+//sbrPresentFlag; 1 uimsbf
+//if (sbrPresentFlag == 1) {
+//extensionSamplingFrequencyIndex; 4 uimsbf
+//if ( extensionSamplingFrequencyIndex == 0xf ) {
+//extensionSamplingFrequency; 24 uimsbf
+//}
+//if ( bits_to_decode() >= 12 ) {
+//syncExtensionType; 11 bslbf
+//if (syncExtesionType == 0x548) {
+//psPresentFlag; 1 uimsbf
+//}
+//}
+//}
+//}
+//if ( extensionAudioObjectType == 22 ) {
+//sbrPresentFlag; 1 uimsbf
+//if (sbrPresentFlag == 1) {
+//extensionSamplingFrequencyIndex; 4 uimsbf
+//if ( extensionSamplingFrequencyIndex == 0xf ) {
+//extensionSamplingFrequency; 24 uimsbf
+//}
+//}
+//extensionChannelConfiguration; 4 uimsbf
+//}
+//}
+//}
+//}
+// }
+//
+// TFCodingType
+//0x0 AAC scaleable
+//0x1 BSAC
+//0x2 TwinVQ
+//0x3 AAC non scaleable (i.e. multichannel)
+//
+// class TFSpecificConfig( uint(4) samplingFrequencyIndex, uint(4) channelConfiguration ) {
+//uint(2) TFCodingType;
+//uint(1) frameLength;
+//uint(1) dependsOnCoreCoder;
+//if (dependsOnCoreCoder == 1){
+//uint(14)coreCoderDelay
+//}
+//if (TFCodingType==BSAC) {
+//uint(11) lslayer_length
+//}
+//uint (1) extensionFlag;
+//if (channelConfiguration == 0 ){
+//program_config_element();
+//}
+//if (extensionFlag==1){
+//<to be defined in mpeg4 phase 2>
+//}
+//}
+//
+//program_config_element()
+//{
+//element_instance_tag 4 uimsbf
+//profile 2 uimsbf
+//sampling_frequency_index 4 uimsbf
+//num_front_channel_elements 4 uimsbf
+//num_side_channel_elements 4 uimsbf
+//num_back_channel_elements 4 uimsbf
+// num_lfe_channel_elements 2 uimsbf
+//num_assoc_data_elements 3 uimsbf
+//num_valid_cc_elements 4 uimsbf
+//mono_mixdown_present 1 uimsbf
+//if ( mono_mixdown_present == 1 )
+//mono_mixdown_element_number 4 uimsbf
+//stereo_mixdown_present 1 uimsbf
+//if ( stereo_mixdown_present == 1 )
+//stereo_mixdown_element_number 4 uimsbf
+//matrix_mixdown_idx_present 1 uimsbf
+//if ( matrix_mixdown_idx_present == 1 ) {
+//matrix_mixdown_idx 2 uimsbf
+//pseudo_surround_enable 1 uimsbf
+//}
+//for ( i = 0; i < num_front_channel_elements; i++) {
+//front_element_is_cpe[i]; 1 bslbf
+//front_element_tag_select[i]; 4 uimsbf
+//}
+//for ( i = 0; i < num_side_channel_elements; i++) {
+//side_element_is_cpe[i]; 1 bslbf
+//side_element_tag_select[i]; 4 uimsbf
+//}
+//for ( i = 0; i < num_back_channel_elements; i++) {
+//back_element_is_cpe[i]; 1 bslbf
+//back_element_tag_select[i]; 4 uimsbf
+//}
+//for ( i = 0; i < num_lfe_channel_elements; i++)
+//lfe_element_tag_select[i]; 4 uimsbf
+//for ( i = 0; i < num_assoc_data_elements; i++)
+//assoc_data_element_tag_select[i]; 4 uimsbf
+//for ( i = 0; i < num_valid_cc_elements; i++) {
+//cc_element_is_ind_sw[i]; 1 uimsbf
+//valid_cc_element_tag_select[i]; 4 uimsbf
+//}
+//byte_alignment()
+//comment_field_bytes 8 uimsbf
+//for ( i = 0; i < comment_field_bytes; i++)
+//comment_field_data[i]; 8 uimsbf
+//}
+
+@Descriptor(tags = 0x5, objectTypeIndication = 0x40)
+public class AudioSpecificConfig extends BaseDescriptor {
+ byte[] configBytes;
+
+ public static Map<Integer, Integer> samplingFrequencyIndexMap = new HashMap<Integer, Integer>();
+ public static Map<Integer, String> audioObjectTypeMap = new HashMap<Integer, String>();
+ int audioObjectType;
+ int samplingFrequencyIndex;
+ int samplingFrequency;
+ int channelConfiguration;
+ int extensionAudioObjectType;
+ int sbrPresentFlag;
+ int psPresentFlag;
+ int extensionSamplingFrequencyIndex;
+ int extensionSamplingFrequency;
+ int extensionChannelConfiguration;
+ int sacPayloadEmbedding;
+ int fillBits;
+ int epConfig;
+ int directMapping;
+ int syncExtensionType;
+
+ //GASpecificConfig
+ int frameLengthFlag;
+ int dependsOnCoreCoder;
+ int coreCoderDelay;
+ int extensionFlag;
+ int layerNr;
+ int numOfSubFrame;
+ int layer_length;
+ int aacSectionDataResilienceFlag;
+ int aacScalefactorDataResilienceFlag;
+ int aacSpectralDataResilienceFlag;
+ int extensionFlag3;
+ boolean gaSpecificConfig;
+
+ //ParametricSpecificConfig
+ int isBaseLayer;
+ int paraMode;
+ int paraExtensionFlag;
+ int hvxcVarMode;
+ int hvxcRateMode;
+ int erHvxcExtensionFlag;
+ int var_ScalableFlag;
+ int hilnQuantMode;
+ int hilnMaxNumLine;
+ int hilnSampleRateCode;
+ int hilnFrameLength;
+ int hilnContMode;
+ int hilnEnhaLayer;
+ int hilnEnhaQuantMode;
+ boolean parametricSpecificConfig;
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ ByteBuffer configBytes = bb.slice();
+ configBytes.limit(sizeOfInstance);
+ bb.position(bb.position() + sizeOfInstance);
+
+ //copy original bytes to internal array for constructing codec config strings (todo until writing of the config is supported)
+ this.configBytes = new byte[sizeOfInstance];
+ configBytes.get(this.configBytes);
+ configBytes.rewind();
+
+ BitReaderBuffer bitReaderBuffer = new BitReaderBuffer(configBytes);
+ audioObjectType = getAudioObjectType(bitReaderBuffer);
+ samplingFrequencyIndex = bitReaderBuffer.readBits(4);
+
+ if (samplingFrequencyIndex == 0xf) {
+ samplingFrequency = bitReaderBuffer.readBits(24);
+ }
+
+ channelConfiguration = bitReaderBuffer.readBits(4);
+
+ if (audioObjectType == 5 ||
+ audioObjectType == 29) {
+ extensionAudioObjectType = 5;
+ sbrPresentFlag = 1;
+ if (audioObjectType == 29) {
+ psPresentFlag = 1;
+ }
+ extensionSamplingFrequencyIndex = bitReaderBuffer.readBits(4);
+ if (extensionSamplingFrequencyIndex == 0xf)
+ extensionSamplingFrequency = bitReaderBuffer.readBits(24);
+ audioObjectType = getAudioObjectType(bitReaderBuffer);
+ if (audioObjectType == 22)
+ extensionChannelConfiguration = bitReaderBuffer.readBits(4);
+ } else {
+ extensionAudioObjectType = 0;
+ }
+
+ switch (audioObjectType) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 6:
+ case 7:
+ case 17:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ parseGaSpecificConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, bitReaderBuffer);
+ //GASpecificConfig();
+ break;
+ case 8:
+ throw new UnsupportedOperationException("can't parse CelpSpecificConfig yet");
+ //CelpSpecificConfig();
+ //break;
+ case 9:
+ throw new UnsupportedOperationException("can't parse HvxcSpecificConfig yet");
+ //HvxcSpecificConfig();
+ //break;
+ case 12:
+ throw new UnsupportedOperationException("can't parse TTSSpecificConfig yet");
+ //TTSSpecificConfig();
+ //break;
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ throw new UnsupportedOperationException("can't parse StructuredAudioSpecificConfig yet");
+ //StructuredAudioSpecificConfig();
+ //break;
+ case 24:
+ throw new UnsupportedOperationException("can't parse ErrorResilientCelpSpecificConfig yet");
+ //ErrorResilientCelpSpecificConfig();
+ //break;
+ case 25:
+ throw new UnsupportedOperationException("can't parse ErrorResilientHvxcSpecificConfig yet");
+ //ErrorResilientHvxcSpecificConfig();
+ //break;
+ case 26:
+ case 27:
+ parseParametricSpecificConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, bitReaderBuffer);
+ //ParametricSpecificConfig();
+ break;
+ case 28:
+ throw new UnsupportedOperationException("can't parse SSCSpecificConfig yet");
+ //SSCSpecificConfig();
+ //break;
+ case 30:
+ sacPayloadEmbedding = bitReaderBuffer.readBits(1);
+ throw new UnsupportedOperationException("can't parse SpatialSpecificConfig yet");
+ //SpatialSpecificConfig();
+ //break;
+ case 32:
+ case 33:
+ case 34:
+ throw new UnsupportedOperationException("can't parse MPEG_1_2_SpecificConfig yet");
+ //MPEG_1_2_SpecificConfig();
+ //break;
+ case 35:
+ throw new UnsupportedOperationException("can't parse DSTSpecificConfig yet");
+ //DSTSpecificConfig();
+ //break;
+ case 36:
+ fillBits = bitReaderBuffer.readBits(5);
+ throw new UnsupportedOperationException("can't parse ALSSpecificConfig yet");
+ //ALSSpecificConfig();
+ //break;
+ case 37:
+ case 38:
+ throw new UnsupportedOperationException("can't parse SLSSpecificConfig yet");
+ //SLSSpecificConfig();
+ //break;
+ case 39:
+ throw new UnsupportedOperationException("can't parse ELDSpecificConfig yet");
+ //ELDSpecificConfig(channelConfiguration);
+ //break;
+ case 40:
+ case 41:
+ throw new UnsupportedOperationException("can't parse SymbolicMusicSpecificConfig yet");
+ //SymbolicMusicSpecificConfig();
+ //break;
+ default:
+ /* reserved */
+ }
+
+ switch (audioObjectType) {
+ case 17:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 39:
+ epConfig = bitReaderBuffer.readBits(2);
+ if (epConfig == 2 || epConfig == 3) {
+ throw new UnsupportedOperationException("can't parse ErrorProtectionSpecificConfig yet");
+ //ErrorProtectionSpecificConfig();
+ }
+ if (epConfig == 3) {
+ directMapping = bitReaderBuffer.readBits(1);
+ if (directMapping == 0) {
+ /* tbd */
+ throw new RuntimeException("not implemented");
+ }
+ }
+ }
+
+ if (extensionAudioObjectType != 5 && bitReaderBuffer.remainingBits() >= 16) {
+ syncExtensionType = bitReaderBuffer.readBits(11);
+ if (syncExtensionType == 0x2b7) {
+ extensionAudioObjectType = getAudioObjectType(bitReaderBuffer);
+ if (extensionAudioObjectType == 5) {
+ sbrPresentFlag = bitReaderBuffer.readBits(1);
+ if (sbrPresentFlag == 1) {
+ extensionSamplingFrequencyIndex = bitReaderBuffer.readBits(4);
+ if (extensionSamplingFrequencyIndex == 0xf) {
+ extensionSamplingFrequency = bitReaderBuffer.readBits(24);
+ }
+ if (bitReaderBuffer.remainingBits() >= 12) {
+ syncExtensionType = bitReaderBuffer.readBits(11); //10101001000
+ if (syncExtensionType == 0x548) {
+ psPresentFlag = bitReaderBuffer.readBits(1);
+ }
+ }
+ }
+ }
+ if (extensionAudioObjectType == 22) {
+ sbrPresentFlag = bitReaderBuffer.readBits(1);
+ if (sbrPresentFlag == 1) {
+ extensionSamplingFrequencyIndex = bitReaderBuffer.readBits(4);
+ if (extensionSamplingFrequencyIndex == 0xf) {
+ extensionSamplingFrequency = bitReaderBuffer.readBits(24);
+ }
+ }
+ extensionChannelConfiguration = bitReaderBuffer.readBits(4);
+ }
+ }
+ }
+ }
+
+ private int gaSpecificConfigSize() {
+ return 0;
+ }
+
+ public int serializedSize() {
+ int out = 4;
+ if (audioObjectType == 2) {
+ out += gaSpecificConfigSize();
+ } else {
+ throw new UnsupportedOperationException("can't serialize that yet");
+ }
+ return out;
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.allocate(serializedSize());
+ IsoTypeWriter.writeUInt8(out, 5);
+ IsoTypeWriter.writeUInt8(out, serializedSize() - 2);
+ BitWriterBuffer bwb = new BitWriterBuffer(out);
+ bwb.writeBits(audioObjectType, 5);
+ bwb.writeBits(samplingFrequencyIndex, 4);
+ if (samplingFrequencyIndex == 0xf) {
+ throw new UnsupportedOperationException("can't serialize that yet");
+ }
+ bwb.writeBits(channelConfiguration, 4);
+
+ // Don't support any extensions, unusual GASpecificConfig other than the default or anything...
+
+ return out;
+ }
+
+ private int getAudioObjectType(BitReaderBuffer in) throws IOException {
+ int audioObjectType = in.readBits(5);
+ if (audioObjectType == 31) {
+ audioObjectType = 32 + in.readBits(6);
+ }
+ return audioObjectType;
+ }
+
+ private void parseGaSpecificConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+// GASpecificConfig (samplingFrequencyIndex,
+// channelConfiguration,
+// audioObjectType)
+// {
+ frameLengthFlag = in.readBits(1);
+ dependsOnCoreCoder = in.readBits(1);
+ if (dependsOnCoreCoder == 1) {
+ coreCoderDelay = in.readBits(14);
+ }
+ extensionFlag = in.readBits(1);
+ if (channelConfiguration == 0) {
+ throw new UnsupportedOperationException("can't parse program_config_element yet");
+ //program_config_element ();
+ }
+ if ((audioObjectType == 6) || (audioObjectType == 20)) {
+ layerNr = in.readBits(3);
+ }
+ if (extensionFlag == 1) {
+ if (audioObjectType == 22) {
+ numOfSubFrame = in.readBits(5);
+ layer_length = in.readBits(11);
+ }
+ if (audioObjectType == 17 || audioObjectType == 19 ||
+ audioObjectType == 20 || audioObjectType == 23) {
+ aacSectionDataResilienceFlag = in.readBits(1);
+ aacScalefactorDataResilienceFlag = in.readBits(1);
+ aacSpectralDataResilienceFlag = in.readBits(1);
+ }
+ extensionFlag3 = in.readBits(1);
+ if (extensionFlag3 == 1) {
+ /* tbd in version 3 */
+ }
+ }
+// }
+ gaSpecificConfig = true;
+ }
+
+ private void parseParametricSpecificConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ ParametricSpecificConfig() {
+ isBaseLayer; 1 uimsbf
+ if (isBaseLayer) {
+ PARAconfig();
+ } else {
+ HILNenexConfig();
+ }
+ }
+ */
+ isBaseLayer = in.readBits(1);
+ if (isBaseLayer == 1) {
+ parseParaConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, in);
+ } else {
+ parseHilnEnexConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, in);
+ }
+ }
+
+ private void parseParaConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ PARAconfig()
+ {
+ PARAmode; 2 uimsbf
+ if (PARAmode != 1) {
+ ErHVXCconfig();
+ }
+ if (PARAmode != 0) {
+ HILNconfig();
+ }
+ PARAextensionFlag; 1 uimsbf
+ if (PARAextensionFlag) {
+ // to be defined in MPEG-4 Phase 3
+ }
+ }
+ */
+ paraMode = in.readBits(2);
+
+ if (paraMode != 1) {
+ parseErHvxcConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, in);
+ }
+ if (paraMode != 0) {
+ parseHilnConfig(samplingFrequencyIndex, channelConfiguration, audioObjectType, in);
+ }
+
+ paraExtensionFlag = in.readBits(1);
+ parametricSpecificConfig = true;
+ }
+
+ private void parseErHvxcConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ ErHVXCconfig()
+ {
+ HVXCvarMode; 1 uimsbf
+ HVXCrateMode; 2 uimsbf
+ extensionFlag; 1 uimsbf
+ if (extensionFlag) {
+ var_ScalableFlag; 1 uimsbf
+ }
+ }
+ */
+ hvxcVarMode = in.readBits(1);
+ hvxcRateMode = in.readBits(2);
+ erHvxcExtensionFlag = in.readBits(1);
+
+ if (erHvxcExtensionFlag == 1) {
+ var_ScalableFlag = in.readBits(1);
+ }
+ }
+
+ private void parseHilnConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ HILNconfig()
+ {
+ HILNquantMode; 1 uimsbf
+ HILNmaxNumLine; 8 uimsbf
+ HILNsampleRateCode; 4 uimsbf
+ HILNframeLength; 12 uimsbf
+ HILNcontMode; 2 uimsbf
+ }
+ */
+ hilnQuantMode = in.readBits(1);
+ hilnMaxNumLine = in.readBits(8);
+ hilnSampleRateCode = in.readBits(4);
+ hilnFrameLength = in.readBits(12);
+ hilnContMode = in.readBits(2);
+ }
+
+ private void parseHilnEnexConfig(int samplingFrequencyIndex, int channelConfiguration, int audioObjectType, BitReaderBuffer in) throws IOException {
+ /*
+ HILNenexConfig()
+ {
+ HILNenhaLayer; 1 uimsbf
+ if (HILNenhaLayer) {
+ HILNenhaQuantMode; 2 uimsbf
+ }
+ }
+ */
+ hilnEnhaLayer = in.readBits(1);
+ if (hilnEnhaLayer == 1) {
+ hilnEnhaQuantMode = in.readBits(2);
+ }
+ }
+
+ public byte[] getConfigBytes() {
+ return configBytes;
+ }
+
+ public int getAudioObjectType() {
+ return audioObjectType;
+ }
+
+ public int getExtensionAudioObjectType() {
+ return extensionAudioObjectType;
+ }
+
+ public int getSbrPresentFlag() {
+ return sbrPresentFlag;
+ }
+
+ public int getPsPresentFlag() {
+ return psPresentFlag;
+ }
+
+ public void setAudioObjectType(int audioObjectType) {
+ this.audioObjectType = audioObjectType;
+ }
+
+ public void setSamplingFrequencyIndex(int samplingFrequencyIndex) {
+ this.samplingFrequencyIndex = samplingFrequencyIndex;
+ }
+
+ public void setSamplingFrequency(int samplingFrequency) {
+ this.samplingFrequency = samplingFrequency;
+ }
+
+ public void setChannelConfiguration(int channelConfiguration) {
+ this.channelConfiguration = channelConfiguration;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("AudioSpecificConfig");
+ sb.append("{configBytes=").append(Hex.encodeHex(configBytes));
+ sb.append(", audioObjectType=").append(audioObjectType).append(" (").append(audioObjectTypeMap.get(audioObjectType)).append(")");
+ sb.append(", samplingFrequencyIndex=").append(samplingFrequencyIndex).append(" (").append(samplingFrequencyIndexMap.get(samplingFrequencyIndex)).append(")");
+ sb.append(", samplingFrequency=").append(samplingFrequency);
+ sb.append(", channelConfiguration=").append(channelConfiguration);
+ if (extensionAudioObjectType > 0) {
+ sb.append(", extensionAudioObjectType=").append(extensionAudioObjectType).append(" (").append(audioObjectTypeMap.get(extensionAudioObjectType)).append(")");
+ sb.append(", sbrPresentFlag=").append(sbrPresentFlag);
+ sb.append(", psPresentFlag=").append(psPresentFlag);
+ sb.append(", extensionSamplingFrequencyIndex=").append(extensionSamplingFrequencyIndex).append(" (").append(samplingFrequencyIndexMap.get(extensionSamplingFrequencyIndex)).append(")");
+ sb.append(", extensionSamplingFrequency=").append(extensionSamplingFrequency);
+ sb.append(", extensionChannelConfiguration=").append(extensionChannelConfiguration);
+ }
+// sb.append(", sacPayloadEmbedding=").append(sacPayloadEmbedding);
+// sb.append(", fillBits=").append(fillBits);
+// sb.append(", epConfig=").append(epConfig);
+// sb.append(", directMapping=").append(directMapping);
+ sb.append(", syncExtensionType=").append(syncExtensionType);
+ if (gaSpecificConfig) {
+ sb.append(", frameLengthFlag=").append(frameLengthFlag);
+ sb.append(", dependsOnCoreCoder=").append(dependsOnCoreCoder);
+ sb.append(", coreCoderDelay=").append(coreCoderDelay);
+ sb.append(", extensionFlag=").append(extensionFlag);
+ sb.append(", layerNr=").append(layerNr);
+ sb.append(", numOfSubFrame=").append(numOfSubFrame);
+ sb.append(", layer_length=").append(layer_length);
+ sb.append(", aacSectionDataResilienceFlag=").append(aacSectionDataResilienceFlag);
+ sb.append(", aacScalefactorDataResilienceFlag=").append(aacScalefactorDataResilienceFlag);
+ sb.append(", aacSpectralDataResilienceFlag=").append(aacSpectralDataResilienceFlag);
+ sb.append(", extensionFlag3=").append(extensionFlag3);
+ }
+ if (parametricSpecificConfig) {
+ sb.append(", isBaseLayer=").append(isBaseLayer);
+ sb.append(", paraMode=").append(paraMode);
+ sb.append(", paraExtensionFlag=").append(paraExtensionFlag);
+ sb.append(", hvxcVarMode=").append(hvxcVarMode);
+ sb.append(", hvxcRateMode=").append(hvxcRateMode);
+ sb.append(", erHvxcExtensionFlag=").append(erHvxcExtensionFlag);
+ sb.append(", var_ScalableFlag=").append(var_ScalableFlag);
+ sb.append(", hilnQuantMode=").append(hilnQuantMode);
+ sb.append(", hilnMaxNumLine=").append(hilnMaxNumLine);
+ sb.append(", hilnSampleRateCode=").append(hilnSampleRateCode);
+ sb.append(", hilnFrameLength=").append(hilnFrameLength);
+ sb.append(", hilnContMode=").append(hilnContMode);
+ sb.append(", hilnEnhaLayer=").append(hilnEnhaLayer);
+ sb.append(", hilnEnhaQuantMode=").append(hilnEnhaQuantMode);
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ static {
+ // sampling_frequency_index sampling frequeny
+//0x0 96000
+//0x1 88200
+//0x2 64000
+//0x3 48000
+//0x4 44100
+//0x5 32000
+//0x6 24000
+//0x7 22050
+//0x8 16000
+//0x9 12000
+//0xa 11025
+//0xb 8000
+//0xc reserved
+//0xd reserved
+//0xe reserved
+//0xf reserved
+ samplingFrequencyIndexMap.put(0x0, 96000);
+ samplingFrequencyIndexMap.put(0x1, 88200);
+ samplingFrequencyIndexMap.put(0x2, 64000);
+ samplingFrequencyIndexMap.put(0x3, 48000);
+ samplingFrequencyIndexMap.put(0x4, 44100);
+ samplingFrequencyIndexMap.put(0x5, 32000);
+ samplingFrequencyIndexMap.put(0x6, 24000);
+ samplingFrequencyIndexMap.put(0x7, 22050);
+ samplingFrequencyIndexMap.put(0x8, 16000);
+ samplingFrequencyIndexMap.put(0x9, 12000);
+ samplingFrequencyIndexMap.put(0xa, 11025);
+ samplingFrequencyIndexMap.put(0xb, 8000);
+
+ /* audioObjectType IDs
+ 0 Null
+ 1 AAC main X X
+ 2 AAC LC X X X X X X X
+ 3 AAC SSR X X
+ 4 AAC LTP X X X X
+ 5 SBR X X
+ 6 AAC Scalable X X X X
+ 7 TwinVQ X X X
+ 8 CELP X X X X X X
+ 9 HVXC X X X X X
+ 10 (reserved)
+ 11 (reserved)
+ 12 TTSI X X X X X X
+ 13 Main synthetic X X
+ 14 Wavetable synthesis X* X*
+ 15 General MIDI X* X*
+ 16 Algorithmic Synthesis and Audio FX X* X*
+ 17 ER AAC LC X X X
+ 18 (reserved)
+ 19 ER AAC LTP X X
+ 20 ER AAC Scalable X X X
+ 21 ER TwinVQ X X
+ 22 ER BSAC X X
+ 23 ER AAC LD X X X X
+ 24 ER CELP X X X
+ 25 ER HVXC X X
+ 26 ER HILN X
+ 27 ER Parametric X
+ 28 SSC
+ 29 PS X
+ 30 MPEG Surround
+ 31 (escape)
+ 32 Layer-1
+ 33 Layer-2
+ 34 Layer-3
+ 35 DST
+ 36 ALS
+ 37 SLS
+ 38 SLS non-core
+ 39 ER AAC ELD
+ 40 SMR Simple
+ 41 SMR Main
+ */
+ audioObjectTypeMap.put(1, "AAC main");
+ audioObjectTypeMap.put(2, "AAC LC");
+ audioObjectTypeMap.put(3, "AAC SSR");
+ audioObjectTypeMap.put(4, "AAC LTP");
+ audioObjectTypeMap.put(5, "SBR");
+ audioObjectTypeMap.put(6, "AAC Scalable");
+ audioObjectTypeMap.put(7, "TwinVQ");
+ audioObjectTypeMap.put(8, "CELP");
+ audioObjectTypeMap.put(9, "HVXC");
+ audioObjectTypeMap.put(10, "(reserved)");
+ audioObjectTypeMap.put(11, "(reserved)");
+ audioObjectTypeMap.put(12, "TTSI");
+ audioObjectTypeMap.put(13, "Main synthetic");
+ audioObjectTypeMap.put(14, "Wavetable synthesis");
+ audioObjectTypeMap.put(15, "General MIDI");
+ audioObjectTypeMap.put(16, "Algorithmic Synthesis and Audio FX");
+ audioObjectTypeMap.put(17, "ER AAC LC");
+ audioObjectTypeMap.put(18, "(reserved)");
+ audioObjectTypeMap.put(19, "ER AAC LTP");
+ audioObjectTypeMap.put(20, "ER AAC Scalable");
+ audioObjectTypeMap.put(21, "ER TwinVQ");
+ audioObjectTypeMap.put(22, "ER BSAC");
+ audioObjectTypeMap.put(23, "ER AAC LD");
+ audioObjectTypeMap.put(24, "ER CELP");
+ audioObjectTypeMap.put(25, "ER HVXC");
+ audioObjectTypeMap.put(26, "ER HILN");
+ audioObjectTypeMap.put(27, "ER Parametric");
+ audioObjectTypeMap.put(28, "SSC");
+ audioObjectTypeMap.put(29, "PS");
+ audioObjectTypeMap.put(30, "MPEG Surround");
+ audioObjectTypeMap.put(31, "(escape)");
+ audioObjectTypeMap.put(32, "Layer-1");
+ audioObjectTypeMap.put(33, "Layer-2");
+ audioObjectTypeMap.put(34, "Layer-3");
+ audioObjectTypeMap.put(35, "DST");
+ audioObjectTypeMap.put(36, "ALS");
+ audioObjectTypeMap.put(37, "SLS");
+ audioObjectTypeMap.put(38, "SLS non-core");
+ audioObjectTypeMap.put(39, "ER AAC ELD");
+ audioObjectTypeMap.put(40, "SMR Simple");
+ audioObjectTypeMap.put(41, "SMR Main");
+
+ /* profileLevelIds
+ 0x00 Reserved for ISO use -
+ 0x01 Main Audio Profile L1
+ 0x02 Main Audio Profile L2
+ 0x03 Main Audio Profile L3
+ 0x04 Main Audio Profile L4
+ 0x05 Scalable Audio Profile L1
+ 0x06 Scalable Audio Profile L2
+ 0x07 Scalable Audio Profile L3
+ 0x08 Scalable Audio Profile L4
+ 0x09 Speech Audio Profile L1
+ 0x0A Speech Audio Profile L2
+ 0x0B Synthetic Audio Profile L1
+ 0x0C Synthetic Audio Profile L2
+ 0x0D Synthetic Audio Profile L3
+ 0x0E High Quality Audio Profile L1
+ 0x0F High Quality Audio Profile L2
+ 0x10 High Quality Audio Profile L3
+ 0x11 High Quality Audio Profile L4
+ 0x12 High Quality Audio Profile L5
+ 0x13 High Quality Audio Profile L6
+ 0x14 High Quality Audio Profile L7
+ 0x15 High Quality Audio Profile L8
+ 0x16 Low Delay Audio Profile L1
+ 0x17 Low Delay Audio Profile L2
+ 0x18 Low Delay Audio Profile L3
+ 0x19 Low Delay Audio Profile L4
+ 0x1A Low Delay Audio Profile L5
+ 0x1B Low Delay Audio Profile L6
+ 0x1C Low Delay Audio Profile L7
+ 0x1D Low Delay Audio Profile L8
+ 0x1E Natural Audio Profile L1
+ 0x1F Natural Audio Profile L2
+ 0x20 Natural Audio Profile L3
+ 0x21 Natural Audio Profile L4
+ 0x22 Mobile Audio Internetworking Profile L1
+ 0x23 Mobile Audio Internetworking Profile L2
+ 0x24 Mobile Audio Internetworking Profile L3
+ 0x25 Mobile Audio Internetworking Profile L4
+ 0x26 Mobile Audio Internetworking Profile L5
+ 0x27 Mobile Audio Internetworking Profile L6
+ 0x28 AAC Profile L1
+ 0x29 AAC Profile L2
+ 0x2A AAC Profile L4
+ 0x2B AAC Profile L5
+ 0x2C High Efficiency AAC Profile L2
+ 0x2D High Efficiency AAC Profile L3
+ 0x2E High Efficiency AAC Profile L4
+ 0x2F High Efficiency AAC Profile L5
+ 0x30 High Efficiency AAC v2 Profile L2
+ 0x31 High Efficiency AAC v2 Profile L3
+ 0x32 High Efficiency AAC v2 Profile L4
+ 0x33 High Efficiency AAC v2 Profile L5
+ 0x34 Low Delay AAC Profile L1
+ 0x35 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L1
+ 0x36 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L2
+ 0x37 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L3
+ 0x38 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L4
+ 0c39 Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L5
+ 0x3A Baseline MPEG Surround Profile (see ISO/IEC
+ 23003-1)
+ L6
+ 0x3B - 0x7F reserved for ISO use -
+ 0x80 - 0xFD user private -
+ 0xFE no audio profile specified -
+ 0xFF no audio capability required -
+
+ */
+ }
+
+
+ public int getSamplingFrequency() {
+ return samplingFrequencyIndex == 0xf ? samplingFrequency : samplingFrequencyIndexMap.get(samplingFrequencyIndex);
+ }
+
+ public int getChannelConfiguration() {
+ return channelConfiguration;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ AudioSpecificConfig that = (AudioSpecificConfig) o;
+
+ if (aacScalefactorDataResilienceFlag != that.aacScalefactorDataResilienceFlag) {
+ return false;
+ }
+ if (aacSectionDataResilienceFlag != that.aacSectionDataResilienceFlag) {
+ return false;
+ }
+ if (aacSpectralDataResilienceFlag != that.aacSpectralDataResilienceFlag) {
+ return false;
+ }
+ if (audioObjectType != that.audioObjectType) {
+ return false;
+ }
+ if (channelConfiguration != that.channelConfiguration) {
+ return false;
+ }
+ if (coreCoderDelay != that.coreCoderDelay) {
+ return false;
+ }
+ if (dependsOnCoreCoder != that.dependsOnCoreCoder) {
+ return false;
+ }
+ if (directMapping != that.directMapping) {
+ return false;
+ }
+ if (epConfig != that.epConfig) {
+ return false;
+ }
+ if (erHvxcExtensionFlag != that.erHvxcExtensionFlag) {
+ return false;
+ }
+ if (extensionAudioObjectType != that.extensionAudioObjectType) {
+ return false;
+ }
+ if (extensionChannelConfiguration != that.extensionChannelConfiguration) {
+ return false;
+ }
+ if (extensionFlag != that.extensionFlag) {
+ return false;
+ }
+ if (extensionFlag3 != that.extensionFlag3) {
+ return false;
+ }
+ if (extensionSamplingFrequency != that.extensionSamplingFrequency) {
+ return false;
+ }
+ if (extensionSamplingFrequencyIndex != that.extensionSamplingFrequencyIndex) {
+ return false;
+ }
+ if (fillBits != that.fillBits) {
+ return false;
+ }
+ if (frameLengthFlag != that.frameLengthFlag) {
+ return false;
+ }
+ if (gaSpecificConfig != that.gaSpecificConfig) {
+ return false;
+ }
+ if (hilnContMode != that.hilnContMode) {
+ return false;
+ }
+ if (hilnEnhaLayer != that.hilnEnhaLayer) {
+ return false;
+ }
+ if (hilnEnhaQuantMode != that.hilnEnhaQuantMode) {
+ return false;
+ }
+ if (hilnFrameLength != that.hilnFrameLength) {
+ return false;
+ }
+ if (hilnMaxNumLine != that.hilnMaxNumLine) {
+ return false;
+ }
+ if (hilnQuantMode != that.hilnQuantMode) {
+ return false;
+ }
+ if (hilnSampleRateCode != that.hilnSampleRateCode) {
+ return false;
+ }
+ if (hvxcRateMode != that.hvxcRateMode) {
+ return false;
+ }
+ if (hvxcVarMode != that.hvxcVarMode) {
+ return false;
+ }
+ if (isBaseLayer != that.isBaseLayer) {
+ return false;
+ }
+ if (layerNr != that.layerNr) {
+ return false;
+ }
+ if (layer_length != that.layer_length) {
+ return false;
+ }
+ if (numOfSubFrame != that.numOfSubFrame) {
+ return false;
+ }
+ if (paraExtensionFlag != that.paraExtensionFlag) {
+ return false;
+ }
+ if (paraMode != that.paraMode) {
+ return false;
+ }
+ if (parametricSpecificConfig != that.parametricSpecificConfig) {
+ return false;
+ }
+ if (psPresentFlag != that.psPresentFlag) {
+ return false;
+ }
+ if (sacPayloadEmbedding != that.sacPayloadEmbedding) {
+ return false;
+ }
+ if (samplingFrequency != that.samplingFrequency) {
+ return false;
+ }
+ if (samplingFrequencyIndex != that.samplingFrequencyIndex) {
+ return false;
+ }
+ if (sbrPresentFlag != that.sbrPresentFlag) {
+ return false;
+ }
+ if (syncExtensionType != that.syncExtensionType) {
+ return false;
+ }
+ if (var_ScalableFlag != that.var_ScalableFlag) {
+ return false;
+ }
+ if (!Arrays.equals(configBytes, that.configBytes)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = configBytes != null ? Arrays.hashCode(configBytes) : 0;
+ result = 31 * result + audioObjectType;
+ result = 31 * result + samplingFrequencyIndex;
+ result = 31 * result + samplingFrequency;
+ result = 31 * result + channelConfiguration;
+ result = 31 * result + extensionAudioObjectType;
+ result = 31 * result + sbrPresentFlag;
+ result = 31 * result + psPresentFlag;
+ result = 31 * result + extensionSamplingFrequencyIndex;
+ result = 31 * result + extensionSamplingFrequency;
+ result = 31 * result + extensionChannelConfiguration;
+ result = 31 * result + sacPayloadEmbedding;
+ result = 31 * result + fillBits;
+ result = 31 * result + epConfig;
+ result = 31 * result + directMapping;
+ result = 31 * result + syncExtensionType;
+ result = 31 * result + frameLengthFlag;
+ result = 31 * result + dependsOnCoreCoder;
+ result = 31 * result + coreCoderDelay;
+ result = 31 * result + extensionFlag;
+ result = 31 * result + layerNr;
+ result = 31 * result + numOfSubFrame;
+ result = 31 * result + layer_length;
+ result = 31 * result + aacSectionDataResilienceFlag;
+ result = 31 * result + aacScalefactorDataResilienceFlag;
+ result = 31 * result + aacSpectralDataResilienceFlag;
+ result = 31 * result + extensionFlag3;
+ result = 31 * result + (gaSpecificConfig ? 1 : 0);
+ result = 31 * result + isBaseLayer;
+ result = 31 * result + paraMode;
+ result = 31 * result + paraExtensionFlag;
+ result = 31 * result + hvxcVarMode;
+ result = 31 * result + hvxcRateMode;
+ result = 31 * result + erHvxcExtensionFlag;
+ result = 31 * result + var_ScalableFlag;
+ result = 31 * result + hilnQuantMode;
+ result = 31 * result + hilnMaxNumLine;
+ result = 31 * result + hilnSampleRateCode;
+ result = 31 * result + hilnFrameLength;
+ result = 31 * result + hilnContMode;
+ result = 31 * result + hilnEnhaLayer;
+ result = 31 * result + hilnEnhaQuantMode;
+ result = 31 * result + (parametricSpecificConfig ? 1 : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BaseDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BaseDescriptor.java
new file mode 100644
index 0000000..6d94680
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BaseDescriptor.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/*
+abstract aligned(8) expandable(228-1) class BaseDescriptor : bit(8) tag=0 {
+// empty. To be filled by classes extending this class.
+}
+
+int sizeOfInstance = 0;
+bit(1) nextByte;
+bit(7) sizeOfInstance;
+while(nextByte) {
+bit(1) nextByte;
+bit(7) sizeByte;
+sizeOfInstance = sizeOfInstance<<7 | sizeByte;
+}
+ */
+@Descriptor(tags = 0x00)
+public abstract class BaseDescriptor {
+ int tag;
+ int sizeOfInstance;
+ int sizeBytes;
+
+ public BaseDescriptor() {
+ }
+
+ public int getTag() {
+ return tag;
+ }
+
+ public int getSize() {
+ return sizeOfInstance
+ + 1//1 for the tag
+ + sizeBytes;
+ }
+
+ public int getSizeOfInstance() {
+ return sizeOfInstance;
+ }
+
+ public int getSizeBytes() {
+ return sizeBytes;
+ }
+
+ public final void parse(int tag, ByteBuffer bb) throws IOException {
+ this.tag = tag;
+
+ int i = 0;
+ int tmp = IsoTypeReader.readUInt8(bb);
+ i++;
+ sizeOfInstance = tmp & 0x7f;
+ while (tmp >>> 7 == 1) { //nextbyte indicator bit
+ tmp = IsoTypeReader.readUInt8(bb);
+ i++;
+ //sizeOfInstance = sizeOfInstance<<7 | sizeByte;
+ sizeOfInstance = sizeOfInstance << 7 | tmp & 0x7f;
+ }
+ sizeBytes = i;
+ ByteBuffer detailSource = bb.slice();
+ detailSource.limit(sizeOfInstance);
+ parseDetail(detailSource);
+ assert detailSource.remaining() == 0: this.getClass().getSimpleName() + " has not been fully parsed";
+ bb.position(bb.position() + sizeOfInstance);
+ }
+
+ public abstract void parseDetail(ByteBuffer bb) throws IOException;
+
+
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("BaseDescriptor");
+ sb.append("{tag=").append(tag);
+ sb.append(", sizeOfInstance=").append(sizeOfInstance);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitReaderBuffer.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitReaderBuffer.java
new file mode 100644
index 0000000..7221503
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitReaderBuffer.java
@@ -0,0 +1,51 @@
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import java.nio.ByteBuffer;
+
+public class BitReaderBuffer {
+
+ private ByteBuffer buffer;
+ int initialPos;
+ int position;
+
+ public BitReaderBuffer(ByteBuffer buffer) {
+ this.buffer = buffer;
+ initialPos = buffer.position();
+ }
+
+ public int readBits(int i) {
+ byte b = buffer.get(initialPos + position / 8);
+ int v = b < 0 ? b + 256 : b;
+ int left = 8 - position % 8;
+ int rc;
+ if (i <= left) {
+ rc = (v << (position % 8) & 0xFF) >> ((position % 8) + (left - i));
+ position += i;
+ } else {
+ int now = left;
+ int then = i - left;
+ rc = readBits(now);
+ rc = rc << then;
+ rc += readBits(then);
+ }
+ buffer.position(initialPos + (int) Math.ceil((double) position / 8));
+ return rc;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public int byteSync() {
+ int left = 8 - position % 8;
+ if (left == 8) {
+ left = 0;
+ }
+ readBits(left);
+ return left;
+ }
+
+ public int remainingBits() {
+ return buffer.limit() * 8 - position;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitWriterBuffer.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitWriterBuffer.java
new file mode 100644
index 0000000..e6ea67f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/BitWriterBuffer.java
@@ -0,0 +1,36 @@
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import java.nio.ByteBuffer;
+
+public class BitWriterBuffer {
+
+ private ByteBuffer buffer;
+ int initialPos;
+ int position = 0;
+
+ public BitWriterBuffer(ByteBuffer buffer) {
+ this.buffer = buffer;
+ this.initialPos = buffer.position();
+ }
+
+ public void writeBits(int i, int numBits) {
+ assert i <= ((1 << numBits)-1): String.format("Trying to write a value bigger (%s) than the number bits (%s) allows. " +
+ "Please mask the value before writing it and make your code is really working as intended.", i, (1<<numBits)-1);
+
+ int left = 8 - position % 8;
+ if (numBits <= left) {
+ int current = (buffer.get(initialPos + position / 8));
+ current = current < 0 ? current + 256 : current;
+ current += i << (left - numBits);
+ buffer.put(initialPos + position / 8, (byte) (current > 127 ? current - 256 : current));
+ position += numBits;
+ } else {
+ int bitsSecondWrite = numBits - left;
+ writeBits(i >> bitsSecondWrite, left);
+ writeBits(i & (1 << bitsSecondWrite) - 1, bitsSecondWrite);
+ }
+ buffer.position(initialPos + position / 8 + ((position % 8 > 0) ? 1 : 0));
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderConfigDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderConfigDescriptor.java
new file mode 100644
index 0000000..69d603a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderConfigDescriptor.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * class DecoderConfigDescriptor extends BaseDescriptor : bit(8)
+ * tag=DecoderConfigDescrTag {
+ * bit(8) objectTypeIndication;
+ * bit(6) streamType;
+ * bit(1) upStream;
+ * const bit(1) reserved=1;
+ * bit(24) bufferSizeDB;
+ * bit(32) maxBitrate;
+ * bit(32) avgBitrate;
+ * DecoderSpecificInfo decSpecificInfo[0 .. 1];
+ * profileLevelIndicationIndexDescriptor profileLevelIndicationIndexDescr
+ * [0..255];
+ * }
+ */
+@Descriptor(tags = {0x04})
+public class DecoderConfigDescriptor extends BaseDescriptor {
+ private static Logger log = Logger.getLogger(DecoderConfigDescriptor.class.getName());
+
+
+ int objectTypeIndication;
+ int streamType;
+ int upStream;
+ int bufferSizeDB;
+ long maxBitRate;
+ long avgBitRate;
+
+ DecoderSpecificInfo decoderSpecificInfo;
+ AudioSpecificConfig audioSpecificInfo;
+ List<ProfileLevelIndicationDescriptor> profileLevelIndicationDescriptors = new ArrayList<ProfileLevelIndicationDescriptor>();
+ byte[] configDescriptorDeadBytes;
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ objectTypeIndication = IsoTypeReader.readUInt8(bb);
+
+ int data = IsoTypeReader.readUInt8(bb);
+ streamType = data >>> 2;
+ upStream = (data >> 1) & 0x1;
+
+ bufferSizeDB = IsoTypeReader.readUInt24(bb);
+ maxBitRate = IsoTypeReader.readUInt32(bb);
+ avgBitRate = IsoTypeReader.readUInt32(bb);
+
+
+
+ BaseDescriptor descriptor;
+ if (bb.remaining() > 2) { //1byte tag + at least 1byte size
+ final int begin = bb.position();
+ descriptor = ObjectDescriptorFactory.createFrom(objectTypeIndication, bb);
+ final int read = bb.position() - begin;
+ log.finer(descriptor + " - DecoderConfigDescr1 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor != null) {
+ final int size = descriptor.getSize();
+ if (read < size) {
+ //skip
+ configDescriptorDeadBytes = new byte[size - read];
+ bb.get(configDescriptorDeadBytes);
+ }
+ }
+ if (descriptor instanceof DecoderSpecificInfo) {
+ decoderSpecificInfo = (DecoderSpecificInfo) descriptor;
+ }
+ if (descriptor instanceof AudioSpecificConfig) {
+ audioSpecificInfo = (AudioSpecificConfig) descriptor;
+ }
+ }
+
+ while (bb.remaining() > 2) {
+ final long begin = bb.position();
+ descriptor = ObjectDescriptorFactory.createFrom(objectTypeIndication, bb);
+ final long read = bb.position() - begin;
+ log.finer(descriptor + " - DecoderConfigDescr2 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor instanceof ProfileLevelIndicationDescriptor) {
+ profileLevelIndicationDescriptors.add((ProfileLevelIndicationDescriptor) descriptor);
+ }
+ }
+ }
+ public int serializedSize() {
+ return 15 + audioSpecificInfo.serializedSize();
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.allocate(serializedSize());
+ IsoTypeWriter.writeUInt8(out, 4);
+ IsoTypeWriter.writeUInt8(out, serializedSize() - 2);
+ IsoTypeWriter.writeUInt8(out, objectTypeIndication);
+ int flags = (streamType << 2) | (upStream << 1) | 1;
+ IsoTypeWriter.writeUInt8(out, flags);
+ IsoTypeWriter.writeUInt24(out, bufferSizeDB);
+ IsoTypeWriter.writeUInt32(out, maxBitRate);
+ IsoTypeWriter.writeUInt32(out, avgBitRate);
+ out.put(audioSpecificInfo.serialize().array());
+ return out;
+ }
+
+ public DecoderSpecificInfo getDecoderSpecificInfo() {
+ return decoderSpecificInfo;
+ }
+
+ public AudioSpecificConfig getAudioSpecificInfo() {
+ return audioSpecificInfo;
+ }
+
+ public void setAudioSpecificInfo(AudioSpecificConfig audioSpecificInfo) {
+ this.audioSpecificInfo = audioSpecificInfo;
+ }
+
+ public List<ProfileLevelIndicationDescriptor> getProfileLevelIndicationDescriptors() {
+ return profileLevelIndicationDescriptors;
+ }
+
+ public int getObjectTypeIndication() {
+ return objectTypeIndication;
+ }
+
+ public void setObjectTypeIndication(int objectTypeIndication) {
+ this.objectTypeIndication = objectTypeIndication;
+ }
+
+ public int getStreamType() {
+ return streamType;
+ }
+
+ public void setStreamType(int streamType) {
+ this.streamType = streamType;
+ }
+
+ public int getUpStream() {
+ return upStream;
+ }
+
+ public void setUpStream(int upStream) {
+ this.upStream = upStream;
+ }
+
+ public int getBufferSizeDB() {
+ return bufferSizeDB;
+ }
+
+ public void setBufferSizeDB(int bufferSizeDB) {
+ this.bufferSizeDB = bufferSizeDB;
+ }
+
+ public long getMaxBitRate() {
+ return maxBitRate;
+ }
+
+ public void setMaxBitRate(long maxBitRate) {
+ this.maxBitRate = maxBitRate;
+ }
+
+ public long getAvgBitRate() {
+ return avgBitRate;
+ }
+
+ public void setAvgBitRate(long avgBitRate) {
+ this.avgBitRate = avgBitRate;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DecoderConfigDescriptor");
+ sb.append("{objectTypeIndication=").append(objectTypeIndication);
+ sb.append(", streamType=").append(streamType);
+ sb.append(", upStream=").append(upStream);
+ sb.append(", bufferSizeDB=").append(bufferSizeDB);
+ sb.append(", maxBitRate=").append(maxBitRate);
+ sb.append(", avgBitRate=").append(avgBitRate);
+ sb.append(", decoderSpecificInfo=").append(decoderSpecificInfo);
+ sb.append(", audioSpecificInfo=").append(audioSpecificInfo);
+ sb.append(", configDescriptorDeadBytes=").append(Hex.encodeHex(configDescriptorDeadBytes != null ? configDescriptorDeadBytes : new byte[]{}));
+ sb.append(", profileLevelIndicationDescriptors=").append(profileLevelIndicationDescriptors == null ? "null" : Arrays.asList(profileLevelIndicationDescriptors).toString());
+ sb.append('}');
+ return sb.toString();
+ }
+ /*objectTypeIndication values
+ 0x00 Forbidden
+ 0x01 Systems ISO/IEC 14496-1 a
+ 0x02 Systems ISO/IEC 14496-1 b
+ 0x03 Interaction Stream
+ 0x04 Systems ISO/IEC 14496-1 Extended BIFS Configuration c
+ 0x05 Systems ISO/IEC 14496-1 AFX d
+ 0x06 Font Data Stream
+ 0x07 Synthesized Texture Stream
+ 0x08 Streaming Text Stream
+ 0x09-0x1F reserved for ISO use
+ 0x20 Visual ISO/IEC 14496-2 e
+ 0x21 Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10 f
+ 0x22 Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10 f
+ 0x23-0x3F reserved for ISO use
+ 0x40 Audio ISO/IEC 14496-3 g
+ 0x41-0x5F reserved for ISO use
+ 0x60 Visual ISO/IEC 13818-2 Simple Profile
+ 0x61 Visual ISO/IEC 13818-2 Main Profile
+ 0x62 Visual ISO/IEC 13818-2 SNR Profile
+ 0x63 Visual ISO/IEC 13818-2 Spatial Profile
+ 0x64 Visual ISO/IEC 13818-2 High Profile
+ 0x65 Visual ISO/IEC 13818-2 422 Profile
+ 0x66 Audio ISO/IEC 13818-7 Main Profile
+ 0x67 Audio ISO/IEC 13818-7 LowComplexity Profile
+ 0x68 Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile
+ 0x69 Audio ISO/IEC 13818-3
+ 0x6A Visual ISO/IEC 11172-2
+ 0x6B Audio ISO/IEC 11172-3
+ 0x6C Visual ISO/IEC 10918-1
+ 0x6D reserved for registration authority i
+ 0x6E Visual ISO/IEC 15444-1
+ 0x6F - 0x9F reserved for ISO use
+ 0xA0 - 0xBF reserved for registration authority i
+ 0xC0 - 0xE0 user private
+ 0xE1 reserved for registration authority i
+ 0xE2 - 0xFE user private
+ 0xFF no object type specified h
+ */
+ /* streamType values
+ 0x00 Forbidden
+ 0x01 ObjectDescriptorStream (see 7.2.5)
+ 0x02 ClockReferenceStream (see 7.3.2.5)
+ 0x03 SceneDescriptionStream (see ISO/IEC 14496-11)
+ 0x04 VisualStream
+ 0x05 AudioStream
+ 0x06 MPEG7Stream
+ 0x07 IPMPStream (see 7.2.3.2)
+ 0x08 ObjectContentInfoStream (see 7.2.4.2)
+ 0x09 MPEGJStream
+ 0x0A Interaction Stream
+ 0x0B IPMPToolStream (see [ISO/IEC 14496-13])
+ 0x0C - 0x1F reserved for ISO use
+ 0x20 - 0x3F user private
+ */
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderSpecificInfo.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderSpecificInfo.java
new file mode 100644
index 0000000..574943c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/DecoderSpecificInfo.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * abstract class DecoderSpecificInfo extends BaseDescriptor : bit(8)
+ * tag=DecSpecificInfoTag
+ * {
+ * // empty. To be filled by classes extending this class.
+ * }
+ */
+@Descriptor(tags = 0x05)
+public class DecoderSpecificInfo extends BaseDescriptor {
+ byte[] bytes;
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ if (sizeOfInstance > 0) {
+ bytes = new byte[sizeOfInstance];
+ bb.get(bytes);
+ }
+ }
+
+ public int serializedSize() {
+ return bytes.length;
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.wrap(bytes);
+
+ return out;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DecoderSpecificInfo");
+ sb.append("{bytes=").append(bytes == null ? "null" : Hex.encodeHex(bytes));
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DecoderSpecificInfo that = (DecoderSpecificInfo) o;
+
+ if (!Arrays.equals(bytes, that.bytes)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return bytes != null ? Arrays.hashCode(bytes) : 0;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/Descriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/Descriptor.java
new file mode 100644
index 0000000..11020c7
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/Descriptor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: mstattma
+ * Date: 06.08.2010
+ * Time: 06:54:58
+ * To change this template use File | Settings | File Templates.
+ */
+@Documented
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Descriptor {
+ int[] tags();
+
+ int objectTypeIndication() default -1;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ESDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ESDescriptor.java
new file mode 100644
index 0000000..3bb4821
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ESDescriptor.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/*
+class ES_Descriptor extends BaseDescriptor : bit(8) tag=ES_DescrTag {
+bit(16) ES_ID;
+bit(1) streamDependenceFlag;
+bit(1) URL_Flag;
+bit(1) OCRstreamFlag;
+bit(5) streamPriority;
+if (streamDependenceFlag)
+bit(16) dependsOn_ES_ID;
+if (URL_Flag) {
+bit(8) URLlength;
+bit(8) URLstring[URLlength];
+}
+if (OCRstreamFlag)
+bit(16) OCR_ES_Id;
+DecoderConfigDescriptor decConfigDescr;
+if (ODProfileLevelIndication==0x01) //no SL extension.
+{
+SLConfigDescriptor slConfigDescr;
+}
+else // SL extension is possible.
+{
+SLConfigDescriptor slConfigDescr;
+}
+IPI_DescrPointer ipiPtr[0 .. 1];
+IP_IdentificationDataSet ipIDS[0 .. 255];
+IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255];
+LanguageDescriptor langDescr[0 .. 255];
+QoS_Descriptor qosDescr[0 .. 1];
+RegistrationDescriptor regDescr[0 .. 1];
+ExtensionDescriptor extDescr[0 .. 255];
+}
+ */
+@Descriptor(tags = {0x03})
+public class ESDescriptor extends BaseDescriptor {
+ private static Logger log = Logger.getLogger(ESDescriptor.class.getName());
+
+ int esId;
+ int streamDependenceFlag;
+ int URLFlag;
+ int oCRstreamFlag;
+ int streamPriority;
+
+
+ int URLLength = 0;
+ String URLString;
+ int remoteODFlag;
+
+ int dependsOnEsId;
+ int oCREsId;
+
+ DecoderConfigDescriptor decoderConfigDescriptor;
+ SLConfigDescriptor slConfigDescriptor;
+ List<BaseDescriptor> otherDescriptors = new ArrayList<BaseDescriptor>();
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ esId = IsoTypeReader.readUInt16(bb);
+
+ int data = IsoTypeReader.readUInt8(bb);
+ streamDependenceFlag = data >>> 7;
+ URLFlag = (data >>> 6) & 0x1;
+ oCRstreamFlag = (data >>> 5) & 0x1;
+ streamPriority = data & 0x1f;
+
+ if (streamDependenceFlag == 1) {
+ dependsOnEsId = IsoTypeReader.readUInt16(bb);
+ }
+ if (URLFlag == 1) {
+ URLLength = IsoTypeReader.readUInt8(bb);
+ URLString = IsoTypeReader.readString(bb, URLLength);
+ }
+ if (oCRstreamFlag == 1) {
+ oCREsId = IsoTypeReader.readUInt16(bb);
+ }
+
+ int baseSize = 1 /*tag*/ + getSizeBytes() + 2 + 1 + (streamDependenceFlag == 1 ? 2 : 0) + (URLFlag == 1 ? 1 + URLLength : 0) + (oCRstreamFlag == 1 ? 2 : 0);
+
+ int begin = bb.position();
+ if (getSize() > baseSize + 2) {
+ BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ final long read = bb.position() - begin;
+ log.finer(descriptor + " - ESDescriptor1 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor != null) {
+ final int size = descriptor.getSize();
+ bb.position(begin + size);
+ baseSize += size;
+ } else {
+ baseSize += read;
+ }
+ if (descriptor instanceof DecoderConfigDescriptor) {
+ decoderConfigDescriptor = (DecoderConfigDescriptor) descriptor;
+ }
+ }
+
+ begin = bb.position();
+ if (getSize() > baseSize + 2) {
+ BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ final long read = bb.position() - begin;
+ log.finer(descriptor + " - ESDescriptor2 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor != null) {
+ final int size = descriptor.getSize();
+ bb.position(begin + size);
+ baseSize += size;
+ } else {
+ baseSize += read;
+ }
+ if (descriptor instanceof SLConfigDescriptor) {
+ slConfigDescriptor = (SLConfigDescriptor) descriptor;
+ }
+ } else {
+ log.warning("SLConfigDescriptor is missing!");
+ }
+
+ while (getSize() - baseSize > 2) {
+ begin = bb.position();
+ BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ final long read = bb.position() - begin;
+ log.finer(descriptor + " - ESDescriptor3 read: " + read + ", size: " + (descriptor != null ? descriptor.getSize() : null));
+ if (descriptor != null) {
+ final int size = descriptor.getSize();
+ bb.position(begin + size);
+ baseSize += size;
+ } else {
+ baseSize += read;
+ }
+ otherDescriptors.add(descriptor);
+ }
+ }
+ public int serializedSize() {
+ int out = 5;
+ if (streamDependenceFlag > 0) {
+ out += 2;
+ }
+ if (URLFlag > 0) {
+ out += 1 + URLLength;
+ }
+ if (oCRstreamFlag > 0) {
+ out += 2;
+ }
+
+ out += decoderConfigDescriptor.serializedSize();
+ out += slConfigDescriptor.serializedSize();
+
+ // Doesn't handle other descriptors yet
+
+ return out;
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.allocate(serializedSize()); // Usually is around 30 bytes, so 200 should be enough...
+ IsoTypeWriter.writeUInt8(out, 3);
+ IsoTypeWriter.writeUInt8(out, serializedSize() - 2); // Not OK for longer sizes!
+ IsoTypeWriter.writeUInt16(out, esId);
+ int flags = (streamDependenceFlag << 7) | (URLFlag << 6) | (oCRstreamFlag << 5) | (streamPriority & 0x1f);
+ IsoTypeWriter.writeUInt8(out, flags);
+ if (streamDependenceFlag > 0) {
+ IsoTypeWriter.writeUInt16(out, dependsOnEsId);
+ }
+ if (URLFlag > 0) {
+ IsoTypeWriter.writeUInt8(out, URLLength);
+ IsoTypeWriter.writeUtf8String(out, URLString);
+ }
+ if (oCRstreamFlag > 0) {
+ IsoTypeWriter.writeUInt16(out, oCREsId);
+ }
+
+ ByteBuffer dec = decoderConfigDescriptor.serialize();
+ ByteBuffer sl = slConfigDescriptor.serialize();
+ out.put(dec.array());
+ out.put(sl.array());
+
+ // Doesn't handle other descriptors yet
+
+ return out;
+ }
+
+// @Override
+// public int getSize() {
+// return 3 + (streamDependenceFlag == 1 ? 2 : 0) +
+// (URLFlag == 1 ? 1 + 8 * URLLength : 0) +
+// (oCRstreamFlag == 1 ? 2 : 0);
+// }
+
+ public DecoderConfigDescriptor getDecoderConfigDescriptor() {
+ return decoderConfigDescriptor;
+ }
+
+ public SLConfigDescriptor getSlConfigDescriptor() {
+ return slConfigDescriptor;
+ }
+
+ public void setDecoderConfigDescriptor(DecoderConfigDescriptor decoderConfigDescriptor) {
+ this.decoderConfigDescriptor = decoderConfigDescriptor;
+ }
+
+ public void setSlConfigDescriptor(SLConfigDescriptor slConfigDescriptor) {
+ this.slConfigDescriptor = slConfigDescriptor;
+ }
+
+ public List<BaseDescriptor> getOtherDescriptors() {
+ return otherDescriptors;
+ }
+
+ public int getoCREsId() {
+ return oCREsId;
+ }
+
+ public void setoCREsId(int oCREsId) {
+ this.oCREsId = oCREsId;
+ }
+
+ public int getEsId() {
+ return esId;
+ }
+
+ public void setEsId(int esId) {
+ this.esId = esId;
+ }
+
+ public int getStreamDependenceFlag() {
+ return streamDependenceFlag;
+ }
+
+ public void setStreamDependenceFlag(int streamDependenceFlag) {
+ this.streamDependenceFlag = streamDependenceFlag;
+ }
+
+ public int getURLFlag() {
+ return URLFlag;
+ }
+
+ public void setURLFlag(int URLFlag) {
+ this.URLFlag = URLFlag;
+ }
+
+ public int getoCRstreamFlag() {
+ return oCRstreamFlag;
+ }
+
+ public void setoCRstreamFlag(int oCRstreamFlag) {
+ this.oCRstreamFlag = oCRstreamFlag;
+ }
+
+ public int getStreamPriority() {
+ return streamPriority;
+ }
+
+ public void setStreamPriority(int streamPriority) {
+ this.streamPriority = streamPriority;
+ }
+
+ public int getURLLength() {
+ return URLLength;
+ }
+
+ public void setURLLength(int URLLength) {
+ this.URLLength = URLLength;
+ }
+
+ public String getURLString() {
+ return URLString;
+ }
+
+ public void setURLString(String URLString) {
+ this.URLString = URLString;
+ }
+
+ public int getRemoteODFlag() {
+ return remoteODFlag;
+ }
+
+ public void setRemoteODFlag(int remoteODFlag) {
+ this.remoteODFlag = remoteODFlag;
+ }
+
+ public int getDependsOnEsId() {
+ return dependsOnEsId;
+ }
+
+ public void setDependsOnEsId(int dependsOnEsId) {
+ this.dependsOnEsId = dependsOnEsId;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ESDescriptor");
+ sb.append("{esId=").append(esId);
+ sb.append(", streamDependenceFlag=").append(streamDependenceFlag);
+ sb.append(", URLFlag=").append(URLFlag);
+ sb.append(", oCRstreamFlag=").append(oCRstreamFlag);
+ sb.append(", streamPriority=").append(streamPriority);
+ sb.append(", URLLength=").append(URLLength);
+ sb.append(", URLString='").append(URLString).append('\'');
+ sb.append(", remoteODFlag=").append(remoteODFlag);
+ sb.append(", dependsOnEsId=").append(dependsOnEsId);
+ sb.append(", oCREsId=").append(oCREsId);
+ sb.append(", decoderConfigDescriptor=").append(decoderConfigDescriptor);
+ sb.append(", slConfigDescriptor=").append(slConfigDescriptor);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ESDescriptor that = (ESDescriptor) o;
+
+ if (URLFlag != that.URLFlag) return false;
+ if (URLLength != that.URLLength) return false;
+ if (dependsOnEsId != that.dependsOnEsId) return false;
+ if (esId != that.esId) return false;
+ if (oCREsId != that.oCREsId) return false;
+ if (oCRstreamFlag != that.oCRstreamFlag) return false;
+ if (remoteODFlag != that.remoteODFlag) return false;
+ if (streamDependenceFlag != that.streamDependenceFlag) return false;
+ if (streamPriority != that.streamPriority) return false;
+ if (URLString != null ? !URLString.equals(that.URLString) : that.URLString != null) return false;
+ if (decoderConfigDescriptor != null ? !decoderConfigDescriptor.equals(that.decoderConfigDescriptor) : that.decoderConfigDescriptor != null)
+ return false;
+ if (otherDescriptors != null ? !otherDescriptors.equals(that.otherDescriptors) : that.otherDescriptors != null)
+ return false;
+ if (slConfigDescriptor != null ? !slConfigDescriptor.equals(that.slConfigDescriptor) : that.slConfigDescriptor != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = esId;
+ result = 31 * result + streamDependenceFlag;
+ result = 31 * result + URLFlag;
+ result = 31 * result + oCRstreamFlag;
+ result = 31 * result + streamPriority;
+ result = 31 * result + URLLength;
+ result = 31 * result + (URLString != null ? URLString.hashCode() : 0);
+ result = 31 * result + remoteODFlag;
+ result = 31 * result + dependsOnEsId;
+ result = 31 * result + oCREsId;
+ result = 31 * result + (decoderConfigDescriptor != null ? decoderConfigDescriptor.hashCode() : 0);
+ result = 31 * result + (slConfigDescriptor != null ? slConfigDescriptor.hashCode() : 0);
+ result = 31 * result + (otherDescriptors != null ? otherDescriptors.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionDescriptor.java
new file mode 100644
index 0000000..7933f5a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionDescriptor.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Logger;
+
+/**
+ * abstract class ExtensionDescriptor extends BaseDescriptor
+ * : bit(8) tag = ExtensionProfileLevelDescrTag, ExtDescrTagStartRange ..
+ * ExtDescrTagEndRange {
+ * // empty. To be filled by classes extending this class.
+ * }
+ */
+@Descriptor(tags = {0x13, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253})
+public class ExtensionDescriptor extends BaseDescriptor {
+ private static Logger log = Logger.getLogger(ExtensionDescriptor.class.getName());
+
+ byte[] bytes;
+
+
+ //todo: add this better to the tags list?
+ //14496-1:2010 p.20:
+ //0x6A-0xBF Reserved for ISO use
+ //0xC0-0xFE User private
+ //
+ //ExtDescrTagStartRange = 0x6A
+ //ExtDescrTagEndRange = 0xFE
+ static int[] allTags() {
+ int[] ints = new int[0xFE - 0x6A];
+
+ for (int i = 0x6A; i < 0xFE; i++) {
+ final int pos = i - 0x6A;
+ log.finest("pos:" + pos);
+ ints[pos] = i;
+ }
+ return ints;
+ }
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ if (getSize() > 0) {
+ bytes = new byte[sizeOfInstance];
+ bb.get(bytes);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ExtensionDescriptor");
+ sb.append("{bytes=").append(bytes == null ? "null" : Hex.encodeHex(bytes));
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionProfileLevelDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionProfileLevelDescriptor.java
new file mode 100644
index 0000000..0cf4915
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ExtensionProfileLevelDescriptor.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.Hex;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * abstract class ExtensionDescriptor extends BaseDescriptor
+ * : bit(8) tag = ExtensionProfileLevelDescrTag, ExtDescrTagStartRange ..
+ * ExtDescrTagEndRange {
+ * // empty. To be filled by classes extending this class.
+ * }
+ */
+@Descriptor(tags = {0x13})
+public class ExtensionProfileLevelDescriptor extends BaseDescriptor {
+ byte[] bytes;
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ if (getSize() > 0) {
+ bytes = new byte[getSize()];
+ bb.get(bytes);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ExtensionDescriptor");
+ sb.append("{bytes=").append(bytes == null ? "null" : Hex.encodeHex(bytes));
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/InitialObjectDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/InitialObjectDescriptor.java
new file mode 100644
index 0000000..7a1f094
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/InitialObjectDescriptor.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+class InitialObjectDescriptor extends ObjectDescriptorBase : bit(8)
+tag=InitialObjectDescrTag {
+bit(10) ObjectDescriptorID;
+bit(1) URL_Flag;
+bit(1) includeInlineProfileLevelFlag;
+const bit(4) reserved=0b1111;
+if (URL_Flag) {
+bit(8) URLlength;
+bit(8) URLstring[URLlength];
+} else {
+bit(8) ODProfileLevelIndication;
+bit(8) sceneProfileLevelIndication;
+bit(8) audioProfileLevelIndication;
+bit(8) visualProfileLevelIndication;
+bit(8) graphicsProfileLevelIndication;
+ES_Descriptor esDescr[1 .. 255];
+OCI_Descriptor ociDescr[0 .. 255];
+IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255];
+IPMP_Descriptor ipmpDescr [0 .. 255];
+IPMP_ToolListDescriptor toolListDescr[0 .. 1];
+}
+ExtensionDescriptor extDescr[0 .. 255];
+}
+*/
+//@Descriptor(tags = {0x02, 0x10})
+public class InitialObjectDescriptor extends ObjectDescriptorBase {
+ private int objectDescriptorId;
+ int urlFlag;
+ int includeInlineProfileLevelFlag;
+
+ int urlLength;
+ String urlString;
+
+ int oDProfileLevelIndication;
+ int sceneProfileLevelIndication;
+ int audioProfileLevelIndication;
+ int visualProfileLevelIndication;
+ int graphicsProfileLevelIndication;
+
+ List<ESDescriptor> esDescriptors = new ArrayList<ESDescriptor>();
+
+ List<ExtensionDescriptor> extensionDescriptors = new ArrayList<ExtensionDescriptor>();
+
+ List<BaseDescriptor> unknownDescriptors = new ArrayList<BaseDescriptor>();
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ int data = IsoTypeReader.readUInt16(bb);
+ objectDescriptorId = (data & 0xFFC0) >> 6;
+
+ urlFlag = (data & 0x3F) >> 5;
+ includeInlineProfileLevelFlag = (data & 0x1F) >> 4;
+
+ int sizeLeft = getSize() - 2;
+ if (urlFlag == 1) {
+ urlLength = IsoTypeReader.readUInt8(bb);
+ urlString = IsoTypeReader.readString(bb, urlLength);
+ sizeLeft = sizeLeft - (1 + urlLength);
+ } else {
+ oDProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+ sceneProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+ audioProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+ visualProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+ graphicsProfileLevelIndication = IsoTypeReader.readUInt8(bb);
+
+ sizeLeft = sizeLeft - 5;
+
+ if (sizeLeft > 2) {
+ final BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ sizeLeft = sizeLeft - descriptor.getSize();
+ if (descriptor instanceof ESDescriptor) {
+ esDescriptors.add((ESDescriptor) descriptor);
+ } else {
+ unknownDescriptors.add(descriptor);
+ }
+ }
+ }
+
+ if (sizeLeft > 2) {
+ final BaseDescriptor descriptor = ObjectDescriptorFactory.createFrom(-1, bb);
+ if (descriptor instanceof ExtensionDescriptor) {
+ extensionDescriptors.add((ExtensionDescriptor) descriptor);
+ } else {
+ unknownDescriptors.add(descriptor);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("InitialObjectDescriptor");
+ sb.append("{objectDescriptorId=").append(objectDescriptorId);
+ sb.append(", urlFlag=").append(urlFlag);
+ sb.append(", includeInlineProfileLevelFlag=").append(includeInlineProfileLevelFlag);
+ sb.append(", urlLength=").append(urlLength);
+ sb.append(", urlString='").append(urlString).append('\'');
+ sb.append(", oDProfileLevelIndication=").append(oDProfileLevelIndication);
+ sb.append(", sceneProfileLevelIndication=").append(sceneProfileLevelIndication);
+ sb.append(", audioProfileLevelIndication=").append(audioProfileLevelIndication);
+ sb.append(", visualProfileLevelIndication=").append(visualProfileLevelIndication);
+ sb.append(", graphicsProfileLevelIndication=").append(graphicsProfileLevelIndication);
+ sb.append(", esDescriptors=").append(esDescriptors);
+ sb.append(", extensionDescriptors=").append(extensionDescriptors);
+ sb.append(", unknownDescriptors=").append(unknownDescriptors);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptor.java_bak b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptor.java_bak
new file mode 100644
index 0000000..c5cb586
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptor.java_bak
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+class ObjectDescriptor extends ObjectDescriptorBase : bit(8) tag=ObjectDescrTag {
+bit(10) ObjectDescriptorID;
+bit(1) URL_Flag;
+const bit(5) reserved=0b1111.1;
+if (URL_Flag) {
+bit(8) URLlength;
+bit(8) URLstring[URLlength];
+} else {
+ES_Descriptor esDescr[1 .. 255];
+OCI_Descriptor ociDescr[0 .. 255];
+IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255];
+IPMP_Descriptor ipmpDescr [0 .. 255];
+}
+ExtensionDescriptor extDescr[0 .. 255];
+}
+*/
+@Descriptor(tags = {0x01, 0x11})
+public class ObjectDescriptor extends ObjectDescriptorBase {
+ private int objectDescriptorId;
+ int objectDescriptorUrlFlag;
+ int objectDescriptorUrlLength;
+ String objectDescriptorUrlString;
+
+
+ private int streamCount;
+ private int extensionFlag;
+ private List<ESDescriptor> esDescriptors = new ArrayList<ESDescriptor>();
+
+ private int descriptorLength;
+ private List<ExtensionDescriptor> extensionDescriptors = new ArrayList<ExtensionDescriptor>();
+
+ public static ObjectDescriptor createFrom(ByteBuffer in) throws IOException {
+/*
+ tmp = in.readUInt16();
+ esDescriptor.objectDescriptorId = tmp & 0x3f;
+ esDescriptor.objectDescriptorUrlFlag = (tmp >> 5) & 0x1;
+ if (esDescriptor.objectDescriptorUrlFlag == 1) {
+ esDescriptor.objectDescriptorUrlLength = in.readUInt8();
+ esDescriptor.objectDescriptorUrlString = new String(in.read(esDescriptor.objectDescriptorUrlLength));
+ }
+ */
+
+ ObjectDescriptor objectDescriptor = new ObjectDescriptor();
+
+ int data = IsoTypeReader.readUInt16(in);
+
+ objectDescriptor.objectDescriptorId = data & 0xFFC0;
+ objectDescriptor.streamCount = data & 0x3E;
+ objectDescriptor.extensionFlag = data & 0x1;
+
+// for (int i = 0; i < objectDescriptor.streamCount; i++) {
+// objectDescriptor.esDescriptors.add(ESDescriptor.createFrom(in));
+// }
+//
+// if (objectDescriptor.extensionFlag == 1) {
+// objectDescriptor.descriptorLength = in.readUInt8();
+// for (int i = 0; i < objectDescriptor.descriptorLength;) {
+// ExtensionDescriptor extensionDescriptor = ExtensionDescriptor.createFrom(in);
+// objectDescriptor.extensionDescriptors.add(extensionDescriptor);
+// i = i + extensionDescriptor.descriptorDataLength + 1;
+// }
+// }
+
+ return objectDescriptor;
+ }
+
+ @Override
+ public String toString() {
+ return "ObjectDescriptor{" +
+ "objectDescriptorId=" + objectDescriptorId +
+ ", streamCount=" + streamCount +
+ ", extensionFlag=" + extensionFlag +
+ ", esDescriptors=" + esDescriptors +
+ ", descriptorLength=" + descriptorLength +
+ ", extensionDescriptors=" + extensionDescriptors +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorBase.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorBase.java
new file mode 100644
index 0000000..69a8684
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorBase.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+/*
+abstract class ObjectDescriptorBase extends BaseDescriptor : bit(8)
+tag=[ObjectDescrTag..InitialObjectDescrTag] {
+// empty. To be filled by classes extending this class.
+}
+ */
+@Descriptor(tags = 0x00)
+public abstract class ObjectDescriptorBase extends BaseDescriptor {
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorFactory.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorFactory.java
new file mode 100644
index 0000000..6afba55
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ObjectDescriptorFactory.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/* class tag values of 14496-1
+0x00 Forbidden
+0x01 ObjectDescrTag
+0x02 InitialObjectDescrTag
+0x03 ES_DescrTag
+0x04 DecoderConfigDescrTag
+0x05 DecSpecificInfoTag
+0x06 SLConfigDescrTag
+0x07 ContentIdentDescrTag
+0x08 SupplContentIdentDescrTag
+0x09 IPI_DescrPointerTag
+0x0A IPMP_DescrPointerTag
+0x0B IPMP_DescrTag
+0x0C QoS_DescrTag
+0x0D RegistrationDescrTag
+0x0E ES_ID_IncTag
+0x0F ES_ID_RefTag
+0x10 MP4_IOD_Tag
+0x11 MP4_OD_Tag
+0x12 IPL_DescrPointerRefTag
+0x13 ExtensionProfileLevelDescrTag
+0x14 profileLevelIndicationIndexDescrTag
+0x15-0x3F Reserved for ISO use
+0x40 ContentClassificationDescrTag
+0x41 KeyWordDescrTag
+0x42 RatingDescrTag
+0x43 LanguageDescrTag
+0x44 ShortTextualDescrTag
+0x45 ExpandedTextualDescrTag
+0x46 ContentCreatorNameDescrTag
+0x47 ContentCreationDateDescrTag
+0x48 OCICreatorNameDescrTag
+0x49 OCICreationDateDescrTag
+0x4A SmpteCameraPositionDescrTag
+0x4B SegmentDescrTag
+0x4C MediaTimeDescrTag
+0x4D-0x5F Reserved for ISO use (OCI extensions)
+0x60 IPMP_ToolsListDescrTag
+0x61 IPMP_ToolTag
+0x62 M4MuxTimingDescrTag
+0x63 M4MuxCodeTableDescrTag
+0x64 ExtSLConfigDescrTag
+0x65 M4MuxBufferSizeDescrTag
+0x66 M4MuxIdentDescrTag
+0x67 DependencyPointerTag
+0x68 DependencyMarkerTag
+0x69 M4MuxChannelDescrTag
+0x6A-0xBF Reserved for ISO use
+0xC0-0xFE User private
+0xFF Forbidden
+ */
+
+/* objectTypeIndication as of 14496-1
+0x00 Forbidden
+0x01 Systems ISO/IEC 14496-1 a
+0x02 Systems ISO/IEC 14496-1 b
+0x03 Interaction Stream
+0x04 Systems ISO/IEC 14496-1 Extended BIFS Configuration c
+0x05 Systems ISO/IEC 14496-1 AFX d
+0x06 Font Data Stream
+0x07 Synthesized Texture Stream
+0x08 Streaming Text Stream
+0x09-0x1F reserved for ISO use
+0x20 Visual ISO/IEC 14496-2 e
+0x21 Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10 f
+0x22 Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10 f
+0x23-0x3F reserved for ISO use
+0x40 Audio ISO/IEC 14496-3 g
+0x41-0x5F reserved for ISO use
+0x60 Visual ISO/IEC 13818-2 Simple Profile
+0x61 Visual ISO/IEC 13818-2 Main Profile
+0x62 Visual ISO/IEC 13818-2 SNR Profile
+0x63 Visual ISO/IEC 13818-2 Spatial Profile
+0x64 Visual ISO/IEC 13818-2 High Profile
+0x65 Visual ISO/IEC 13818-2 422 Profile
+0x66 Audio ISO/IEC 13818-7 Main Profile
+0x67 Audio ISO/IEC 13818-7 LowComplexity Profile
+0x68 Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile
+0x69 Audio ISO/IEC 13818-3
+0x6A Visual ISO/IEC 11172-2
+0x6B Audio ISO/IEC 11172-3
+0x6C Visual ISO/IEC 10918-1
+0x6D reserved for registration authority
+0x6E Visual ISO/IEC 15444-1
+0x6F - 0x9F reserved for ISO use
+0xA0 - 0xBF reserved for registration authority i
+0xC0 - 0xE0 user private
+0xE1 reserved for registration authority i
+0xE2 - 0xFE user private
+0xFF no object type specified h
+ */
+public class ObjectDescriptorFactory {
+ protected static Logger log = Logger.getLogger(ObjectDescriptorFactory.class.getName());
+
+ protected static Map<Integer, Map<Integer, Class<? extends BaseDescriptor>>> descriptorRegistry = new HashMap<Integer, Map<Integer, Class<? extends BaseDescriptor>>>();
+
+ static {
+ Set<Class<? extends BaseDescriptor>> annotated = new HashSet<Class<? extends BaseDescriptor>>();
+
+ annotated.add(DecoderSpecificInfo.class);
+ annotated.add(SLConfigDescriptor.class);
+ annotated.add(BaseDescriptor.class);
+ annotated.add(ExtensionDescriptor.class);
+ annotated.add(ObjectDescriptorBase.class);
+ annotated.add(ProfileLevelIndicationDescriptor.class);
+ annotated.add(AudioSpecificConfig.class);
+ annotated.add(ExtensionProfileLevelDescriptor.class);
+ annotated.add(ESDescriptor.class);
+ annotated.add(DecoderConfigDescriptor.class);
+ //annotated.add(ObjectDescriptor.class);
+
+ for (Class<? extends BaseDescriptor> clazz : annotated) {
+ final Descriptor descriptor = clazz.getAnnotation(Descriptor.class);
+ final int[] tags = descriptor.tags();
+ final int objectTypeInd = descriptor.objectTypeIndication();
+
+ Map<Integer, Class<? extends BaseDescriptor>> tagMap = descriptorRegistry.get(objectTypeInd);
+ if (tagMap == null) {
+ tagMap = new HashMap<Integer, Class<? extends BaseDescriptor>>();
+ }
+ for (int tag : tags) {
+ tagMap.put(tag, clazz);
+ }
+ descriptorRegistry.put(objectTypeInd, tagMap);
+ }
+ }
+
+ public static BaseDescriptor createFrom(int objectTypeIndication, ByteBuffer bb) throws IOException {
+ int tag = IsoTypeReader.readUInt8(bb);
+
+ Map<Integer, Class<? extends BaseDescriptor>> tagMap = descriptorRegistry.get(objectTypeIndication);
+ if (tagMap == null) {
+ tagMap = descriptorRegistry.get(-1);
+ }
+ Class<? extends BaseDescriptor> aClass = tagMap.get(tag);
+
+// if (tag == 0x00) {
+// log.warning("Found illegal tag 0x00! objectTypeIndication " + Integer.toHexString(objectTypeIndication) +
+// " and tag " + Integer.toHexString(tag) + " using: " + aClass);
+// aClass = BaseDescriptor.class;
+// }
+
+ BaseDescriptor baseDescriptor;
+ if (aClass == null || aClass.isInterface() || Modifier.isAbstract(aClass.getModifiers())) {
+ log.warning("No ObjectDescriptor found for objectTypeIndication " + Integer.toHexString(objectTypeIndication) +
+ " and tag " + Integer.toHexString(tag) + " found: " + aClass);
+ baseDescriptor = new UnknownDescriptor();
+ } else {
+ try {
+ baseDescriptor = aClass.newInstance();
+ } catch (Exception e) {
+ log.log(Level.SEVERE, "Couldn't instantiate BaseDescriptor class " + aClass + " for objectTypeIndication " + objectTypeIndication + " and tag " + tag, e);
+ throw new RuntimeException(e);
+ }
+ }
+ baseDescriptor.parse(tag, bb);
+ return baseDescriptor;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ProfileLevelIndicationDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ProfileLevelIndicationDescriptor.java
new file mode 100644
index 0000000..625277e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/ProfileLevelIndicationDescriptor.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * class ProfileLevelIndicationIndexDescriptor () extends BaseDescriptor
+ * : bit(8) ProfileLevelIndicationIndexDescrTag {
+ * bit(8) profileLevelIndicationIndex;
+ * }
+ */
+@Descriptor(tags = 0x14)
+public class ProfileLevelIndicationDescriptor extends BaseDescriptor {
+ int profileLevelIndicationIndex;
+
+ @Override
+ public void parseDetail( ByteBuffer bb) throws IOException {
+ profileLevelIndicationIndex = IsoTypeReader.readUInt8(bb);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ProfileLevelIndicationDescriptor");
+ sb.append("{profileLevelIndicationIndex=").append(Integer.toHexString(profileLevelIndicationIndex));
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ProfileLevelIndicationDescriptor that = (ProfileLevelIndicationDescriptor) o;
+
+ if (profileLevelIndicationIndex != that.profileLevelIndicationIndex) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return profileLevelIndicationIndex;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/SLConfigDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/SLConfigDescriptor.java
new file mode 100644
index 0000000..70a58e6
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/SLConfigDescriptor.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * class SLConfigDescriptor extends BaseDescriptor : bit(8) tag=SLConfigDescrTag {
+ * bit(8) predefined;
+ * if (predefined==0) {
+ * bit(1) useAccessUnitStartFlag;
+ * bit(1) useAccessUnitEndFlag;
+ * bit(1) useRandomAccessPointFlag;
+ * bit(1) hasRandomAccessUnitsOnlyFlag;
+ * bit(1) usePaddingFlag;
+ * bit(1) useTimeStampsFlag;
+ * bit(1) useIdleFlag;
+ * bit(1) durationFlag;
+ * bit(32) timeStampResolution;
+ * bit(32) OCRResolution;
+ * bit(8) timeStampLength; // must be ≤ 64
+ * bit(8) OCRLength; // must be ≤ 64
+ * bit(8) AU_Length; // must be ≤ 32
+ * bit(8) instantBitrateLength;
+ * bit(4) degradationPriorityLength;
+ * bit(5) AU_seqNumLength; // must be ≤ 16
+ * bit(5) packetSeqNumLength; // must be ≤ 16
+ * bit(2) reserved=0b11;
+ * }
+ * if (durationFlag) {
+ * bit(32) timeScale;
+ * bit(16) accessUnitDuration;
+ * bit(16) compositionUnitDuration;
+ * }
+ * if (!useTimeStampsFlag) {
+ * bit(timeStampLength) startDecodingTimeStamp;
+ * bit(timeStampLength) startCompositionTimeStamp;
+ * }
+ * }
+ */
+@Descriptor(tags = {0x06})
+public class SLConfigDescriptor extends BaseDescriptor {
+ int predefined;
+
+ public int getPredefined() {
+ return predefined;
+ }
+
+ public void setPredefined(int predefined) {
+ this.predefined = predefined;
+ }
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ predefined = IsoTypeReader.readUInt8(bb);
+ }
+
+ public int serializedSize() {
+ return 3;
+ }
+
+ public ByteBuffer serialize() {
+ ByteBuffer out = ByteBuffer.allocate(3);
+ IsoTypeWriter.writeUInt8(out, 6);
+ IsoTypeWriter.writeUInt8(out, 1);
+ IsoTypeWriter.writeUInt8(out, predefined);
+ return out;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("SLConfigDescriptor");
+ sb.append("{predefined=").append(predefined);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ SLConfigDescriptor that = (SLConfigDescriptor) o;
+
+ if (predefined != that.predefined) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return predefined;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/UnknownDescriptor.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/UnknownDescriptor.java
new file mode 100644
index 0000000..dd75a0f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/objectdescriptors/UnknownDescriptor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.objectdescriptors;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Logger;
+
+public class UnknownDescriptor extends BaseDescriptor {
+ private ByteBuffer data;
+ private static Logger log = Logger.getLogger(UnknownDescriptor.class.getName());
+
+ @Override
+ public void parseDetail(ByteBuffer bb) throws IOException {
+ data = (ByteBuffer) bb.slice().limit(this.getSizeOfInstance());
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("UnknownDescriptor");
+ sb.append("{tag=").append(tag);
+ sb.append(", sizeOfInstance=").append(sizeOfInstance);
+ sb.append(", data=").append(data);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/all-wcprops
new file mode 100644
index 0000000..7ac0085
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/all-wcprops
@@ -0,0 +1,59 @@
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping
+END
+RateShareEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java
+END
+UnknownEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java
+END
+VisualRandomAccessEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java
+END
+RollRecoveryEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java
+END
+SampleToGroupBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java
+END
+CencSampleEncryptionInformationGroupEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 144
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java
+END
+SampleGroupDescriptionBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 128
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java
+END
+TemporalLevelEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 121
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java
+END
+GroupEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/entries
new file mode 100644
index 0000000..05c761e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/entries
@@ -0,0 +1,334 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+RateShareEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+800d0a2f813152ba00d293052be8b937
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8043
+
+UnknownEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+92c8a49a5fb8f163a1a38714e8776de1
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2046
+
+VisualRandomAccessEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+544b84d4026c3b8926ba2a44b11ebfcb
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3781
+
+RollRecoveryEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+9233a1017082e95aff5d23c84d635589
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2417
+
+SampleToGroupBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+5eca2acada7e010970a7037166d46bc1
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5685
+
+CencSampleEncryptionInformationGroupEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+fc1a2ad8992cd88b104626d531df492b
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3641
+
+SampleGroupDescriptionBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+07cd2c76b48eea326d9865d4ba5320e3
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7107
+
+TemporalLevelEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+4657c50c8e3abaf12747fc9413eb83a0
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3101
+
+GroupEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+a699e1a559c1de4043b594f65d462a82
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+877
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/CencSampleEncryptionInformationGroupEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/CencSampleEncryptionInformationGroupEntry.java.svn-base
new file mode 100644
index 0000000..b54f4d9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/CencSampleEncryptionInformationGroupEntry.java.svn-base
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Each sample in a protected track shall be associated with an IsEncrypted flag, IV_Size, and KID.
+ * This can be accomplished by (a) relying on the default values in the TrackEncryptionBox
+ * (see 8.2), or (b) specifying the parameters by sample group, or (c) using a combination of these two techniques.
+ * <p/>
+ * When specifying the parameters by sample group, the SampleToGroupBox in the sample table or track
+ * fragment specifies which samples use which sample group description from the SampleGroupDescriptionBox.
+ */
+public class CencSampleEncryptionInformationGroupEntry extends GroupEntry {
+ public static final String TYPE = "seig";
+
+ private int isEncrypted;
+ private byte ivSize;
+ private byte[] kid = new byte[16];
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ isEncrypted = IsoTypeReader.readUInt24(byteBuffer);
+ ivSize = (byte) IsoTypeReader.readUInt8(byteBuffer);
+ kid = new byte[16];
+ byteBuffer.get(kid);
+
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(20);
+ IsoTypeWriter.writeUInt24(byteBuffer, isEncrypted);
+ IsoTypeWriter.writeUInt8(byteBuffer, ivSize);
+ byteBuffer.put(kid);
+ byteBuffer.rewind();
+ return byteBuffer;
+ }
+
+ public int getEncrypted() {
+ return isEncrypted;
+ }
+
+ public void setEncrypted(int encrypted) {
+ isEncrypted = encrypted;
+ }
+
+ public byte getIvSize() {
+ return ivSize;
+ }
+
+ public void setIvSize(byte ivSize) {
+ this.ivSize = ivSize;
+ }
+
+ public byte[] getKid() {
+ return kid;
+ }
+
+ public void setKid(byte[] kid) {
+ assert kid.length == 16;
+ this.kid = kid;
+ }
+
+ @Override
+ public String toString() {
+ return "CencSampleEncryptionInformationGroupEntry{" +
+ "isEncrypted=" + isEncrypted +
+ ", ivSize=" + ivSize +
+ ", kid=" + Hex.encodeHex(kid) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CencSampleEncryptionInformationGroupEntry that = (CencSampleEncryptionInformationGroupEntry) o;
+
+ if (isEncrypted != that.isEncrypted) {
+ return false;
+ }
+ if (ivSize != that.ivSize) {
+ return false;
+ }
+ if (!Arrays.equals(kid, that.kid)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = isEncrypted;
+ result = 31 * result + (int) ivSize;
+ result = 31 * result + (kid != null ? Arrays.hashCode(kid) : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/GroupEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/GroupEntry.java.svn-base
new file mode 100644
index 0000000..0d78d25
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/GroupEntry.java.svn-base
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+public abstract class GroupEntry {
+ public abstract void parse(ByteBuffer byteBuffer);
+ public abstract ByteBuffer get();
+
+ public int size() {
+ return get().limit();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RateShareEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RateShareEntry.java.svn-base
new file mode 100644
index 0000000..ae5d380
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RateShareEntry.java.svn-base
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Each sample of a track may be associated to (zero or) one of a number of sample group descriptions, each of
+ * which defines a record of rate-share information. Typically the same rate-share information applies to many
+ * consecutive samples and it may therefore be enough to define two or three sample group descriptions that
+ * can be used at different time intervals.
+ * <p/>
+ * The grouping type 'rash' (short for rate share) is defined as the grouping criterion for rate share information.
+ * Zero or one sample-to-group box ('sbgp') for the grouping type 'rash' can be contained in the sample
+ * table box ('stbl') of a track. It shall reside in a hint track, if a hint track is used, otherwise in a media track.
+ * <p/>
+ * Target rate share may be specified for several operation points that are defined in terms of the total available
+ * bitrate, i.e., the bitrate that should be shared. If only one operation point is defined, the target rate share
+ * applies to all available bitrates. If several operation points are defined, then each operation point specifies a
+ * target rate share. Target rate share values specified for the first and the last operation points also specify the
+ * target rate share values at lower and higher available bitrates, respectively. The target rate share between two
+ * operation points is specified to be in the range between the target rate shares of those operation points. One
+ * possibility is to estimate with linear interpolation.
+ */
+public class RateShareEntry extends GroupEntry {
+ public static final String TYPE = "rash";
+
+ private short operationPointCut;
+ private short targetRateShare;
+ private List<Entry> entries = new LinkedList<Entry>();
+ private int maximumBitrate;
+ private int minimumBitrate;
+ private short discardPriority;
+
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ operationPointCut = byteBuffer.getShort();
+ if (operationPointCut == 1) {
+ targetRateShare = byteBuffer.getShort();
+ } else {
+ int entriesLeft = operationPointCut;
+ while (entriesLeft-- > 0) {
+ entries.add(new Entry(l2i(IsoTypeReader.readUInt32(byteBuffer)), byteBuffer.getShort()));
+ }
+ }
+ maximumBitrate = l2i(IsoTypeReader.readUInt32(byteBuffer));
+ minimumBitrate = l2i(IsoTypeReader.readUInt32(byteBuffer));
+ discardPriority = (short) IsoTypeReader.readUInt8(byteBuffer);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer buf = ByteBuffer.allocate(operationPointCut == 1?13:(operationPointCut * 6 + 11 ));
+ buf.putShort(operationPointCut);
+ if (operationPointCut == 1) {
+ buf.putShort(targetRateShare );
+ } else {
+ for (Entry entry : entries) {
+ buf.putInt(entry.getAvailableBitrate());
+ buf.putShort(entry.getTargetRateShare());
+ }
+ }
+ buf.putInt(maximumBitrate);
+ buf.putInt(minimumBitrate);
+ IsoTypeWriter.writeUInt8(buf, discardPriority);
+ buf.rewind();
+ return buf;
+ }
+
+ public static class Entry {
+ public Entry(int availableBitrate, short targetRateShare) {
+ this.availableBitrate = availableBitrate;
+ this.targetRateShare = targetRateShare;
+ }
+
+ int availableBitrate;
+ short targetRateShare;
+
+ @Override
+ public String toString() {
+ return "{" +
+ "availableBitrate=" + availableBitrate +
+ ", targetRateShare=" + targetRateShare +
+ '}';
+ }
+
+ public int getAvailableBitrate() {
+ return availableBitrate;
+ }
+
+ public void setAvailableBitrate(int availableBitrate) {
+ this.availableBitrate = availableBitrate;
+ }
+
+ public short getTargetRateShare() {
+ return targetRateShare;
+ }
+
+ public void setTargetRateShare(short targetRateShare) {
+ this.targetRateShare = targetRateShare;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (availableBitrate != entry.availableBitrate) {
+ return false;
+ }
+ if (targetRateShare != entry.targetRateShare) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = availableBitrate;
+ result = 31 * result + (int) targetRateShare;
+ return result;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RateShareEntry that = (RateShareEntry) o;
+
+ if (discardPriority != that.discardPriority) {
+ return false;
+ }
+ if (maximumBitrate != that.maximumBitrate) {
+ return false;
+ }
+ if (minimumBitrate != that.minimumBitrate) {
+ return false;
+ }
+ if (operationPointCut != that.operationPointCut) {
+ return false;
+ }
+ if (targetRateShare != that.targetRateShare) {
+ return false;
+ }
+ if (entries != null ? !entries.equals(that.entries) : that.entries != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) operationPointCut;
+ result = 31 * result + (int) targetRateShare;
+ result = 31 * result + (entries != null ? entries.hashCode() : 0);
+ result = 31 * result + maximumBitrate;
+ result = 31 * result + minimumBitrate;
+ result = 31 * result + (int) discardPriority;
+ return result;
+ }
+
+ public short getOperationPointCut() {
+ return operationPointCut;
+ }
+
+ public void setOperationPointCut(short operationPointCut) {
+ this.operationPointCut = operationPointCut;
+ }
+
+ public short getTargetRateShare() {
+ return targetRateShare;
+ }
+
+ public void setTargetRateShare(short targetRateShare) {
+ this.targetRateShare = targetRateShare;
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public int getMaximumBitrate() {
+ return maximumBitrate;
+ }
+
+ public void setMaximumBitrate(int maximumBitrate) {
+ this.maximumBitrate = maximumBitrate;
+ }
+
+ public int getMinimumBitrate() {
+ return minimumBitrate;
+ }
+
+ public void setMinimumBitrate(int minimumBitrate) {
+ this.minimumBitrate = minimumBitrate;
+ }
+
+ public short getDiscardPriority() {
+ return discardPriority;
+ }
+
+ public void setDiscardPriority(short discardPriority) {
+ this.discardPriority = discardPriority;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RollRecoveryEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RollRecoveryEntry.java.svn-base
new file mode 100644
index 0000000..bd5b89e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RollRecoveryEntry.java.svn-base
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+/**
+ * roll_distance is a signed integer that gives the number of samples that must be decoded in order for
+ * a sample to be decoded correctly. A positive value indicates the number of samples after the sample
+ * that is a group member that must be decoded such that at the last of these recovery is complete, i.e.
+ * the last sample is correct. A negative value indicates the number of samples before the sample that is
+ * a group member that must be decoded in order for recovery to be complete at the marked sample.
+ * The value zero must not be used; the sync sample table documents random access points for which
+ * no recovery roll is needed.
+ */
+public class RollRecoveryEntry extends GroupEntry {
+ public static final String TYPE = "roll";
+ private short rollDistance;
+
+ public short getRollDistance() {
+ return rollDistance;
+ }
+
+ public void setRollDistance(short rollDistance) {
+ this.rollDistance = rollDistance;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ rollDistance = byteBuffer.getShort();
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(2);
+ content.putShort(rollDistance);
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RollRecoveryEntry entry = (RollRecoveryEntry) o;
+
+ if (rollDistance != entry.rollDistance) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) rollDistance;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleGroupDescriptionBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleGroupDescriptionBox.java.svn-base
new file mode 100644
index 0000000..df4a96f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleGroupDescriptionBox.java.svn-base
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * This description table gives information about the characteristics of sample groups. The descriptive
+ * information is any other information needed to define or characterize the sample group.
+ * <p/>
+ * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
+ * track. Each instance of the SampleGroupDescription box has a type code that distinguishes different
+ * sample groupings. Within a track, there shall be at most one instance of this box with a particular grouping
+ * type. The associated SampleToGroup shall indicate the same value for the grouping type.
+ * <p/>
+ * The information is stored in the sample group description box after the entry-count. An abstract entry type is
+ * defined and sample groupings shall define derived types to represent the description of each sample group.
+ * For video tracks, an abstract VisualSampleGroupEntry is used with similar types for audio and hint tracks.
+ */
+public class SampleGroupDescriptionBox extends AbstractFullBox {
+ public static final String TYPE = "sgpd";
+
+ private String groupingType;
+ private int defaultLength;
+ private List<GroupEntry> groupEntries = new LinkedList<GroupEntry>();
+ private int descriptionLength;
+
+ public SampleGroupDescriptionBox() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 8;
+ if (getVersion() == 1) {
+ size += 4;
+ }
+ size += 4; // entryCount
+ for (GroupEntry groupEntry : groupEntries) {
+ if (getVersion() == 1 && defaultLength == 0) {
+ size += 4;
+ }
+ size += groupEntry.size();
+ }
+ return size;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(groupingType.getBytes());
+ if (this.getVersion() == 1) {
+ IsoTypeWriter.writeUInt32(byteBuffer, defaultLength);
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, this.groupEntries.size());
+ for (GroupEntry entry : groupEntries) {
+ if (this.getVersion() == 1 && defaultLength == 0) {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.get().limit());
+ }
+ byteBuffer.put(entry.get());
+ }
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ if (this.getVersion() != 1) {
+ throw new RuntimeException("SampleGroupDescriptionBox are only supported in version 1");
+ }
+ groupingType = IsoTypeReader.read4cc(content);
+ if (this.getVersion() == 1) {
+ defaultLength = l2i(IsoTypeReader.readUInt32(content));
+ }
+ long entryCount = IsoTypeReader.readUInt32(content);
+ while (entryCount-- > 0) {
+ int length = defaultLength;
+ if (this.getVersion() == 1) {
+ if (defaultLength == 0) {
+ descriptionLength = l2i(IsoTypeReader.readUInt32(content));
+ length = descriptionLength;
+ }
+ } else {
+ throw new RuntimeException("This should be implemented");
+ }
+ int finalPos = content.position() + length;
+ ByteBuffer parseMe = content.slice();
+ parseMe.limit(length);
+ groupEntries.add(parseGroupEntry(parseMe, groupingType));
+ content.position(finalPos);
+ }
+
+ }
+
+ private GroupEntry parseGroupEntry(ByteBuffer content, String groupingType) {
+ GroupEntry groupEntry;
+ if (RollRecoveryEntry.TYPE.equals(groupingType)) {
+ groupEntry = new RollRecoveryEntry();
+ } else if (RateShareEntry.TYPE.equals(groupingType)) {
+ groupEntry = new RateShareEntry();
+ } else if (CencSampleEncryptionInformationGroupEntry.TYPE.equals(groupingType)) {
+ groupEntry = new CencSampleEncryptionInformationGroupEntry();
+ } else if (VisualRandomAccessEntry.TYPE.equals(groupingType)) {
+ groupEntry = new VisualRandomAccessEntry();
+ } else if (TemporalLevelEntry.TYPE.equals(groupingType)) {
+ groupEntry = new TemporalLevelEntry();
+ } else {
+ groupEntry = new UnknownEntry();
+ }
+ groupEntry.parse(content);
+ return groupEntry;
+ }
+
+
+ public String getGroupingType() {
+ return groupingType;
+ }
+
+ public void setGroupingType(String groupingType) {
+ this.groupingType = groupingType;
+ }
+
+ public int getDefaultLength() {
+ return defaultLength;
+ }
+
+ public void setDefaultLength(int defaultLength) {
+ this.defaultLength = defaultLength;
+ }
+
+ public List<GroupEntry> getGroupEntries() {
+ return groupEntries;
+ }
+
+ public void setGroupEntries(List<GroupEntry> groupEntries) {
+ this.groupEntries = groupEntries;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ SampleGroupDescriptionBox that = (SampleGroupDescriptionBox) o;
+
+ if (defaultLength != that.defaultLength) {
+ return false;
+ }
+ if (groupEntries != null ? !groupEntries.equals(that.groupEntries) : that.groupEntries != null) {
+ return false;
+ }
+ if (groupingType != null ? !groupingType.equals(that.groupingType) : that.groupingType != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = groupingType != null ? groupingType.hashCode() : 0;
+ result = 31 * result + defaultLength;
+ result = 31 * result + (groupEntries != null ? groupEntries.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "SampleGroupDescriptionBox{" +
+ "groupingType='" + groupingType + '\'' +
+ ", defaultLength=" + defaultLength +
+ ", groupEntries=" + groupEntries +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleToGroupBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleToGroupBox.java.svn-base
new file mode 100644
index 0000000..0fa059e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleToGroupBox.java.svn-base
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * This table can be used to find the group that a sample belongs to and the associated description of that
+ * sample group. The table is compactly coded with each entry giving the index of the first sample of a run of
+ * samples with the same sample group descriptor. The sample group description ID is an index that refers to a
+ * SampleGroupDescription box, which contains entries describing the characteristics of each sample group.
+ * <p/>
+ * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
+ * track. Each instance of the SampleToGroup box has a type code that distinguishes different sample
+ * groupings. Within a track, there shall be at most one instance of this box with a particular grouping type. The
+ * associated SampleGroupDescription shall indicate the same value for the grouping type.
+ * <p/>
+ * Version 1 of this box should only be used if a grouping type parameter is needed.
+ */
+public class SampleToGroupBox extends AbstractFullBox {
+ public static final String TYPE = "sbgp";
+
+
+ private String groupingType;
+ private String groupingTypeParameter;
+
+ List<Entry> entries = new LinkedList<Entry>();
+
+ public SampleToGroupBox() {
+ super(TYPE);
+
+ }
+
+ @Override
+ protected long getContentSize() {
+ return this.getVersion() == 1 ? entries.size() * 8 + 16 : entries.size() * 8 + 12;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(groupingType.getBytes());
+ if (this.getVersion() == 1) {
+ byteBuffer.put(groupingTypeParameter.getBytes());
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
+ for (Entry entry : entries) {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getSampleCount());
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getGroupDescriptionIndex());
+ }
+
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ groupingType = IsoTypeReader.read4cc(content);
+ if (this.getVersion() == 1) {
+ groupingTypeParameter = IsoTypeReader.read4cc(content);
+ }
+ long entryCount = IsoTypeReader.readUInt32(content);
+ while (entryCount-- > 0) {
+ entries.add(new Entry(l2i(IsoTypeReader.readUInt32(content)), l2i(IsoTypeReader.readUInt32(content))));
+ }
+ }
+
+ public static class Entry {
+ private long sampleCount;
+ private int groupDescriptionIndex;
+
+ public Entry(long sampleCount, int groupDescriptionIndex) {
+ this.sampleCount = sampleCount;
+ this.groupDescriptionIndex = groupDescriptionIndex;
+ }
+
+ public long getSampleCount() {
+ return sampleCount;
+ }
+
+ public void setSampleCount(long sampleCount) {
+ this.sampleCount = sampleCount;
+ }
+
+ public int getGroupDescriptionIndex() {
+ return groupDescriptionIndex;
+ }
+
+ public void setGroupDescriptionIndex(int groupDescriptionIndex) {
+ this.groupDescriptionIndex = groupDescriptionIndex;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "sampleCount=" + sampleCount +
+ ", groupDescriptionIndex=" + groupDescriptionIndex +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (groupDescriptionIndex != entry.groupDescriptionIndex) {
+ return false;
+ }
+ if (sampleCount != entry.sampleCount) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (sampleCount ^ (sampleCount >>> 32));
+ result = 31 * result + groupDescriptionIndex;
+ return result;
+ }
+ }
+
+ public String getGroupingType() {
+ return groupingType;
+ }
+
+ public void setGroupingType(String groupingType) {
+ this.groupingType = groupingType;
+ }
+
+ public String getGroupingTypeParameter() {
+ return groupingTypeParameter;
+ }
+
+ public void setGroupingTypeParameter(String groupingTypeParameter) {
+ this.groupingTypeParameter = groupingTypeParameter;
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/TemporalLevelEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/TemporalLevelEntry.java.svn-base
new file mode 100644
index 0000000..798fd9c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/TemporalLevelEntry.java.svn-base
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+/**
+ * The Temporal Level sample grouping ('tele') provides a codec-independent sample grouping that can be used to group samples (access units) in a track (and potential track fragments) according to temporal level, where samples of one temporal level have no coding dependencies on samples of higher temporal levels. The temporal level equals the sample group description index (taking values 1, 2, 3, etc). The bitstream containing only the access units from the first temporal level to a higher temporal level remains conforming to the coding standard.
+ *
+ * A grouping according to temporal level facilitates easy extraction of temporal subsequences, for instance using the Subsegment Indexing box in 0.
+ *
+ */
+public class TemporalLevelEntry extends GroupEntry {
+ public static final String TYPE = "tele";
+ private boolean levelIndependentlyDecodable;
+ private short reserved;
+
+ public boolean isLevelIndependentlyDecodable() {
+ return levelIndependentlyDecodable;
+ }
+
+ public void setLevelIndependentlyDecodable(boolean levelIndependentlyDecodable) {
+ this.levelIndependentlyDecodable = levelIndependentlyDecodable;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ final byte b = byteBuffer.get();
+ levelIndependentlyDecodable = ((b & 0x80) == 0x80);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(1);
+ content.put((byte) (levelIndependentlyDecodable ? 0x80 : 0x00));
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TemporalLevelEntry that = (TemporalLevelEntry) o;
+
+ if (levelIndependentlyDecodable != that.levelIndependentlyDecodable) return false;
+ if (reserved != that.reserved) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (levelIndependentlyDecodable ? 1 : 0);
+ result = 31 * result + (int) reserved;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("TemporalLevelEntry");
+ sb.append("{levelIndependentlyDecodable=").append(levelIndependentlyDecodable);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/UnknownEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/UnknownEntry.java.svn-base
new file mode 100644
index 0000000..9efcbea
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/UnknownEntry.java.svn-base
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.Hex;
+
+import java.nio.ByteBuffer;
+
+/**
+ *
+ */
+public class UnknownEntry extends GroupEntry {
+ private ByteBuffer content;
+
+ public UnknownEntry() {
+ }
+
+ public ByteBuffer getContent() {
+ return content;
+ }
+
+ public void setContent(ByteBuffer content) {
+ this.content = (ByteBuffer) content.duplicate().rewind();
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ this.content = (ByteBuffer) byteBuffer.duplicate().rewind();
+ }
+
+ @Override
+ public ByteBuffer get() {
+ return content.duplicate();
+ }
+
+ @Override
+ public String toString() {
+ ByteBuffer bb = content.duplicate();
+ bb.rewind();
+ byte[] b = new byte[bb.limit()];
+ bb.get(b);
+ return "UnknownEntry{" +
+ "content=" + Hex.encodeHex(b) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ UnknownEntry that = (UnknownEntry) o;
+
+ if (content != null ? !content.equals(that.content) : that.content != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return content != null ? content.hashCode() : 0;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/VisualRandomAccessEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/VisualRandomAccessEntry.java.svn-base
new file mode 100644
index 0000000..ed5d199
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/VisualRandomAccessEntry.java.svn-base
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.nio.ByteBuffer;
+
+/**
+ * For some coding systems a sync sample is specified to be a random access point after which all samples in decoding order can be correctly decoded. However, it may be possible to encode an “open” random access point, after which all samples in output order can be correctly decoded, but some samples following the random access point in decoding order and preceding the random access point in output order need not be correctly decodable. For example, an intra picture starting an open group of pictures can be followed in decoding order by (bi-)predicted pictures that however precede the intra picture in output order; though they possibly cannot be correctly decoded if the decoding starts from the intra picture, they are not needed.
+ *
+ * Such “open” random-access samples can be marked by being a member of this group. Samples marked by this group must be random access points, and may also be sync points (i.e. it is not required that samples marked by the sync sample table be excluded).
+ *
+ */
+public class VisualRandomAccessEntry extends GroupEntry {
+ public static final String TYPE = "rap ";
+ private boolean numLeadingSamplesKnown;
+ private short numLeadingSamples;
+
+ public boolean isNumLeadingSamplesKnown() {
+ return numLeadingSamplesKnown;
+ }
+
+ public void setNumLeadingSamplesKnown(boolean numLeadingSamplesKnown) {
+ this.numLeadingSamplesKnown = numLeadingSamplesKnown;
+ }
+
+ public short getNumLeadingSamples() {
+ return numLeadingSamples;
+ }
+
+ public void setNumLeadingSamples(short numLeadingSamples) {
+ this.numLeadingSamples = numLeadingSamples;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ final byte b = byteBuffer.get();
+ numLeadingSamplesKnown = ((b & 0x80) == 0x80);
+ numLeadingSamples = (short) (b & 0x7f);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(1);
+ content.put((byte) ((numLeadingSamplesKnown? 0x80 : 0x00)| (numLeadingSamples & 0x7f)));
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ VisualRandomAccessEntry that = (VisualRandomAccessEntry) o;
+
+ if (numLeadingSamples != that.numLeadingSamples) return false;
+ if (numLeadingSamplesKnown != that.numLeadingSamplesKnown) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (numLeadingSamplesKnown ? 1 : 0);
+ result = 31 * result + (int) numLeadingSamples;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("VisualRandomAccessEntry");
+ sb.append("{numLeadingSamplesKnown=").append(numLeadingSamplesKnown);
+ sb.append(", numLeadingSamples=").append(numLeadingSamples);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java
new file mode 100644
index 0000000..b54f4d9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Each sample in a protected track shall be associated with an IsEncrypted flag, IV_Size, and KID.
+ * This can be accomplished by (a) relying on the default values in the TrackEncryptionBox
+ * (see 8.2), or (b) specifying the parameters by sample group, or (c) using a combination of these two techniques.
+ * <p/>
+ * When specifying the parameters by sample group, the SampleToGroupBox in the sample table or track
+ * fragment specifies which samples use which sample group description from the SampleGroupDescriptionBox.
+ */
+public class CencSampleEncryptionInformationGroupEntry extends GroupEntry {
+ public static final String TYPE = "seig";
+
+ private int isEncrypted;
+ private byte ivSize;
+ private byte[] kid = new byte[16];
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ isEncrypted = IsoTypeReader.readUInt24(byteBuffer);
+ ivSize = (byte) IsoTypeReader.readUInt8(byteBuffer);
+ kid = new byte[16];
+ byteBuffer.get(kid);
+
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(20);
+ IsoTypeWriter.writeUInt24(byteBuffer, isEncrypted);
+ IsoTypeWriter.writeUInt8(byteBuffer, ivSize);
+ byteBuffer.put(kid);
+ byteBuffer.rewind();
+ return byteBuffer;
+ }
+
+ public int getEncrypted() {
+ return isEncrypted;
+ }
+
+ public void setEncrypted(int encrypted) {
+ isEncrypted = encrypted;
+ }
+
+ public byte getIvSize() {
+ return ivSize;
+ }
+
+ public void setIvSize(byte ivSize) {
+ this.ivSize = ivSize;
+ }
+
+ public byte[] getKid() {
+ return kid;
+ }
+
+ public void setKid(byte[] kid) {
+ assert kid.length == 16;
+ this.kid = kid;
+ }
+
+ @Override
+ public String toString() {
+ return "CencSampleEncryptionInformationGroupEntry{" +
+ "isEncrypted=" + isEncrypted +
+ ", ivSize=" + ivSize +
+ ", kid=" + Hex.encodeHex(kid) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CencSampleEncryptionInformationGroupEntry that = (CencSampleEncryptionInformationGroupEntry) o;
+
+ if (isEncrypted != that.isEncrypted) {
+ return false;
+ }
+ if (ivSize != that.ivSize) {
+ return false;
+ }
+ if (!Arrays.equals(kid, that.kid)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = isEncrypted;
+ result = 31 * result + (int) ivSize;
+ result = 31 * result + (kid != null ? Arrays.hashCode(kid) : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java
new file mode 100644
index 0000000..0d78d25
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+public abstract class GroupEntry {
+ public abstract void parse(ByteBuffer byteBuffer);
+ public abstract ByteBuffer get();
+
+ public int size() {
+ return get().limit();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java
new file mode 100644
index 0000000..ae5d380
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Each sample of a track may be associated to (zero or) one of a number of sample group descriptions, each of
+ * which defines a record of rate-share information. Typically the same rate-share information applies to many
+ * consecutive samples and it may therefore be enough to define two or three sample group descriptions that
+ * can be used at different time intervals.
+ * <p/>
+ * The grouping type 'rash' (short for rate share) is defined as the grouping criterion for rate share information.
+ * Zero or one sample-to-group box ('sbgp') for the grouping type 'rash' can be contained in the sample
+ * table box ('stbl') of a track. It shall reside in a hint track, if a hint track is used, otherwise in a media track.
+ * <p/>
+ * Target rate share may be specified for several operation points that are defined in terms of the total available
+ * bitrate, i.e., the bitrate that should be shared. If only one operation point is defined, the target rate share
+ * applies to all available bitrates. If several operation points are defined, then each operation point specifies a
+ * target rate share. Target rate share values specified for the first and the last operation points also specify the
+ * target rate share values at lower and higher available bitrates, respectively. The target rate share between two
+ * operation points is specified to be in the range between the target rate shares of those operation points. One
+ * possibility is to estimate with linear interpolation.
+ */
+public class RateShareEntry extends GroupEntry {
+ public static final String TYPE = "rash";
+
+ private short operationPointCut;
+ private short targetRateShare;
+ private List<Entry> entries = new LinkedList<Entry>();
+ private int maximumBitrate;
+ private int minimumBitrate;
+ private short discardPriority;
+
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ operationPointCut = byteBuffer.getShort();
+ if (operationPointCut == 1) {
+ targetRateShare = byteBuffer.getShort();
+ } else {
+ int entriesLeft = operationPointCut;
+ while (entriesLeft-- > 0) {
+ entries.add(new Entry(l2i(IsoTypeReader.readUInt32(byteBuffer)), byteBuffer.getShort()));
+ }
+ }
+ maximumBitrate = l2i(IsoTypeReader.readUInt32(byteBuffer));
+ minimumBitrate = l2i(IsoTypeReader.readUInt32(byteBuffer));
+ discardPriority = (short) IsoTypeReader.readUInt8(byteBuffer);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer buf = ByteBuffer.allocate(operationPointCut == 1?13:(operationPointCut * 6 + 11 ));
+ buf.putShort(operationPointCut);
+ if (operationPointCut == 1) {
+ buf.putShort(targetRateShare );
+ } else {
+ for (Entry entry : entries) {
+ buf.putInt(entry.getAvailableBitrate());
+ buf.putShort(entry.getTargetRateShare());
+ }
+ }
+ buf.putInt(maximumBitrate);
+ buf.putInt(minimumBitrate);
+ IsoTypeWriter.writeUInt8(buf, discardPriority);
+ buf.rewind();
+ return buf;
+ }
+
+ public static class Entry {
+ public Entry(int availableBitrate, short targetRateShare) {
+ this.availableBitrate = availableBitrate;
+ this.targetRateShare = targetRateShare;
+ }
+
+ int availableBitrate;
+ short targetRateShare;
+
+ @Override
+ public String toString() {
+ return "{" +
+ "availableBitrate=" + availableBitrate +
+ ", targetRateShare=" + targetRateShare +
+ '}';
+ }
+
+ public int getAvailableBitrate() {
+ return availableBitrate;
+ }
+
+ public void setAvailableBitrate(int availableBitrate) {
+ this.availableBitrate = availableBitrate;
+ }
+
+ public short getTargetRateShare() {
+ return targetRateShare;
+ }
+
+ public void setTargetRateShare(short targetRateShare) {
+ this.targetRateShare = targetRateShare;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (availableBitrate != entry.availableBitrate) {
+ return false;
+ }
+ if (targetRateShare != entry.targetRateShare) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = availableBitrate;
+ result = 31 * result + (int) targetRateShare;
+ return result;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RateShareEntry that = (RateShareEntry) o;
+
+ if (discardPriority != that.discardPriority) {
+ return false;
+ }
+ if (maximumBitrate != that.maximumBitrate) {
+ return false;
+ }
+ if (minimumBitrate != that.minimumBitrate) {
+ return false;
+ }
+ if (operationPointCut != that.operationPointCut) {
+ return false;
+ }
+ if (targetRateShare != that.targetRateShare) {
+ return false;
+ }
+ if (entries != null ? !entries.equals(that.entries) : that.entries != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) operationPointCut;
+ result = 31 * result + (int) targetRateShare;
+ result = 31 * result + (entries != null ? entries.hashCode() : 0);
+ result = 31 * result + maximumBitrate;
+ result = 31 * result + minimumBitrate;
+ result = 31 * result + (int) discardPriority;
+ return result;
+ }
+
+ public short getOperationPointCut() {
+ return operationPointCut;
+ }
+
+ public void setOperationPointCut(short operationPointCut) {
+ this.operationPointCut = operationPointCut;
+ }
+
+ public short getTargetRateShare() {
+ return targetRateShare;
+ }
+
+ public void setTargetRateShare(short targetRateShare) {
+ this.targetRateShare = targetRateShare;
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public int getMaximumBitrate() {
+ return maximumBitrate;
+ }
+
+ public void setMaximumBitrate(int maximumBitrate) {
+ this.maximumBitrate = maximumBitrate;
+ }
+
+ public int getMinimumBitrate() {
+ return minimumBitrate;
+ }
+
+ public void setMinimumBitrate(int minimumBitrate) {
+ this.minimumBitrate = minimumBitrate;
+ }
+
+ public short getDiscardPriority() {
+ return discardPriority;
+ }
+
+ public void setDiscardPriority(short discardPriority) {
+ this.discardPriority = discardPriority;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java
new file mode 100644
index 0000000..bd5b89e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+/**
+ * roll_distance is a signed integer that gives the number of samples that must be decoded in order for
+ * a sample to be decoded correctly. A positive value indicates the number of samples after the sample
+ * that is a group member that must be decoded such that at the last of these recovery is complete, i.e.
+ * the last sample is correct. A negative value indicates the number of samples before the sample that is
+ * a group member that must be decoded in order for recovery to be complete at the marked sample.
+ * The value zero must not be used; the sync sample table documents random access points for which
+ * no recovery roll is needed.
+ */
+public class RollRecoveryEntry extends GroupEntry {
+ public static final String TYPE = "roll";
+ private short rollDistance;
+
+ public short getRollDistance() {
+ return rollDistance;
+ }
+
+ public void setRollDistance(short rollDistance) {
+ this.rollDistance = rollDistance;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ rollDistance = byteBuffer.getShort();
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(2);
+ content.putShort(rollDistance);
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RollRecoveryEntry entry = (RollRecoveryEntry) o;
+
+ if (rollDistance != entry.rollDistance) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) rollDistance;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java
new file mode 100644
index 0000000..df4a96f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * This description table gives information about the characteristics of sample groups. The descriptive
+ * information is any other information needed to define or characterize the sample group.
+ * <p/>
+ * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
+ * track. Each instance of the SampleGroupDescription box has a type code that distinguishes different
+ * sample groupings. Within a track, there shall be at most one instance of this box with a particular grouping
+ * type. The associated SampleToGroup shall indicate the same value for the grouping type.
+ * <p/>
+ * The information is stored in the sample group description box after the entry-count. An abstract entry type is
+ * defined and sample groupings shall define derived types to represent the description of each sample group.
+ * For video tracks, an abstract VisualSampleGroupEntry is used with similar types for audio and hint tracks.
+ */
+public class SampleGroupDescriptionBox extends AbstractFullBox {
+ public static final String TYPE = "sgpd";
+
+ private String groupingType;
+ private int defaultLength;
+ private List<GroupEntry> groupEntries = new LinkedList<GroupEntry>();
+ private int descriptionLength;
+
+ public SampleGroupDescriptionBox() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 8;
+ if (getVersion() == 1) {
+ size += 4;
+ }
+ size += 4; // entryCount
+ for (GroupEntry groupEntry : groupEntries) {
+ if (getVersion() == 1 && defaultLength == 0) {
+ size += 4;
+ }
+ size += groupEntry.size();
+ }
+ return size;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(groupingType.getBytes());
+ if (this.getVersion() == 1) {
+ IsoTypeWriter.writeUInt32(byteBuffer, defaultLength);
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, this.groupEntries.size());
+ for (GroupEntry entry : groupEntries) {
+ if (this.getVersion() == 1 && defaultLength == 0) {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.get().limit());
+ }
+ byteBuffer.put(entry.get());
+ }
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ if (this.getVersion() != 1) {
+ throw new RuntimeException("SampleGroupDescriptionBox are only supported in version 1");
+ }
+ groupingType = IsoTypeReader.read4cc(content);
+ if (this.getVersion() == 1) {
+ defaultLength = l2i(IsoTypeReader.readUInt32(content));
+ }
+ long entryCount = IsoTypeReader.readUInt32(content);
+ while (entryCount-- > 0) {
+ int length = defaultLength;
+ if (this.getVersion() == 1) {
+ if (defaultLength == 0) {
+ descriptionLength = l2i(IsoTypeReader.readUInt32(content));
+ length = descriptionLength;
+ }
+ } else {
+ throw new RuntimeException("This should be implemented");
+ }
+ int finalPos = content.position() + length;
+ ByteBuffer parseMe = content.slice();
+ parseMe.limit(length);
+ groupEntries.add(parseGroupEntry(parseMe, groupingType));
+ content.position(finalPos);
+ }
+
+ }
+
+ private GroupEntry parseGroupEntry(ByteBuffer content, String groupingType) {
+ GroupEntry groupEntry;
+ if (RollRecoveryEntry.TYPE.equals(groupingType)) {
+ groupEntry = new RollRecoveryEntry();
+ } else if (RateShareEntry.TYPE.equals(groupingType)) {
+ groupEntry = new RateShareEntry();
+ } else if (CencSampleEncryptionInformationGroupEntry.TYPE.equals(groupingType)) {
+ groupEntry = new CencSampleEncryptionInformationGroupEntry();
+ } else if (VisualRandomAccessEntry.TYPE.equals(groupingType)) {
+ groupEntry = new VisualRandomAccessEntry();
+ } else if (TemporalLevelEntry.TYPE.equals(groupingType)) {
+ groupEntry = new TemporalLevelEntry();
+ } else {
+ groupEntry = new UnknownEntry();
+ }
+ groupEntry.parse(content);
+ return groupEntry;
+ }
+
+
+ public String getGroupingType() {
+ return groupingType;
+ }
+
+ public void setGroupingType(String groupingType) {
+ this.groupingType = groupingType;
+ }
+
+ public int getDefaultLength() {
+ return defaultLength;
+ }
+
+ public void setDefaultLength(int defaultLength) {
+ this.defaultLength = defaultLength;
+ }
+
+ public List<GroupEntry> getGroupEntries() {
+ return groupEntries;
+ }
+
+ public void setGroupEntries(List<GroupEntry> groupEntries) {
+ this.groupEntries = groupEntries;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ SampleGroupDescriptionBox that = (SampleGroupDescriptionBox) o;
+
+ if (defaultLength != that.defaultLength) {
+ return false;
+ }
+ if (groupEntries != null ? !groupEntries.equals(that.groupEntries) : that.groupEntries != null) {
+ return false;
+ }
+ if (groupingType != null ? !groupingType.equals(that.groupingType) : that.groupingType != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = groupingType != null ? groupingType.hashCode() : 0;
+ result = 31 * result + defaultLength;
+ result = 31 * result + (groupEntries != null ? groupEntries.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "SampleGroupDescriptionBox{" +
+ "groupingType='" + groupingType + '\'' +
+ ", defaultLength=" + defaultLength +
+ ", groupEntries=" + groupEntries +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java
new file mode 100644
index 0000000..0fa059e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * This table can be used to find the group that a sample belongs to and the associated description of that
+ * sample group. The table is compactly coded with each entry giving the index of the first sample of a run of
+ * samples with the same sample group descriptor. The sample group description ID is an index that refers to a
+ * SampleGroupDescription box, which contains entries describing the characteristics of each sample group.
+ * <p/>
+ * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
+ * track. Each instance of the SampleToGroup box has a type code that distinguishes different sample
+ * groupings. Within a track, there shall be at most one instance of this box with a particular grouping type. The
+ * associated SampleGroupDescription shall indicate the same value for the grouping type.
+ * <p/>
+ * Version 1 of this box should only be used if a grouping type parameter is needed.
+ */
+public class SampleToGroupBox extends AbstractFullBox {
+ public static final String TYPE = "sbgp";
+
+
+ private String groupingType;
+ private String groupingTypeParameter;
+
+ List<Entry> entries = new LinkedList<Entry>();
+
+ public SampleToGroupBox() {
+ super(TYPE);
+
+ }
+
+ @Override
+ protected long getContentSize() {
+ return this.getVersion() == 1 ? entries.size() * 8 + 16 : entries.size() * 8 + 12;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(groupingType.getBytes());
+ if (this.getVersion() == 1) {
+ byteBuffer.put(groupingTypeParameter.getBytes());
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
+ for (Entry entry : entries) {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getSampleCount());
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getGroupDescriptionIndex());
+ }
+
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ groupingType = IsoTypeReader.read4cc(content);
+ if (this.getVersion() == 1) {
+ groupingTypeParameter = IsoTypeReader.read4cc(content);
+ }
+ long entryCount = IsoTypeReader.readUInt32(content);
+ while (entryCount-- > 0) {
+ entries.add(new Entry(l2i(IsoTypeReader.readUInt32(content)), l2i(IsoTypeReader.readUInt32(content))));
+ }
+ }
+
+ public static class Entry {
+ private long sampleCount;
+ private int groupDescriptionIndex;
+
+ public Entry(long sampleCount, int groupDescriptionIndex) {
+ this.sampleCount = sampleCount;
+ this.groupDescriptionIndex = groupDescriptionIndex;
+ }
+
+ public long getSampleCount() {
+ return sampleCount;
+ }
+
+ public void setSampleCount(long sampleCount) {
+ this.sampleCount = sampleCount;
+ }
+
+ public int getGroupDescriptionIndex() {
+ return groupDescriptionIndex;
+ }
+
+ public void setGroupDescriptionIndex(int groupDescriptionIndex) {
+ this.groupDescriptionIndex = groupDescriptionIndex;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "sampleCount=" + sampleCount +
+ ", groupDescriptionIndex=" + groupDescriptionIndex +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (groupDescriptionIndex != entry.groupDescriptionIndex) {
+ return false;
+ }
+ if (sampleCount != entry.sampleCount) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (sampleCount ^ (sampleCount >>> 32));
+ result = 31 * result + groupDescriptionIndex;
+ return result;
+ }
+ }
+
+ public String getGroupingType() {
+ return groupingType;
+ }
+
+ public void setGroupingType(String groupingType) {
+ this.groupingType = groupingType;
+ }
+
+ public String getGroupingTypeParameter() {
+ return groupingTypeParameter;
+ }
+
+ public void setGroupingTypeParameter(String groupingTypeParameter) {
+ this.groupingTypeParameter = groupingTypeParameter;
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java
new file mode 100644
index 0000000..798fd9c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+/**
+ * The Temporal Level sample grouping ('tele') provides a codec-independent sample grouping that can be used to group samples (access units) in a track (and potential track fragments) according to temporal level, where samples of one temporal level have no coding dependencies on samples of higher temporal levels. The temporal level equals the sample group description index (taking values 1, 2, 3, etc). The bitstream containing only the access units from the first temporal level to a higher temporal level remains conforming to the coding standard.
+ *
+ * A grouping according to temporal level facilitates easy extraction of temporal subsequences, for instance using the Subsegment Indexing box in 0.
+ *
+ */
+public class TemporalLevelEntry extends GroupEntry {
+ public static final String TYPE = "tele";
+ private boolean levelIndependentlyDecodable;
+ private short reserved;
+
+ public boolean isLevelIndependentlyDecodable() {
+ return levelIndependentlyDecodable;
+ }
+
+ public void setLevelIndependentlyDecodable(boolean levelIndependentlyDecodable) {
+ this.levelIndependentlyDecodable = levelIndependentlyDecodable;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ final byte b = byteBuffer.get();
+ levelIndependentlyDecodable = ((b & 0x80) == 0x80);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(1);
+ content.put((byte) (levelIndependentlyDecodable ? 0x80 : 0x00));
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TemporalLevelEntry that = (TemporalLevelEntry) o;
+
+ if (levelIndependentlyDecodable != that.levelIndependentlyDecodable) return false;
+ if (reserved != that.reserved) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (levelIndependentlyDecodable ? 1 : 0);
+ result = 31 * result + (int) reserved;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("TemporalLevelEntry");
+ sb.append("{levelIndependentlyDecodable=").append(levelIndependentlyDecodable);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java
new file mode 100644
index 0000000..9efcbea
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.Hex;
+
+import java.nio.ByteBuffer;
+
+/**
+ *
+ */
+public class UnknownEntry extends GroupEntry {
+ private ByteBuffer content;
+
+ public UnknownEntry() {
+ }
+
+ public ByteBuffer getContent() {
+ return content;
+ }
+
+ public void setContent(ByteBuffer content) {
+ this.content = (ByteBuffer) content.duplicate().rewind();
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ this.content = (ByteBuffer) byteBuffer.duplicate().rewind();
+ }
+
+ @Override
+ public ByteBuffer get() {
+ return content.duplicate();
+ }
+
+ @Override
+ public String toString() {
+ ByteBuffer bb = content.duplicate();
+ bb.rewind();
+ byte[] b = new byte[bb.limit()];
+ bb.get(b);
+ return "UnknownEntry{" +
+ "content=" + Hex.encodeHex(b) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ UnknownEntry that = (UnknownEntry) o;
+
+ if (content != null ? !content.equals(that.content) : that.content != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return content != null ? content.hashCode() : 0;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java
new file mode 100644
index 0000000..ed5d199
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.nio.ByteBuffer;
+
+/**
+ * For some coding systems a sync sample is specified to be a random access point after which all samples in decoding order can be correctly decoded. However, it may be possible to encode an “open” random access point, after which all samples in output order can be correctly decoded, but some samples following the random access point in decoding order and preceding the random access point in output order need not be correctly decodable. For example, an intra picture starting an open group of pictures can be followed in decoding order by (bi-)predicted pictures that however precede the intra picture in output order; though they possibly cannot be correctly decoded if the decoding starts from the intra picture, they are not needed.
+ *
+ * Such “open” random-access samples can be marked by being a member of this group. Samples marked by this group must be random access points, and may also be sync points (i.e. it is not required that samples marked by the sync sample table be excluded).
+ *
+ */
+public class VisualRandomAccessEntry extends GroupEntry {
+ public static final String TYPE = "rap ";
+ private boolean numLeadingSamplesKnown;
+ private short numLeadingSamples;
+
+ public boolean isNumLeadingSamplesKnown() {
+ return numLeadingSamplesKnown;
+ }
+
+ public void setNumLeadingSamplesKnown(boolean numLeadingSamplesKnown) {
+ this.numLeadingSamplesKnown = numLeadingSamplesKnown;
+ }
+
+ public short getNumLeadingSamples() {
+ return numLeadingSamples;
+ }
+
+ public void setNumLeadingSamples(short numLeadingSamples) {
+ this.numLeadingSamples = numLeadingSamples;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ final byte b = byteBuffer.get();
+ numLeadingSamplesKnown = ((b & 0x80) == 0x80);
+ numLeadingSamples = (short) (b & 0x7f);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(1);
+ content.put((byte) ((numLeadingSamplesKnown? 0x80 : 0x00)| (numLeadingSamples & 0x7f)));
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ VisualRandomAccessEntry that = (VisualRandomAccessEntry) o;
+
+ if (numLeadingSamples != that.numLeadingSamples) return false;
+ if (numLeadingSamplesKnown != that.numLeadingSamplesKnown) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (numLeadingSamplesKnown ? 1 : 0);
+ result = 31 * result + (int) numLeadingSamples;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("VisualRandomAccessEntry");
+ sb.append("{numLeadingSamplesKnown=").append(numLeadingSamplesKnown);
+ sb.append(", numLeadingSamples=").append(numLeadingSamples);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/all-wcprops
new file mode 100644
index 0000000..73d7e4e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/616/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff
+END
+PlayReadyHeader.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/525/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java
+END
+ProtectionSpecificHeader.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/527/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/ProtectionSpecificHeader.java
+END
+UuidBasedProtectionSystemSpecificHeaderBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 131
+/svn/!svn/ver/616/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/UuidBasedProtectionSystemSpecificHeaderBox.java
+END
+PiffSampleEncryptionBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 112
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffSampleEncryptionBox.java
+END
+TfrfBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfrfBox.java
+END
+PiffTrackEncryptionBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 111
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffTrackEncryptionBox.java
+END
+TfxdBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfxdBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/entries
new file mode 100644
index 0000000..f869342
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/entries
@@ -0,0 +1,266 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-05-17T09:04:15.805545Z
+616
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+PlayReadyHeader.java
+file
+
+
+
+
+2012-09-14T17:27:50.797223Z
+414299d2fe72802bfdba588cd197cc8d
+2012-04-25T19:24:04.485529Z
+525
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8099
+
+ProtectionSpecificHeader.java
+file
+
+
+
+
+2012-09-14T17:27:50.797223Z
+206bcc330dd14e88a9b36dbdab4b5676
+2012-04-25T23:16:14.370289Z
+527
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2339
+
+UuidBasedProtectionSystemSpecificHeaderBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.797223Z
+bee6a83aa218637d445d55bb317bf1f0
+2012-05-17T09:04:15.805545Z
+616
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3419
+
+PiffSampleEncryptionBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.797223Z
+e8e29f48bf61c02cd75f62b654664a1d
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1187
+
+TfrfBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.797223Z
+87a71e91a262d3294bb9b1e4959b0b99
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4379
+
+PiffTrackEncryptionBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.797223Z
+f412ae4506ee42fea327a2b274c0602d
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+854
+
+TfxdBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.807223Z
+d98ba32db0ee574c4130985dee95179a
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2998
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PiffSampleEncryptionBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PiffSampleEncryptionBox.java.svn-base
new file mode 100644
index 0000000..da3512d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PiffSampleEncryptionBox.java.svn-base
@@ -0,0 +1,45 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.googlecode.mp4parser.boxes.AbstractSampleEncryptionBox;
+
+/**
+ * <pre>
+ * aligned(8) class SampleEncryptionBox extends FullBox(‘uuid’, extended_type= 0xA2394F52-5A9B-4f14-A244-6C427C648DF4, version=0, flags=0)
+ * {
+ * if (flags & 0x000001)
+ * {
+ * unsigned int(24) AlgorithmID;
+ * unsigned int(8) IV_size;
+ * unsigned int(8)[16] KID;
+ * }
+ * unsigned int (32) sample_count;
+ * {
+ * unsigned int(IV_size) InitializationVector;
+ * if (flags & 0x000002)
+ * {
+ * unsigned int(16) NumberOfEntries;
+ * {
+ * unsigned int(16) BytesOfClearData;
+ * unsigned int(32) BytesOfEncryptedData;
+ * } [ NumberOfEntries]
+ * }
+ * }[ sample_count ]
+ * }
+ * </pre>
+ */
+public class PiffSampleEncryptionBox extends AbstractSampleEncryptionBox {
+
+ /**
+ * Creates a AbstractSampleEncryptionBox for non-h264 tracks.
+ */
+ public PiffSampleEncryptionBox() {
+ super("uuid");
+
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return new byte[]{(byte) 0xA2, 0x39, 0x4F, 0x52, 0x5A, (byte) 0x9B, 0x4f, 0x14, (byte) 0xA2, 0x44, 0x6C, 0x42, 0x7C, 0x64, (byte) 0x8D, (byte) 0xF4};
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PiffTrackEncryptionBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PiffTrackEncryptionBox.java.svn-base
new file mode 100644
index 0000000..f92c0f3
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PiffTrackEncryptionBox.java.svn-base
@@ -0,0 +1,34 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.googlecode.mp4parser.boxes.AbstractTrackEncryptionBox;
+
+/**
+ * aligned(8) class TrackEncryptionBox extends FullBox(‘uuid’,
+ * extended_type=0x8974dbce-7be7-4c51-84f9-7148f9882554, version=0,
+ * flags=0)
+ * {
+ * unsigned int(24) default_AlgorithmID;
+ * unsigned int(8) default_IV_size;
+ * unsigned int(8)[16] default_KID;
+ * }
+ */
+public class PiffTrackEncryptionBox extends AbstractTrackEncryptionBox {
+
+
+ public PiffTrackEncryptionBox() {
+ super("uuid");
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return new byte[]{(byte) 0x89, 0x74, (byte) 0xdb, (byte) 0xce, 0x7b, (byte) 0xe7, 0x4c, 0x51,
+ (byte) 0x84, (byte) 0xf9, 0x71, 0x48, (byte) 0xf9, (byte) 0x88, 0x25, 0x54};
+ }
+
+ @Override
+ public int getFlags() {
+ return 0;
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PlayReadyHeader.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PlayReadyHeader.java.svn-base
new file mode 100644
index 0000000..74246d0
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/PlayReadyHeader.java.svn-base
@@ -0,0 +1,253 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.util.Path;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Specifications > Microsoft PlayReady Format Specification > 2. PlayReady Media Format > 2.7. ASF GUIDs
+ * <p/>
+ * <p/>
+ * ASF_Protection_System_Identifier_Object
+ * 9A04F079-9840-4286-AB92E65BE0885F95
+ * <p/>
+ * ASF_Content_Protection_System_Microsoft_PlayReady
+ * F4637010-03C3-42CD-B932B48ADF3A6A54
+ * <p/>
+ * ASF_StreamType_PlayReady_Encrypted_Command_Media
+ * 8683973A-6639-463A-ABD764F1CE3EEAE0
+ * <p/>
+ * <p/>
+ * Specifications > Microsoft PlayReady Format Specification > 2. PlayReady Media Format > 2.5. Data Objects > 2.5.1. Payload Extension for AES in Counter Mode
+ * <p/>
+ * The sample Id is used as the IV in CTR mode. Block offset, starting at 0 and incremented by 1 after every 16 bytes, from the beginning of the sample is used as the Counter.
+ * <p/>
+ * The sample ID for each sample (media object) is stored as an ASF payload extension system with the ID of ASF_Payload_Extension_Encryption_SampleID = {6698B84E-0AFA-4330-AEB2-1C0A98D7A44D}. The payload extension can be stored as a fixed size extension of 8 bytes.
+ * <p/>
+ * The sample ID is always stored in big-endian byte order.
+ */
+public class PlayReadyHeader extends ProtectionSpecificHeader {
+ private long length;
+ private List<PlayReadyRecord> records;
+
+ public PlayReadyHeader() {
+
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ /*
+ Length DWORD 32
+
+ PlayReady Record Count WORD 16
+
+ PlayReady Records See Text Varies
+
+ */
+
+ length = IsoTypeReader.readUInt32BE(byteBuffer);
+ int recordCount = IsoTypeReader.readUInt16BE(byteBuffer);
+
+ records = PlayReadyRecord.createFor(byteBuffer, recordCount);
+ }
+
+ @Override
+ public ByteBuffer getData() {
+
+ int size = 4 + 2;
+ for (PlayReadyRecord record : records) {
+ size += 2 + 2;
+ size += record.getValue().rewind().limit();
+ }
+ ByteBuffer byteBuffer = ByteBuffer.allocate(size);
+
+ IsoTypeWriter.writeUInt32BE(byteBuffer, size);
+ IsoTypeWriter.writeUInt16BE(byteBuffer, records.size());
+ for (PlayReadyRecord record : records) {
+ IsoTypeWriter.writeUInt16BE(byteBuffer, record.type);
+ IsoTypeWriter.writeUInt16BE(byteBuffer, record.getValue().limit());
+ ByteBuffer tmp4debug = record.getValue();
+ byteBuffer.put(tmp4debug);
+ }
+
+ return byteBuffer;
+ }
+
+ public void setRecords(List<PlayReadyRecord> records) {
+ this.records = records;
+ }
+
+ public List<PlayReadyRecord> getRecords() {
+ return Collections.unmodifiableList(records);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("PlayReadyHeader");
+ sb.append("{length=").append(length);
+ sb.append(", recordCount=").append(records.size());
+ sb.append(", records=").append(records);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public static abstract class PlayReadyRecord {
+ int type;
+
+
+ public PlayReadyRecord(int type) {
+ this.type = type;
+ }
+
+ public static List<PlayReadyRecord> createFor(ByteBuffer byteBuffer, int recordCount) {
+ List<PlayReadyRecord> records = new ArrayList<PlayReadyRecord>(recordCount);
+
+ for (int i = 0; i < recordCount; i++) {
+ PlayReadyRecord record;
+ int type = IsoTypeReader.readUInt16BE(byteBuffer);
+ int length = IsoTypeReader.readUInt16BE(byteBuffer);
+ switch (type) {
+ case 0x1:
+ record = new RMHeader();
+ break;
+ case 0x2:
+ record = new DefaulPlayReadyRecord(0x02);
+ break;
+ case 0x3:
+ record = new EmeddedLicenseStore();
+ break;
+ default:
+ record = new DefaulPlayReadyRecord(type);
+ }
+ record.parse((ByteBuffer) byteBuffer.slice().limit(length));
+ byteBuffer.position(byteBuffer.position() + length);
+ records.add(record);
+ }
+
+ return records;
+ }
+
+ public abstract void parse(ByteBuffer bytes);
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("PlayReadyRecord");
+ sb.append("{type=").append(type);
+ sb.append(", length=").append(getValue().limit());
+// sb.append(", value=").append(Hex.encodeHex(getValue())).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public abstract ByteBuffer getValue();
+
+ public static class RMHeader extends PlayReadyRecord {
+ String header;
+
+ public RMHeader() {
+ super(0x01);
+ }
+
+ @Override
+ public void parse(ByteBuffer bytes) {
+ try {
+ byte[] str = new byte[bytes.slice().limit()];
+ bytes.get(str);
+ header = new String(str, "UTF-16LE");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ByteBuffer getValue() {
+ byte[] headerBytes;
+ try {
+ headerBytes = header.getBytes("UTF-16LE");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ return ByteBuffer.wrap(headerBytes);
+ }
+
+ public void setHeader(String header) {
+ this.header = header;
+ }
+
+ public String getHeader() {
+ return header;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("RMHeader");
+ sb.append("{length=").append(getValue().limit());
+ sb.append(", header='").append(header).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ public static class EmeddedLicenseStore extends PlayReadyRecord {
+ ByteBuffer value;
+
+ public EmeddedLicenseStore() {
+ super(0x03);
+ }
+
+ @Override
+ public void parse(ByteBuffer bytes) {
+ this.value = bytes.duplicate();
+ }
+
+ @Override
+ public ByteBuffer getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("EmeddedLicenseStore");
+ sb.append("{length=").append(getValue().limit());
+ //sb.append(", value='").append(Hex.encodeHex(getValue())).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ public static class DefaulPlayReadyRecord extends PlayReadyRecord {
+ ByteBuffer value;
+
+ public DefaulPlayReadyRecord(int type) {
+ super(type);
+ }
+
+ @Override
+ public void parse(ByteBuffer bytes) {
+ this.value = bytes.duplicate();
+ }
+
+ @Override
+ public ByteBuffer getValue() {
+ return value;
+ }
+
+ }
+
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/ProtectionSpecificHeader.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/ProtectionSpecificHeader.java.svn-base
new file mode 100644
index 0000000..0d2f344
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/ProtectionSpecificHeader.java.svn-base
@@ -0,0 +1,79 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+
+import com.coremedia.iso.Hex;
+
+import java.lang.Class;
+import java.lang.IllegalAccessException;
+import java.lang.InstantiationException;
+import java.lang.Object;
+import java.lang.Override;
+import java.lang.RuntimeException;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+
+public class ProtectionSpecificHeader {
+ protected static Map<UUID, Class<? extends ProtectionSpecificHeader>> uuidRegistry = new HashMap<UUID, Class<? extends ProtectionSpecificHeader>>();
+ ByteBuffer data;
+
+ static {
+ uuidRegistry.put(UUID.fromString("9A04F079-9840-4286-AB92-E65BE0885F95"), PlayReadyHeader.class);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ProtectionSpecificHeader) {
+ if (this.getClass().equals(obj.getClass())) {
+ return data.equals(((ProtectionSpecificHeader) obj).data);
+ }
+ }
+ return false;
+ }
+
+ public static ProtectionSpecificHeader createFor(UUID systemId, ByteBuffer bufferWrapper) {
+ final Class<? extends ProtectionSpecificHeader> aClass = uuidRegistry.get(systemId);
+
+ ProtectionSpecificHeader protectionSpecificHeader = new ProtectionSpecificHeader();
+ if (aClass != null) {
+ try {
+ protectionSpecificHeader = aClass.newInstance();
+
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ protectionSpecificHeader.parse(bufferWrapper);
+ return protectionSpecificHeader;
+
+ }
+
+ public void parse(ByteBuffer buffer) {
+ data = buffer;
+
+ }
+
+ public ByteBuffer getData() {
+ return data;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ProtectionSpecificHeader");
+ sb.append("{data=");
+ ByteBuffer data = getData().duplicate();
+ data.rewind();
+ byte[] bytes = new byte[data.limit()];
+ data.get(bytes);
+ sb.append(Hex.encodeHex(bytes));
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/TfrfBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/TfrfBox.java.svn-base
new file mode 100644
index 0000000..1e862e9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/TfrfBox.java.svn-base
@@ -0,0 +1,129 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The syntax of the fields defined in this section, specified in ABNF [RFC5234], is as follows:
+ * TfrfBox = TfrfBoxLength TfrfBoxType [TfrfBoxLongLength] TfrfBoxUUID TfrfBoxFields
+ * TfrfBoxChildren
+ * TfrfBoxType = "u" "u" "i" "d"
+ * TfrfBoxLength = BoxLength
+ * TfrfBoxLongLength = LongBoxLength
+ * TfrfBoxUUID = %xD4 %x80 %x7E %xF2 %xCA %x39 %x46 %x95
+ * %x8E %x54 %x26 %xCB %x9E %x46 %xA7 %x9F
+ * TfrfBoxFields = TfrfBoxVersion
+ * TfrfBoxFlags
+ * FragmentCount
+ * (1* TfrfBoxDataFields32) / (1* TfrfBoxDataFields64)
+ * TfrfBoxVersion = %x00 / %x01
+ * TfrfBoxFlags = 24*24 RESERVED_BIT
+ * FragmentCount = UINT8
+ * TfrfBoxDataFields32 = FragmentAbsoluteTime32
+ * FragmentDuration32
+ * TfrfBoxDataFields64 = FragmentAbsoluteTime64
+ * FragmentDuration64
+ * FragmentAbsoluteTime64 = UNSIGNED_INT32
+ * FragmentDuration64 = UNSIGNED_INT32
+ * FragmentAbsoluteTime64 = UNSIGNED_INT64
+ * FragmentDuration64 = UNSIGNED_INT64
+ * TfrfBoxChildren = *( VendorExtensionUUIDBox )
+ */
+public class TfrfBox extends AbstractFullBox {
+ public List<Entry> entries = new ArrayList<Entry>();
+
+ public TfrfBox() {
+ super("uuid");
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return new byte[]{(byte) 0xd4, (byte) 0x80, (byte) 0x7e, (byte) 0xf2, (byte) 0xca, (byte) 0x39, (byte) 0x46,
+ (byte) 0x95, (byte) 0x8e, (byte) 0x54, 0x26, (byte) 0xcb, (byte) 0x9e, (byte) 0x46, (byte) 0xa7, (byte) 0x9f};
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 5 + entries.size() * (getVersion() == 0x01 ? 16 : 8);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ IsoTypeWriter.writeUInt8(byteBuffer, entries.size());
+
+ for (Entry entry : entries) {
+ if (getVersion() == 0x01) {
+ IsoTypeWriter.writeUInt64(byteBuffer, entry.fragmentAbsoluteTime);
+ IsoTypeWriter.writeUInt64(byteBuffer, entry.fragmentAbsoluteDuration);
+ } else {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.fragmentAbsoluteTime);
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.fragmentAbsoluteDuration);
+ }
+ }
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ int fragmentCount = IsoTypeReader.readUInt8(content);
+
+ for (int i = 0; i < fragmentCount; i++) {
+ Entry entry = new Entry();
+ if (getVersion() == 0x01) {
+ entry.fragmentAbsoluteTime = IsoTypeReader.readUInt64(content);
+ entry.fragmentAbsoluteDuration = IsoTypeReader.readUInt64(content);
+ } else {
+ entry.fragmentAbsoluteTime = IsoTypeReader.readUInt32(content);
+ entry.fragmentAbsoluteDuration = IsoTypeReader.readUInt32(content);
+ }
+ entries.add(entry);
+ }
+ }
+
+
+ public long getFragmentCount() {
+ return entries.size();
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("TfrfBox");
+ sb.append("{entries=").append(entries);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public class Entry {
+ long fragmentAbsoluteTime;
+ long fragmentAbsoluteDuration;
+
+ public long getFragmentAbsoluteTime() {
+ return fragmentAbsoluteTime;
+ }
+
+ public long getFragmentAbsoluteDuration() {
+ return fragmentAbsoluteDuration;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Entry");
+ sb.append("{fragmentAbsoluteTime=").append(fragmentAbsoluteTime);
+ sb.append(", fragmentAbsoluteDuration=").append(fragmentAbsoluteDuration);
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/TfxdBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/TfxdBox.java.svn-base
new file mode 100644
index 0000000..52e8e87
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/TfxdBox.java.svn-base
@@ -0,0 +1,85 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ * The syntax of the fields defined in this section, specified in ABNF [RFC5234], is as follows:
+ * TfxdBox = TfxdBoxLength TfxdBoxType [TfxdBoxLongLength] TfxdBoxUUID TfxdBoxFields
+ * TfxdBoxChildren
+ * TfxdBoxType = "u" "u" "i" "d"
+ * TfxdBoxLength = BoxLength
+ * TfxdBoxLongLength = LongBoxLength
+ * TfxdBoxUUID = %x6D %x1D %x9B %x05 %x42 %xD5 %x44 %xE6
+ * %x80 %xE2 %x14 %x1D %xAF %xF7 %x57 %xB2
+ * TfxdBoxFields = TfxdBoxVersion
+ * TfxdBoxFlags
+ * TfxdBoxDataFields32 / TfxdBoxDataFields64
+ * TfxdBoxVersion = %x00 / %x01
+ * TfxdBoxFlags = 24*24 RESERVED_BIT
+ * TfxdBoxDataFields32 = FragmentAbsoluteTime32
+ * FragmentDuration32
+ * TfxdBoxDataFields64 = FragmentAbsoluteTime64
+ * FragmentDuration64
+ * FragmentAbsoluteTime64 = UNSIGNED_INT32
+ * FragmentDuration64 = UNSIGNED_INT32
+ * FragmentAbsoluteTime64 = UNSIGNED_INT64
+ * FragmentDuration64 = UNSIGNED_INT64
+ * TfxdBoxChildren = *( VendorExtensionUUIDBox )
+ */
+//@ExtendedUserType(uuid = "6d1d9b05-42d5-44e6-80e2-141daff757b2")
+public class TfxdBox extends AbstractFullBox {
+ public long fragmentAbsoluteTime;
+ public long fragmentAbsoluteDuration;
+
+ public TfxdBox() {
+ super("uuid");
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return new byte[]{(byte) 0x6d, (byte) 0x1d, (byte) 0x9b, (byte) 0x05, (byte) 0x42, (byte) 0xd5, (byte) 0x44,
+ (byte) 0xe6, (byte) 0x80, (byte) 0xe2, 0x14, (byte) 0x1d, (byte) 0xaf, (byte) 0xf7, (byte) 0x57, (byte) 0xb2};
+ }
+
+ @Override
+ protected long getContentSize() {
+ return getVersion() == 0x01 ? 20 : 12;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+
+ if (getVersion() == 0x01) {
+ fragmentAbsoluteTime = IsoTypeReader.readUInt64(content);
+ fragmentAbsoluteDuration = IsoTypeReader.readUInt64(content);
+ } else {
+ fragmentAbsoluteTime = IsoTypeReader.readUInt32(content);
+ fragmentAbsoluteDuration = IsoTypeReader.readUInt32(content);
+ }
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ if (getVersion() == 0x01) {
+ IsoTypeWriter.writeUInt64(byteBuffer, fragmentAbsoluteTime);
+ IsoTypeWriter.writeUInt64(byteBuffer, fragmentAbsoluteDuration);
+ } else {
+ IsoTypeWriter.writeUInt32(byteBuffer, fragmentAbsoluteTime);
+ IsoTypeWriter.writeUInt32(byteBuffer, fragmentAbsoluteDuration);
+ }
+ }
+
+ public long getFragmentAbsoluteTime() {
+ return fragmentAbsoluteTime;
+ }
+
+ public long getFragmentAbsoluteDuration() {
+ return fragmentAbsoluteDuration;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/UuidBasedProtectionSystemSpecificHeaderBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/UuidBasedProtectionSystemSpecificHeaderBox.java.svn-base
new file mode 100644
index 0000000..b147398
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/.svn/text-base/UuidBasedProtectionSystemSpecificHeaderBox.java.svn-base
@@ -0,0 +1,106 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.util.Path;
+import com.googlecode.mp4parser.util.UUIDConverter;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.Override;import java.lang.String;import java.lang.StringBuilder;import java.nio.ByteBuffer;
+import java.util.UUID;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * aligned(8) class UuidBasedProtectionSystemSpecificHeaderBox extends FullBox(‘uuid’,
+ * extended_type=0xd08a4f18-10f3-4a82-b6c8-32d8aba183d3,
+ * version=0, flags=0)
+ * {
+ * unsigned int(8)[16] SystemID;
+ * unsigned int(32) DataSize;
+ * unsigned int(8)[DataSize] Data;
+ * }
+ */
+public class UuidBasedProtectionSystemSpecificHeaderBox extends AbstractFullBox {
+ public static byte[] USER_TYPE = new byte[]{(byte) 0xd0, (byte) 0x8a, 0x4f, 0x18, 0x10, (byte) 0xf3, 0x4a, (byte) 0x82,
+ (byte) 0xb6, (byte) 0xc8, 0x32, (byte) 0xd8, (byte) 0xab, (byte) 0xa1, (byte) 0x83, (byte) 0xd3};
+
+ UUID systemId;
+
+ ProtectionSpecificHeader protectionSpecificHeader;
+
+ public UuidBasedProtectionSystemSpecificHeaderBox() {
+ super("uuid", USER_TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 24 + protectionSpecificHeader.getData().limit();
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return USER_TYPE;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ IsoTypeWriter.writeUInt64(byteBuffer, systemId.getMostSignificantBits());
+ IsoTypeWriter.writeUInt64(byteBuffer, systemId.getLeastSignificantBits());
+ ByteBuffer data = protectionSpecificHeader.getData();
+ data.rewind();
+ IsoTypeWriter.writeUInt32(byteBuffer, data.limit());
+ byteBuffer.put(data);
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ byte[] systemIdBytes = new byte[16];
+ content.get(systemIdBytes);
+ systemId = UUIDConverter.convert(systemIdBytes);
+ int dataSize = l2i(IsoTypeReader.readUInt32(content));
+ protectionSpecificHeader = ProtectionSpecificHeader.createFor(systemId, content);
+ }
+
+ public UUID getSystemId() {
+ return systemId;
+ }
+
+ public void setSystemId(UUID systemId) {
+ this.systemId = systemId;
+ }
+
+ public String getSystemIdString() {
+ return systemId.toString();
+ }
+
+ public ProtectionSpecificHeader getProtectionSpecificHeader() {
+ return protectionSpecificHeader;
+ }
+
+ public String getProtectionSpecificHeaderString() {
+ return protectionSpecificHeader.toString();
+ }
+
+ public void setProtectionSpecificHeader(ProtectionSpecificHeader protectionSpecificHeader) {
+ this.protectionSpecificHeader = protectionSpecificHeader;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("UuidBasedProtectionSystemSpecificHeaderBox");
+ sb.append("{systemId=").append(systemId.toString());
+ sb.append(", dataSize=").append(protectionSpecificHeader.getData().limit());
+ sb.append('}');
+ return sb.toString();
+ }
+
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffSampleEncryptionBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffSampleEncryptionBox.java
new file mode 100644
index 0000000..da3512d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffSampleEncryptionBox.java
@@ -0,0 +1,45 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.googlecode.mp4parser.boxes.AbstractSampleEncryptionBox;
+
+/**
+ * <pre>
+ * aligned(8) class SampleEncryptionBox extends FullBox(‘uuid’, extended_type= 0xA2394F52-5A9B-4f14-A244-6C427C648DF4, version=0, flags=0)
+ * {
+ * if (flags & 0x000001)
+ * {
+ * unsigned int(24) AlgorithmID;
+ * unsigned int(8) IV_size;
+ * unsigned int(8)[16] KID;
+ * }
+ * unsigned int (32) sample_count;
+ * {
+ * unsigned int(IV_size) InitializationVector;
+ * if (flags & 0x000002)
+ * {
+ * unsigned int(16) NumberOfEntries;
+ * {
+ * unsigned int(16) BytesOfClearData;
+ * unsigned int(32) BytesOfEncryptedData;
+ * } [ NumberOfEntries]
+ * }
+ * }[ sample_count ]
+ * }
+ * </pre>
+ */
+public class PiffSampleEncryptionBox extends AbstractSampleEncryptionBox {
+
+ /**
+ * Creates a AbstractSampleEncryptionBox for non-h264 tracks.
+ */
+ public PiffSampleEncryptionBox() {
+ super("uuid");
+
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return new byte[]{(byte) 0xA2, 0x39, 0x4F, 0x52, 0x5A, (byte) 0x9B, 0x4f, 0x14, (byte) 0xA2, 0x44, 0x6C, 0x42, 0x7C, 0x64, (byte) 0x8D, (byte) 0xF4};
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffTrackEncryptionBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffTrackEncryptionBox.java
new file mode 100644
index 0000000..f92c0f3
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PiffTrackEncryptionBox.java
@@ -0,0 +1,34 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.googlecode.mp4parser.boxes.AbstractTrackEncryptionBox;
+
+/**
+ * aligned(8) class TrackEncryptionBox extends FullBox(‘uuid’,
+ * extended_type=0x8974dbce-7be7-4c51-84f9-7148f9882554, version=0,
+ * flags=0)
+ * {
+ * unsigned int(24) default_AlgorithmID;
+ * unsigned int(8) default_IV_size;
+ * unsigned int(8)[16] default_KID;
+ * }
+ */
+public class PiffTrackEncryptionBox extends AbstractTrackEncryptionBox {
+
+
+ public PiffTrackEncryptionBox() {
+ super("uuid");
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return new byte[]{(byte) 0x89, 0x74, (byte) 0xdb, (byte) 0xce, 0x7b, (byte) 0xe7, 0x4c, 0x51,
+ (byte) 0x84, (byte) 0xf9, 0x71, 0x48, (byte) 0xf9, (byte) 0x88, 0x25, 0x54};
+ }
+
+ @Override
+ public int getFlags() {
+ return 0;
+ }
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java
new file mode 100644
index 0000000..74246d0
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/PlayReadyHeader.java
@@ -0,0 +1,253 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.util.Path;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Specifications > Microsoft PlayReady Format Specification > 2. PlayReady Media Format > 2.7. ASF GUIDs
+ * <p/>
+ * <p/>
+ * ASF_Protection_System_Identifier_Object
+ * 9A04F079-9840-4286-AB92E65BE0885F95
+ * <p/>
+ * ASF_Content_Protection_System_Microsoft_PlayReady
+ * F4637010-03C3-42CD-B932B48ADF3A6A54
+ * <p/>
+ * ASF_StreamType_PlayReady_Encrypted_Command_Media
+ * 8683973A-6639-463A-ABD764F1CE3EEAE0
+ * <p/>
+ * <p/>
+ * Specifications > Microsoft PlayReady Format Specification > 2. PlayReady Media Format > 2.5. Data Objects > 2.5.1. Payload Extension for AES in Counter Mode
+ * <p/>
+ * The sample Id is used as the IV in CTR mode. Block offset, starting at 0 and incremented by 1 after every 16 bytes, from the beginning of the sample is used as the Counter.
+ * <p/>
+ * The sample ID for each sample (media object) is stored as an ASF payload extension system with the ID of ASF_Payload_Extension_Encryption_SampleID = {6698B84E-0AFA-4330-AEB2-1C0A98D7A44D}. The payload extension can be stored as a fixed size extension of 8 bytes.
+ * <p/>
+ * The sample ID is always stored in big-endian byte order.
+ */
+public class PlayReadyHeader extends ProtectionSpecificHeader {
+ private long length;
+ private List<PlayReadyRecord> records;
+
+ public PlayReadyHeader() {
+
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ /*
+ Length DWORD 32
+
+ PlayReady Record Count WORD 16
+
+ PlayReady Records See Text Varies
+
+ */
+
+ length = IsoTypeReader.readUInt32BE(byteBuffer);
+ int recordCount = IsoTypeReader.readUInt16BE(byteBuffer);
+
+ records = PlayReadyRecord.createFor(byteBuffer, recordCount);
+ }
+
+ @Override
+ public ByteBuffer getData() {
+
+ int size = 4 + 2;
+ for (PlayReadyRecord record : records) {
+ size += 2 + 2;
+ size += record.getValue().rewind().limit();
+ }
+ ByteBuffer byteBuffer = ByteBuffer.allocate(size);
+
+ IsoTypeWriter.writeUInt32BE(byteBuffer, size);
+ IsoTypeWriter.writeUInt16BE(byteBuffer, records.size());
+ for (PlayReadyRecord record : records) {
+ IsoTypeWriter.writeUInt16BE(byteBuffer, record.type);
+ IsoTypeWriter.writeUInt16BE(byteBuffer, record.getValue().limit());
+ ByteBuffer tmp4debug = record.getValue();
+ byteBuffer.put(tmp4debug);
+ }
+
+ return byteBuffer;
+ }
+
+ public void setRecords(List<PlayReadyRecord> records) {
+ this.records = records;
+ }
+
+ public List<PlayReadyRecord> getRecords() {
+ return Collections.unmodifiableList(records);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("PlayReadyHeader");
+ sb.append("{length=").append(length);
+ sb.append(", recordCount=").append(records.size());
+ sb.append(", records=").append(records);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public static abstract class PlayReadyRecord {
+ int type;
+
+
+ public PlayReadyRecord(int type) {
+ this.type = type;
+ }
+
+ public static List<PlayReadyRecord> createFor(ByteBuffer byteBuffer, int recordCount) {
+ List<PlayReadyRecord> records = new ArrayList<PlayReadyRecord>(recordCount);
+
+ for (int i = 0; i < recordCount; i++) {
+ PlayReadyRecord record;
+ int type = IsoTypeReader.readUInt16BE(byteBuffer);
+ int length = IsoTypeReader.readUInt16BE(byteBuffer);
+ switch (type) {
+ case 0x1:
+ record = new RMHeader();
+ break;
+ case 0x2:
+ record = new DefaulPlayReadyRecord(0x02);
+ break;
+ case 0x3:
+ record = new EmeddedLicenseStore();
+ break;
+ default:
+ record = new DefaulPlayReadyRecord(type);
+ }
+ record.parse((ByteBuffer) byteBuffer.slice().limit(length));
+ byteBuffer.position(byteBuffer.position() + length);
+ records.add(record);
+ }
+
+ return records;
+ }
+
+ public abstract void parse(ByteBuffer bytes);
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("PlayReadyRecord");
+ sb.append("{type=").append(type);
+ sb.append(", length=").append(getValue().limit());
+// sb.append(", value=").append(Hex.encodeHex(getValue())).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public abstract ByteBuffer getValue();
+
+ public static class RMHeader extends PlayReadyRecord {
+ String header;
+
+ public RMHeader() {
+ super(0x01);
+ }
+
+ @Override
+ public void parse(ByteBuffer bytes) {
+ try {
+ byte[] str = new byte[bytes.slice().limit()];
+ bytes.get(str);
+ header = new String(str, "UTF-16LE");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ByteBuffer getValue() {
+ byte[] headerBytes;
+ try {
+ headerBytes = header.getBytes("UTF-16LE");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ return ByteBuffer.wrap(headerBytes);
+ }
+
+ public void setHeader(String header) {
+ this.header = header;
+ }
+
+ public String getHeader() {
+ return header;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("RMHeader");
+ sb.append("{length=").append(getValue().limit());
+ sb.append(", header='").append(header).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ public static class EmeddedLicenseStore extends PlayReadyRecord {
+ ByteBuffer value;
+
+ public EmeddedLicenseStore() {
+ super(0x03);
+ }
+
+ @Override
+ public void parse(ByteBuffer bytes) {
+ this.value = bytes.duplicate();
+ }
+
+ @Override
+ public ByteBuffer getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("EmeddedLicenseStore");
+ sb.append("{length=").append(getValue().limit());
+ //sb.append(", value='").append(Hex.encodeHex(getValue())).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ public static class DefaulPlayReadyRecord extends PlayReadyRecord {
+ ByteBuffer value;
+
+ public DefaulPlayReadyRecord(int type) {
+ super(type);
+ }
+
+ @Override
+ public void parse(ByteBuffer bytes) {
+ this.value = bytes.duplicate();
+ }
+
+ @Override
+ public ByteBuffer getValue() {
+ return value;
+ }
+
+ }
+
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/ProtectionSpecificHeader.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/ProtectionSpecificHeader.java
new file mode 100644
index 0000000..0d2f344
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/ProtectionSpecificHeader.java
@@ -0,0 +1,79 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+
+import com.coremedia.iso.Hex;
+
+import java.lang.Class;
+import java.lang.IllegalAccessException;
+import java.lang.InstantiationException;
+import java.lang.Object;
+import java.lang.Override;
+import java.lang.RuntimeException;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+
+public class ProtectionSpecificHeader {
+ protected static Map<UUID, Class<? extends ProtectionSpecificHeader>> uuidRegistry = new HashMap<UUID, Class<? extends ProtectionSpecificHeader>>();
+ ByteBuffer data;
+
+ static {
+ uuidRegistry.put(UUID.fromString("9A04F079-9840-4286-AB92-E65BE0885F95"), PlayReadyHeader.class);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ProtectionSpecificHeader) {
+ if (this.getClass().equals(obj.getClass())) {
+ return data.equals(((ProtectionSpecificHeader) obj).data);
+ }
+ }
+ return false;
+ }
+
+ public static ProtectionSpecificHeader createFor(UUID systemId, ByteBuffer bufferWrapper) {
+ final Class<? extends ProtectionSpecificHeader> aClass = uuidRegistry.get(systemId);
+
+ ProtectionSpecificHeader protectionSpecificHeader = new ProtectionSpecificHeader();
+ if (aClass != null) {
+ try {
+ protectionSpecificHeader = aClass.newInstance();
+
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ protectionSpecificHeader.parse(bufferWrapper);
+ return protectionSpecificHeader;
+
+ }
+
+ public void parse(ByteBuffer buffer) {
+ data = buffer;
+
+ }
+
+ public ByteBuffer getData() {
+ return data;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ProtectionSpecificHeader");
+ sb.append("{data=");
+ ByteBuffer data = getData().duplicate();
+ data.rewind();
+ byte[] bytes = new byte[data.limit()];
+ data.get(bytes);
+ sb.append(Hex.encodeHex(bytes));
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfrfBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfrfBox.java
new file mode 100644
index 0000000..1e862e9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfrfBox.java
@@ -0,0 +1,129 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The syntax of the fields defined in this section, specified in ABNF [RFC5234], is as follows:
+ * TfrfBox = TfrfBoxLength TfrfBoxType [TfrfBoxLongLength] TfrfBoxUUID TfrfBoxFields
+ * TfrfBoxChildren
+ * TfrfBoxType = "u" "u" "i" "d"
+ * TfrfBoxLength = BoxLength
+ * TfrfBoxLongLength = LongBoxLength
+ * TfrfBoxUUID = %xD4 %x80 %x7E %xF2 %xCA %x39 %x46 %x95
+ * %x8E %x54 %x26 %xCB %x9E %x46 %xA7 %x9F
+ * TfrfBoxFields = TfrfBoxVersion
+ * TfrfBoxFlags
+ * FragmentCount
+ * (1* TfrfBoxDataFields32) / (1* TfrfBoxDataFields64)
+ * TfrfBoxVersion = %x00 / %x01
+ * TfrfBoxFlags = 24*24 RESERVED_BIT
+ * FragmentCount = UINT8
+ * TfrfBoxDataFields32 = FragmentAbsoluteTime32
+ * FragmentDuration32
+ * TfrfBoxDataFields64 = FragmentAbsoluteTime64
+ * FragmentDuration64
+ * FragmentAbsoluteTime64 = UNSIGNED_INT32
+ * FragmentDuration64 = UNSIGNED_INT32
+ * FragmentAbsoluteTime64 = UNSIGNED_INT64
+ * FragmentDuration64 = UNSIGNED_INT64
+ * TfrfBoxChildren = *( VendorExtensionUUIDBox )
+ */
+public class TfrfBox extends AbstractFullBox {
+ public List<Entry> entries = new ArrayList<Entry>();
+
+ public TfrfBox() {
+ super("uuid");
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return new byte[]{(byte) 0xd4, (byte) 0x80, (byte) 0x7e, (byte) 0xf2, (byte) 0xca, (byte) 0x39, (byte) 0x46,
+ (byte) 0x95, (byte) 0x8e, (byte) 0x54, 0x26, (byte) 0xcb, (byte) 0x9e, (byte) 0x46, (byte) 0xa7, (byte) 0x9f};
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 5 + entries.size() * (getVersion() == 0x01 ? 16 : 8);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ IsoTypeWriter.writeUInt8(byteBuffer, entries.size());
+
+ for (Entry entry : entries) {
+ if (getVersion() == 0x01) {
+ IsoTypeWriter.writeUInt64(byteBuffer, entry.fragmentAbsoluteTime);
+ IsoTypeWriter.writeUInt64(byteBuffer, entry.fragmentAbsoluteDuration);
+ } else {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.fragmentAbsoluteTime);
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.fragmentAbsoluteDuration);
+ }
+ }
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ int fragmentCount = IsoTypeReader.readUInt8(content);
+
+ for (int i = 0; i < fragmentCount; i++) {
+ Entry entry = new Entry();
+ if (getVersion() == 0x01) {
+ entry.fragmentAbsoluteTime = IsoTypeReader.readUInt64(content);
+ entry.fragmentAbsoluteDuration = IsoTypeReader.readUInt64(content);
+ } else {
+ entry.fragmentAbsoluteTime = IsoTypeReader.readUInt32(content);
+ entry.fragmentAbsoluteDuration = IsoTypeReader.readUInt32(content);
+ }
+ entries.add(entry);
+ }
+ }
+
+
+ public long getFragmentCount() {
+ return entries.size();
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("TfrfBox");
+ sb.append("{entries=").append(entries);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public class Entry {
+ long fragmentAbsoluteTime;
+ long fragmentAbsoluteDuration;
+
+ public long getFragmentAbsoluteTime() {
+ return fragmentAbsoluteTime;
+ }
+
+ public long getFragmentAbsoluteDuration() {
+ return fragmentAbsoluteDuration;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Entry");
+ sb.append("{fragmentAbsoluteTime=").append(fragmentAbsoluteTime);
+ sb.append(", fragmentAbsoluteDuration=").append(fragmentAbsoluteDuration);
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfxdBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfxdBox.java
new file mode 100644
index 0000000..52e8e87
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/TfxdBox.java
@@ -0,0 +1,85 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ * The syntax of the fields defined in this section, specified in ABNF [RFC5234], is as follows:
+ * TfxdBox = TfxdBoxLength TfxdBoxType [TfxdBoxLongLength] TfxdBoxUUID TfxdBoxFields
+ * TfxdBoxChildren
+ * TfxdBoxType = "u" "u" "i" "d"
+ * TfxdBoxLength = BoxLength
+ * TfxdBoxLongLength = LongBoxLength
+ * TfxdBoxUUID = %x6D %x1D %x9B %x05 %x42 %xD5 %x44 %xE6
+ * %x80 %xE2 %x14 %x1D %xAF %xF7 %x57 %xB2
+ * TfxdBoxFields = TfxdBoxVersion
+ * TfxdBoxFlags
+ * TfxdBoxDataFields32 / TfxdBoxDataFields64
+ * TfxdBoxVersion = %x00 / %x01
+ * TfxdBoxFlags = 24*24 RESERVED_BIT
+ * TfxdBoxDataFields32 = FragmentAbsoluteTime32
+ * FragmentDuration32
+ * TfxdBoxDataFields64 = FragmentAbsoluteTime64
+ * FragmentDuration64
+ * FragmentAbsoluteTime64 = UNSIGNED_INT32
+ * FragmentDuration64 = UNSIGNED_INT32
+ * FragmentAbsoluteTime64 = UNSIGNED_INT64
+ * FragmentDuration64 = UNSIGNED_INT64
+ * TfxdBoxChildren = *( VendorExtensionUUIDBox )
+ */
+//@ExtendedUserType(uuid = "6d1d9b05-42d5-44e6-80e2-141daff757b2")
+public class TfxdBox extends AbstractFullBox {
+ public long fragmentAbsoluteTime;
+ public long fragmentAbsoluteDuration;
+
+ public TfxdBox() {
+ super("uuid");
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return new byte[]{(byte) 0x6d, (byte) 0x1d, (byte) 0x9b, (byte) 0x05, (byte) 0x42, (byte) 0xd5, (byte) 0x44,
+ (byte) 0xe6, (byte) 0x80, (byte) 0xe2, 0x14, (byte) 0x1d, (byte) 0xaf, (byte) 0xf7, (byte) 0x57, (byte) 0xb2};
+ }
+
+ @Override
+ protected long getContentSize() {
+ return getVersion() == 0x01 ? 20 : 12;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+
+ if (getVersion() == 0x01) {
+ fragmentAbsoluteTime = IsoTypeReader.readUInt64(content);
+ fragmentAbsoluteDuration = IsoTypeReader.readUInt64(content);
+ } else {
+ fragmentAbsoluteTime = IsoTypeReader.readUInt32(content);
+ fragmentAbsoluteDuration = IsoTypeReader.readUInt32(content);
+ }
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ if (getVersion() == 0x01) {
+ IsoTypeWriter.writeUInt64(byteBuffer, fragmentAbsoluteTime);
+ IsoTypeWriter.writeUInt64(byteBuffer, fragmentAbsoluteDuration);
+ } else {
+ IsoTypeWriter.writeUInt32(byteBuffer, fragmentAbsoluteTime);
+ IsoTypeWriter.writeUInt32(byteBuffer, fragmentAbsoluteDuration);
+ }
+ }
+
+ public long getFragmentAbsoluteTime() {
+ return fragmentAbsoluteTime;
+ }
+
+ public long getFragmentAbsoluteDuration() {
+ return fragmentAbsoluteDuration;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/UuidBasedProtectionSystemSpecificHeaderBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/UuidBasedProtectionSystemSpecificHeaderBox.java
new file mode 100644
index 0000000..b147398
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/piff/UuidBasedProtectionSystemSpecificHeaderBox.java
@@ -0,0 +1,106 @@
+package com.googlecode.mp4parser.boxes.piff;
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.util.Path;
+import com.googlecode.mp4parser.util.UUIDConverter;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.Override;import java.lang.String;import java.lang.StringBuilder;import java.nio.ByteBuffer;
+import java.util.UUID;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * aligned(8) class UuidBasedProtectionSystemSpecificHeaderBox extends FullBox(‘uuid’,
+ * extended_type=0xd08a4f18-10f3-4a82-b6c8-32d8aba183d3,
+ * version=0, flags=0)
+ * {
+ * unsigned int(8)[16] SystemID;
+ * unsigned int(32) DataSize;
+ * unsigned int(8)[DataSize] Data;
+ * }
+ */
+public class UuidBasedProtectionSystemSpecificHeaderBox extends AbstractFullBox {
+ public static byte[] USER_TYPE = new byte[]{(byte) 0xd0, (byte) 0x8a, 0x4f, 0x18, 0x10, (byte) 0xf3, 0x4a, (byte) 0x82,
+ (byte) 0xb6, (byte) 0xc8, 0x32, (byte) 0xd8, (byte) 0xab, (byte) 0xa1, (byte) 0x83, (byte) 0xd3};
+
+ UUID systemId;
+
+ ProtectionSpecificHeader protectionSpecificHeader;
+
+ public UuidBasedProtectionSystemSpecificHeaderBox() {
+ super("uuid", USER_TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 24 + protectionSpecificHeader.getData().limit();
+ }
+
+ @Override
+ public byte[] getUserType() {
+ return USER_TYPE;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ IsoTypeWriter.writeUInt64(byteBuffer, systemId.getMostSignificantBits());
+ IsoTypeWriter.writeUInt64(byteBuffer, systemId.getLeastSignificantBits());
+ ByteBuffer data = protectionSpecificHeader.getData();
+ data.rewind();
+ IsoTypeWriter.writeUInt32(byteBuffer, data.limit());
+ byteBuffer.put(data);
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ byte[] systemIdBytes = new byte[16];
+ content.get(systemIdBytes);
+ systemId = UUIDConverter.convert(systemIdBytes);
+ int dataSize = l2i(IsoTypeReader.readUInt32(content));
+ protectionSpecificHeader = ProtectionSpecificHeader.createFor(systemId, content);
+ }
+
+ public UUID getSystemId() {
+ return systemId;
+ }
+
+ public void setSystemId(UUID systemId) {
+ this.systemId = systemId;
+ }
+
+ public String getSystemIdString() {
+ return systemId.toString();
+ }
+
+ public ProtectionSpecificHeader getProtectionSpecificHeader() {
+ return protectionSpecificHeader;
+ }
+
+ public String getProtectionSpecificHeaderString() {
+ return protectionSpecificHeader.toString();
+ }
+
+ public void setProtectionSpecificHeader(ProtectionSpecificHeader protectionSpecificHeader) {
+ this.protectionSpecificHeader = protectionSpecificHeader;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("UuidBasedProtectionSystemSpecificHeaderBox");
+ sb.append("{systemId=").append(systemId.toString());
+ sb.append(", dataSize=").append(protectionSpecificHeader.getData().limit());
+ sb.append('}');
+ return sb.toString();
+ }
+
+
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/all-wcprops
new file mode 100644
index 0000000..1f9ed7a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/683/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244
+END
+SegmentIndexBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/683/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/SegmentIndexBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/entries
new file mode 100644
index 0000000..954a06a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-06-23T08:51:59.024275Z
+683
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+SegmentIndexBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.567220Z
+1d0e3a16184ab4820e3c6dc9ebdcea87
+2012-06-23T08:51:59.024275Z
+683
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8722
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/text-base/SegmentIndexBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/text-base/SegmentIndexBox.java.svn-base
new file mode 100644
index 0000000..638a87b
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/.svn/text-base/SegmentIndexBox.java.svn-base
@@ -0,0 +1,283 @@
+package com.googlecode.mp4parser.boxes.threegpp26244;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <pre>
+ * aligned(8) class SegmentIndexBox extends FullBox(‘sidx’, version, 0) {
+ * unsigned int(32) reference_ID;
+ * unsigned int(32) timescale;
+ * if (version==0)
+ * {
+ * unsigned int(32) earliest_presentation_time;
+ * unsigned int(32) first_offset;
+ * }
+ * else
+ * {
+ * unsigned int(64) earliest_presentation_time;
+ * unsigned int(64) first_offset;
+ * }
+ * unsigned int(16) reserved = 0;
+ * unsigned int(16) reference_count;
+ * for(i=1; i <= reference_count; i++)
+ * {
+ * bit (1) reference_type;
+ * unsigned int(31) referenced_size;
+ * unsigned int(32) subsegment_duration;
+ * bit(1) starts_with_SAP;
+ * unsigned int(3) SAP_type;
+ * unsigned int(28) SAP_delta_time;
+ * }
+ * }
+ * </pre>
+ */
+public class SegmentIndexBox extends AbstractFullBox {
+ public static final String TYPE = "sidx";
+ List<Entry> entries = new ArrayList<Entry>();
+
+ long referenceId;
+ long timeScale;
+ long earliestPresentationTime;
+ long firstOffset;
+ int reserved;
+
+
+ public SegmentIndexBox() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 4;
+ size += 4;
+ size += 4;
+ size += getVersion() == 0 ? 8 : 16;
+ size += 2; // reserved
+ size += 2; // reference count
+
+ size += entries.size() * 12;
+
+ return size;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ IsoTypeWriter.writeUInt32(byteBuffer, referenceId);
+ IsoTypeWriter.writeUInt32(byteBuffer, timeScale);
+ if (getVersion() == 0) {
+ IsoTypeWriter.writeUInt32(byteBuffer, earliestPresentationTime);
+ IsoTypeWriter.writeUInt32(byteBuffer, firstOffset);
+ } else {
+ IsoTypeWriter.writeUInt64(byteBuffer, earliestPresentationTime);
+ IsoTypeWriter.writeUInt64(byteBuffer, firstOffset);
+ }
+ IsoTypeWriter.writeUInt16(byteBuffer, reserved);
+ IsoTypeWriter.writeUInt16(byteBuffer, entries.size());
+ for (Entry entry : entries) {
+ BitWriterBuffer b = new BitWriterBuffer(byteBuffer);
+ b.writeBits(entry.getReferenceType(), 1);
+ b.writeBits(entry.getReferencedSize(), 31);
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getSubsegmentDuration());
+ b = new BitWriterBuffer(byteBuffer);
+ b.writeBits(entry.getStartsWithSap(), 1);
+ b.writeBits(entry.getSapType(), 3);
+ b.writeBits(entry.getSapDeltaTime(), 28);
+ }
+
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ referenceId = IsoTypeReader.readUInt32(content);
+ timeScale = IsoTypeReader.readUInt32(content);
+ if (getVersion() == 0) {
+ earliestPresentationTime = IsoTypeReader.readUInt32(content);
+ firstOffset = IsoTypeReader.readUInt32(content);
+ } else {
+ earliestPresentationTime = IsoTypeReader.readUInt64(content);
+ firstOffset = IsoTypeReader.readUInt64(content);
+ }
+ reserved = IsoTypeReader.readUInt16(content);
+ int numEntries = IsoTypeReader.readUInt16(content);
+ for (int i = 0; i < numEntries; i++) {
+ BitReaderBuffer b = new BitReaderBuffer(content);
+ Entry e = new Entry();
+ e.setReferenceType((byte) b.readBits(1));
+ e.setReferencedSize(b.readBits(31));
+ e.setSubsegmentDuration(IsoTypeReader.readUInt32(content));
+ b = new BitReaderBuffer(content);
+ e.setStartsWithSap((byte) b.readBits(1));
+ e.setSapType((byte) b.readBits(3));
+ e.setSapDeltaTime(b.readBits(28));
+ entries.add(e);
+ }
+ }
+
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public long getReferenceId() {
+ return referenceId;
+ }
+
+ public void setReferenceId(long referenceId) {
+ this.referenceId = referenceId;
+ }
+
+ public long getTimeScale() {
+ return timeScale;
+ }
+
+ public void setTimeScale(long timeScale) {
+ this.timeScale = timeScale;
+ }
+
+ public long getEarliestPresentationTime() {
+ return earliestPresentationTime;
+ }
+
+ public void setEarliestPresentationTime(long earliestPresentationTime) {
+ this.earliestPresentationTime = earliestPresentationTime;
+ }
+
+ public long getFirstOffset() {
+ return firstOffset;
+ }
+
+ public void setFirstOffset(long firstOffset) {
+ this.firstOffset = firstOffset;
+ }
+
+ public int getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(int reserved) {
+ this.reserved = reserved;
+ }
+
+ public static class Entry {
+ byte referenceType;
+ int referencedSize;
+ long subsegmentDuration;
+ byte startsWithSap;
+ byte sapType;
+ int sapDeltaTime;
+
+ public Entry() {
+ }
+
+ public Entry(byte referenceType, int referencedSize, long subsegmentDuration, byte startsWithSap, byte sapType, int sapDeltaTime) {
+ this.referenceType = referenceType;
+ this.referencedSize = referencedSize;
+ this.subsegmentDuration = subsegmentDuration;
+ this.startsWithSap = startsWithSap;
+ this.sapType = sapType;
+ this.sapDeltaTime = sapDeltaTime;
+ }
+
+ public byte getReferenceType() {
+ return referenceType;
+ }
+
+ public void setReferenceType(byte referenceType) {
+ this.referenceType = referenceType;
+ }
+
+ public int getReferencedSize() {
+ return referencedSize;
+ }
+
+ public void setReferencedSize(int referencedSize) {
+ this.referencedSize = referencedSize;
+ }
+
+ public long getSubsegmentDuration() {
+ return subsegmentDuration;
+ }
+
+ public void setSubsegmentDuration(long subsegmentDuration) {
+ this.subsegmentDuration = subsegmentDuration;
+ }
+
+ public byte getStartsWithSap() {
+ return startsWithSap;
+ }
+
+ public void setStartsWithSap(byte startsWithSap) {
+ this.startsWithSap = startsWithSap;
+ }
+
+ public byte getSapType() {
+ return sapType;
+ }
+
+ public void setSapType(byte sapType) {
+ this.sapType = sapType;
+ }
+
+ public int getSapDeltaTime() {
+ return sapDeltaTime;
+ }
+
+ public void setSapDeltaTime(int sapDeltaTime) {
+ this.sapDeltaTime = sapDeltaTime;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "referenceType=" + referenceType +
+ ", referencedSize=" + referencedSize +
+ ", subsegmentDuration=" + subsegmentDuration +
+ ", startsWithSap=" + startsWithSap +
+ ", sapType=" + sapType +
+ ", sapDeltaTime=" + sapDeltaTime +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Entry entry = (Entry) o;
+
+ if (referenceType != entry.referenceType) return false;
+ if (referencedSize != entry.referencedSize) return false;
+ if (sapDeltaTime != entry.sapDeltaTime) return false;
+ if (sapType != entry.sapType) return false;
+ if (startsWithSap != entry.startsWithSap) return false;
+ if (subsegmentDuration != entry.subsegmentDuration) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) referenceType;
+ result = 31 * result + referencedSize;
+ result = 31 * result + (int) (subsegmentDuration ^ (subsegmentDuration >>> 32));
+ result = 31 * result + (int) startsWithSap;
+ result = 31 * result + (int) sapType;
+ result = 31 * result + sapDeltaTime;
+ return result;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/SegmentIndexBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/SegmentIndexBox.java
new file mode 100644
index 0000000..638a87b
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26244/SegmentIndexBox.java
@@ -0,0 +1,283 @@
+package com.googlecode.mp4parser.boxes.threegpp26244;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <pre>
+ * aligned(8) class SegmentIndexBox extends FullBox(‘sidx’, version, 0) {
+ * unsigned int(32) reference_ID;
+ * unsigned int(32) timescale;
+ * if (version==0)
+ * {
+ * unsigned int(32) earliest_presentation_time;
+ * unsigned int(32) first_offset;
+ * }
+ * else
+ * {
+ * unsigned int(64) earliest_presentation_time;
+ * unsigned int(64) first_offset;
+ * }
+ * unsigned int(16) reserved = 0;
+ * unsigned int(16) reference_count;
+ * for(i=1; i <= reference_count; i++)
+ * {
+ * bit (1) reference_type;
+ * unsigned int(31) referenced_size;
+ * unsigned int(32) subsegment_duration;
+ * bit(1) starts_with_SAP;
+ * unsigned int(3) SAP_type;
+ * unsigned int(28) SAP_delta_time;
+ * }
+ * }
+ * </pre>
+ */
+public class SegmentIndexBox extends AbstractFullBox {
+ public static final String TYPE = "sidx";
+ List<Entry> entries = new ArrayList<Entry>();
+
+ long referenceId;
+ long timeScale;
+ long earliestPresentationTime;
+ long firstOffset;
+ int reserved;
+
+
+ public SegmentIndexBox() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 4;
+ size += 4;
+ size += 4;
+ size += getVersion() == 0 ? 8 : 16;
+ size += 2; // reserved
+ size += 2; // reference count
+
+ size += entries.size() * 12;
+
+ return size;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ IsoTypeWriter.writeUInt32(byteBuffer, referenceId);
+ IsoTypeWriter.writeUInt32(byteBuffer, timeScale);
+ if (getVersion() == 0) {
+ IsoTypeWriter.writeUInt32(byteBuffer, earliestPresentationTime);
+ IsoTypeWriter.writeUInt32(byteBuffer, firstOffset);
+ } else {
+ IsoTypeWriter.writeUInt64(byteBuffer, earliestPresentationTime);
+ IsoTypeWriter.writeUInt64(byteBuffer, firstOffset);
+ }
+ IsoTypeWriter.writeUInt16(byteBuffer, reserved);
+ IsoTypeWriter.writeUInt16(byteBuffer, entries.size());
+ for (Entry entry : entries) {
+ BitWriterBuffer b = new BitWriterBuffer(byteBuffer);
+ b.writeBits(entry.getReferenceType(), 1);
+ b.writeBits(entry.getReferencedSize(), 31);
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getSubsegmentDuration());
+ b = new BitWriterBuffer(byteBuffer);
+ b.writeBits(entry.getStartsWithSap(), 1);
+ b.writeBits(entry.getSapType(), 3);
+ b.writeBits(entry.getSapDeltaTime(), 28);
+ }
+
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ referenceId = IsoTypeReader.readUInt32(content);
+ timeScale = IsoTypeReader.readUInt32(content);
+ if (getVersion() == 0) {
+ earliestPresentationTime = IsoTypeReader.readUInt32(content);
+ firstOffset = IsoTypeReader.readUInt32(content);
+ } else {
+ earliestPresentationTime = IsoTypeReader.readUInt64(content);
+ firstOffset = IsoTypeReader.readUInt64(content);
+ }
+ reserved = IsoTypeReader.readUInt16(content);
+ int numEntries = IsoTypeReader.readUInt16(content);
+ for (int i = 0; i < numEntries; i++) {
+ BitReaderBuffer b = new BitReaderBuffer(content);
+ Entry e = new Entry();
+ e.setReferenceType((byte) b.readBits(1));
+ e.setReferencedSize(b.readBits(31));
+ e.setSubsegmentDuration(IsoTypeReader.readUInt32(content));
+ b = new BitReaderBuffer(content);
+ e.setStartsWithSap((byte) b.readBits(1));
+ e.setSapType((byte) b.readBits(3));
+ e.setSapDeltaTime(b.readBits(28));
+ entries.add(e);
+ }
+ }
+
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public long getReferenceId() {
+ return referenceId;
+ }
+
+ public void setReferenceId(long referenceId) {
+ this.referenceId = referenceId;
+ }
+
+ public long getTimeScale() {
+ return timeScale;
+ }
+
+ public void setTimeScale(long timeScale) {
+ this.timeScale = timeScale;
+ }
+
+ public long getEarliestPresentationTime() {
+ return earliestPresentationTime;
+ }
+
+ public void setEarliestPresentationTime(long earliestPresentationTime) {
+ this.earliestPresentationTime = earliestPresentationTime;
+ }
+
+ public long getFirstOffset() {
+ return firstOffset;
+ }
+
+ public void setFirstOffset(long firstOffset) {
+ this.firstOffset = firstOffset;
+ }
+
+ public int getReserved() {
+ return reserved;
+ }
+
+ public void setReserved(int reserved) {
+ this.reserved = reserved;
+ }
+
+ public static class Entry {
+ byte referenceType;
+ int referencedSize;
+ long subsegmentDuration;
+ byte startsWithSap;
+ byte sapType;
+ int sapDeltaTime;
+
+ public Entry() {
+ }
+
+ public Entry(byte referenceType, int referencedSize, long subsegmentDuration, byte startsWithSap, byte sapType, int sapDeltaTime) {
+ this.referenceType = referenceType;
+ this.referencedSize = referencedSize;
+ this.subsegmentDuration = subsegmentDuration;
+ this.startsWithSap = startsWithSap;
+ this.sapType = sapType;
+ this.sapDeltaTime = sapDeltaTime;
+ }
+
+ public byte getReferenceType() {
+ return referenceType;
+ }
+
+ public void setReferenceType(byte referenceType) {
+ this.referenceType = referenceType;
+ }
+
+ public int getReferencedSize() {
+ return referencedSize;
+ }
+
+ public void setReferencedSize(int referencedSize) {
+ this.referencedSize = referencedSize;
+ }
+
+ public long getSubsegmentDuration() {
+ return subsegmentDuration;
+ }
+
+ public void setSubsegmentDuration(long subsegmentDuration) {
+ this.subsegmentDuration = subsegmentDuration;
+ }
+
+ public byte getStartsWithSap() {
+ return startsWithSap;
+ }
+
+ public void setStartsWithSap(byte startsWithSap) {
+ this.startsWithSap = startsWithSap;
+ }
+
+ public byte getSapType() {
+ return sapType;
+ }
+
+ public void setSapType(byte sapType) {
+ this.sapType = sapType;
+ }
+
+ public int getSapDeltaTime() {
+ return sapDeltaTime;
+ }
+
+ public void setSapDeltaTime(int sapDeltaTime) {
+ this.sapDeltaTime = sapDeltaTime;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "referenceType=" + referenceType +
+ ", referencedSize=" + referencedSize +
+ ", subsegmentDuration=" + subsegmentDuration +
+ ", startsWithSap=" + startsWithSap +
+ ", sapType=" + sapType +
+ ", sapDeltaTime=" + sapDeltaTime +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Entry entry = (Entry) o;
+
+ if (referenceType != entry.referenceType) return false;
+ if (referencedSize != entry.referencedSize) return false;
+ if (sapDeltaTime != entry.sapDeltaTime) return false;
+ if (sapType != entry.sapType) return false;
+ if (startsWithSap != entry.startsWithSap) return false;
+ if (subsegmentDuration != entry.subsegmentDuration) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) referenceType;
+ result = 31 * result + referencedSize;
+ result = 31 * result + (int) (subsegmentDuration ^ (subsegmentDuration >>> 32));
+ result = 31 * result + (int) startsWithSap;
+ result = 31 * result + (int) sapType;
+ result = 31 * result + sapDeltaTime;
+ return result;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/all-wcprops
new file mode 100644
index 0000000..92aa246
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245
+END
+FontTableBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 110
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/FontTableBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/entries
new file mode 100644
index 0000000..a126e77
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+FontTableBox.java
+file
+
+
+
+
+2012-09-14T17:27:50.567220Z
+edbb8c6ba22fb01c3b13ce20e34513b8
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2444
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/text-base/FontTableBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/text-base/FontTableBox.java.svn-base
new file mode 100644
index 0000000..2e3f640
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/.svn/text-base/FontTableBox.java.svn-base
@@ -0,0 +1,95 @@
+package com.googlecode.mp4parser.boxes.threegpp26245;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.Utf8;
+import com.googlecode.mp4parser.AbstractBox;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class FontTableBox extends AbstractBox {
+ List<FontRecord> entries = new LinkedList<FontRecord>();
+
+ public FontTableBox() {
+ super("ftab");
+ }
+
+ @Override
+ protected long getContentSize() {
+ int size = 2;
+ for (FontRecord fontRecord : entries) {
+ size += fontRecord.getSize();
+ }
+ return size;
+ }
+
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ int numberOfRecords = IsoTypeReader.readUInt16(content);
+ for (int i = 0; i < numberOfRecords; i++) {
+ FontRecord fr = new FontRecord();
+ fr.parse(content);
+ entries.add(fr);
+ }
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ IsoTypeWriter.writeUInt16(byteBuffer, entries.size());
+ for (FontRecord record : entries) {
+ record.getContent(byteBuffer);
+ }
+ }
+
+ public List<FontRecord> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<FontRecord> entries) {
+ this.entries = entries;
+ }
+
+ public static class FontRecord {
+ int fontId;
+ String fontname;
+
+ public FontRecord() {
+ }
+
+ public FontRecord(int fontId, String fontname) {
+ this.fontId = fontId;
+ this.fontname = fontname;
+ }
+
+ public void parse(ByteBuffer bb) {
+ fontId = IsoTypeReader.readUInt16(bb);
+ int length = IsoTypeReader.readUInt8(bb);
+ fontname = IsoTypeReader.readString(bb, length);
+ }
+
+ public void getContent(ByteBuffer bb) {
+ IsoTypeWriter.writeUInt16(bb, fontId);
+ IsoTypeWriter.writeUInt8(bb, fontname.length());
+ bb.put(Utf8.convert(fontname));
+ }
+
+ public int getSize() {
+ return Utf8.utf8StringLengthInBytes(fontname) + 3;
+ }
+
+ @Override
+ public String toString() {
+ return "FontRecord{" +
+ "fontId=" + fontId +
+ ", fontname='" + fontname + '\'' +
+ '}';
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/FontTableBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/FontTableBox.java
new file mode 100644
index 0000000..2e3f640
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/threegpp26245/FontTableBox.java
@@ -0,0 +1,95 @@
+package com.googlecode.mp4parser.boxes.threegpp26245;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.coremedia.iso.Utf8;
+import com.googlecode.mp4parser.AbstractBox;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class FontTableBox extends AbstractBox {
+ List<FontRecord> entries = new LinkedList<FontRecord>();
+
+ public FontTableBox() {
+ super("ftab");
+ }
+
+ @Override
+ protected long getContentSize() {
+ int size = 2;
+ for (FontRecord fontRecord : entries) {
+ size += fontRecord.getSize();
+ }
+ return size;
+ }
+
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ int numberOfRecords = IsoTypeReader.readUInt16(content);
+ for (int i = 0; i < numberOfRecords; i++) {
+ FontRecord fr = new FontRecord();
+ fr.parse(content);
+ entries.add(fr);
+ }
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ IsoTypeWriter.writeUInt16(byteBuffer, entries.size());
+ for (FontRecord record : entries) {
+ record.getContent(byteBuffer);
+ }
+ }
+
+ public List<FontRecord> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<FontRecord> entries) {
+ this.entries = entries;
+ }
+
+ public static class FontRecord {
+ int fontId;
+ String fontname;
+
+ public FontRecord() {
+ }
+
+ public FontRecord(int fontId, String fontname) {
+ this.fontId = fontId;
+ this.fontname = fontname;
+ }
+
+ public void parse(ByteBuffer bb) {
+ fontId = IsoTypeReader.readUInt16(bb);
+ int length = IsoTypeReader.readUInt8(bb);
+ fontname = IsoTypeReader.readString(bb, length);
+ }
+
+ public void getContent(ByteBuffer bb) {
+ IsoTypeWriter.writeUInt16(bb, fontId);
+ IsoTypeWriter.writeUInt8(bb, fontname.length());
+ bb.put(Utf8.convert(fontname));
+ }
+
+ public int getSize() {
+ return Utf8.utf8StringLengthInBytes(fontname) + 3;
+ }
+
+ @Override
+ public String toString() {
+ return "FontRecord{" +
+ "fontId=" + fontId +
+ ", fontname='" + fontname + '\'' +
+ '}';
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/all-wcprops
new file mode 100644
index 0000000..4e87a87
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet
+END
+BaseLocationBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 111
+/svn/!svn/ver/507/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/BaseLocationBox.java
+END
+AssetInformationBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/505/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/AssetInformationBox.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/entries
new file mode 100644
index 0000000..c7166b8
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+BaseLocationBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+40378a4f505496f720804c1f201fc542
+2012-04-21T22:05:38.425329Z
+507
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3319
+
+AssetInformationBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.307230Z
+a43de0d08b7416e7a9034f0840cc2eeb
+2012-04-21T21:18:31.685061Z
+505
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2063
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/text-base/AssetInformationBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/text-base/AssetInformationBox.java.svn-base
new file mode 100644
index 0000000..a2a9e8a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/text-base/AssetInformationBox.java.svn-base
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.ultraviolet;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.Utf8;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ * AssetInformationBox as defined Common File Format Spec.
+ */
+public class AssetInformationBox extends AbstractFullBox {
+ String apid = "";
+ String profileVersion = "0000";
+
+ public AssetInformationBox() {
+ super("ainf");
+ }
+
+ @Override
+ protected long getContentSize() {
+ return Utf8.utf8StringLengthInBytes(apid) + 9;
+ }
+
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(Utf8.convert(profileVersion), 0, 4);
+ byteBuffer.put(Utf8.convert(apid));
+ byteBuffer.put((byte) 0);
+ }
+
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ profileVersion = IsoTypeReader.readString(content, 4);
+ apid = IsoTypeReader.readString(content);
+ content = null;
+ }
+
+ public String getApid() {
+ return apid;
+ }
+
+ public void setApid(String apid) {
+ this.apid = apid;
+ }
+
+ public String getProfileVersion() {
+ return profileVersion;
+ }
+
+ public void setProfileVersion(String profileVersion) {
+ assert profileVersion != null && profileVersion.length() == 4;
+ this.profileVersion = profileVersion;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/text-base/BaseLocationBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/text-base/BaseLocationBox.java.svn-base
new file mode 100644
index 0000000..1c63293
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/.svn/text-base/BaseLocationBox.java.svn-base
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.ultraviolet;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.Utf8;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ *
+ */
+public class BaseLocationBox extends AbstractFullBox {
+ String baseLocation = "";
+ String purchaseLocation = "";
+
+ public BaseLocationBox() {
+ super("bloc");
+ }
+
+ public BaseLocationBox(String baseLocation, String purchaseLocation) {
+ super("bloc");
+ this.baseLocation = baseLocation;
+ this.purchaseLocation = purchaseLocation;
+ }
+
+ public String getBaseLocation() {
+ return baseLocation;
+ }
+
+ public void setBaseLocation(String baseLocation) {
+ this.baseLocation = baseLocation;
+ }
+
+ public String getPurchaseLocation() {
+ return purchaseLocation;
+ }
+
+ public void setPurchaseLocation(String purchaseLocation) {
+ this.purchaseLocation = purchaseLocation;
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 1028;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ baseLocation = IsoTypeReader.readString(content);
+ content.get(new byte[256 - Utf8.utf8StringLengthInBytes(baseLocation) - 1]);
+ purchaseLocation = IsoTypeReader.readString(content);
+ content.get(new byte[256 - Utf8.utf8StringLengthInBytes(purchaseLocation) - 1]);
+ content.get(new byte[512]);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(Utf8.convert(baseLocation));
+ byteBuffer.put(new byte[256 - Utf8.utf8StringLengthInBytes(baseLocation)]); // string plus term zero
+ byteBuffer.put(Utf8.convert(purchaseLocation));
+ byteBuffer.put(new byte[256 - Utf8.utf8StringLengthInBytes(purchaseLocation)]); // string plus term zero
+ byteBuffer.put(new byte[512]);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BaseLocationBox that = (BaseLocationBox) o;
+
+ if (baseLocation != null ? !baseLocation.equals(that.baseLocation) : that.baseLocation != null) return false;
+ if (purchaseLocation != null ? !purchaseLocation.equals(that.purchaseLocation) : that.purchaseLocation != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = baseLocation != null ? baseLocation.hashCode() : 0;
+ result = 31 * result + (purchaseLocation != null ? purchaseLocation.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/AssetInformationBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/AssetInformationBox.java
new file mode 100644
index 0000000..a2a9e8a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/AssetInformationBox.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.ultraviolet;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.Utf8;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ * AssetInformationBox as defined Common File Format Spec.
+ */
+public class AssetInformationBox extends AbstractFullBox {
+ String apid = "";
+ String profileVersion = "0000";
+
+ public AssetInformationBox() {
+ super("ainf");
+ }
+
+ @Override
+ protected long getContentSize() {
+ return Utf8.utf8StringLengthInBytes(apid) + 9;
+ }
+
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(Utf8.convert(profileVersion), 0, 4);
+ byteBuffer.put(Utf8.convert(apid));
+ byteBuffer.put((byte) 0);
+ }
+
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ profileVersion = IsoTypeReader.readString(content, 4);
+ apid = IsoTypeReader.readString(content);
+ content = null;
+ }
+
+ public String getApid() {
+ return apid;
+ }
+
+ public void setApid(String apid) {
+ this.apid = apid;
+ }
+
+ public String getProfileVersion() {
+ return profileVersion;
+ }
+
+ public void setProfileVersion(String profileVersion) {
+ assert profileVersion != null && profileVersion.length() == 4;
+ this.profileVersion = profileVersion;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/BaseLocationBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/BaseLocationBox.java
new file mode 100644
index 0000000..1c63293
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/ultraviolet/BaseLocationBox.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2011 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.ultraviolet;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.Utf8;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+
+/**
+ *
+ */
+public class BaseLocationBox extends AbstractFullBox {
+ String baseLocation = "";
+ String purchaseLocation = "";
+
+ public BaseLocationBox() {
+ super("bloc");
+ }
+
+ public BaseLocationBox(String baseLocation, String purchaseLocation) {
+ super("bloc");
+ this.baseLocation = baseLocation;
+ this.purchaseLocation = purchaseLocation;
+ }
+
+ public String getBaseLocation() {
+ return baseLocation;
+ }
+
+ public void setBaseLocation(String baseLocation) {
+ this.baseLocation = baseLocation;
+ }
+
+ public String getPurchaseLocation() {
+ return purchaseLocation;
+ }
+
+ public void setPurchaseLocation(String purchaseLocation) {
+ this.purchaseLocation = purchaseLocation;
+ }
+
+ @Override
+ protected long getContentSize() {
+ return 1028;
+ }
+
+ @Override
+ public void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ baseLocation = IsoTypeReader.readString(content);
+ content.get(new byte[256 - Utf8.utf8StringLengthInBytes(baseLocation) - 1]);
+ purchaseLocation = IsoTypeReader.readString(content);
+ content.get(new byte[256 - Utf8.utf8StringLengthInBytes(purchaseLocation) - 1]);
+ content.get(new byte[512]);
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(Utf8.convert(baseLocation));
+ byteBuffer.put(new byte[256 - Utf8.utf8StringLengthInBytes(baseLocation)]); // string plus term zero
+ byteBuffer.put(Utf8.convert(purchaseLocation));
+ byteBuffer.put(new byte[256 - Utf8.utf8StringLengthInBytes(purchaseLocation)]); // string plus term zero
+ byteBuffer.put(new byte[512]);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BaseLocationBox that = (BaseLocationBox) o;
+
+ if (baseLocation != null ? !baseLocation.equals(that.baseLocation) : that.baseLocation != null) return false;
+ if (purchaseLocation != null ? !purchaseLocation.equals(that.purchaseLocation) : that.purchaseLocation != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = baseLocation != null ? baseLocation.hashCode() : 0;
+ result = 31 * result + (purchaseLocation != null ? purchaseLocation.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/all-wcprops
new file mode 100644
index 0000000..6a318ac
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/528/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264
+END
+CharCache.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/236/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/CharCache.java
+END
+BTree.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/236/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/BTree.java
+END
+Debug.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/236/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/Debug.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/entries
new file mode 100644
index 0000000..a6fb43e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/entries
@@ -0,0 +1,139 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-04-26T13:35:04.700844Z
+528
+hoemmagnus@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+model
+dir
+
+CharCache.java
+file
+
+
+
+
+2012-09-14T17:27:51.457232Z
+c3b2a16e50a2b43f07cc729ba83f6b84
+2011-09-20T18:03:23.375910Z
+236
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1868
+
+BTree.java
+file
+
+
+
+
+2012-09-14T17:27:51.457232Z
+3dd3d07d1bd0c3bdbcb2aeb45c375f1d
+2011-09-20T18:03:23.375910Z
+236
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2116
+
+Debug.java
+file
+
+
+
+
+2012-09-14T17:27:51.457232Z
+f136a4c416c039d97eedee9f96667284
+2011-09-20T18:03:23.375910Z
+236
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2796
+
+write
+dir
+
+read
+dir
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/BTree.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/BTree.java.svn-base
new file mode 100644
index 0000000..57391ba
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/BTree.java.svn-base
@@ -0,0 +1,69 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264;
+
+
+/**
+ * Simple BTree implementation needed for haffman tables
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class BTree {
+ private BTree zero;
+ private BTree one;
+ private Object value;
+
+ /**
+ * Adds a leaf value to a binary path specified by path
+ *
+ * @param str
+ * @param value
+ */
+ public void addString(String path, Object value) {
+ if (path.length() == 0) {
+ this.value = value;
+ return;
+ }
+ char charAt = path.charAt(0);
+ BTree branch;
+ if (charAt == '0') {
+ if (zero == null)
+ zero = new BTree();
+ branch = zero;
+ } else {
+ if (one == null)
+ one = new BTree();
+ branch = one;
+ }
+ branch.addString(path.substring(1), value);
+ }
+
+ public BTree down(int b) {
+ if (b == 0)
+ return zero;
+ else
+ return one;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/CharCache.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/CharCache.java.svn-base
new file mode 100644
index 0000000..2fe8ead
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/CharCache.java.svn-base
@@ -0,0 +1,57 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264;
+
+public class CharCache {
+ private char[] cache;
+ private int pos;
+
+ public CharCache(int capacity) {
+ cache = new char[capacity];
+ }
+
+ public void append(String str) {
+ char[] chars = str.toCharArray();
+ int available = cache.length - pos;
+ int toWrite = chars.length < available ? chars.length : available;
+ System.arraycopy(chars, 0, cache, pos, toWrite);
+ pos += toWrite;
+ }
+
+ public String toString() {
+ return new String(cache, 0, pos);
+ }
+
+ public void clear() {
+ pos = 0;
+ }
+
+ public void append(char c) {
+ if (pos < cache.length - 1) {
+ cache[pos] = c;
+ pos++;
+ }
+ }
+
+ public int length() {
+ return pos;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/Debug.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/Debug.java.svn-base
new file mode 100644
index 0000000..d0bea73
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/.svn/text-base/Debug.java.svn-base
@@ -0,0 +1,88 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264;
+
+import java.nio.ShortBuffer;
+
+public class Debug {
+ public final static void print8x8(int[] output) {
+ int i = 0;
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ System.out.printf("%3d, ", output[i]);
+ i++;
+ }
+ System.out.println();
+ }
+ }
+
+ public final static void print8x8(short[] output) {
+ int i = 0;
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ System.out.printf("%3d, ", output[i]);
+ i++;
+ }
+ System.out.println();
+ }
+ }
+
+ public final static void print8x8(ShortBuffer output) {
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ System.out.printf("%3d, ", output.get());
+ }
+ System.out.println();
+ }
+ }
+
+ public static void print(short[] table) {
+ int i = 0;
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ System.out.printf("%3d, ", table[i]);
+ i++;
+ }
+ System.out.println();
+ }
+ }
+
+ public static void trace(String format, Object... args) {
+ // System.out.printf("> " + format + "\n", args);
+ }
+
+ public final static boolean debug = false;
+
+ public static void print(int i) {
+ if (debug)
+ System.out.print(i);
+ }
+
+ public static void print(String string) {
+ if (debug)
+ System.out.print(string);
+ }
+
+ public static void println(String string) {
+ if (debug)
+ System.out.println(string);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/BTree.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/BTree.java
new file mode 100644
index 0000000..57391ba
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/BTree.java
@@ -0,0 +1,69 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264;
+
+
+/**
+ * Simple BTree implementation needed for haffman tables
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class BTree {
+ private BTree zero;
+ private BTree one;
+ private Object value;
+
+ /**
+ * Adds a leaf value to a binary path specified by path
+ *
+ * @param str
+ * @param value
+ */
+ public void addString(String path, Object value) {
+ if (path.length() == 0) {
+ this.value = value;
+ return;
+ }
+ char charAt = path.charAt(0);
+ BTree branch;
+ if (charAt == '0') {
+ if (zero == null)
+ zero = new BTree();
+ branch = zero;
+ } else {
+ if (one == null)
+ one = new BTree();
+ branch = one;
+ }
+ branch.addString(path.substring(1), value);
+ }
+
+ public BTree down(int b) {
+ if (b == 0)
+ return zero;
+ else
+ return one;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/CharCache.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/CharCache.java
new file mode 100644
index 0000000..2fe8ead
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/CharCache.java
@@ -0,0 +1,57 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264;
+
+public class CharCache {
+ private char[] cache;
+ private int pos;
+
+ public CharCache(int capacity) {
+ cache = new char[capacity];
+ }
+
+ public void append(String str) {
+ char[] chars = str.toCharArray();
+ int available = cache.length - pos;
+ int toWrite = chars.length < available ? chars.length : available;
+ System.arraycopy(chars, 0, cache, pos, toWrite);
+ pos += toWrite;
+ }
+
+ public String toString() {
+ return new String(cache, 0, pos);
+ }
+
+ public void clear() {
+ pos = 0;
+ }
+
+ public void append(char c) {
+ if (pos < cache.length - 1) {
+ cache[pos] = c;
+ pos++;
+ }
+ }
+
+ public int length() {
+ return pos;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/Debug.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/Debug.java
new file mode 100644
index 0000000..d0bea73
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/Debug.java
@@ -0,0 +1,88 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264;
+
+import java.nio.ShortBuffer;
+
+public class Debug {
+ public final static void print8x8(int[] output) {
+ int i = 0;
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ System.out.printf("%3d, ", output[i]);
+ i++;
+ }
+ System.out.println();
+ }
+ }
+
+ public final static void print8x8(short[] output) {
+ int i = 0;
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ System.out.printf("%3d, ", output[i]);
+ i++;
+ }
+ System.out.println();
+ }
+ }
+
+ public final static void print8x8(ShortBuffer output) {
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ System.out.printf("%3d, ", output.get());
+ }
+ System.out.println();
+ }
+ }
+
+ public static void print(short[] table) {
+ int i = 0;
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ System.out.printf("%3d, ", table[i]);
+ i++;
+ }
+ System.out.println();
+ }
+ }
+
+ public static void trace(String format, Object... args) {
+ // System.out.printf("> " + format + "\n", args);
+ }
+
+ public final static boolean debug = false;
+
+ public static void print(int i) {
+ if (debug)
+ System.out.print(i);
+ }
+
+ public static void print(String string) {
+ if (debug)
+ System.out.print(string);
+ }
+
+ public static void println(String string) {
+ if (debug)
+ System.out.println(string);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/all-wcprops
new file mode 100644
index 0000000..87c4f5d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/all-wcprops
@@ -0,0 +1,59 @@
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/528/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model
+END
+HRDParameters.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/381/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/HRDParameters.java
+END
+ChromaFormat.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/244/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ChromaFormat.java
+END
+BitstreamElement.java
+K 25
+svn:wc:ra_dav:version-url
+V 105
+/svn/!svn/ver/236/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/BitstreamElement.java
+END
+SeqParameterSet.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/SeqParameterSet.java
+END
+ScalingMatrix.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/244/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingMatrix.java
+END
+VUIParameters.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/244/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/VUIParameters.java
+END
+PictureParameterSet.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/528/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/PictureParameterSet.java
+END
+ScalingList.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/244/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingList.java
+END
+AspectRatio.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/236/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/AspectRatio.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/entries
new file mode 100644
index 0000000..57346e5
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/entries
@@ -0,0 +1,334 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/model
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-04-26T13:35:04.700844Z
+528
+hoemmagnus@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+HRDParameters.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+b975fc17f18909ecfd3e15a29d8fb70e
+2012-03-06T23:45:39.821994Z
+381
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2410
+
+ChromaFormat.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+55075c51ac229ada06e17133f52fbc2b
+2011-10-15T21:12:03.008630Z
+244
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2498
+
+BitstreamElement.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+36a082110b2e0d7feab53e78bd5447e9
+2011-09-20T18:03:23.375910Z
+236
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1287
+
+SeqParameterSet.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+f5f16b7f8e3609e73614f978009d472a
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+28600
+
+ScalingMatrix.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+d7e4473348da54dc5434b84745704e62
+2011-10-15T21:12:03.008630Z
+244
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1582
+
+VUIParameters.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+c39f83d225178ec9873004550130700b
+2011-10-15T21:12:03.008630Z
+244
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4563
+
+PictureParameterSet.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+bfe87344433e296b0c8b953ddad5e78f
+2012-04-26T13:35:04.700844Z
+528
+hoemmagnus@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+19980
+
+ScalingList.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+6f3a11866f0bcd61a9868f949a3c5c27
+2011-10-15T21:12:03.008630Z
+244
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2945
+
+AspectRatio.java
+file
+
+
+
+
+2012-09-14T17:27:51.377231Z
+4471abf932d16399567945fce05d656c
+2011-09-20T18:03:23.375910Z
+236
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1640
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/AspectRatio.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/AspectRatio.java.svn-base
new file mode 100644
index 0000000..bc66b1a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/AspectRatio.java.svn-base
@@ -0,0 +1,50 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+/**
+ * Aspect ratio
+ * <p/>
+ * dynamic enum
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class AspectRatio {
+
+ public static final AspectRatio Extended_SAR = new AspectRatio(255);
+
+ private int value;
+
+ private AspectRatio(int value) {
+ this.value = value;
+ }
+
+ public static AspectRatio fromValue(int value) {
+ if (value == Extended_SAR.value) {
+ return Extended_SAR;
+ }
+ return new AspectRatio(value);
+ }
+
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/BitstreamElement.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/BitstreamElement.java.svn-base
new file mode 100644
index 0000000..f16c5e9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/BitstreamElement.java.svn-base
@@ -0,0 +1,29 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public abstract class BitstreamElement {
+
+ public abstract void write(OutputStream out) throws IOException;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ChromaFormat.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ChromaFormat.java.svn-base
new file mode 100644
index 0000000..2af2966
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ChromaFormat.java.svn-base
@@ -0,0 +1,77 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+/**
+ * Chroma format enum
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class ChromaFormat {
+ public static ChromaFormat MONOCHROME = new ChromaFormat(0, 0, 0);
+ public static ChromaFormat YUV_420 = new ChromaFormat(1, 2, 2);
+ public static ChromaFormat YUV_422 = new ChromaFormat(2, 2, 1);
+ public static ChromaFormat YUV_444 = new ChromaFormat(3, 1, 1);
+
+ private int id;
+ private int subWidth;
+ private int subHeight;
+
+ public ChromaFormat(int id, int subWidth, int subHeight) {
+ this.id = id;
+ this.subWidth = subWidth;
+ this.subHeight = subHeight;
+ }
+
+ public static ChromaFormat fromId(int id) {
+ if (id == MONOCHROME.id) {
+ return MONOCHROME;
+ } else if (id == YUV_420.id) {
+ return YUV_420;
+ } else if (id == YUV_422.id) {
+ return YUV_422;
+ } else if (id == YUV_444.id) {
+ return YUV_444;
+ }
+ return null;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public int getSubWidth() {
+ return subWidth;
+ }
+
+ public int getSubHeight() {
+ return subHeight;
+ }
+
+ @Override
+ public String toString() {
+ return "ChromaFormat{" + "\n" +
+ "id=" + id + ",\n" +
+ " subWidth=" + subWidth + ",\n" +
+ " subHeight=" + subHeight +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/HRDParameters.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/HRDParameters.java.svn-base
new file mode 100644
index 0000000..f713ab2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/HRDParameters.java.svn-base
@@ -0,0 +1,53 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import java.util.Arrays;
+
+public class HRDParameters {
+
+ public int cpb_cnt_minus1;
+ public int bit_rate_scale;
+ public int cpb_size_scale;
+ public int[] bit_rate_value_minus1;
+ public int[] cpb_size_value_minus1;
+ public boolean[] cbr_flag;
+ public int initial_cpb_removal_delay_length_minus1;
+ public int cpb_removal_delay_length_minus1;
+ public int dpb_output_delay_length_minus1;
+ public int time_offset_length;
+
+ @Override
+ public String toString() {
+ return "HRDParameters{" +
+ "cpb_cnt_minus1=" + cpb_cnt_minus1 +
+ ", bit_rate_scale=" + bit_rate_scale +
+ ", cpb_size_scale=" + cpb_size_scale +
+ ", bit_rate_value_minus1=" + Arrays.toString(bit_rate_value_minus1) +
+ ", cpb_size_value_minus1=" + Arrays.toString(cpb_size_value_minus1) +
+ ", cbr_flag=" + Arrays.toString(cbr_flag) +
+ ", initial_cpb_removal_delay_length_minus1=" + initial_cpb_removal_delay_length_minus1 +
+ ", cpb_removal_delay_length_minus1=" + cpb_removal_delay_length_minus1 +
+ ", dpb_output_delay_length_minus1=" + dpb_output_delay_length_minus1 +
+ ", time_offset_length=" + time_offset_length +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/PictureParameterSet.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/PictureParameterSet.java.svn-base
new file mode 100644
index 0000000..9154c38
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/PictureParameterSet.java.svn-base
@@ -0,0 +1,406 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+import com.googlecode.mp4parser.h264.write.CAVLCWriter;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * Picture Parameter Set entity of H264 bitstream
+ * <p/>
+ * capable to serialize / deserialize with CAVLC bitstream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class PictureParameterSet extends BitstreamElement {
+
+ public static class PPSExt {
+ public boolean transform_8x8_mode_flag;
+ public ScalingMatrix scalindMatrix = new ScalingMatrix();
+ public int second_chroma_qp_index_offset;
+ public boolean[] pic_scaling_list_present_flag;
+
+ @Override
+ public String toString() {
+ return "PPSExt{" +
+ "transform_8x8_mode_flag=" + transform_8x8_mode_flag +
+ ", scalindMatrix=" + scalindMatrix +
+ ", second_chroma_qp_index_offset=" + second_chroma_qp_index_offset +
+ ", pic_scaling_list_present_flag=" + pic_scaling_list_present_flag +
+ '}';
+ }
+ }
+
+ public boolean entropy_coding_mode_flag;
+ public int num_ref_idx_l0_active_minus1;
+ public int num_ref_idx_l1_active_minus1;
+ public int slice_group_change_rate_minus1;
+ public int pic_parameter_set_id;
+ public int seq_parameter_set_id;
+ public boolean pic_order_present_flag;
+ public int num_slice_groups_minus1;
+ public int slice_group_map_type;
+ public boolean weighted_pred_flag;
+ public int weighted_bipred_idc;
+ public int pic_init_qp_minus26;
+ public int pic_init_qs_minus26;
+ public int chroma_qp_index_offset;
+ public boolean deblocking_filter_control_present_flag;
+ public boolean constrained_intra_pred_flag;
+ public boolean redundant_pic_cnt_present_flag;
+ public int[] top_left;
+ public int[] bottom_right;
+ public int[] run_length_minus1;
+ public boolean slice_group_change_direction_flag;
+ public int[] slice_group_id;
+ public PPSExt extended;
+
+ public static PictureParameterSet read(byte[] b) throws IOException {
+ return read(new ByteArrayInputStream(b));
+ }
+
+ public static PictureParameterSet read(InputStream is) throws IOException {
+ CAVLCReader reader = new CAVLCReader(is);
+ PictureParameterSet pps = new PictureParameterSet();
+
+ pps.pic_parameter_set_id = reader.readUE("PPS: pic_parameter_set_id");
+ pps.seq_parameter_set_id = reader.readUE("PPS: seq_parameter_set_id");
+ pps.entropy_coding_mode_flag = reader
+ .readBool("PPS: entropy_coding_mode_flag");
+ pps.pic_order_present_flag = reader
+ .readBool("PPS: pic_order_present_flag");
+ pps.num_slice_groups_minus1 = reader
+ .readUE("PPS: num_slice_groups_minus1");
+ if (pps.num_slice_groups_minus1 > 0) {
+ pps.slice_group_map_type = reader
+ .readUE("PPS: slice_group_map_type");
+ pps.top_left = new int[pps.num_slice_groups_minus1 + 1];
+ pps.bottom_right = new int[pps.num_slice_groups_minus1 + 1];
+ pps.run_length_minus1 = new int[pps.num_slice_groups_minus1 + 1];
+ if (pps.slice_group_map_type == 0)
+ for (int iGroup = 0; iGroup <= pps.num_slice_groups_minus1; iGroup++)
+ pps.run_length_minus1[iGroup] = reader
+ .readUE("PPS: run_length_minus1");
+ else if (pps.slice_group_map_type == 2)
+ for (int iGroup = 0; iGroup < pps.num_slice_groups_minus1; iGroup++) {
+ pps.top_left[iGroup] = reader.readUE("PPS: top_left");
+ pps.bottom_right[iGroup] = reader
+ .readUE("PPS: bottom_right");
+ }
+ else if (pps.slice_group_map_type == 3
+ || pps.slice_group_map_type == 4
+ || pps.slice_group_map_type == 5) {
+ pps.slice_group_change_direction_flag = reader
+ .readBool("PPS: slice_group_change_direction_flag");
+ pps.slice_group_change_rate_minus1 = reader
+ .readUE("PPS: slice_group_change_rate_minus1");
+ } else if (pps.slice_group_map_type == 6) {
+ int NumberBitsPerSliceGroupId;
+ if (pps.num_slice_groups_minus1 + 1 > 4)
+ NumberBitsPerSliceGroupId = 3;
+ else if (pps.num_slice_groups_minus1 + 1 > 2)
+ NumberBitsPerSliceGroupId = 2;
+ else
+ NumberBitsPerSliceGroupId = 1;
+ int pic_size_in_map_units_minus1 = reader
+ .readUE("PPS: pic_size_in_map_units_minus1");
+ pps.slice_group_id = new int[pic_size_in_map_units_minus1 + 1];
+ for (int i = 0; i <= pic_size_in_map_units_minus1; i++) {
+ pps.slice_group_id[i] = reader.readU(
+ NumberBitsPerSliceGroupId, "PPS: slice_group_id ["
+ + i + "]f");
+ }
+ }
+ }
+ pps.num_ref_idx_l0_active_minus1 = reader
+ .readUE("PPS: num_ref_idx_l0_active_minus1");
+ pps.num_ref_idx_l1_active_minus1 = reader
+ .readUE("PPS: num_ref_idx_l1_active_minus1");
+ pps.weighted_pred_flag = reader.readBool("PPS: weighted_pred_flag");
+ pps.weighted_bipred_idc = (int) reader.readNBit(2,
+ "PPS: weighted_bipred_idc");
+ pps.pic_init_qp_minus26 = reader.readSE("PPS: pic_init_qp_minus26");
+ pps.pic_init_qs_minus26 = reader.readSE("PPS: pic_init_qs_minus26");
+ pps.chroma_qp_index_offset = reader
+ .readSE("PPS: chroma_qp_index_offset");
+ pps.deblocking_filter_control_present_flag = reader
+ .readBool("PPS: deblocking_filter_control_present_flag");
+ pps.constrained_intra_pred_flag = reader
+ .readBool("PPS: constrained_intra_pred_flag");
+ pps.redundant_pic_cnt_present_flag = reader
+ .readBool("PPS: redundant_pic_cnt_present_flag");
+ if (reader.moreRBSPData()) {
+ pps.extended = new PictureParameterSet.PPSExt();
+ pps.extended.transform_8x8_mode_flag = reader
+ .readBool("PPS: transform_8x8_mode_flag");
+ boolean pic_scaling_matrix_present_flag = reader
+ .readBool("PPS: pic_scaling_matrix_present_flag");
+ if (pic_scaling_matrix_present_flag) {
+ for (int i = 0; i < 6 + 2 * (pps.extended.transform_8x8_mode_flag ? 1
+ : 0); i++) {
+ boolean pic_scaling_list_present_flag = reader
+ .readBool("PPS: pic_scaling_list_present_flag");
+ if (pic_scaling_list_present_flag) {
+ pps.extended.scalindMatrix.ScalingList4x4 = new ScalingList[8];
+ pps.extended.scalindMatrix.ScalingList8x8 = new ScalingList[8];
+ if (i < 6) {
+ pps.extended.scalindMatrix.ScalingList4x4[i] = ScalingList
+ .read(reader, 16);
+ } else {
+ pps.extended.scalindMatrix.ScalingList8x8[i - 6] = ScalingList
+ .read(reader, 64);
+ }
+ }
+ }
+ }
+ pps.extended.second_chroma_qp_index_offset = reader
+ .readSE("PPS: second_chroma_qp_index_offset");
+ }
+
+ reader.readTrailingBits();
+
+ return pps;
+ }
+
+ public void write(OutputStream out) throws IOException {
+ CAVLCWriter writer = new CAVLCWriter(out);
+
+ writer.writeUE(pic_parameter_set_id, "PPS: pic_parameter_set_id");
+ writer.writeUE(seq_parameter_set_id, "PPS: seq_parameter_set_id");
+ writer.writeBool(entropy_coding_mode_flag,
+ "PPS: entropy_coding_mode_flag");
+ writer.writeBool(pic_order_present_flag, "PPS: pic_order_present_flag");
+ writer.writeUE(num_slice_groups_minus1, "PPS: num_slice_groups_minus1");
+ if (num_slice_groups_minus1 > 0) {
+ writer.writeUE(slice_group_map_type, "PPS: slice_group_map_type");
+ int[] top_left = new int[1];
+ int[] bottom_right = new int[1];
+ int[] run_length_minus1 = new int[1];
+ if (slice_group_map_type == 0) {
+ for (int iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++) {
+ writer.writeUE(run_length_minus1[iGroup], "PPS: ");
+ }
+ } else if (slice_group_map_type == 2) {
+ for (int iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++) {
+ writer.writeUE(top_left[iGroup], "PPS: ");
+ writer.writeUE(bottom_right[iGroup], "PPS: ");
+ }
+ } else if (slice_group_map_type == 3 || slice_group_map_type == 4
+ || slice_group_map_type == 5) {
+ writer.writeBool(slice_group_change_direction_flag,
+ "PPS: slice_group_change_direction_flag");
+ writer.writeUE(slice_group_change_rate_minus1,
+ "PPS: slice_group_change_rate_minus1");
+ } else if (slice_group_map_type == 6) {
+ int NumberBitsPerSliceGroupId;
+ if (num_slice_groups_minus1 + 1 > 4)
+ NumberBitsPerSliceGroupId = 3;
+ else if (num_slice_groups_minus1 + 1 > 2)
+ NumberBitsPerSliceGroupId = 2;
+ else
+ NumberBitsPerSliceGroupId = 1;
+ writer.writeUE(slice_group_id.length, "PPS: ");
+ for (int i = 0; i <= slice_group_id.length; i++) {
+ writer.writeU(slice_group_id[i], NumberBitsPerSliceGroupId);
+ }
+ }
+ }
+ writer.writeUE(num_ref_idx_l0_active_minus1,
+ "PPS: num_ref_idx_l0_active_minus1");
+ writer.writeUE(num_ref_idx_l1_active_minus1,
+ "PPS: num_ref_idx_l1_active_minus1");
+ writer.writeBool(weighted_pred_flag, "PPS: weighted_pred_flag");
+ writer.writeNBit(weighted_bipred_idc, 2, "PPS: weighted_bipred_idc");
+ writer.writeSE(pic_init_qp_minus26, "PPS: pic_init_qp_minus26");
+ writer.writeSE(pic_init_qs_minus26, "PPS: pic_init_qs_minus26");
+ writer.writeSE(chroma_qp_index_offset, "PPS: chroma_qp_index_offset");
+ writer.writeBool(deblocking_filter_control_present_flag,
+ "PPS: deblocking_filter_control_present_flag");
+ writer.writeBool(constrained_intra_pred_flag,
+ "PPS: constrained_intra_pred_flag");
+ writer.writeBool(redundant_pic_cnt_present_flag,
+ "PPS: redundant_pic_cnt_present_flag");
+ if (extended != null) {
+ writer.writeBool(extended.transform_8x8_mode_flag,
+ "PPS: transform_8x8_mode_flag");
+ writer.writeBool(extended.scalindMatrix != null,
+ "PPS: scalindMatrix");
+ if (extended.scalindMatrix != null) {
+ for (int i = 0; i < 6 + 2 * (extended.transform_8x8_mode_flag ? 1
+ : 0); i++) {
+ if (i < 6) {
+ writer
+ .writeBool(
+ extended.scalindMatrix.ScalingList4x4[i] != null,
+ "PPS: ");
+ if (extended.scalindMatrix.ScalingList4x4[i] != null) {
+ extended.scalindMatrix.ScalingList4x4[i]
+ .write(writer);
+ }
+
+ } else {
+ writer
+ .writeBool(
+ extended.scalindMatrix.ScalingList8x8[i - 6] != null,
+ "PPS: ");
+ if (extended.scalindMatrix.ScalingList8x8[i - 6] != null) {
+ extended.scalindMatrix.ScalingList8x8[i - 6]
+ .write(writer);
+ }
+ }
+ }
+ }
+ writer.writeSE(extended.second_chroma_qp_index_offset, "PPS: ");
+ }
+
+ writer.writeTrailingBits();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(bottom_right);
+ result = prime * result + chroma_qp_index_offset;
+ result = prime * result + (constrained_intra_pred_flag ? 1231 : 1237);
+ result = prime * result
+ + (deblocking_filter_control_present_flag ? 1231 : 1237);
+ result = prime * result + (entropy_coding_mode_flag ? 1231 : 1237);
+ result = prime * result
+ + ((extended == null) ? 0 : extended.hashCode());
+ result = prime * result + num_ref_idx_l0_active_minus1;
+ result = prime * result + num_ref_idx_l1_active_minus1;
+ result = prime * result + num_slice_groups_minus1;
+ result = prime * result + pic_init_qp_minus26;
+ result = prime * result + pic_init_qs_minus26;
+ result = prime * result + (pic_order_present_flag ? 1231 : 1237);
+ result = prime * result + pic_parameter_set_id;
+ result = prime * result
+ + (redundant_pic_cnt_present_flag ? 1231 : 1237);
+ result = prime * result + Arrays.hashCode(run_length_minus1);
+ result = prime * result + seq_parameter_set_id;
+ result = prime * result
+ + (slice_group_change_direction_flag ? 1231 : 1237);
+ result = prime * result + slice_group_change_rate_minus1;
+ result = prime * result + Arrays.hashCode(slice_group_id);
+ result = prime * result + slice_group_map_type;
+ result = prime * result + Arrays.hashCode(top_left);
+ result = prime * result + weighted_bipred_idc;
+ result = prime * result + (weighted_pred_flag ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ PictureParameterSet other = (PictureParameterSet) obj;
+ if (!Arrays.equals(bottom_right, other.bottom_right))
+ return false;
+ if (chroma_qp_index_offset != other.chroma_qp_index_offset)
+ return false;
+ if (constrained_intra_pred_flag != other.constrained_intra_pred_flag)
+ return false;
+ if (deblocking_filter_control_present_flag != other.deblocking_filter_control_present_flag)
+ return false;
+ if (entropy_coding_mode_flag != other.entropy_coding_mode_flag)
+ return false;
+ if (extended == null) {
+ if (other.extended != null)
+ return false;
+ } else if (!extended.equals(other.extended))
+ return false;
+ if (num_ref_idx_l0_active_minus1 != other.num_ref_idx_l0_active_minus1)
+ return false;
+ if (num_ref_idx_l1_active_minus1 != other.num_ref_idx_l1_active_minus1)
+ return false;
+ if (num_slice_groups_minus1 != other.num_slice_groups_minus1)
+ return false;
+ if (pic_init_qp_minus26 != other.pic_init_qp_minus26)
+ return false;
+ if (pic_init_qs_minus26 != other.pic_init_qs_minus26)
+ return false;
+ if (pic_order_present_flag != other.pic_order_present_flag)
+ return false;
+ if (pic_parameter_set_id != other.pic_parameter_set_id)
+ return false;
+ if (redundant_pic_cnt_present_flag != other.redundant_pic_cnt_present_flag)
+ return false;
+ if (!Arrays.equals(run_length_minus1, other.run_length_minus1))
+ return false;
+ if (seq_parameter_set_id != other.seq_parameter_set_id)
+ return false;
+ if (slice_group_change_direction_flag != other.slice_group_change_direction_flag)
+ return false;
+ if (slice_group_change_rate_minus1 != other.slice_group_change_rate_minus1)
+ return false;
+ if (!Arrays.equals(slice_group_id, other.slice_group_id))
+ return false;
+ if (slice_group_map_type != other.slice_group_map_type)
+ return false;
+ if (!Arrays.equals(top_left, other.top_left))
+ return false;
+ if (weighted_bipred_idc != other.weighted_bipred_idc)
+ return false;
+ if (weighted_pred_flag != other.weighted_pred_flag)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "PictureParameterSet{" +
+ "\n entropy_coding_mode_flag=" + entropy_coding_mode_flag +
+ ",\n num_ref_idx_l0_active_minus1=" + num_ref_idx_l0_active_minus1 +
+ ",\n num_ref_idx_l1_active_minus1=" + num_ref_idx_l1_active_minus1 +
+ ",\n slice_group_change_rate_minus1=" + slice_group_change_rate_minus1 +
+ ",\n pic_parameter_set_id=" + pic_parameter_set_id +
+ ",\n seq_parameter_set_id=" + seq_parameter_set_id +
+ ",\n pic_order_present_flag=" + pic_order_present_flag +
+ ",\n num_slice_groups_minus1=" + num_slice_groups_minus1 +
+ ",\n slice_group_map_type=" + slice_group_map_type +
+ ",\n weighted_pred_flag=" + weighted_pred_flag +
+ ",\n weighted_bipred_idc=" + weighted_bipred_idc +
+ ",\n pic_init_qp_minus26=" + pic_init_qp_minus26 +
+ ",\n pic_init_qs_minus26=" + pic_init_qs_minus26 +
+ ",\n chroma_qp_index_offset=" + chroma_qp_index_offset +
+ ",\n deblocking_filter_control_present_flag=" + deblocking_filter_control_present_flag +
+ ",\n constrained_intra_pred_flag=" + constrained_intra_pred_flag +
+ ",\n redundant_pic_cnt_present_flag=" + redundant_pic_cnt_present_flag +
+ ",\n top_left=" + top_left +
+ ",\n bottom_right=" + bottom_right +
+ ",\n run_length_minus1=" + run_length_minus1 +
+ ",\n slice_group_change_direction_flag=" + slice_group_change_direction_flag +
+ ",\n slice_group_id=" + slice_group_id +
+ ",\n extended=" + extended +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ScalingList.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ScalingList.java.svn-base
new file mode 100644
index 0000000..5d272bf
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ScalingList.java.svn-base
@@ -0,0 +1,83 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+import com.googlecode.mp4parser.h264.write.CAVLCWriter;
+
+import java.io.IOException;
+
+/**
+ * Scaling list entity
+ * <p/>
+ * capable to serialize / deserialize with CAVLC bitstream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class ScalingList {
+
+ public int[] scalingList;
+ public boolean useDefaultScalingMatrixFlag;
+
+ public void write(CAVLCWriter out) throws IOException {
+ if (useDefaultScalingMatrixFlag) {
+ out.writeSE(0, "SPS: ");
+ return;
+ }
+
+ int lastScale = 8;
+ int nextScale = 8;
+ for (int j = 0; j < scalingList.length; j++) {
+ if (nextScale != 0) {
+ int deltaScale = scalingList[j] - lastScale - 256;
+ out.writeSE(deltaScale, "SPS: ");
+ }
+ lastScale = scalingList[j];
+ }
+ }
+
+ public static ScalingList read(CAVLCReader is, int sizeOfScalingList)
+ throws IOException {
+
+ ScalingList sl = new ScalingList();
+ sl.scalingList = new int[sizeOfScalingList];
+ int lastScale = 8;
+ int nextScale = 8;
+ for (int j = 0; j < sizeOfScalingList; j++) {
+ if (nextScale != 0) {
+ int deltaScale = is.readSE("deltaScale");
+ nextScale = (lastScale + deltaScale + 256) % 256;
+ sl.useDefaultScalingMatrixFlag = (j == 0 && nextScale == 0);
+ }
+ sl.scalingList[j] = nextScale == 0 ? lastScale : nextScale;
+ lastScale = sl.scalingList[j];
+ }
+ return sl;
+ }
+
+ @Override
+ public String toString() {
+ return "ScalingList{" +
+ "scalingList=" + scalingList +
+ ", useDefaultScalingMatrixFlag=" + useDefaultScalingMatrixFlag +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ScalingMatrix.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ScalingMatrix.java.svn-base
new file mode 100644
index 0000000..d04af8e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/ScalingMatrix.java.svn-base
@@ -0,0 +1,37 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import java.util.Arrays;
+
+public class ScalingMatrix {
+
+ public ScalingList[] ScalingList4x4;
+ public ScalingList[] ScalingList8x8;
+
+ @Override
+ public String toString() {
+ return "ScalingMatrix{" +
+ "ScalingList4x4=" + (ScalingList4x4 == null ? null : Arrays.asList(ScalingList4x4)) + "\n" +
+ ", ScalingList8x8=" + (ScalingList8x8 == null ? null : Arrays.asList(ScalingList8x8)) + "\n" +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/SeqParameterSet.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/SeqParameterSet.java.svn-base
new file mode 100644
index 0000000..4894df8
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/SeqParameterSet.java.svn-base
@@ -0,0 +1,556 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+import com.googlecode.mp4parser.h264.write.CAVLCWriter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Sequence Parameter Set structure of h264 bitstream
+ * <p/>
+ * capable to serialize and deserialize with CAVLC bitstream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class SeqParameterSet extends BitstreamElement {
+ public int pic_order_cnt_type;
+ public boolean field_pic_flag;
+ public boolean delta_pic_order_always_zero_flag;
+ public boolean weighted_pred_flag;
+ public int weighted_bipred_idc;
+ public boolean entropy_coding_mode_flag;
+ public boolean mb_adaptive_frame_field_flag;
+ public boolean direct_8x8_inference_flag;
+ public ChromaFormat chroma_format_idc;
+ public int log2_max_frame_num_minus4;
+ public int log2_max_pic_order_cnt_lsb_minus4;
+ public int pic_height_in_map_units_minus1;
+ public int pic_width_in_mbs_minus1;
+ public int bit_depth_luma_minus8;
+ public int bit_depth_chroma_minus8;
+ public boolean qpprime_y_zero_transform_bypass_flag;
+ public int profile_idc;
+ public boolean constraint_set_0_flag;
+ public boolean constraint_set_1_flag;
+ public boolean constraint_set_2_flag;
+ public boolean constraint_set_3_flag;
+ public int level_idc;
+ public int seq_parameter_set_id;
+ public boolean residual_color_transform_flag;
+ public int offset_for_non_ref_pic;
+ public int offset_for_top_to_bottom_field;
+ public int num_ref_frames;
+ public boolean gaps_in_frame_num_value_allowed_flag;
+ public boolean frame_mbs_only_flag;
+ public boolean frame_cropping_flag;
+ public int frame_crop_left_offset;
+ public int frame_crop_right_offset;
+ public int frame_crop_top_offset;
+ public int frame_crop_bottom_offset;
+ public int[] offsetForRefFrame;
+ public VUIParameters vuiParams;
+ public ScalingMatrix scalingMatrix;
+ public int num_ref_frames_in_pic_order_cnt_cycle;
+
+ public static SeqParameterSet read(InputStream is) throws IOException {
+ CAVLCReader reader = new CAVLCReader(is);
+ SeqParameterSet sps = new SeqParameterSet();
+
+ sps.profile_idc = (int) reader.readNBit(8, "SPS: profile_idc");
+ sps.constraint_set_0_flag = reader
+ .readBool("SPS: constraint_set_0_flag");
+ sps.constraint_set_1_flag = reader
+ .readBool("SPS: constraint_set_1_flag");
+ sps.constraint_set_2_flag = reader
+ .readBool("SPS: constraint_set_2_flag");
+ sps.constraint_set_3_flag = reader
+ .readBool("SPS: constraint_set_3_flag");
+ reader.readNBit(4, "SPS: reserved_zero_4bits");
+ sps.level_idc = (int) reader.readNBit(8, "SPS: level_idc");
+ sps.seq_parameter_set_id = reader.readUE("SPS: seq_parameter_set_id");
+
+ if (sps.profile_idc == 100 || sps.profile_idc == 110
+ || sps.profile_idc == 122 || sps.profile_idc == 144) {
+ sps.chroma_format_idc = ChromaFormat.fromId(reader
+ .readUE("SPS: chroma_format_idc"));
+ if (sps.chroma_format_idc == ChromaFormat.YUV_444) {
+ sps.residual_color_transform_flag = reader
+ .readBool("SPS: residual_color_transform_flag");
+ }
+ sps.bit_depth_luma_minus8 = reader
+ .readUE("SPS: bit_depth_luma_minus8");
+ sps.bit_depth_chroma_minus8 = reader
+ .readUE("SPS: bit_depth_chroma_minus8");
+ sps.qpprime_y_zero_transform_bypass_flag = reader
+ .readBool("SPS: qpprime_y_zero_transform_bypass_flag");
+ boolean seqScalingMatrixPresent = reader
+ .readBool("SPS: seq_scaling_matrix_present_lag");
+ if (seqScalingMatrixPresent) {
+ readScalingListMatrix(reader, sps);
+ }
+ } else {
+ sps.chroma_format_idc = ChromaFormat.YUV_420;
+ }
+ sps.log2_max_frame_num_minus4 = reader
+ .readUE("SPS: log2_max_frame_num_minus4");
+ sps.pic_order_cnt_type = reader.readUE("SPS: pic_order_cnt_type");
+ if (sps.pic_order_cnt_type == 0) {
+ sps.log2_max_pic_order_cnt_lsb_minus4 = reader
+ .readUE("SPS: log2_max_pic_order_cnt_lsb_minus4");
+ } else if (sps.pic_order_cnt_type == 1) {
+ sps.delta_pic_order_always_zero_flag = reader
+ .readBool("SPS: delta_pic_order_always_zero_flag");
+ sps.offset_for_non_ref_pic = reader
+ .readSE("SPS: offset_for_non_ref_pic");
+ sps.offset_for_top_to_bottom_field = reader
+ .readSE("SPS: offset_for_top_to_bottom_field");
+ sps.num_ref_frames_in_pic_order_cnt_cycle = reader
+ .readUE("SPS: num_ref_frames_in_pic_order_cnt_cycle");
+ sps.offsetForRefFrame = new int[sps.num_ref_frames_in_pic_order_cnt_cycle];
+ for (int i = 0; i < sps.num_ref_frames_in_pic_order_cnt_cycle; i++) {
+ sps.offsetForRefFrame[i] = reader
+ .readSE("SPS: offsetForRefFrame [" + i + "]");
+ }
+ }
+ sps.num_ref_frames = reader.readUE("SPS: num_ref_frames");
+ sps.gaps_in_frame_num_value_allowed_flag = reader
+ .readBool("SPS: gaps_in_frame_num_value_allowed_flag");
+ sps.pic_width_in_mbs_minus1 = reader
+ .readUE("SPS: pic_width_in_mbs_minus1");
+ sps.pic_height_in_map_units_minus1 = reader
+ .readUE("SPS: pic_height_in_map_units_minus1");
+ sps.frame_mbs_only_flag = reader.readBool("SPS: frame_mbs_only_flag");
+ if (!sps.frame_mbs_only_flag) {
+ sps.mb_adaptive_frame_field_flag = reader
+ .readBool("SPS: mb_adaptive_frame_field_flag");
+ }
+ sps.direct_8x8_inference_flag = reader
+ .readBool("SPS: direct_8x8_inference_flag");
+ sps.frame_cropping_flag = reader.readBool("SPS: frame_cropping_flag");
+ if (sps.frame_cropping_flag) {
+ sps.frame_crop_left_offset = reader
+ .readUE("SPS: frame_crop_left_offset");
+ sps.frame_crop_right_offset = reader
+ .readUE("SPS: frame_crop_right_offset");
+ sps.frame_crop_top_offset = reader
+ .readUE("SPS: frame_crop_top_offset");
+ sps.frame_crop_bottom_offset = reader
+ .readUE("SPS: frame_crop_bottom_offset");
+ }
+ boolean vui_parameters_present_flag = reader
+ .readBool("SPS: vui_parameters_present_flag");
+ if (vui_parameters_present_flag)
+ sps.vuiParams = ReadVUIParameters(reader);
+
+ reader.readTrailingBits();
+
+ return sps;
+ }
+
+ private static void readScalingListMatrix(CAVLCReader reader,
+ SeqParameterSet sps) throws IOException {
+ sps.scalingMatrix = new ScalingMatrix();
+ for (int i = 0; i < 8; i++) {
+ boolean seqScalingListPresentFlag = reader
+ .readBool("SPS: seqScalingListPresentFlag");
+ if (seqScalingListPresentFlag) {
+ sps.scalingMatrix.ScalingList4x4 = new ScalingList[8];
+ sps.scalingMatrix.ScalingList8x8 = new ScalingList[8];
+ if (i < 6) {
+ sps.scalingMatrix.ScalingList4x4[i] = ScalingList.read(
+ reader, 16);
+ } else {
+ sps.scalingMatrix.ScalingList8x8[i - 6] = ScalingList.read(
+ reader, 64);
+ }
+ }
+ }
+ }
+
+ private static VUIParameters ReadVUIParameters(CAVLCReader reader)
+ throws IOException {
+ VUIParameters vuip = new VUIParameters();
+ vuip.aspect_ratio_info_present_flag = reader
+ .readBool("VUI: aspect_ratio_info_present_flag");
+ if (vuip.aspect_ratio_info_present_flag) {
+ vuip.aspect_ratio = AspectRatio.fromValue((int) reader.readNBit(8,
+ "VUI: aspect_ratio"));
+ if (vuip.aspect_ratio == AspectRatio.Extended_SAR) {
+ vuip.sar_width = (int) reader.readNBit(16, "VUI: sar_width");
+ vuip.sar_height = (int) reader.readNBit(16, "VUI: sar_height");
+ }
+ }
+ vuip.overscan_info_present_flag = reader
+ .readBool("VUI: overscan_info_present_flag");
+ if (vuip.overscan_info_present_flag) {
+ vuip.overscan_appropriate_flag = reader
+ .readBool("VUI: overscan_appropriate_flag");
+ }
+ vuip.video_signal_type_present_flag = reader
+ .readBool("VUI: video_signal_type_present_flag");
+ if (vuip.video_signal_type_present_flag) {
+ vuip.video_format = (int) reader.readNBit(3, "VUI: video_format");
+ vuip.video_full_range_flag = reader
+ .readBool("VUI: video_full_range_flag");
+ vuip.colour_description_present_flag = reader
+ .readBool("VUI: colour_description_present_flag");
+ if (vuip.colour_description_present_flag) {
+ vuip.colour_primaries = (int) reader.readNBit(8,
+ "VUI: colour_primaries");
+ vuip.transfer_characteristics = (int) reader.readNBit(8,
+ "VUI: transfer_characteristics");
+ vuip.matrix_coefficients = (int) reader.readNBit(8,
+ "VUI: matrix_coefficients");
+ }
+ }
+ vuip.chroma_loc_info_present_flag = reader
+ .readBool("VUI: chroma_loc_info_present_flag");
+ if (vuip.chroma_loc_info_present_flag) {
+ vuip.chroma_sample_loc_type_top_field = reader
+ .readUE("VUI chroma_sample_loc_type_top_field");
+ vuip.chroma_sample_loc_type_bottom_field = reader
+ .readUE("VUI chroma_sample_loc_type_bottom_field");
+ }
+ vuip.timing_info_present_flag = reader
+ .readBool("VUI: timing_info_present_flag");
+ if (vuip.timing_info_present_flag) {
+ vuip.num_units_in_tick = (int) reader.readNBit(32,
+ "VUI: num_units_in_tick");
+ vuip.time_scale = (int) reader.readNBit(32, "VUI: time_scale");
+ vuip.fixed_frame_rate_flag = reader
+ .readBool("VUI: fixed_frame_rate_flag");
+ }
+ boolean nal_hrd_parameters_present_flag = reader
+ .readBool("VUI: nal_hrd_parameters_present_flag");
+ if (nal_hrd_parameters_present_flag)
+ vuip.nalHRDParams = readHRDParameters(reader);
+ boolean vcl_hrd_parameters_present_flag = reader
+ .readBool("VUI: vcl_hrd_parameters_present_flag");
+ if (vcl_hrd_parameters_present_flag)
+ vuip.vclHRDParams = readHRDParameters(reader);
+ if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
+ vuip.low_delay_hrd_flag = reader
+ .readBool("VUI: low_delay_hrd_flag");
+ }
+ vuip.pic_struct_present_flag = reader
+ .readBool("VUI: pic_struct_present_flag");
+ boolean bitstream_restriction_flag = reader
+ .readBool("VUI: bitstream_restriction_flag");
+ if (bitstream_restriction_flag) {
+ vuip.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
+ vuip.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = reader
+ .readBool("VUI: motion_vectors_over_pic_boundaries_flag");
+ vuip.bitstreamRestriction.max_bytes_per_pic_denom = reader
+ .readUE("VUI max_bytes_per_pic_denom");
+ vuip.bitstreamRestriction.max_bits_per_mb_denom = reader
+ .readUE("VUI max_bits_per_mb_denom");
+ vuip.bitstreamRestriction.log2_max_mv_length_horizontal = reader
+ .readUE("VUI log2_max_mv_length_horizontal");
+ vuip.bitstreamRestriction.log2_max_mv_length_vertical = reader
+ .readUE("VUI log2_max_mv_length_vertical");
+ vuip.bitstreamRestriction.num_reorder_frames = reader
+ .readUE("VUI num_reorder_frames");
+ vuip.bitstreamRestriction.max_dec_frame_buffering = reader
+ .readUE("VUI max_dec_frame_buffering");
+ }
+
+ return vuip;
+ }
+
+ private static HRDParameters readHRDParameters(CAVLCReader reader)
+ throws IOException {
+ HRDParameters hrd = new HRDParameters();
+ hrd.cpb_cnt_minus1 = reader.readUE("SPS: cpb_cnt_minus1");
+ hrd.bit_rate_scale = (int) reader.readNBit(4, "HRD: bit_rate_scale");
+ hrd.cpb_size_scale = (int) reader.readNBit(4, "HRD: cpb_size_scale");
+ hrd.bit_rate_value_minus1 = new int[hrd.cpb_cnt_minus1 + 1];
+ hrd.cpb_size_value_minus1 = new int[hrd.cpb_cnt_minus1 + 1];
+ hrd.cbr_flag = new boolean[hrd.cpb_cnt_minus1 + 1];
+
+ for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpb_cnt_minus1; SchedSelIdx++) {
+ hrd.bit_rate_value_minus1[SchedSelIdx] = reader
+ .readUE("HRD: bit_rate_value_minus1");
+ hrd.cpb_size_value_minus1[SchedSelIdx] = reader
+ .readUE("HRD: cpb_size_value_minus1");
+ hrd.cbr_flag[SchedSelIdx] = reader.readBool("HRD: cbr_flag");
+ }
+ hrd.initial_cpb_removal_delay_length_minus1 = (int) reader.readNBit(5,
+ "HRD: initial_cpb_removal_delay_length_minus1");
+ hrd.cpb_removal_delay_length_minus1 = (int) reader.readNBit(5,
+ "HRD: cpb_removal_delay_length_minus1");
+ hrd.dpb_output_delay_length_minus1 = (int) reader.readNBit(5,
+ "HRD: dpb_output_delay_length_minus1");
+ hrd.time_offset_length = (int) reader.readNBit(5,
+ "HRD: time_offset_length");
+ return hrd;
+ }
+
+ public void write(OutputStream out) throws IOException {
+ CAVLCWriter writer = new CAVLCWriter(out);
+
+ writer.writeNBit(profile_idc, 8, "SPS: profile_idc");
+ writer.writeBool(constraint_set_0_flag, "SPS: constraint_set_0_flag");
+ writer.writeBool(constraint_set_1_flag, "SPS: constraint_set_1_flag");
+ writer.writeBool(constraint_set_2_flag, "SPS: constraint_set_2_flag");
+ writer.writeBool(constraint_set_3_flag, "SPS: constraint_set_3_flag");
+ writer.writeNBit(0, 4, "SPS: reserved");
+ writer.writeNBit(level_idc, 8, "SPS: level_idc");
+ writer.writeUE(seq_parameter_set_id, "SPS: seq_parameter_set_id");
+
+ if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122
+ || profile_idc == 144) {
+ writer.writeUE(chroma_format_idc.getId(), "SPS: chroma_format_idc");
+ if (chroma_format_idc == ChromaFormat.YUV_444) {
+ writer.writeBool(residual_color_transform_flag,
+ "SPS: residual_color_transform_flag");
+ }
+ writer.writeUE(bit_depth_luma_minus8, "SPS: ");
+ writer.writeUE(bit_depth_chroma_minus8, "SPS: ");
+ writer.writeBool(qpprime_y_zero_transform_bypass_flag,
+ "SPS: qpprime_y_zero_transform_bypass_flag");
+ writer.writeBool(scalingMatrix != null, "SPS: ");
+ if (scalingMatrix != null) {
+ for (int i = 0; i < 8; i++) {
+ if (i < 6) {
+ writer.writeBool(
+ scalingMatrix.ScalingList4x4[i] != null,
+ "SPS: ");
+ if (scalingMatrix.ScalingList4x4[i] != null) {
+ scalingMatrix.ScalingList4x4[i].write(writer);
+ }
+ } else {
+ writer.writeBool(
+ scalingMatrix.ScalingList8x8[i - 6] != null,
+ "SPS: ");
+ if (scalingMatrix.ScalingList8x8[i - 6] != null) {
+ scalingMatrix.ScalingList8x8[i - 6].write(writer);
+ }
+ }
+ }
+ }
+ }
+ writer.writeUE(log2_max_frame_num_minus4,
+ "SPS: log2_max_frame_num_minus4");
+ writer.writeUE(pic_order_cnt_type, "SPS: pic_order_cnt_type");
+ if (pic_order_cnt_type == 0) {
+ writer.writeUE(log2_max_pic_order_cnt_lsb_minus4,
+ "SPS: log2_max_pic_order_cnt_lsb_minus4");
+ } else if (pic_order_cnt_type == 1) {
+ writer.writeBool(delta_pic_order_always_zero_flag,
+ "SPS: delta_pic_order_always_zero_flag");
+ writer.writeSE(offset_for_non_ref_pic,
+ "SPS: offset_for_non_ref_pic");
+ writer.writeSE(offset_for_top_to_bottom_field,
+ "SPS: offset_for_top_to_bottom_field");
+ writer.writeUE(offsetForRefFrame.length, "SPS: ");
+ for (int i = 0; i < offsetForRefFrame.length; i++)
+ writer.writeSE(offsetForRefFrame[i], "SPS: ");
+ }
+ writer.writeUE(num_ref_frames, "SPS: num_ref_frames");
+ writer.writeBool(gaps_in_frame_num_value_allowed_flag,
+ "SPS: gaps_in_frame_num_value_allowed_flag");
+ writer.writeUE(pic_width_in_mbs_minus1, "SPS: pic_width_in_mbs_minus1");
+ writer.writeUE(pic_height_in_map_units_minus1,
+ "SPS: pic_height_in_map_units_minus1");
+ writer.writeBool(frame_mbs_only_flag, "SPS: frame_mbs_only_flag");
+ if (!frame_mbs_only_flag) {
+ writer.writeBool(mb_adaptive_frame_field_flag,
+ "SPS: mb_adaptive_frame_field_flag");
+ }
+ writer.writeBool(direct_8x8_inference_flag,
+ "SPS: direct_8x8_inference_flag");
+ writer.writeBool(frame_cropping_flag, "SPS: frame_cropping_flag");
+ if (frame_cropping_flag) {
+ writer.writeUE(frame_crop_left_offset,
+ "SPS: frame_crop_left_offset");
+ writer.writeUE(frame_crop_right_offset,
+ "SPS: frame_crop_right_offset");
+ writer.writeUE(frame_crop_top_offset, "SPS: frame_crop_top_offset");
+ writer.writeUE(frame_crop_bottom_offset,
+ "SPS: frame_crop_bottom_offset");
+ }
+ writer.writeBool(vuiParams != null, "SPS: ");
+ if (vuiParams != null)
+ writeVUIParameters(vuiParams, writer);
+
+ writer.writeTrailingBits();
+ }
+
+ private void writeVUIParameters(VUIParameters vuip, CAVLCWriter writer)
+ throws IOException {
+ writer.writeBool(vuip.aspect_ratio_info_present_flag,
+ "VUI: aspect_ratio_info_present_flag");
+ if (vuip.aspect_ratio_info_present_flag) {
+ writer.writeNBit(vuip.aspect_ratio.getValue(), 8,
+ "VUI: aspect_ratio");
+ if (vuip.aspect_ratio == AspectRatio.Extended_SAR) {
+ writer.writeNBit(vuip.sar_width, 16, "VUI: sar_width");
+ writer.writeNBit(vuip.sar_height, 16, "VUI: sar_height");
+ }
+ }
+ writer.writeBool(vuip.overscan_info_present_flag,
+ "VUI: overscan_info_present_flag");
+ if (vuip.overscan_info_present_flag) {
+ writer.writeBool(vuip.overscan_appropriate_flag,
+ "VUI: overscan_appropriate_flag");
+ }
+ writer.writeBool(vuip.video_signal_type_present_flag,
+ "VUI: video_signal_type_present_flag");
+ if (vuip.video_signal_type_present_flag) {
+ writer.writeNBit(vuip.video_format, 3, "VUI: video_format");
+ writer.writeBool(vuip.video_full_range_flag,
+ "VUI: video_full_range_flag");
+ writer.writeBool(vuip.colour_description_present_flag,
+ "VUI: colour_description_present_flag");
+ if (vuip.colour_description_present_flag) {
+ writer.writeNBit(vuip.colour_primaries, 8,
+ "VUI: colour_primaries");
+ writer.writeNBit(vuip.transfer_characteristics, 8,
+ "VUI: transfer_characteristics");
+ writer.writeNBit(vuip.matrix_coefficients, 8,
+ "VUI: matrix_coefficients");
+ }
+ }
+ writer.writeBool(vuip.chroma_loc_info_present_flag,
+ "VUI: chroma_loc_info_present_flag");
+ if (vuip.chroma_loc_info_present_flag) {
+ writer.writeUE(vuip.chroma_sample_loc_type_top_field,
+ "VUI: chroma_sample_loc_type_top_field");
+ writer.writeUE(vuip.chroma_sample_loc_type_bottom_field,
+ "VUI: chroma_sample_loc_type_bottom_field");
+ }
+ writer.writeBool(vuip.timing_info_present_flag,
+ "VUI: timing_info_present_flag");
+ if (vuip.timing_info_present_flag) {
+ writer.writeNBit(vuip.num_units_in_tick, 32,
+ "VUI: num_units_in_tick");
+ writer.writeNBit(vuip.time_scale, 32, "VUI: time_scale");
+ writer.writeBool(vuip.fixed_frame_rate_flag,
+ "VUI: fixed_frame_rate_flag");
+ }
+ writer.writeBool(vuip.nalHRDParams != null, "VUI: ");
+ if (vuip.nalHRDParams != null) {
+ writeHRDParameters(vuip.nalHRDParams, writer);
+ }
+ writer.writeBool(vuip.vclHRDParams != null, "VUI: ");
+ if (vuip.vclHRDParams != null) {
+ writeHRDParameters(vuip.vclHRDParams, writer);
+ }
+
+ if (vuip.nalHRDParams != null || vuip.vclHRDParams != null) {
+ writer
+ .writeBool(vuip.low_delay_hrd_flag,
+ "VUI: low_delay_hrd_flag");
+ }
+ writer.writeBool(vuip.pic_struct_present_flag,
+ "VUI: pic_struct_present_flag");
+ writer.writeBool(vuip.bitstreamRestriction != null, "VUI: ");
+ if (vuip.bitstreamRestriction != null) {
+ writer
+ .writeBool(
+ vuip.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag,
+ "VUI: motion_vectors_over_pic_boundaries_flag");
+ writer.writeUE(vuip.bitstreamRestriction.max_bytes_per_pic_denom,
+ "VUI: max_bytes_per_pic_denom");
+ writer.writeUE(vuip.bitstreamRestriction.max_bits_per_mb_denom,
+ "VUI: max_bits_per_mb_denom");
+ writer.writeUE(
+ vuip.bitstreamRestriction.log2_max_mv_length_horizontal,
+ "VUI: log2_max_mv_length_horizontal");
+ writer.writeUE(
+ vuip.bitstreamRestriction.log2_max_mv_length_vertical,
+ "VUI: log2_max_mv_length_vertical");
+ writer.writeUE(vuip.bitstreamRestriction.num_reorder_frames,
+ "VUI: num_reorder_frames");
+ writer.writeUE(vuip.bitstreamRestriction.max_dec_frame_buffering,
+ "VUI: max_dec_frame_buffering");
+ }
+
+ }
+
+ private void writeHRDParameters(HRDParameters hrd, CAVLCWriter writer)
+ throws IOException {
+ writer.writeUE(hrd.cpb_cnt_minus1, "HRD: cpb_cnt_minus1");
+ writer.writeNBit(hrd.bit_rate_scale, 4, "HRD: bit_rate_scale");
+ writer.writeNBit(hrd.cpb_size_scale, 4, "HRD: cpb_size_scale");
+
+ for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpb_cnt_minus1; SchedSelIdx++) {
+ writer.writeUE(hrd.bit_rate_value_minus1[SchedSelIdx], "HRD: ");
+ writer.writeUE(hrd.cpb_size_value_minus1[SchedSelIdx], "HRD: ");
+ writer.writeBool(hrd.cbr_flag[SchedSelIdx], "HRD: ");
+ }
+ writer.writeNBit(hrd.initial_cpb_removal_delay_length_minus1, 5,
+ "HRD: initial_cpb_removal_delay_length_minus1");
+ writer.writeNBit(hrd.cpb_removal_delay_length_minus1, 5,
+ "HRD: cpb_removal_delay_length_minus1");
+ writer.writeNBit(hrd.dpb_output_delay_length_minus1, 5,
+ "HRD: dpb_output_delay_length_minus1");
+ writer.writeNBit(hrd.time_offset_length, 5, "HRD: time_offset_length");
+ }
+
+ @Override
+ public String toString() {
+ return "SeqParameterSet{ " +
+ "\n pic_order_cnt_type=" + pic_order_cnt_type +
+ ", \n field_pic_flag=" + field_pic_flag +
+ ", \n delta_pic_order_always_zero_flag=" + delta_pic_order_always_zero_flag +
+ ", \n weighted_pred_flag=" + weighted_pred_flag +
+ ", \n weighted_bipred_idc=" + weighted_bipred_idc +
+ ", \n entropy_coding_mode_flag=" + entropy_coding_mode_flag +
+ ", \n mb_adaptive_frame_field_flag=" + mb_adaptive_frame_field_flag +
+ ", \n direct_8x8_inference_flag=" + direct_8x8_inference_flag +
+ ", \n chroma_format_idc=" + chroma_format_idc +
+ ", \n log2_max_frame_num_minus4=" + log2_max_frame_num_minus4 +
+ ", \n log2_max_pic_order_cnt_lsb_minus4=" + log2_max_pic_order_cnt_lsb_minus4 +
+ ", \n pic_height_in_map_units_minus1=" + pic_height_in_map_units_minus1 +
+ ", \n pic_width_in_mbs_minus1=" + pic_width_in_mbs_minus1 +
+ ", \n bit_depth_luma_minus8=" + bit_depth_luma_minus8 +
+ ", \n bit_depth_chroma_minus8=" + bit_depth_chroma_minus8 +
+ ", \n qpprime_y_zero_transform_bypass_flag=" + qpprime_y_zero_transform_bypass_flag +
+ ", \n profile_idc=" + profile_idc +
+ ", \n constraint_set_0_flag=" + constraint_set_0_flag +
+ ", \n constraint_set_1_flag=" + constraint_set_1_flag +
+ ", \n constraint_set_2_flag=" + constraint_set_2_flag +
+ ", \n constraint_set_3_flag=" + constraint_set_3_flag +
+ ", \n level_idc=" + level_idc +
+ ", \n seq_parameter_set_id=" + seq_parameter_set_id +
+ ", \n residual_color_transform_flag=" + residual_color_transform_flag +
+ ", \n offset_for_non_ref_pic=" + offset_for_non_ref_pic +
+ ", \n offset_for_top_to_bottom_field=" + offset_for_top_to_bottom_field +
+ ", \n num_ref_frames=" + num_ref_frames +
+ ", \n gaps_in_frame_num_value_allowed_flag=" + gaps_in_frame_num_value_allowed_flag +
+ ", \n frame_mbs_only_flag=" + frame_mbs_only_flag +
+ ", \n frame_cropping_flag=" + frame_cropping_flag +
+ ", \n frame_crop_left_offset=" + frame_crop_left_offset +
+ ", \n frame_crop_right_offset=" + frame_crop_right_offset +
+ ", \n frame_crop_top_offset=" + frame_crop_top_offset +
+ ", \n frame_crop_bottom_offset=" + frame_crop_bottom_offset +
+ ", \n offsetForRefFrame=" + offsetForRefFrame +
+ ", \n vuiParams=" + vuiParams +
+ ", \n scalingMatrix=" + scalingMatrix +
+ ", \n num_ref_frames_in_pic_order_cnt_cycle=" + num_ref_frames_in_pic_order_cnt_cycle +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/VUIParameters.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/VUIParameters.java.svn-base
new file mode 100644
index 0000000..eec7880
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/.svn/text-base/VUIParameters.java.svn-base
@@ -0,0 +1,94 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+public class VUIParameters {
+
+ public static class BitstreamRestriction {
+
+ public boolean motion_vectors_over_pic_boundaries_flag;
+ public int max_bytes_per_pic_denom;
+ public int max_bits_per_mb_denom;
+ public int log2_max_mv_length_horizontal;
+ public int log2_max_mv_length_vertical;
+ public int num_reorder_frames;
+ public int max_dec_frame_buffering;
+
+ }
+
+ public boolean aspect_ratio_info_present_flag;
+ public int sar_width;
+ public int sar_height;
+ public boolean overscan_info_present_flag;
+ public boolean overscan_appropriate_flag;
+ public boolean video_signal_type_present_flag;
+ public int video_format;
+ public boolean video_full_range_flag;
+ public boolean colour_description_present_flag;
+ public int colour_primaries;
+ public int transfer_characteristics;
+ public int matrix_coefficients;
+ public boolean chroma_loc_info_present_flag;
+ public int chroma_sample_loc_type_top_field;
+ public int chroma_sample_loc_type_bottom_field;
+ public boolean timing_info_present_flag;
+ public int num_units_in_tick;
+ public int time_scale;
+ public boolean fixed_frame_rate_flag;
+ public boolean low_delay_hrd_flag;
+ public boolean pic_struct_present_flag;
+ public HRDParameters nalHRDParams;
+ public HRDParameters vclHRDParams;
+
+ public BitstreamRestriction bitstreamRestriction;
+ public AspectRatio aspect_ratio;
+
+ @Override
+ public String toString() {
+ return "VUIParameters{" + "\n" +
+ "aspect_ratio_info_present_flag=" + aspect_ratio_info_present_flag + "\n" +
+ ", sar_width=" + sar_width + "\n" +
+ ", sar_height=" + sar_height + "\n" +
+ ", overscan_info_present_flag=" + overscan_info_present_flag + "\n" +
+ ", overscan_appropriate_flag=" + overscan_appropriate_flag + "\n" +
+ ", video_signal_type_present_flag=" + video_signal_type_present_flag + "\n" +
+ ", video_format=" + video_format + "\n" +
+ ", video_full_range_flag=" + video_full_range_flag + "\n" +
+ ", colour_description_present_flag=" + colour_description_present_flag + "\n" +
+ ", colour_primaries=" + colour_primaries + "\n" +
+ ", transfer_characteristics=" + transfer_characteristics + "\n" +
+ ", matrix_coefficients=" + matrix_coefficients + "\n" +
+ ", chroma_loc_info_present_flag=" + chroma_loc_info_present_flag + "\n" +
+ ", chroma_sample_loc_type_top_field=" + chroma_sample_loc_type_top_field + "\n" +
+ ", chroma_sample_loc_type_bottom_field=" + chroma_sample_loc_type_bottom_field + "\n" +
+ ", timing_info_present_flag=" + timing_info_present_flag + "\n" +
+ ", num_units_in_tick=" + num_units_in_tick + "\n" +
+ ", time_scale=" + time_scale + "\n" +
+ ", fixed_frame_rate_flag=" + fixed_frame_rate_flag + "\n" +
+ ", low_delay_hrd_flag=" + low_delay_hrd_flag + "\n" +
+ ", pic_struct_present_flag=" + pic_struct_present_flag + "\n" +
+ ", nalHRDParams=" + nalHRDParams + "\n" +
+ ", vclHRDParams=" + vclHRDParams + "\n" +
+ ", bitstreamRestriction=" + bitstreamRestriction + "\n" +
+ ", aspect_ratio=" + aspect_ratio + "\n" +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/AspectRatio.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/AspectRatio.java
new file mode 100644
index 0000000..bc66b1a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/AspectRatio.java
@@ -0,0 +1,50 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+/**
+ * Aspect ratio
+ * <p/>
+ * dynamic enum
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class AspectRatio {
+
+ public static final AspectRatio Extended_SAR = new AspectRatio(255);
+
+ private int value;
+
+ private AspectRatio(int value) {
+ this.value = value;
+ }
+
+ public static AspectRatio fromValue(int value) {
+ if (value == Extended_SAR.value) {
+ return Extended_SAR;
+ }
+ return new AspectRatio(value);
+ }
+
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/BitstreamElement.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/BitstreamElement.java
new file mode 100644
index 0000000..f16c5e9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/BitstreamElement.java
@@ -0,0 +1,29 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public abstract class BitstreamElement {
+
+ public abstract void write(OutputStream out) throws IOException;
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ChromaFormat.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ChromaFormat.java
new file mode 100644
index 0000000..2af2966
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ChromaFormat.java
@@ -0,0 +1,77 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+/**
+ * Chroma format enum
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class ChromaFormat {
+ public static ChromaFormat MONOCHROME = new ChromaFormat(0, 0, 0);
+ public static ChromaFormat YUV_420 = new ChromaFormat(1, 2, 2);
+ public static ChromaFormat YUV_422 = new ChromaFormat(2, 2, 1);
+ public static ChromaFormat YUV_444 = new ChromaFormat(3, 1, 1);
+
+ private int id;
+ private int subWidth;
+ private int subHeight;
+
+ public ChromaFormat(int id, int subWidth, int subHeight) {
+ this.id = id;
+ this.subWidth = subWidth;
+ this.subHeight = subHeight;
+ }
+
+ public static ChromaFormat fromId(int id) {
+ if (id == MONOCHROME.id) {
+ return MONOCHROME;
+ } else if (id == YUV_420.id) {
+ return YUV_420;
+ } else if (id == YUV_422.id) {
+ return YUV_422;
+ } else if (id == YUV_444.id) {
+ return YUV_444;
+ }
+ return null;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public int getSubWidth() {
+ return subWidth;
+ }
+
+ public int getSubHeight() {
+ return subHeight;
+ }
+
+ @Override
+ public String toString() {
+ return "ChromaFormat{" + "\n" +
+ "id=" + id + ",\n" +
+ " subWidth=" + subWidth + ",\n" +
+ " subHeight=" + subHeight +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/HRDParameters.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/HRDParameters.java
new file mode 100644
index 0000000..f713ab2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/HRDParameters.java
@@ -0,0 +1,53 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import java.util.Arrays;
+
+public class HRDParameters {
+
+ public int cpb_cnt_minus1;
+ public int bit_rate_scale;
+ public int cpb_size_scale;
+ public int[] bit_rate_value_minus1;
+ public int[] cpb_size_value_minus1;
+ public boolean[] cbr_flag;
+ public int initial_cpb_removal_delay_length_minus1;
+ public int cpb_removal_delay_length_minus1;
+ public int dpb_output_delay_length_minus1;
+ public int time_offset_length;
+
+ @Override
+ public String toString() {
+ return "HRDParameters{" +
+ "cpb_cnt_minus1=" + cpb_cnt_minus1 +
+ ", bit_rate_scale=" + bit_rate_scale +
+ ", cpb_size_scale=" + cpb_size_scale +
+ ", bit_rate_value_minus1=" + Arrays.toString(bit_rate_value_minus1) +
+ ", cpb_size_value_minus1=" + Arrays.toString(cpb_size_value_minus1) +
+ ", cbr_flag=" + Arrays.toString(cbr_flag) +
+ ", initial_cpb_removal_delay_length_minus1=" + initial_cpb_removal_delay_length_minus1 +
+ ", cpb_removal_delay_length_minus1=" + cpb_removal_delay_length_minus1 +
+ ", dpb_output_delay_length_minus1=" + dpb_output_delay_length_minus1 +
+ ", time_offset_length=" + time_offset_length +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/PictureParameterSet.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/PictureParameterSet.java
new file mode 100644
index 0000000..9154c38
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/PictureParameterSet.java
@@ -0,0 +1,406 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+import com.googlecode.mp4parser.h264.write.CAVLCWriter;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * Picture Parameter Set entity of H264 bitstream
+ * <p/>
+ * capable to serialize / deserialize with CAVLC bitstream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class PictureParameterSet extends BitstreamElement {
+
+ public static class PPSExt {
+ public boolean transform_8x8_mode_flag;
+ public ScalingMatrix scalindMatrix = new ScalingMatrix();
+ public int second_chroma_qp_index_offset;
+ public boolean[] pic_scaling_list_present_flag;
+
+ @Override
+ public String toString() {
+ return "PPSExt{" +
+ "transform_8x8_mode_flag=" + transform_8x8_mode_flag +
+ ", scalindMatrix=" + scalindMatrix +
+ ", second_chroma_qp_index_offset=" + second_chroma_qp_index_offset +
+ ", pic_scaling_list_present_flag=" + pic_scaling_list_present_flag +
+ '}';
+ }
+ }
+
+ public boolean entropy_coding_mode_flag;
+ public int num_ref_idx_l0_active_minus1;
+ public int num_ref_idx_l1_active_minus1;
+ public int slice_group_change_rate_minus1;
+ public int pic_parameter_set_id;
+ public int seq_parameter_set_id;
+ public boolean pic_order_present_flag;
+ public int num_slice_groups_minus1;
+ public int slice_group_map_type;
+ public boolean weighted_pred_flag;
+ public int weighted_bipred_idc;
+ public int pic_init_qp_minus26;
+ public int pic_init_qs_minus26;
+ public int chroma_qp_index_offset;
+ public boolean deblocking_filter_control_present_flag;
+ public boolean constrained_intra_pred_flag;
+ public boolean redundant_pic_cnt_present_flag;
+ public int[] top_left;
+ public int[] bottom_right;
+ public int[] run_length_minus1;
+ public boolean slice_group_change_direction_flag;
+ public int[] slice_group_id;
+ public PPSExt extended;
+
+ public static PictureParameterSet read(byte[] b) throws IOException {
+ return read(new ByteArrayInputStream(b));
+ }
+
+ public static PictureParameterSet read(InputStream is) throws IOException {
+ CAVLCReader reader = new CAVLCReader(is);
+ PictureParameterSet pps = new PictureParameterSet();
+
+ pps.pic_parameter_set_id = reader.readUE("PPS: pic_parameter_set_id");
+ pps.seq_parameter_set_id = reader.readUE("PPS: seq_parameter_set_id");
+ pps.entropy_coding_mode_flag = reader
+ .readBool("PPS: entropy_coding_mode_flag");
+ pps.pic_order_present_flag = reader
+ .readBool("PPS: pic_order_present_flag");
+ pps.num_slice_groups_minus1 = reader
+ .readUE("PPS: num_slice_groups_minus1");
+ if (pps.num_slice_groups_minus1 > 0) {
+ pps.slice_group_map_type = reader
+ .readUE("PPS: slice_group_map_type");
+ pps.top_left = new int[pps.num_slice_groups_minus1 + 1];
+ pps.bottom_right = new int[pps.num_slice_groups_minus1 + 1];
+ pps.run_length_minus1 = new int[pps.num_slice_groups_minus1 + 1];
+ if (pps.slice_group_map_type == 0)
+ for (int iGroup = 0; iGroup <= pps.num_slice_groups_minus1; iGroup++)
+ pps.run_length_minus1[iGroup] = reader
+ .readUE("PPS: run_length_minus1");
+ else if (pps.slice_group_map_type == 2)
+ for (int iGroup = 0; iGroup < pps.num_slice_groups_minus1; iGroup++) {
+ pps.top_left[iGroup] = reader.readUE("PPS: top_left");
+ pps.bottom_right[iGroup] = reader
+ .readUE("PPS: bottom_right");
+ }
+ else if (pps.slice_group_map_type == 3
+ || pps.slice_group_map_type == 4
+ || pps.slice_group_map_type == 5) {
+ pps.slice_group_change_direction_flag = reader
+ .readBool("PPS: slice_group_change_direction_flag");
+ pps.slice_group_change_rate_minus1 = reader
+ .readUE("PPS: slice_group_change_rate_minus1");
+ } else if (pps.slice_group_map_type == 6) {
+ int NumberBitsPerSliceGroupId;
+ if (pps.num_slice_groups_minus1 + 1 > 4)
+ NumberBitsPerSliceGroupId = 3;
+ else if (pps.num_slice_groups_minus1 + 1 > 2)
+ NumberBitsPerSliceGroupId = 2;
+ else
+ NumberBitsPerSliceGroupId = 1;
+ int pic_size_in_map_units_minus1 = reader
+ .readUE("PPS: pic_size_in_map_units_minus1");
+ pps.slice_group_id = new int[pic_size_in_map_units_minus1 + 1];
+ for (int i = 0; i <= pic_size_in_map_units_minus1; i++) {
+ pps.slice_group_id[i] = reader.readU(
+ NumberBitsPerSliceGroupId, "PPS: slice_group_id ["
+ + i + "]f");
+ }
+ }
+ }
+ pps.num_ref_idx_l0_active_minus1 = reader
+ .readUE("PPS: num_ref_idx_l0_active_minus1");
+ pps.num_ref_idx_l1_active_minus1 = reader
+ .readUE("PPS: num_ref_idx_l1_active_minus1");
+ pps.weighted_pred_flag = reader.readBool("PPS: weighted_pred_flag");
+ pps.weighted_bipred_idc = (int) reader.readNBit(2,
+ "PPS: weighted_bipred_idc");
+ pps.pic_init_qp_minus26 = reader.readSE("PPS: pic_init_qp_minus26");
+ pps.pic_init_qs_minus26 = reader.readSE("PPS: pic_init_qs_minus26");
+ pps.chroma_qp_index_offset = reader
+ .readSE("PPS: chroma_qp_index_offset");
+ pps.deblocking_filter_control_present_flag = reader
+ .readBool("PPS: deblocking_filter_control_present_flag");
+ pps.constrained_intra_pred_flag = reader
+ .readBool("PPS: constrained_intra_pred_flag");
+ pps.redundant_pic_cnt_present_flag = reader
+ .readBool("PPS: redundant_pic_cnt_present_flag");
+ if (reader.moreRBSPData()) {
+ pps.extended = new PictureParameterSet.PPSExt();
+ pps.extended.transform_8x8_mode_flag = reader
+ .readBool("PPS: transform_8x8_mode_flag");
+ boolean pic_scaling_matrix_present_flag = reader
+ .readBool("PPS: pic_scaling_matrix_present_flag");
+ if (pic_scaling_matrix_present_flag) {
+ for (int i = 0; i < 6 + 2 * (pps.extended.transform_8x8_mode_flag ? 1
+ : 0); i++) {
+ boolean pic_scaling_list_present_flag = reader
+ .readBool("PPS: pic_scaling_list_present_flag");
+ if (pic_scaling_list_present_flag) {
+ pps.extended.scalindMatrix.ScalingList4x4 = new ScalingList[8];
+ pps.extended.scalindMatrix.ScalingList8x8 = new ScalingList[8];
+ if (i < 6) {
+ pps.extended.scalindMatrix.ScalingList4x4[i] = ScalingList
+ .read(reader, 16);
+ } else {
+ pps.extended.scalindMatrix.ScalingList8x8[i - 6] = ScalingList
+ .read(reader, 64);
+ }
+ }
+ }
+ }
+ pps.extended.second_chroma_qp_index_offset = reader
+ .readSE("PPS: second_chroma_qp_index_offset");
+ }
+
+ reader.readTrailingBits();
+
+ return pps;
+ }
+
+ public void write(OutputStream out) throws IOException {
+ CAVLCWriter writer = new CAVLCWriter(out);
+
+ writer.writeUE(pic_parameter_set_id, "PPS: pic_parameter_set_id");
+ writer.writeUE(seq_parameter_set_id, "PPS: seq_parameter_set_id");
+ writer.writeBool(entropy_coding_mode_flag,
+ "PPS: entropy_coding_mode_flag");
+ writer.writeBool(pic_order_present_flag, "PPS: pic_order_present_flag");
+ writer.writeUE(num_slice_groups_minus1, "PPS: num_slice_groups_minus1");
+ if (num_slice_groups_minus1 > 0) {
+ writer.writeUE(slice_group_map_type, "PPS: slice_group_map_type");
+ int[] top_left = new int[1];
+ int[] bottom_right = new int[1];
+ int[] run_length_minus1 = new int[1];
+ if (slice_group_map_type == 0) {
+ for (int iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++) {
+ writer.writeUE(run_length_minus1[iGroup], "PPS: ");
+ }
+ } else if (slice_group_map_type == 2) {
+ for (int iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++) {
+ writer.writeUE(top_left[iGroup], "PPS: ");
+ writer.writeUE(bottom_right[iGroup], "PPS: ");
+ }
+ } else if (slice_group_map_type == 3 || slice_group_map_type == 4
+ || slice_group_map_type == 5) {
+ writer.writeBool(slice_group_change_direction_flag,
+ "PPS: slice_group_change_direction_flag");
+ writer.writeUE(slice_group_change_rate_minus1,
+ "PPS: slice_group_change_rate_minus1");
+ } else if (slice_group_map_type == 6) {
+ int NumberBitsPerSliceGroupId;
+ if (num_slice_groups_minus1 + 1 > 4)
+ NumberBitsPerSliceGroupId = 3;
+ else if (num_slice_groups_minus1 + 1 > 2)
+ NumberBitsPerSliceGroupId = 2;
+ else
+ NumberBitsPerSliceGroupId = 1;
+ writer.writeUE(slice_group_id.length, "PPS: ");
+ for (int i = 0; i <= slice_group_id.length; i++) {
+ writer.writeU(slice_group_id[i], NumberBitsPerSliceGroupId);
+ }
+ }
+ }
+ writer.writeUE(num_ref_idx_l0_active_minus1,
+ "PPS: num_ref_idx_l0_active_minus1");
+ writer.writeUE(num_ref_idx_l1_active_minus1,
+ "PPS: num_ref_idx_l1_active_minus1");
+ writer.writeBool(weighted_pred_flag, "PPS: weighted_pred_flag");
+ writer.writeNBit(weighted_bipred_idc, 2, "PPS: weighted_bipred_idc");
+ writer.writeSE(pic_init_qp_minus26, "PPS: pic_init_qp_minus26");
+ writer.writeSE(pic_init_qs_minus26, "PPS: pic_init_qs_minus26");
+ writer.writeSE(chroma_qp_index_offset, "PPS: chroma_qp_index_offset");
+ writer.writeBool(deblocking_filter_control_present_flag,
+ "PPS: deblocking_filter_control_present_flag");
+ writer.writeBool(constrained_intra_pred_flag,
+ "PPS: constrained_intra_pred_flag");
+ writer.writeBool(redundant_pic_cnt_present_flag,
+ "PPS: redundant_pic_cnt_present_flag");
+ if (extended != null) {
+ writer.writeBool(extended.transform_8x8_mode_flag,
+ "PPS: transform_8x8_mode_flag");
+ writer.writeBool(extended.scalindMatrix != null,
+ "PPS: scalindMatrix");
+ if (extended.scalindMatrix != null) {
+ for (int i = 0; i < 6 + 2 * (extended.transform_8x8_mode_flag ? 1
+ : 0); i++) {
+ if (i < 6) {
+ writer
+ .writeBool(
+ extended.scalindMatrix.ScalingList4x4[i] != null,
+ "PPS: ");
+ if (extended.scalindMatrix.ScalingList4x4[i] != null) {
+ extended.scalindMatrix.ScalingList4x4[i]
+ .write(writer);
+ }
+
+ } else {
+ writer
+ .writeBool(
+ extended.scalindMatrix.ScalingList8x8[i - 6] != null,
+ "PPS: ");
+ if (extended.scalindMatrix.ScalingList8x8[i - 6] != null) {
+ extended.scalindMatrix.ScalingList8x8[i - 6]
+ .write(writer);
+ }
+ }
+ }
+ }
+ writer.writeSE(extended.second_chroma_qp_index_offset, "PPS: ");
+ }
+
+ writer.writeTrailingBits();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(bottom_right);
+ result = prime * result + chroma_qp_index_offset;
+ result = prime * result + (constrained_intra_pred_flag ? 1231 : 1237);
+ result = prime * result
+ + (deblocking_filter_control_present_flag ? 1231 : 1237);
+ result = prime * result + (entropy_coding_mode_flag ? 1231 : 1237);
+ result = prime * result
+ + ((extended == null) ? 0 : extended.hashCode());
+ result = prime * result + num_ref_idx_l0_active_minus1;
+ result = prime * result + num_ref_idx_l1_active_minus1;
+ result = prime * result + num_slice_groups_minus1;
+ result = prime * result + pic_init_qp_minus26;
+ result = prime * result + pic_init_qs_minus26;
+ result = prime * result + (pic_order_present_flag ? 1231 : 1237);
+ result = prime * result + pic_parameter_set_id;
+ result = prime * result
+ + (redundant_pic_cnt_present_flag ? 1231 : 1237);
+ result = prime * result + Arrays.hashCode(run_length_minus1);
+ result = prime * result + seq_parameter_set_id;
+ result = prime * result
+ + (slice_group_change_direction_flag ? 1231 : 1237);
+ result = prime * result + slice_group_change_rate_minus1;
+ result = prime * result + Arrays.hashCode(slice_group_id);
+ result = prime * result + slice_group_map_type;
+ result = prime * result + Arrays.hashCode(top_left);
+ result = prime * result + weighted_bipred_idc;
+ result = prime * result + (weighted_pred_flag ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ PictureParameterSet other = (PictureParameterSet) obj;
+ if (!Arrays.equals(bottom_right, other.bottom_right))
+ return false;
+ if (chroma_qp_index_offset != other.chroma_qp_index_offset)
+ return false;
+ if (constrained_intra_pred_flag != other.constrained_intra_pred_flag)
+ return false;
+ if (deblocking_filter_control_present_flag != other.deblocking_filter_control_present_flag)
+ return false;
+ if (entropy_coding_mode_flag != other.entropy_coding_mode_flag)
+ return false;
+ if (extended == null) {
+ if (other.extended != null)
+ return false;
+ } else if (!extended.equals(other.extended))
+ return false;
+ if (num_ref_idx_l0_active_minus1 != other.num_ref_idx_l0_active_minus1)
+ return false;
+ if (num_ref_idx_l1_active_minus1 != other.num_ref_idx_l1_active_minus1)
+ return false;
+ if (num_slice_groups_minus1 != other.num_slice_groups_minus1)
+ return false;
+ if (pic_init_qp_minus26 != other.pic_init_qp_minus26)
+ return false;
+ if (pic_init_qs_minus26 != other.pic_init_qs_minus26)
+ return false;
+ if (pic_order_present_flag != other.pic_order_present_flag)
+ return false;
+ if (pic_parameter_set_id != other.pic_parameter_set_id)
+ return false;
+ if (redundant_pic_cnt_present_flag != other.redundant_pic_cnt_present_flag)
+ return false;
+ if (!Arrays.equals(run_length_minus1, other.run_length_minus1))
+ return false;
+ if (seq_parameter_set_id != other.seq_parameter_set_id)
+ return false;
+ if (slice_group_change_direction_flag != other.slice_group_change_direction_flag)
+ return false;
+ if (slice_group_change_rate_minus1 != other.slice_group_change_rate_minus1)
+ return false;
+ if (!Arrays.equals(slice_group_id, other.slice_group_id))
+ return false;
+ if (slice_group_map_type != other.slice_group_map_type)
+ return false;
+ if (!Arrays.equals(top_left, other.top_left))
+ return false;
+ if (weighted_bipred_idc != other.weighted_bipred_idc)
+ return false;
+ if (weighted_pred_flag != other.weighted_pred_flag)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "PictureParameterSet{" +
+ "\n entropy_coding_mode_flag=" + entropy_coding_mode_flag +
+ ",\n num_ref_idx_l0_active_minus1=" + num_ref_idx_l0_active_minus1 +
+ ",\n num_ref_idx_l1_active_minus1=" + num_ref_idx_l1_active_minus1 +
+ ",\n slice_group_change_rate_minus1=" + slice_group_change_rate_minus1 +
+ ",\n pic_parameter_set_id=" + pic_parameter_set_id +
+ ",\n seq_parameter_set_id=" + seq_parameter_set_id +
+ ",\n pic_order_present_flag=" + pic_order_present_flag +
+ ",\n num_slice_groups_minus1=" + num_slice_groups_minus1 +
+ ",\n slice_group_map_type=" + slice_group_map_type +
+ ",\n weighted_pred_flag=" + weighted_pred_flag +
+ ",\n weighted_bipred_idc=" + weighted_bipred_idc +
+ ",\n pic_init_qp_minus26=" + pic_init_qp_minus26 +
+ ",\n pic_init_qs_minus26=" + pic_init_qs_minus26 +
+ ",\n chroma_qp_index_offset=" + chroma_qp_index_offset +
+ ",\n deblocking_filter_control_present_flag=" + deblocking_filter_control_present_flag +
+ ",\n constrained_intra_pred_flag=" + constrained_intra_pred_flag +
+ ",\n redundant_pic_cnt_present_flag=" + redundant_pic_cnt_present_flag +
+ ",\n top_left=" + top_left +
+ ",\n bottom_right=" + bottom_right +
+ ",\n run_length_minus1=" + run_length_minus1 +
+ ",\n slice_group_change_direction_flag=" + slice_group_change_direction_flag +
+ ",\n slice_group_id=" + slice_group_id +
+ ",\n extended=" + extended +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingList.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingList.java
new file mode 100644
index 0000000..5d272bf
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingList.java
@@ -0,0 +1,83 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+import com.googlecode.mp4parser.h264.write.CAVLCWriter;
+
+import java.io.IOException;
+
+/**
+ * Scaling list entity
+ * <p/>
+ * capable to serialize / deserialize with CAVLC bitstream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class ScalingList {
+
+ public int[] scalingList;
+ public boolean useDefaultScalingMatrixFlag;
+
+ public void write(CAVLCWriter out) throws IOException {
+ if (useDefaultScalingMatrixFlag) {
+ out.writeSE(0, "SPS: ");
+ return;
+ }
+
+ int lastScale = 8;
+ int nextScale = 8;
+ for (int j = 0; j < scalingList.length; j++) {
+ if (nextScale != 0) {
+ int deltaScale = scalingList[j] - lastScale - 256;
+ out.writeSE(deltaScale, "SPS: ");
+ }
+ lastScale = scalingList[j];
+ }
+ }
+
+ public static ScalingList read(CAVLCReader is, int sizeOfScalingList)
+ throws IOException {
+
+ ScalingList sl = new ScalingList();
+ sl.scalingList = new int[sizeOfScalingList];
+ int lastScale = 8;
+ int nextScale = 8;
+ for (int j = 0; j < sizeOfScalingList; j++) {
+ if (nextScale != 0) {
+ int deltaScale = is.readSE("deltaScale");
+ nextScale = (lastScale + deltaScale + 256) % 256;
+ sl.useDefaultScalingMatrixFlag = (j == 0 && nextScale == 0);
+ }
+ sl.scalingList[j] = nextScale == 0 ? lastScale : nextScale;
+ lastScale = sl.scalingList[j];
+ }
+ return sl;
+ }
+
+ @Override
+ public String toString() {
+ return "ScalingList{" +
+ "scalingList=" + scalingList +
+ ", useDefaultScalingMatrixFlag=" + useDefaultScalingMatrixFlag +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingMatrix.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingMatrix.java
new file mode 100644
index 0000000..d04af8e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/ScalingMatrix.java
@@ -0,0 +1,37 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import java.util.Arrays;
+
+public class ScalingMatrix {
+
+ public ScalingList[] ScalingList4x4;
+ public ScalingList[] ScalingList8x8;
+
+ @Override
+ public String toString() {
+ return "ScalingMatrix{" +
+ "ScalingList4x4=" + (ScalingList4x4 == null ? null : Arrays.asList(ScalingList4x4)) + "\n" +
+ ", ScalingList8x8=" + (ScalingList8x8 == null ? null : Arrays.asList(ScalingList8x8)) + "\n" +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/SeqParameterSet.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/SeqParameterSet.java
new file mode 100644
index 0000000..4894df8
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/SeqParameterSet.java
@@ -0,0 +1,556 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+import com.googlecode.mp4parser.h264.write.CAVLCWriter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Sequence Parameter Set structure of h264 bitstream
+ * <p/>
+ * capable to serialize and deserialize with CAVLC bitstream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class SeqParameterSet extends BitstreamElement {
+ public int pic_order_cnt_type;
+ public boolean field_pic_flag;
+ public boolean delta_pic_order_always_zero_flag;
+ public boolean weighted_pred_flag;
+ public int weighted_bipred_idc;
+ public boolean entropy_coding_mode_flag;
+ public boolean mb_adaptive_frame_field_flag;
+ public boolean direct_8x8_inference_flag;
+ public ChromaFormat chroma_format_idc;
+ public int log2_max_frame_num_minus4;
+ public int log2_max_pic_order_cnt_lsb_minus4;
+ public int pic_height_in_map_units_minus1;
+ public int pic_width_in_mbs_minus1;
+ public int bit_depth_luma_minus8;
+ public int bit_depth_chroma_minus8;
+ public boolean qpprime_y_zero_transform_bypass_flag;
+ public int profile_idc;
+ public boolean constraint_set_0_flag;
+ public boolean constraint_set_1_flag;
+ public boolean constraint_set_2_flag;
+ public boolean constraint_set_3_flag;
+ public int level_idc;
+ public int seq_parameter_set_id;
+ public boolean residual_color_transform_flag;
+ public int offset_for_non_ref_pic;
+ public int offset_for_top_to_bottom_field;
+ public int num_ref_frames;
+ public boolean gaps_in_frame_num_value_allowed_flag;
+ public boolean frame_mbs_only_flag;
+ public boolean frame_cropping_flag;
+ public int frame_crop_left_offset;
+ public int frame_crop_right_offset;
+ public int frame_crop_top_offset;
+ public int frame_crop_bottom_offset;
+ public int[] offsetForRefFrame;
+ public VUIParameters vuiParams;
+ public ScalingMatrix scalingMatrix;
+ public int num_ref_frames_in_pic_order_cnt_cycle;
+
+ public static SeqParameterSet read(InputStream is) throws IOException {
+ CAVLCReader reader = new CAVLCReader(is);
+ SeqParameterSet sps = new SeqParameterSet();
+
+ sps.profile_idc = (int) reader.readNBit(8, "SPS: profile_idc");
+ sps.constraint_set_0_flag = reader
+ .readBool("SPS: constraint_set_0_flag");
+ sps.constraint_set_1_flag = reader
+ .readBool("SPS: constraint_set_1_flag");
+ sps.constraint_set_2_flag = reader
+ .readBool("SPS: constraint_set_2_flag");
+ sps.constraint_set_3_flag = reader
+ .readBool("SPS: constraint_set_3_flag");
+ reader.readNBit(4, "SPS: reserved_zero_4bits");
+ sps.level_idc = (int) reader.readNBit(8, "SPS: level_idc");
+ sps.seq_parameter_set_id = reader.readUE("SPS: seq_parameter_set_id");
+
+ if (sps.profile_idc == 100 || sps.profile_idc == 110
+ || sps.profile_idc == 122 || sps.profile_idc == 144) {
+ sps.chroma_format_idc = ChromaFormat.fromId(reader
+ .readUE("SPS: chroma_format_idc"));
+ if (sps.chroma_format_idc == ChromaFormat.YUV_444) {
+ sps.residual_color_transform_flag = reader
+ .readBool("SPS: residual_color_transform_flag");
+ }
+ sps.bit_depth_luma_minus8 = reader
+ .readUE("SPS: bit_depth_luma_minus8");
+ sps.bit_depth_chroma_minus8 = reader
+ .readUE("SPS: bit_depth_chroma_minus8");
+ sps.qpprime_y_zero_transform_bypass_flag = reader
+ .readBool("SPS: qpprime_y_zero_transform_bypass_flag");
+ boolean seqScalingMatrixPresent = reader
+ .readBool("SPS: seq_scaling_matrix_present_lag");
+ if (seqScalingMatrixPresent) {
+ readScalingListMatrix(reader, sps);
+ }
+ } else {
+ sps.chroma_format_idc = ChromaFormat.YUV_420;
+ }
+ sps.log2_max_frame_num_minus4 = reader
+ .readUE("SPS: log2_max_frame_num_minus4");
+ sps.pic_order_cnt_type = reader.readUE("SPS: pic_order_cnt_type");
+ if (sps.pic_order_cnt_type == 0) {
+ sps.log2_max_pic_order_cnt_lsb_minus4 = reader
+ .readUE("SPS: log2_max_pic_order_cnt_lsb_minus4");
+ } else if (sps.pic_order_cnt_type == 1) {
+ sps.delta_pic_order_always_zero_flag = reader
+ .readBool("SPS: delta_pic_order_always_zero_flag");
+ sps.offset_for_non_ref_pic = reader
+ .readSE("SPS: offset_for_non_ref_pic");
+ sps.offset_for_top_to_bottom_field = reader
+ .readSE("SPS: offset_for_top_to_bottom_field");
+ sps.num_ref_frames_in_pic_order_cnt_cycle = reader
+ .readUE("SPS: num_ref_frames_in_pic_order_cnt_cycle");
+ sps.offsetForRefFrame = new int[sps.num_ref_frames_in_pic_order_cnt_cycle];
+ for (int i = 0; i < sps.num_ref_frames_in_pic_order_cnt_cycle; i++) {
+ sps.offsetForRefFrame[i] = reader
+ .readSE("SPS: offsetForRefFrame [" + i + "]");
+ }
+ }
+ sps.num_ref_frames = reader.readUE("SPS: num_ref_frames");
+ sps.gaps_in_frame_num_value_allowed_flag = reader
+ .readBool("SPS: gaps_in_frame_num_value_allowed_flag");
+ sps.pic_width_in_mbs_minus1 = reader
+ .readUE("SPS: pic_width_in_mbs_minus1");
+ sps.pic_height_in_map_units_minus1 = reader
+ .readUE("SPS: pic_height_in_map_units_minus1");
+ sps.frame_mbs_only_flag = reader.readBool("SPS: frame_mbs_only_flag");
+ if (!sps.frame_mbs_only_flag) {
+ sps.mb_adaptive_frame_field_flag = reader
+ .readBool("SPS: mb_adaptive_frame_field_flag");
+ }
+ sps.direct_8x8_inference_flag = reader
+ .readBool("SPS: direct_8x8_inference_flag");
+ sps.frame_cropping_flag = reader.readBool("SPS: frame_cropping_flag");
+ if (sps.frame_cropping_flag) {
+ sps.frame_crop_left_offset = reader
+ .readUE("SPS: frame_crop_left_offset");
+ sps.frame_crop_right_offset = reader
+ .readUE("SPS: frame_crop_right_offset");
+ sps.frame_crop_top_offset = reader
+ .readUE("SPS: frame_crop_top_offset");
+ sps.frame_crop_bottom_offset = reader
+ .readUE("SPS: frame_crop_bottom_offset");
+ }
+ boolean vui_parameters_present_flag = reader
+ .readBool("SPS: vui_parameters_present_flag");
+ if (vui_parameters_present_flag)
+ sps.vuiParams = ReadVUIParameters(reader);
+
+ reader.readTrailingBits();
+
+ return sps;
+ }
+
+ private static void readScalingListMatrix(CAVLCReader reader,
+ SeqParameterSet sps) throws IOException {
+ sps.scalingMatrix = new ScalingMatrix();
+ for (int i = 0; i < 8; i++) {
+ boolean seqScalingListPresentFlag = reader
+ .readBool("SPS: seqScalingListPresentFlag");
+ if (seqScalingListPresentFlag) {
+ sps.scalingMatrix.ScalingList4x4 = new ScalingList[8];
+ sps.scalingMatrix.ScalingList8x8 = new ScalingList[8];
+ if (i < 6) {
+ sps.scalingMatrix.ScalingList4x4[i] = ScalingList.read(
+ reader, 16);
+ } else {
+ sps.scalingMatrix.ScalingList8x8[i - 6] = ScalingList.read(
+ reader, 64);
+ }
+ }
+ }
+ }
+
+ private static VUIParameters ReadVUIParameters(CAVLCReader reader)
+ throws IOException {
+ VUIParameters vuip = new VUIParameters();
+ vuip.aspect_ratio_info_present_flag = reader
+ .readBool("VUI: aspect_ratio_info_present_flag");
+ if (vuip.aspect_ratio_info_present_flag) {
+ vuip.aspect_ratio = AspectRatio.fromValue((int) reader.readNBit(8,
+ "VUI: aspect_ratio"));
+ if (vuip.aspect_ratio == AspectRatio.Extended_SAR) {
+ vuip.sar_width = (int) reader.readNBit(16, "VUI: sar_width");
+ vuip.sar_height = (int) reader.readNBit(16, "VUI: sar_height");
+ }
+ }
+ vuip.overscan_info_present_flag = reader
+ .readBool("VUI: overscan_info_present_flag");
+ if (vuip.overscan_info_present_flag) {
+ vuip.overscan_appropriate_flag = reader
+ .readBool("VUI: overscan_appropriate_flag");
+ }
+ vuip.video_signal_type_present_flag = reader
+ .readBool("VUI: video_signal_type_present_flag");
+ if (vuip.video_signal_type_present_flag) {
+ vuip.video_format = (int) reader.readNBit(3, "VUI: video_format");
+ vuip.video_full_range_flag = reader
+ .readBool("VUI: video_full_range_flag");
+ vuip.colour_description_present_flag = reader
+ .readBool("VUI: colour_description_present_flag");
+ if (vuip.colour_description_present_flag) {
+ vuip.colour_primaries = (int) reader.readNBit(8,
+ "VUI: colour_primaries");
+ vuip.transfer_characteristics = (int) reader.readNBit(8,
+ "VUI: transfer_characteristics");
+ vuip.matrix_coefficients = (int) reader.readNBit(8,
+ "VUI: matrix_coefficients");
+ }
+ }
+ vuip.chroma_loc_info_present_flag = reader
+ .readBool("VUI: chroma_loc_info_present_flag");
+ if (vuip.chroma_loc_info_present_flag) {
+ vuip.chroma_sample_loc_type_top_field = reader
+ .readUE("VUI chroma_sample_loc_type_top_field");
+ vuip.chroma_sample_loc_type_bottom_field = reader
+ .readUE("VUI chroma_sample_loc_type_bottom_field");
+ }
+ vuip.timing_info_present_flag = reader
+ .readBool("VUI: timing_info_present_flag");
+ if (vuip.timing_info_present_flag) {
+ vuip.num_units_in_tick = (int) reader.readNBit(32,
+ "VUI: num_units_in_tick");
+ vuip.time_scale = (int) reader.readNBit(32, "VUI: time_scale");
+ vuip.fixed_frame_rate_flag = reader
+ .readBool("VUI: fixed_frame_rate_flag");
+ }
+ boolean nal_hrd_parameters_present_flag = reader
+ .readBool("VUI: nal_hrd_parameters_present_flag");
+ if (nal_hrd_parameters_present_flag)
+ vuip.nalHRDParams = readHRDParameters(reader);
+ boolean vcl_hrd_parameters_present_flag = reader
+ .readBool("VUI: vcl_hrd_parameters_present_flag");
+ if (vcl_hrd_parameters_present_flag)
+ vuip.vclHRDParams = readHRDParameters(reader);
+ if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
+ vuip.low_delay_hrd_flag = reader
+ .readBool("VUI: low_delay_hrd_flag");
+ }
+ vuip.pic_struct_present_flag = reader
+ .readBool("VUI: pic_struct_present_flag");
+ boolean bitstream_restriction_flag = reader
+ .readBool("VUI: bitstream_restriction_flag");
+ if (bitstream_restriction_flag) {
+ vuip.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
+ vuip.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = reader
+ .readBool("VUI: motion_vectors_over_pic_boundaries_flag");
+ vuip.bitstreamRestriction.max_bytes_per_pic_denom = reader
+ .readUE("VUI max_bytes_per_pic_denom");
+ vuip.bitstreamRestriction.max_bits_per_mb_denom = reader
+ .readUE("VUI max_bits_per_mb_denom");
+ vuip.bitstreamRestriction.log2_max_mv_length_horizontal = reader
+ .readUE("VUI log2_max_mv_length_horizontal");
+ vuip.bitstreamRestriction.log2_max_mv_length_vertical = reader
+ .readUE("VUI log2_max_mv_length_vertical");
+ vuip.bitstreamRestriction.num_reorder_frames = reader
+ .readUE("VUI num_reorder_frames");
+ vuip.bitstreamRestriction.max_dec_frame_buffering = reader
+ .readUE("VUI max_dec_frame_buffering");
+ }
+
+ return vuip;
+ }
+
+ private static HRDParameters readHRDParameters(CAVLCReader reader)
+ throws IOException {
+ HRDParameters hrd = new HRDParameters();
+ hrd.cpb_cnt_minus1 = reader.readUE("SPS: cpb_cnt_minus1");
+ hrd.bit_rate_scale = (int) reader.readNBit(4, "HRD: bit_rate_scale");
+ hrd.cpb_size_scale = (int) reader.readNBit(4, "HRD: cpb_size_scale");
+ hrd.bit_rate_value_minus1 = new int[hrd.cpb_cnt_minus1 + 1];
+ hrd.cpb_size_value_minus1 = new int[hrd.cpb_cnt_minus1 + 1];
+ hrd.cbr_flag = new boolean[hrd.cpb_cnt_minus1 + 1];
+
+ for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpb_cnt_minus1; SchedSelIdx++) {
+ hrd.bit_rate_value_minus1[SchedSelIdx] = reader
+ .readUE("HRD: bit_rate_value_minus1");
+ hrd.cpb_size_value_minus1[SchedSelIdx] = reader
+ .readUE("HRD: cpb_size_value_minus1");
+ hrd.cbr_flag[SchedSelIdx] = reader.readBool("HRD: cbr_flag");
+ }
+ hrd.initial_cpb_removal_delay_length_minus1 = (int) reader.readNBit(5,
+ "HRD: initial_cpb_removal_delay_length_minus1");
+ hrd.cpb_removal_delay_length_minus1 = (int) reader.readNBit(5,
+ "HRD: cpb_removal_delay_length_minus1");
+ hrd.dpb_output_delay_length_minus1 = (int) reader.readNBit(5,
+ "HRD: dpb_output_delay_length_minus1");
+ hrd.time_offset_length = (int) reader.readNBit(5,
+ "HRD: time_offset_length");
+ return hrd;
+ }
+
+ public void write(OutputStream out) throws IOException {
+ CAVLCWriter writer = new CAVLCWriter(out);
+
+ writer.writeNBit(profile_idc, 8, "SPS: profile_idc");
+ writer.writeBool(constraint_set_0_flag, "SPS: constraint_set_0_flag");
+ writer.writeBool(constraint_set_1_flag, "SPS: constraint_set_1_flag");
+ writer.writeBool(constraint_set_2_flag, "SPS: constraint_set_2_flag");
+ writer.writeBool(constraint_set_3_flag, "SPS: constraint_set_3_flag");
+ writer.writeNBit(0, 4, "SPS: reserved");
+ writer.writeNBit(level_idc, 8, "SPS: level_idc");
+ writer.writeUE(seq_parameter_set_id, "SPS: seq_parameter_set_id");
+
+ if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122
+ || profile_idc == 144) {
+ writer.writeUE(chroma_format_idc.getId(), "SPS: chroma_format_idc");
+ if (chroma_format_idc == ChromaFormat.YUV_444) {
+ writer.writeBool(residual_color_transform_flag,
+ "SPS: residual_color_transform_flag");
+ }
+ writer.writeUE(bit_depth_luma_minus8, "SPS: ");
+ writer.writeUE(bit_depth_chroma_minus8, "SPS: ");
+ writer.writeBool(qpprime_y_zero_transform_bypass_flag,
+ "SPS: qpprime_y_zero_transform_bypass_flag");
+ writer.writeBool(scalingMatrix != null, "SPS: ");
+ if (scalingMatrix != null) {
+ for (int i = 0; i < 8; i++) {
+ if (i < 6) {
+ writer.writeBool(
+ scalingMatrix.ScalingList4x4[i] != null,
+ "SPS: ");
+ if (scalingMatrix.ScalingList4x4[i] != null) {
+ scalingMatrix.ScalingList4x4[i].write(writer);
+ }
+ } else {
+ writer.writeBool(
+ scalingMatrix.ScalingList8x8[i - 6] != null,
+ "SPS: ");
+ if (scalingMatrix.ScalingList8x8[i - 6] != null) {
+ scalingMatrix.ScalingList8x8[i - 6].write(writer);
+ }
+ }
+ }
+ }
+ }
+ writer.writeUE(log2_max_frame_num_minus4,
+ "SPS: log2_max_frame_num_minus4");
+ writer.writeUE(pic_order_cnt_type, "SPS: pic_order_cnt_type");
+ if (pic_order_cnt_type == 0) {
+ writer.writeUE(log2_max_pic_order_cnt_lsb_minus4,
+ "SPS: log2_max_pic_order_cnt_lsb_minus4");
+ } else if (pic_order_cnt_type == 1) {
+ writer.writeBool(delta_pic_order_always_zero_flag,
+ "SPS: delta_pic_order_always_zero_flag");
+ writer.writeSE(offset_for_non_ref_pic,
+ "SPS: offset_for_non_ref_pic");
+ writer.writeSE(offset_for_top_to_bottom_field,
+ "SPS: offset_for_top_to_bottom_field");
+ writer.writeUE(offsetForRefFrame.length, "SPS: ");
+ for (int i = 0; i < offsetForRefFrame.length; i++)
+ writer.writeSE(offsetForRefFrame[i], "SPS: ");
+ }
+ writer.writeUE(num_ref_frames, "SPS: num_ref_frames");
+ writer.writeBool(gaps_in_frame_num_value_allowed_flag,
+ "SPS: gaps_in_frame_num_value_allowed_flag");
+ writer.writeUE(pic_width_in_mbs_minus1, "SPS: pic_width_in_mbs_minus1");
+ writer.writeUE(pic_height_in_map_units_minus1,
+ "SPS: pic_height_in_map_units_minus1");
+ writer.writeBool(frame_mbs_only_flag, "SPS: frame_mbs_only_flag");
+ if (!frame_mbs_only_flag) {
+ writer.writeBool(mb_adaptive_frame_field_flag,
+ "SPS: mb_adaptive_frame_field_flag");
+ }
+ writer.writeBool(direct_8x8_inference_flag,
+ "SPS: direct_8x8_inference_flag");
+ writer.writeBool(frame_cropping_flag, "SPS: frame_cropping_flag");
+ if (frame_cropping_flag) {
+ writer.writeUE(frame_crop_left_offset,
+ "SPS: frame_crop_left_offset");
+ writer.writeUE(frame_crop_right_offset,
+ "SPS: frame_crop_right_offset");
+ writer.writeUE(frame_crop_top_offset, "SPS: frame_crop_top_offset");
+ writer.writeUE(frame_crop_bottom_offset,
+ "SPS: frame_crop_bottom_offset");
+ }
+ writer.writeBool(vuiParams != null, "SPS: ");
+ if (vuiParams != null)
+ writeVUIParameters(vuiParams, writer);
+
+ writer.writeTrailingBits();
+ }
+
+ private void writeVUIParameters(VUIParameters vuip, CAVLCWriter writer)
+ throws IOException {
+ writer.writeBool(vuip.aspect_ratio_info_present_flag,
+ "VUI: aspect_ratio_info_present_flag");
+ if (vuip.aspect_ratio_info_present_flag) {
+ writer.writeNBit(vuip.aspect_ratio.getValue(), 8,
+ "VUI: aspect_ratio");
+ if (vuip.aspect_ratio == AspectRatio.Extended_SAR) {
+ writer.writeNBit(vuip.sar_width, 16, "VUI: sar_width");
+ writer.writeNBit(vuip.sar_height, 16, "VUI: sar_height");
+ }
+ }
+ writer.writeBool(vuip.overscan_info_present_flag,
+ "VUI: overscan_info_present_flag");
+ if (vuip.overscan_info_present_flag) {
+ writer.writeBool(vuip.overscan_appropriate_flag,
+ "VUI: overscan_appropriate_flag");
+ }
+ writer.writeBool(vuip.video_signal_type_present_flag,
+ "VUI: video_signal_type_present_flag");
+ if (vuip.video_signal_type_present_flag) {
+ writer.writeNBit(vuip.video_format, 3, "VUI: video_format");
+ writer.writeBool(vuip.video_full_range_flag,
+ "VUI: video_full_range_flag");
+ writer.writeBool(vuip.colour_description_present_flag,
+ "VUI: colour_description_present_flag");
+ if (vuip.colour_description_present_flag) {
+ writer.writeNBit(vuip.colour_primaries, 8,
+ "VUI: colour_primaries");
+ writer.writeNBit(vuip.transfer_characteristics, 8,
+ "VUI: transfer_characteristics");
+ writer.writeNBit(vuip.matrix_coefficients, 8,
+ "VUI: matrix_coefficients");
+ }
+ }
+ writer.writeBool(vuip.chroma_loc_info_present_flag,
+ "VUI: chroma_loc_info_present_flag");
+ if (vuip.chroma_loc_info_present_flag) {
+ writer.writeUE(vuip.chroma_sample_loc_type_top_field,
+ "VUI: chroma_sample_loc_type_top_field");
+ writer.writeUE(vuip.chroma_sample_loc_type_bottom_field,
+ "VUI: chroma_sample_loc_type_bottom_field");
+ }
+ writer.writeBool(vuip.timing_info_present_flag,
+ "VUI: timing_info_present_flag");
+ if (vuip.timing_info_present_flag) {
+ writer.writeNBit(vuip.num_units_in_tick, 32,
+ "VUI: num_units_in_tick");
+ writer.writeNBit(vuip.time_scale, 32, "VUI: time_scale");
+ writer.writeBool(vuip.fixed_frame_rate_flag,
+ "VUI: fixed_frame_rate_flag");
+ }
+ writer.writeBool(vuip.nalHRDParams != null, "VUI: ");
+ if (vuip.nalHRDParams != null) {
+ writeHRDParameters(vuip.nalHRDParams, writer);
+ }
+ writer.writeBool(vuip.vclHRDParams != null, "VUI: ");
+ if (vuip.vclHRDParams != null) {
+ writeHRDParameters(vuip.vclHRDParams, writer);
+ }
+
+ if (vuip.nalHRDParams != null || vuip.vclHRDParams != null) {
+ writer
+ .writeBool(vuip.low_delay_hrd_flag,
+ "VUI: low_delay_hrd_flag");
+ }
+ writer.writeBool(vuip.pic_struct_present_flag,
+ "VUI: pic_struct_present_flag");
+ writer.writeBool(vuip.bitstreamRestriction != null, "VUI: ");
+ if (vuip.bitstreamRestriction != null) {
+ writer
+ .writeBool(
+ vuip.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag,
+ "VUI: motion_vectors_over_pic_boundaries_flag");
+ writer.writeUE(vuip.bitstreamRestriction.max_bytes_per_pic_denom,
+ "VUI: max_bytes_per_pic_denom");
+ writer.writeUE(vuip.bitstreamRestriction.max_bits_per_mb_denom,
+ "VUI: max_bits_per_mb_denom");
+ writer.writeUE(
+ vuip.bitstreamRestriction.log2_max_mv_length_horizontal,
+ "VUI: log2_max_mv_length_horizontal");
+ writer.writeUE(
+ vuip.bitstreamRestriction.log2_max_mv_length_vertical,
+ "VUI: log2_max_mv_length_vertical");
+ writer.writeUE(vuip.bitstreamRestriction.num_reorder_frames,
+ "VUI: num_reorder_frames");
+ writer.writeUE(vuip.bitstreamRestriction.max_dec_frame_buffering,
+ "VUI: max_dec_frame_buffering");
+ }
+
+ }
+
+ private void writeHRDParameters(HRDParameters hrd, CAVLCWriter writer)
+ throws IOException {
+ writer.writeUE(hrd.cpb_cnt_minus1, "HRD: cpb_cnt_minus1");
+ writer.writeNBit(hrd.bit_rate_scale, 4, "HRD: bit_rate_scale");
+ writer.writeNBit(hrd.cpb_size_scale, 4, "HRD: cpb_size_scale");
+
+ for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpb_cnt_minus1; SchedSelIdx++) {
+ writer.writeUE(hrd.bit_rate_value_minus1[SchedSelIdx], "HRD: ");
+ writer.writeUE(hrd.cpb_size_value_minus1[SchedSelIdx], "HRD: ");
+ writer.writeBool(hrd.cbr_flag[SchedSelIdx], "HRD: ");
+ }
+ writer.writeNBit(hrd.initial_cpb_removal_delay_length_minus1, 5,
+ "HRD: initial_cpb_removal_delay_length_minus1");
+ writer.writeNBit(hrd.cpb_removal_delay_length_minus1, 5,
+ "HRD: cpb_removal_delay_length_minus1");
+ writer.writeNBit(hrd.dpb_output_delay_length_minus1, 5,
+ "HRD: dpb_output_delay_length_minus1");
+ writer.writeNBit(hrd.time_offset_length, 5, "HRD: time_offset_length");
+ }
+
+ @Override
+ public String toString() {
+ return "SeqParameterSet{ " +
+ "\n pic_order_cnt_type=" + pic_order_cnt_type +
+ ", \n field_pic_flag=" + field_pic_flag +
+ ", \n delta_pic_order_always_zero_flag=" + delta_pic_order_always_zero_flag +
+ ", \n weighted_pred_flag=" + weighted_pred_flag +
+ ", \n weighted_bipred_idc=" + weighted_bipred_idc +
+ ", \n entropy_coding_mode_flag=" + entropy_coding_mode_flag +
+ ", \n mb_adaptive_frame_field_flag=" + mb_adaptive_frame_field_flag +
+ ", \n direct_8x8_inference_flag=" + direct_8x8_inference_flag +
+ ", \n chroma_format_idc=" + chroma_format_idc +
+ ", \n log2_max_frame_num_minus4=" + log2_max_frame_num_minus4 +
+ ", \n log2_max_pic_order_cnt_lsb_minus4=" + log2_max_pic_order_cnt_lsb_minus4 +
+ ", \n pic_height_in_map_units_minus1=" + pic_height_in_map_units_minus1 +
+ ", \n pic_width_in_mbs_minus1=" + pic_width_in_mbs_minus1 +
+ ", \n bit_depth_luma_minus8=" + bit_depth_luma_minus8 +
+ ", \n bit_depth_chroma_minus8=" + bit_depth_chroma_minus8 +
+ ", \n qpprime_y_zero_transform_bypass_flag=" + qpprime_y_zero_transform_bypass_flag +
+ ", \n profile_idc=" + profile_idc +
+ ", \n constraint_set_0_flag=" + constraint_set_0_flag +
+ ", \n constraint_set_1_flag=" + constraint_set_1_flag +
+ ", \n constraint_set_2_flag=" + constraint_set_2_flag +
+ ", \n constraint_set_3_flag=" + constraint_set_3_flag +
+ ", \n level_idc=" + level_idc +
+ ", \n seq_parameter_set_id=" + seq_parameter_set_id +
+ ", \n residual_color_transform_flag=" + residual_color_transform_flag +
+ ", \n offset_for_non_ref_pic=" + offset_for_non_ref_pic +
+ ", \n offset_for_top_to_bottom_field=" + offset_for_top_to_bottom_field +
+ ", \n num_ref_frames=" + num_ref_frames +
+ ", \n gaps_in_frame_num_value_allowed_flag=" + gaps_in_frame_num_value_allowed_flag +
+ ", \n frame_mbs_only_flag=" + frame_mbs_only_flag +
+ ", \n frame_cropping_flag=" + frame_cropping_flag +
+ ", \n frame_crop_left_offset=" + frame_crop_left_offset +
+ ", \n frame_crop_right_offset=" + frame_crop_right_offset +
+ ", \n frame_crop_top_offset=" + frame_crop_top_offset +
+ ", \n frame_crop_bottom_offset=" + frame_crop_bottom_offset +
+ ", \n offsetForRefFrame=" + offsetForRefFrame +
+ ", \n vuiParams=" + vuiParams +
+ ", \n scalingMatrix=" + scalingMatrix +
+ ", \n num_ref_frames_in_pic_order_cnt_cycle=" + num_ref_frames_in_pic_order_cnt_cycle +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/VUIParameters.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/VUIParameters.java
new file mode 100644
index 0000000..eec7880
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/VUIParameters.java
@@ -0,0 +1,94 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.model;
+
+public class VUIParameters {
+
+ public static class BitstreamRestriction {
+
+ public boolean motion_vectors_over_pic_boundaries_flag;
+ public int max_bytes_per_pic_denom;
+ public int max_bits_per_mb_denom;
+ public int log2_max_mv_length_horizontal;
+ public int log2_max_mv_length_vertical;
+ public int num_reorder_frames;
+ public int max_dec_frame_buffering;
+
+ }
+
+ public boolean aspect_ratio_info_present_flag;
+ public int sar_width;
+ public int sar_height;
+ public boolean overscan_info_present_flag;
+ public boolean overscan_appropriate_flag;
+ public boolean video_signal_type_present_flag;
+ public int video_format;
+ public boolean video_full_range_flag;
+ public boolean colour_description_present_flag;
+ public int colour_primaries;
+ public int transfer_characteristics;
+ public int matrix_coefficients;
+ public boolean chroma_loc_info_present_flag;
+ public int chroma_sample_loc_type_top_field;
+ public int chroma_sample_loc_type_bottom_field;
+ public boolean timing_info_present_flag;
+ public int num_units_in_tick;
+ public int time_scale;
+ public boolean fixed_frame_rate_flag;
+ public boolean low_delay_hrd_flag;
+ public boolean pic_struct_present_flag;
+ public HRDParameters nalHRDParams;
+ public HRDParameters vclHRDParams;
+
+ public BitstreamRestriction bitstreamRestriction;
+ public AspectRatio aspect_ratio;
+
+ @Override
+ public String toString() {
+ return "VUIParameters{" + "\n" +
+ "aspect_ratio_info_present_flag=" + aspect_ratio_info_present_flag + "\n" +
+ ", sar_width=" + sar_width + "\n" +
+ ", sar_height=" + sar_height + "\n" +
+ ", overscan_info_present_flag=" + overscan_info_present_flag + "\n" +
+ ", overscan_appropriate_flag=" + overscan_appropriate_flag + "\n" +
+ ", video_signal_type_present_flag=" + video_signal_type_present_flag + "\n" +
+ ", video_format=" + video_format + "\n" +
+ ", video_full_range_flag=" + video_full_range_flag + "\n" +
+ ", colour_description_present_flag=" + colour_description_present_flag + "\n" +
+ ", colour_primaries=" + colour_primaries + "\n" +
+ ", transfer_characteristics=" + transfer_characteristics + "\n" +
+ ", matrix_coefficients=" + matrix_coefficients + "\n" +
+ ", chroma_loc_info_present_flag=" + chroma_loc_info_present_flag + "\n" +
+ ", chroma_sample_loc_type_top_field=" + chroma_sample_loc_type_top_field + "\n" +
+ ", chroma_sample_loc_type_bottom_field=" + chroma_sample_loc_type_bottom_field + "\n" +
+ ", timing_info_present_flag=" + timing_info_present_flag + "\n" +
+ ", num_units_in_tick=" + num_units_in_tick + "\n" +
+ ", time_scale=" + time_scale + "\n" +
+ ", fixed_frame_rate_flag=" + fixed_frame_rate_flag + "\n" +
+ ", low_delay_hrd_flag=" + low_delay_hrd_flag + "\n" +
+ ", pic_struct_present_flag=" + pic_struct_present_flag + "\n" +
+ ", nalHRDParams=" + nalHRDParams + "\n" +
+ ", vclHRDParams=" + vclHRDParams + "\n" +
+ ", bitstreamRestriction=" + bitstreamRestriction + "\n" +
+ ", aspect_ratio=" + aspect_ratio + "\n" +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/all-wcprops
new file mode 100644
index 0000000..39f268a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/read
+END
+CAVLCReader.java
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/CAVLCReader.java
+END
+BitstreamReader.java
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/377/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/BitstreamReader.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/entries
new file mode 100644
index 0000000..f0111dc
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/read
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+CAVLCReader.java
+file
+
+
+
+
+2012-09-14T17:27:51.457232Z
+bcd623b178982adc4723a481ed9ee8c8
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5069
+
+BitstreamReader.java
+file
+
+
+
+
+2012-09-14T17:27:51.457232Z
+0a82a1920e98d55c972bc7e536bfb158
+2012-03-05T23:28:24.666173Z
+377
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4982
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/text-base/BitstreamReader.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/text-base/BitstreamReader.java.svn-base
new file mode 100644
index 0000000..816af6a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/text-base/BitstreamReader.java.svn-base
@@ -0,0 +1,194 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.read;
+
+import com.googlecode.mp4parser.h264.CharCache;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A dummy implementation of H264 RBSP reading
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class BitstreamReader {
+ private InputStream is;
+ private int curByte;
+ private int nextByte;
+ int nBit;
+ protected static int bitsRead;
+
+ protected CharCache debugBits = new CharCache(50);
+
+ public BitstreamReader(InputStream is) throws IOException {
+ this.is = is;
+ curByte = is.read();
+ nextByte = is.read();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#read1Bit()
+ */
+ public int read1Bit() throws IOException {
+ if (nBit == 8) {
+ advance();
+ if (curByte == -1) {
+ return -1;
+ }
+ }
+ int res = (curByte >> (7 - nBit)) & 1;
+ nBit++;
+
+ debugBits.append(res == 0 ? '0' : '1');
+ ++bitsRead;
+
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readNBit(int)
+ */
+ public long readNBit(int n) throws IOException {
+ if (n > 64)
+ throw new IllegalArgumentException("Can not readByte more then 64 bit");
+
+ long val = 0;
+
+ for (int i = 0; i < n; i++) {
+ val <<= 1;
+ val |= read1Bit();
+ }
+
+ return val;
+ }
+
+ private void advance() throws IOException {
+ curByte = nextByte;
+ nextByte = is.read();
+ nBit = 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readByte()
+ */
+ public int readByte() throws IOException {
+ if (nBit > 0) {
+ advance();
+ }
+
+ int res = curByte;
+
+ advance();
+
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#moreRBSPData()
+ */
+ public boolean moreRBSPData() throws IOException {
+ if (nBit == 8) {
+ advance();
+ }
+ int tail = 1 << (8 - nBit - 1);
+ int mask = ((tail << 1) - 1);
+ boolean hasTail = (curByte & mask) == tail;
+
+ return !(curByte == -1 || (nextByte == -1 && hasTail));
+ }
+
+ public long getBitPosition() {
+ return (bitsRead * 8 + (nBit % 8));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readRemainingByte()
+ */
+ public long readRemainingByte() throws IOException {
+ return readNBit(8 - nBit);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#next_bits(int)
+ */
+ public int peakNextBits(int n) throws IOException {
+ if (n > 8)
+ throw new IllegalArgumentException("N should be less then 8");
+ if (nBit == 8) {
+ advance();
+ if (curByte == -1) {
+ return -1;
+ }
+ }
+ int[] bits = new int[16 - nBit];
+
+ int cnt = 0;
+ for (int i = nBit; i < 8; i++) {
+ bits[cnt++] = (curByte >> (7 - i)) & 0x1;
+ }
+
+ for (int i = 0; i < 8; i++) {
+ bits[cnt++] = (nextByte >> (7 - i)) & 0x1;
+ }
+
+ int result = 0;
+ for (int i = 0; i < n; i++) {
+ result <<= 1;
+ result |= bits[i];
+ }
+
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#byte_aligned()
+ */
+ public boolean isByteAligned() {
+ return (nBit % 8) == 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#close()
+ */
+ public void close() throws IOException {
+ }
+
+ public int getCurBit() {
+ return nBit;
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/text-base/CAVLCReader.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/text-base/CAVLCReader.java.svn-base
new file mode 100644
index 0000000..07c7f71
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/.svn/text-base/CAVLCReader.java.svn-base
@@ -0,0 +1,185 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.read;
+
+
+import com.googlecode.mp4parser.h264.BTree;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static com.googlecode.mp4parser.h264.Debug.println;
+
+
+public class CAVLCReader extends BitstreamReader {
+
+ public CAVLCReader(InputStream is) throws IOException {
+ super(is);
+ }
+
+ public long readNBit(int n, String message) throws IOException {
+ long val = readNBit(n);
+
+ trace(message, String.valueOf(val));
+
+ return val;
+ }
+
+ /**
+ * Read unsigned exp-golomb code
+ *
+ * @return
+ * @throws java.io.IOException
+ * @throws java.io.IOException
+ */
+ private int readUE() throws IOException {
+ int cnt = 0;
+ while (read1Bit() == 0)
+ cnt++;
+
+ int res = 0;
+ if (cnt > 0) {
+ long val = readNBit(cnt);
+
+ res = (int) ((1 << cnt) - 1 + val);
+ }
+
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * ua.org.jplayer.javcodec.h264.H264BitInputStream#readUE(java.lang.String)
+ */
+ public int readUE(String message) throws IOException {
+ int res = readUE();
+
+ trace(message, String.valueOf(res));
+
+ return res;
+ }
+
+ public int readSE(String message) throws IOException {
+ int val = readUE();
+
+ int sign = ((val & 0x1) << 1) - 1;
+ val = ((val >> 1) + (val & 0x1)) * sign;
+
+ trace(message, String.valueOf(val));
+
+ return val;
+ }
+
+ public boolean readBool(String message) throws IOException {
+
+ boolean res = read1Bit() == 0 ? false : true;
+
+ trace(message, res ? "1" : "0");
+
+ return res;
+ }
+
+ public int readU(int i, String string) throws IOException {
+ return (int) readNBit(i, string);
+ }
+
+ public byte[] read(int payloadSize) throws IOException {
+ byte[] result = new byte[payloadSize];
+ for (int i = 0; i < payloadSize; i++) {
+ result[i] = (byte) readByte();
+ }
+ return result;
+ }
+
+ public boolean readAE() {
+ // TODO: do it!!
+ throw new UnsupportedOperationException("Stan");
+ }
+
+ public int readTE(int max) throws IOException {
+ if (max > 1)
+ return readUE();
+ return ~read1Bit() & 0x1;
+ }
+
+ public int readAEI() {
+ // TODO: do it!!
+ throw new UnsupportedOperationException("Stan");
+ }
+
+ public int readME(String string) throws IOException {
+ return readUE(string);
+ }
+
+ public Object readCE(BTree bt, String message) throws IOException {
+ while (true) {
+ int bit = read1Bit();
+ bt = bt.down(bit);
+ if (bt == null) {
+ throw new RuntimeException("Illegal code");
+ }
+ Object i = bt.getValue();
+ if (i != null) {
+ trace(message, i.toString());
+ return i;
+ }
+ }
+ }
+
+ public int readZeroBitCount(String message) throws IOException {
+ int count = 0;
+ while (read1Bit() == 0)
+ count++;
+
+ trace(message, String.valueOf(count));
+
+ return count;
+ }
+
+ public void readTrailingBits() throws IOException {
+ read1Bit();
+ readRemainingByte();
+ }
+
+ private void trace(String message, String val) {
+ StringBuilder traceBuilder = new StringBuilder();
+ int spaces;
+ String pos = String.valueOf(bitsRead - debugBits.length());
+ spaces = 8 - pos.length();
+
+ traceBuilder.append("@" + pos);
+
+ for (int i = 0; i < spaces; i++)
+ traceBuilder.append(' ');
+
+ traceBuilder.append(message);
+ spaces = 100 - traceBuilder.length() - debugBits.length();
+ for (int i = 0; i < spaces; i++)
+ traceBuilder.append(' ');
+ traceBuilder.append(debugBits);
+ traceBuilder.append(" (" + val + ")");
+ debugBits.clear();
+
+ println(traceBuilder.toString());
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/BitstreamReader.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/BitstreamReader.java
new file mode 100644
index 0000000..816af6a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/BitstreamReader.java
@@ -0,0 +1,194 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.read;
+
+import com.googlecode.mp4parser.h264.CharCache;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A dummy implementation of H264 RBSP reading
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class BitstreamReader {
+ private InputStream is;
+ private int curByte;
+ private int nextByte;
+ int nBit;
+ protected static int bitsRead;
+
+ protected CharCache debugBits = new CharCache(50);
+
+ public BitstreamReader(InputStream is) throws IOException {
+ this.is = is;
+ curByte = is.read();
+ nextByte = is.read();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#read1Bit()
+ */
+ public int read1Bit() throws IOException {
+ if (nBit == 8) {
+ advance();
+ if (curByte == -1) {
+ return -1;
+ }
+ }
+ int res = (curByte >> (7 - nBit)) & 1;
+ nBit++;
+
+ debugBits.append(res == 0 ? '0' : '1');
+ ++bitsRead;
+
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readNBit(int)
+ */
+ public long readNBit(int n) throws IOException {
+ if (n > 64)
+ throw new IllegalArgumentException("Can not readByte more then 64 bit");
+
+ long val = 0;
+
+ for (int i = 0; i < n; i++) {
+ val <<= 1;
+ val |= read1Bit();
+ }
+
+ return val;
+ }
+
+ private void advance() throws IOException {
+ curByte = nextByte;
+ nextByte = is.read();
+ nBit = 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readByte()
+ */
+ public int readByte() throws IOException {
+ if (nBit > 0) {
+ advance();
+ }
+
+ int res = curByte;
+
+ advance();
+
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#moreRBSPData()
+ */
+ public boolean moreRBSPData() throws IOException {
+ if (nBit == 8) {
+ advance();
+ }
+ int tail = 1 << (8 - nBit - 1);
+ int mask = ((tail << 1) - 1);
+ boolean hasTail = (curByte & mask) == tail;
+
+ return !(curByte == -1 || (nextByte == -1 && hasTail));
+ }
+
+ public long getBitPosition() {
+ return (bitsRead * 8 + (nBit % 8));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#readRemainingByte()
+ */
+ public long readRemainingByte() throws IOException {
+ return readNBit(8 - nBit);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#next_bits(int)
+ */
+ public int peakNextBits(int n) throws IOException {
+ if (n > 8)
+ throw new IllegalArgumentException("N should be less then 8");
+ if (nBit == 8) {
+ advance();
+ if (curByte == -1) {
+ return -1;
+ }
+ }
+ int[] bits = new int[16 - nBit];
+
+ int cnt = 0;
+ for (int i = nBit; i < 8; i++) {
+ bits[cnt++] = (curByte >> (7 - i)) & 0x1;
+ }
+
+ for (int i = 0; i < 8; i++) {
+ bits[cnt++] = (nextByte >> (7 - i)) & 0x1;
+ }
+
+ int result = 0;
+ for (int i = 0; i < n; i++) {
+ result <<= 1;
+ result |= bits[i];
+ }
+
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#byte_aligned()
+ */
+ public boolean isByteAligned() {
+ return (nBit % 8) == 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.RBSPInputStream#close()
+ */
+ public void close() throws IOException {
+ }
+
+ public int getCurBit() {
+ return nBit;
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/CAVLCReader.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/CAVLCReader.java
new file mode 100644
index 0000000..07c7f71
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/read/CAVLCReader.java
@@ -0,0 +1,185 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.read;
+
+
+import com.googlecode.mp4parser.h264.BTree;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static com.googlecode.mp4parser.h264.Debug.println;
+
+
+public class CAVLCReader extends BitstreamReader {
+
+ public CAVLCReader(InputStream is) throws IOException {
+ super(is);
+ }
+
+ public long readNBit(int n, String message) throws IOException {
+ long val = readNBit(n);
+
+ trace(message, String.valueOf(val));
+
+ return val;
+ }
+
+ /**
+ * Read unsigned exp-golomb code
+ *
+ * @return
+ * @throws java.io.IOException
+ * @throws java.io.IOException
+ */
+ private int readUE() throws IOException {
+ int cnt = 0;
+ while (read1Bit() == 0)
+ cnt++;
+
+ int res = 0;
+ if (cnt > 0) {
+ long val = readNBit(cnt);
+
+ res = (int) ((1 << cnt) - 1 + val);
+ }
+
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * ua.org.jplayer.javcodec.h264.H264BitInputStream#readUE(java.lang.String)
+ */
+ public int readUE(String message) throws IOException {
+ int res = readUE();
+
+ trace(message, String.valueOf(res));
+
+ return res;
+ }
+
+ public int readSE(String message) throws IOException {
+ int val = readUE();
+
+ int sign = ((val & 0x1) << 1) - 1;
+ val = ((val >> 1) + (val & 0x1)) * sign;
+
+ trace(message, String.valueOf(val));
+
+ return val;
+ }
+
+ public boolean readBool(String message) throws IOException {
+
+ boolean res = read1Bit() == 0 ? false : true;
+
+ trace(message, res ? "1" : "0");
+
+ return res;
+ }
+
+ public int readU(int i, String string) throws IOException {
+ return (int) readNBit(i, string);
+ }
+
+ public byte[] read(int payloadSize) throws IOException {
+ byte[] result = new byte[payloadSize];
+ for (int i = 0; i < payloadSize; i++) {
+ result[i] = (byte) readByte();
+ }
+ return result;
+ }
+
+ public boolean readAE() {
+ // TODO: do it!!
+ throw new UnsupportedOperationException("Stan");
+ }
+
+ public int readTE(int max) throws IOException {
+ if (max > 1)
+ return readUE();
+ return ~read1Bit() & 0x1;
+ }
+
+ public int readAEI() {
+ // TODO: do it!!
+ throw new UnsupportedOperationException("Stan");
+ }
+
+ public int readME(String string) throws IOException {
+ return readUE(string);
+ }
+
+ public Object readCE(BTree bt, String message) throws IOException {
+ while (true) {
+ int bit = read1Bit();
+ bt = bt.down(bit);
+ if (bt == null) {
+ throw new RuntimeException("Illegal code");
+ }
+ Object i = bt.getValue();
+ if (i != null) {
+ trace(message, i.toString());
+ return i;
+ }
+ }
+ }
+
+ public int readZeroBitCount(String message) throws IOException {
+ int count = 0;
+ while (read1Bit() == 0)
+ count++;
+
+ trace(message, String.valueOf(count));
+
+ return count;
+ }
+
+ public void readTrailingBits() throws IOException {
+ read1Bit();
+ readRemainingByte();
+ }
+
+ private void trace(String message, String val) {
+ StringBuilder traceBuilder = new StringBuilder();
+ int spaces;
+ String pos = String.valueOf(bitsRead - debugBits.length());
+ spaces = 8 - pos.length();
+
+ traceBuilder.append("@" + pos);
+
+ for (int i = 0; i < spaces; i++)
+ traceBuilder.append(' ');
+
+ traceBuilder.append(message);
+ spaces = 100 - traceBuilder.length() - debugBits.length();
+ for (int i = 0; i < spaces; i++)
+ traceBuilder.append(' ');
+ traceBuilder.append(debugBits);
+ traceBuilder.append(" (" + val + ")");
+ debugBits.clear();
+
+ println(traceBuilder.toString());
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/all-wcprops
new file mode 100644
index 0000000..eb9d014
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/236/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/write
+END
+CAVLCWriter.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/236/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/CAVLCWriter.java
+END
+BitstreamWriter.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/236/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/BitstreamWriter.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/entries
new file mode 100644
index 0000000..17c897c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/h264/write
+http://mp4parser.googlecode.com/svn
+
+
+
+2011-09-20T18:03:23.375910Z
+236
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+CAVLCWriter.java
+file
+
+
+
+
+2012-09-14T17:27:51.397232Z
+466f7fb1a81ac3195414706a32abdee2
+2011-09-20T18:03:23.375910Z
+236
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3214
+
+BitstreamWriter.java
+file
+
+
+
+
+2012-09-14T17:27:51.397232Z
+816b2b532d6ccee0eda214f86cde8ebc
+2011-09-20T18:03:23.375910Z
+236
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3192
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/text-base/BitstreamWriter.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/text-base/BitstreamWriter.java.svn-base
new file mode 100644
index 0000000..b382400
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/text-base/BitstreamWriter.java.svn-base
@@ -0,0 +1,108 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.write;
+
+import com.googlecode.mp4parser.h264.Debug;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A dummy implementation of H264 RBSP output stream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class BitstreamWriter {
+
+ private final OutputStream os;
+ private int[] curByte = new int[8];
+ private int curBit;
+
+ public BitstreamWriter(OutputStream out) {
+ this.os = out;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.H264BitOutputStream#flush()
+ */
+ public void flush() throws IOException {
+ for (int i = curBit; i < 8; i++) {
+ curByte[i] = 0;
+ }
+ curBit = 0;
+ writeCurByte();
+ }
+
+ private void writeCurByte() throws IOException {
+ int toWrite = (curByte[0] << 7) | (curByte[1] << 6) | (curByte[2] << 5)
+ | (curByte[3] << 4) | (curByte[4] << 3) | (curByte[5] << 2)
+ | (curByte[6] << 1) | curByte[7];
+ os.write(toWrite);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.H264BitOutputStream#write1Bit(int)
+ */
+ public void write1Bit(int value) throws IOException {
+ Debug.print(value);
+ if (curBit == 8) {
+ curBit = 0;
+ writeCurByte();
+ }
+ curByte[curBit++] = value;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.H264BitOutputStream#writeNBit(long,
+ * int)
+ */
+ public void writeNBit(long value, int n) throws IOException {
+ for (int i = 0; i < n; i++) {
+ write1Bit((int) (value >> (n - i - 1)) & 0x1);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * ua.org.jplayer.javcodec.h264.H264BitOutputStream#writeRemainingZero()
+ */
+ public void writeRemainingZero() throws IOException {
+ writeNBit(0, 8 - curBit);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.H264BitOutputStream#writeByte(int)
+ */
+ public void writeByte(int b) throws IOException {
+ os.write(b);
+
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/text-base/CAVLCWriter.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/text-base/CAVLCWriter.java.svn-base
new file mode 100644
index 0000000..c4e0026
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/.svn/text-base/CAVLCWriter.java.svn-base
@@ -0,0 +1,100 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.write;
+
+import com.googlecode.mp4parser.h264.Debug;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * A class responsible for outputting exp-Golumb values into binary stream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class CAVLCWriter extends BitstreamWriter {
+
+ public CAVLCWriter(OutputStream out) {
+ super(out);
+ }
+
+ public void writeU(int value, int n, String string) throws IOException {
+ Debug.print(string + "\t");
+ writeNBit(value, n);
+ Debug.println("\t" + value);
+ }
+
+ public void writeUE(int value) throws IOException {
+ int bits = 0;
+ int cumul = 0;
+ for (int i = 0; i < 15; i++) {
+ if (value < cumul + (1 << i)) {
+ bits = i;
+ break;
+ }
+ cumul += (1 << i);
+ }
+ writeNBit(0, bits);
+ write1Bit(1);
+ writeNBit(value - cumul, bits);
+ }
+
+ public void writeUE(int value, String string) throws IOException {
+ Debug.print(string + "\t");
+ writeUE(value);
+ Debug.println("\t" + value);
+ }
+
+ public void writeSE(int value, String string) throws IOException {
+ Debug.print(string + "\t");
+ writeUE((value << 1) * (value < 0 ? -1 : 1) + (value > 0 ? 1 : 0));
+ Debug.println("\t" + value);
+ }
+
+ public void writeBool(boolean value, String string) throws IOException {
+ Debug.print(string + "\t");
+ write1Bit(value ? 1 : 0);
+ Debug.println("\t" + value);
+ }
+
+ public void writeU(int i, int n) throws IOException {
+ writeNBit(i, n);
+ }
+
+ public void writeNBit(long value, int n, String string) throws IOException {
+ Debug.print(string + "\t");
+ for (int i = 0; i < n; i++) {
+ write1Bit((int) (value >> (n - i - 1)) & 0x1);
+ }
+ Debug.println("\t" + value);
+ }
+
+ public void writeTrailingBits() throws IOException {
+ write1Bit(1);
+ writeRemainingZero();
+ flush();
+ }
+
+ public void writeSliceTrailingBits() {
+ throw new IllegalStateException("todo");
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/BitstreamWriter.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/BitstreamWriter.java
new file mode 100644
index 0000000..b382400
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/BitstreamWriter.java
@@ -0,0 +1,108 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.write;
+
+import com.googlecode.mp4parser.h264.Debug;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A dummy implementation of H264 RBSP output stream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class BitstreamWriter {
+
+ private final OutputStream os;
+ private int[] curByte = new int[8];
+ private int curBit;
+
+ public BitstreamWriter(OutputStream out) {
+ this.os = out;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.H264BitOutputStream#flush()
+ */
+ public void flush() throws IOException {
+ for (int i = curBit; i < 8; i++) {
+ curByte[i] = 0;
+ }
+ curBit = 0;
+ writeCurByte();
+ }
+
+ private void writeCurByte() throws IOException {
+ int toWrite = (curByte[0] << 7) | (curByte[1] << 6) | (curByte[2] << 5)
+ | (curByte[3] << 4) | (curByte[4] << 3) | (curByte[5] << 2)
+ | (curByte[6] << 1) | curByte[7];
+ os.write(toWrite);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.H264BitOutputStream#write1Bit(int)
+ */
+ public void write1Bit(int value) throws IOException {
+ Debug.print(value);
+ if (curBit == 8) {
+ curBit = 0;
+ writeCurByte();
+ }
+ curByte[curBit++] = value;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.H264BitOutputStream#writeNBit(long,
+ * int)
+ */
+ public void writeNBit(long value, int n) throws IOException {
+ for (int i = 0; i < n; i++) {
+ write1Bit((int) (value >> (n - i - 1)) & 0x1);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * ua.org.jplayer.javcodec.h264.H264BitOutputStream#writeRemainingZero()
+ */
+ public void writeRemainingZero() throws IOException {
+ writeNBit(0, 8 - curBit);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ua.org.jplayer.javcodec.h264.H264BitOutputStream#writeByte(int)
+ */
+ public void writeByte(int b) throws IOException {
+ os.write(b);
+
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/CAVLCWriter.java b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/CAVLCWriter.java
new file mode 100644
index 0000000..c4e0026
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/h264/write/CAVLCWriter.java
@@ -0,0 +1,100 @@
+/*
+Copyright (c) 2011 Stanislav Vitvitskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package com.googlecode.mp4parser.h264.write;
+
+import com.googlecode.mp4parser.h264.Debug;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * A class responsible for outputting exp-Golumb values into binary stream
+ *
+ * @author Stanislav Vitvitskiy
+ */
+public class CAVLCWriter extends BitstreamWriter {
+
+ public CAVLCWriter(OutputStream out) {
+ super(out);
+ }
+
+ public void writeU(int value, int n, String string) throws IOException {
+ Debug.print(string + "\t");
+ writeNBit(value, n);
+ Debug.println("\t" + value);
+ }
+
+ public void writeUE(int value) throws IOException {
+ int bits = 0;
+ int cumul = 0;
+ for (int i = 0; i < 15; i++) {
+ if (value < cumul + (1 << i)) {
+ bits = i;
+ break;
+ }
+ cumul += (1 << i);
+ }
+ writeNBit(0, bits);
+ write1Bit(1);
+ writeNBit(value - cumul, bits);
+ }
+
+ public void writeUE(int value, String string) throws IOException {
+ Debug.print(string + "\t");
+ writeUE(value);
+ Debug.println("\t" + value);
+ }
+
+ public void writeSE(int value, String string) throws IOException {
+ Debug.print(string + "\t");
+ writeUE((value << 1) * (value < 0 ? -1 : 1) + (value > 0 ? 1 : 0));
+ Debug.println("\t" + value);
+ }
+
+ public void writeBool(boolean value, String string) throws IOException {
+ Debug.print(string + "\t");
+ write1Bit(value ? 1 : 0);
+ Debug.println("\t" + value);
+ }
+
+ public void writeU(int i, int n) throws IOException {
+ writeNBit(i, n);
+ }
+
+ public void writeNBit(long value, int n, String string) throws IOException {
+ Debug.print(string + "\t");
+ for (int i = 0; i < n; i++) {
+ write1Bit((int) (value >> (n - i - 1)) & 0x1);
+ }
+ Debug.println("\t" + value);
+ }
+
+ public void writeTrailingBits() throws IOException {
+ write1Bit(1);
+ writeRemainingZero();
+ flush();
+ }
+
+ public void writeSliceTrailingBits() {
+ throw new IllegalStateException("todo");
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/all-wcprops
new file mode 100644
index 0000000..47e82bc
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 76
+/svn/!svn/ver/242/trunk/isoparser/src/main/java/com/googlecode/mp4parser/srt
+END
+SrtParser.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/242/trunk/isoparser/src/main/java/com/googlecode/mp4parser/srt/SrtParser.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/entries
new file mode 100644
index 0000000..9f98b72
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/srt
+http://mp4parser.googlecode.com/svn
+
+
+
+2011-10-15T21:10:20.063570Z
+242
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+SrtParser.java
+file
+
+
+
+
+2012-09-14T17:27:51.527233Z
+fc7724dd7559c66f7843417fcdea2d12
+2011-10-15T21:10:20.063570Z
+242
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2127
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/text-base/SrtParser.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/text-base/SrtParser.java.svn-base
new file mode 100644
index 0000000..3e3a3b9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/srt/.svn/text-base/SrtParser.java.svn-base
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2011 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.srt;
+
+import com.googlecode.mp4parser.authoring.tracks.TextTrackImpl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+
+/**
+ * Parses a .srt file and creates a Track for it.
+ */
+public class SrtParser {
+ public static TextTrackImpl parse(InputStream is) throws IOException {
+ LineNumberReader r = new LineNumberReader(new InputStreamReader(is, "UTF-8"));
+ TextTrackImpl track = new TextTrackImpl();
+ String numberString;
+ while ((numberString = r.readLine()) != null) {
+ String timeString = r.readLine();
+ String lineString = "";
+ String s;
+ while (!((s = r.readLine()) == null || s.trim().equals(""))) {
+ lineString += s + "\n";
+ }
+
+ long startTime = parse(timeString.split("-->")[0]);
+ long endTime = parse(timeString.split("-->")[1]);
+
+ track.getSubs().add(new TextTrackImpl.Line(startTime, endTime, lineString));
+
+ }
+ return track;
+ }
+
+ private static long parse(String in) {
+ long hours = Long.parseLong(in.split(":")[0].trim());
+ long minutes = Long.parseLong(in.split(":")[1].trim());
+ long seconds = Long.parseLong(in.split(":")[2].split(",")[0].trim());
+ long millies = Long.parseLong(in.split(":")[2].split(",")[1].trim());
+
+ return hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000 + millies;
+
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/srt/SrtParser.java b/isoparser/src/main/java/com/googlecode/mp4parser/srt/SrtParser.java
new file mode 100644
index 0000000..3e3a3b9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/srt/SrtParser.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2011 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.srt;
+
+import com.googlecode.mp4parser.authoring.tracks.TextTrackImpl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+
+/**
+ * Parses a .srt file and creates a Track for it.
+ */
+public class SrtParser {
+ public static TextTrackImpl parse(InputStream is) throws IOException {
+ LineNumberReader r = new LineNumberReader(new InputStreamReader(is, "UTF-8"));
+ TextTrackImpl track = new TextTrackImpl();
+ String numberString;
+ while ((numberString = r.readLine()) != null) {
+ String timeString = r.readLine();
+ String lineString = "";
+ String s;
+ while (!((s = r.readLine()) == null || s.trim().equals(""))) {
+ lineString += s + "\n";
+ }
+
+ long startTime = parse(timeString.split("-->")[0]);
+ long endTime = parse(timeString.split("-->")[1]);
+
+ track.getSubs().add(new TextTrackImpl.Line(startTime, endTime, lineString));
+
+ }
+ return track;
+ }
+
+ private static long parse(String in) {
+ long hours = Long.parseLong(in.split(":")[0].trim());
+ long minutes = Long.parseLong(in.split(":")[1].trim());
+ long seconds = Long.parseLong(in.split(":")[2].split(",")[0].trim());
+ long millies = Long.parseLong(in.split(":")[2].split(",")[1].trim());
+
+ return hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000 + millies;
+
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/all-wcprops
new file mode 100644
index 0000000..6decd2f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/647/trunk/isoparser/src/main/java/com/googlecode/mp4parser/util
+END
+Math.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/591/trunk/isoparser/src/main/java/com/googlecode/mp4parser/util/Math.java
+END
+ByteBufferByteChannel.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/505/trunk/isoparser/src/main/java/com/googlecode/mp4parser/util/ByteBufferByteChannel.java
+END
+CastUtils.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/505/trunk/isoparser/src/main/java/com/googlecode/mp4parser/util/CastUtils.java
+END
+Path.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/647/trunk/isoparser/src/main/java/com/googlecode/mp4parser/util/Path.java
+END
+UUIDConverter.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/418/trunk/isoparser/src/main/java/com/googlecode/mp4parser/util/UUIDConverter.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/entries
new file mode 100644
index 0000000..df7f2b0
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/entries
@@ -0,0 +1,198 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/util
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-05-27T05:36:06.980381Z
+647
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+Math.java
+file
+
+
+
+
+2012-09-14T17:27:51.667235Z
+be07b5a338a5ad8f6491ffec0f4d5e11
+2012-05-07T16:21:15.288279Z
+591
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+619
+
+ByteBufferByteChannel.java
+file
+
+
+
+
+2012-09-14T17:27:51.667235Z
+8e8bdec08e05ef5d4dbeb46b7e1d35c2
+2012-04-21T21:18:31.685061Z
+505
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1654
+
+CastUtils.java
+file
+
+
+
+
+2012-09-14T17:27:51.667235Z
+8cda078221f55ca06cf0cdc49e2cbbba
+2012-04-21T21:18:31.685061Z
+505
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1326
+
+Path.java
+file
+
+
+
+
+2012-09-14T17:27:51.667235Z
+f0ae3de018d1fb2645b0eb2beefeeb01
+2012-05-27T05:36:06.980381Z
+647
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3884
+
+UUIDConverter.java
+file
+
+
+
+
+2012-09-14T17:27:51.667235Z
+d9d8f271318ba9ee779efd10904d7ad3
+2012-03-11T20:54:45.638478Z
+418
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1413
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/ByteBufferByteChannel.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/ByteBufferByteChannel.java.svn-base
new file mode 100644
index 0000000..9f3264b
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/ByteBufferByteChannel.java.svn-base
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.util;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+
+/**
+ * Creates a <code>ReadableByteChannel</code> that is backed by a <code>ByteBuffer</code>.
+ */
+public class ByteBufferByteChannel implements ByteChannel {
+ ByteBuffer byteBuffer;
+
+ public ByteBufferByteChannel(ByteBuffer byteBuffer) {
+ this.byteBuffer = byteBuffer;
+ }
+
+ public int read(ByteBuffer dst) throws IOException {
+ byte[] b = dst.array();
+ int r = dst.remaining();
+ if (byteBuffer.remaining() >= r) {
+ byteBuffer.get(b, dst.position(), r);
+ return r;
+ } else {
+ throw new EOFException("Reading beyond end of stream");
+ }
+ }
+
+ public boolean isOpen() {
+ return true;
+ }
+
+ public void close() throws IOException {
+ }
+
+ public int write(ByteBuffer src) throws IOException {
+ int r = src.remaining();
+ byteBuffer.put(src);
+ return r;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/CastUtils.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/CastUtils.java.svn-base
new file mode 100644
index 0000000..2dd011a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/CastUtils.java.svn-base
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.util;
+
+
+public class CastUtils {
+ /**
+ * Casts a long to an int. In many cases I use a long for a UInt32 but this cannot be used to allocate
+ * ByteBuffers or arrays since they restricted to <code>Integer.MAX_VALUE</code> this cast-method will throw
+ * a RuntimeException if the cast would cause a loss of information.
+ *
+ * @param l the long value
+ * @return the long value as int
+ */
+ public static int l2i(long l) {
+ if (l > Integer.MAX_VALUE || l < Integer.MIN_VALUE) {
+ throw new RuntimeException("A cast to int has gone wrong. Please contact the mp4parser discussion group (" + l + ")");
+ }
+ return (int) l;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/Math.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/Math.java.svn-base
new file mode 100644
index 0000000..27fd4b2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/Math.java.svn-base
@@ -0,0 +1,30 @@
+package com.googlecode.mp4parser.util;
+
+public class Math {
+ public static long gcd(long a, long b) {
+ while (b > 0) {
+ long temp = b;
+ b = a % b; // % is remainder
+ a = temp;
+ }
+ return a;
+ }
+
+ public static int gcd(int a, int b) {
+ while (b > 0) {
+ int temp = b;
+ b = a % b; // % is remainder
+ a = temp;
+ }
+ return a;
+ }
+
+ public static long lcm(long a, long b) {
+ return a * (b / gcd(a, b));
+ }
+
+ public static int lcm(int a, int b) {
+ return a * (b / gcd(a, b));
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/Path.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/Path.java.svn-base
new file mode 100644
index 0000000..a6066c8
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/Path.java.svn-base
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.util;
+
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.ContainerBox;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Path {
+
+ private Path() {
+ }
+
+ static Pattern component = Pattern.compile("(....|\\.\\.)(\\[(.*)\\])?");
+
+ public static String createPath(Box box) {
+ return createPath(box, "");
+ }
+
+ private static String createPath(Box box, String path) {
+ if (box instanceof IsoFile) {
+ return path;
+ } else {
+ List<?> boxesOfBoxType = box.getParent().getBoxes(box.getClass());
+ int index = boxesOfBoxType.indexOf(box);
+ path = String.format("/%s[%d]", box.getType(), index) + path;
+
+ return createPath(box.getParent(), path);
+ }
+ }
+
+ public static Box getPath(Box box, String path) {
+ List<Box> all = getPaths(box, path);
+ return all.isEmpty() ? null : all.get(0);
+ }
+
+
+ public static List<Box> getPaths(Box box, String path) {
+ if (path.startsWith("/")) {
+ Box isoFile = box;
+ while (isoFile.getParent() != null) {
+ isoFile = isoFile.getParent();
+ }
+ assert isoFile instanceof IsoFile : isoFile.getType() + " has no parent";
+ return getPaths(isoFile, path.substring(1));
+ } else if (path.isEmpty()) {
+ return Collections.singletonList(box);
+ } else {
+ String later;
+ String now;
+ if (path.contains("/")) {
+ later = path.substring(path.indexOf('/') + 1);
+ now = path.substring(0, path.indexOf('/'));
+ } else {
+ now = path;
+ later = "";
+ }
+
+ Matcher m = component.matcher(now);
+ if (m.matches()) {
+ String type = m.group(1);
+ if ("..".equals(type)) {
+ return getPaths(box.getParent(), later);
+ } else {
+ int index = -1;
+ if (m.group(2) != null) {
+ // we have a specific index
+ String indexString = m.group(3);
+ index = Integer.parseInt(indexString);
+ }
+ List<Box> children = new LinkedList<Box>();
+ int currentIndex = 0;
+ for (Box box1 : ((ContainerBox) box).getBoxes()) {
+ if (box1.getType().matches(type)) {
+ if (index == -1 || index == currentIndex) {
+ children.addAll(getPaths(box1, later));
+ }
+ currentIndex++;
+ }
+ }
+ return children;
+ }
+ } else {
+ throw new RuntimeException(now + " is invalid path.");
+ }
+ }
+
+ }
+
+
+ public static boolean isContained(Box box, String path) {
+ assert path.startsWith("/") : "Absolute path required";
+ return getPaths(box, path).contains(box);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/UUIDConverter.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/UUIDConverter.java.svn-base
new file mode 100644
index 0000000..635e4c1
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/.svn/text-base/UUIDConverter.java.svn-base
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.util;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.UUID;
+
+/**
+ * UUID from/to byte array.
+ */
+public class UUIDConverter {
+ public static byte[] convert(UUID uuid) {
+
+ long msb = uuid.getMostSignificantBits();
+ long lsb = uuid.getLeastSignificantBits();
+ byte[] buffer = new byte[16];
+
+ for (int i = 0; i < 8; i++) {
+ buffer[i] = (byte) (msb >>> 8 * (7 - i));
+ }
+ for (int i = 8; i < 16; i++) {
+ buffer[i] = (byte) (lsb >>> 8 * (7 - i));
+ }
+
+ return buffer;
+
+ }
+
+ public static UUID convert(byte[] uuidBytes) {
+ ByteBuffer b = ByteBuffer.wrap(uuidBytes);
+ b.order(ByteOrder.BIG_ENDIAN);
+ return new UUID(b.getLong(), b.getLong());
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/ByteBufferByteChannel.java b/isoparser/src/main/java/com/googlecode/mp4parser/util/ByteBufferByteChannel.java
new file mode 100644
index 0000000..9f3264b
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/ByteBufferByteChannel.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.util;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+
+/**
+ * Creates a <code>ReadableByteChannel</code> that is backed by a <code>ByteBuffer</code>.
+ */
+public class ByteBufferByteChannel implements ByteChannel {
+ ByteBuffer byteBuffer;
+
+ public ByteBufferByteChannel(ByteBuffer byteBuffer) {
+ this.byteBuffer = byteBuffer;
+ }
+
+ public int read(ByteBuffer dst) throws IOException {
+ byte[] b = dst.array();
+ int r = dst.remaining();
+ if (byteBuffer.remaining() >= r) {
+ byteBuffer.get(b, dst.position(), r);
+ return r;
+ } else {
+ throw new EOFException("Reading beyond end of stream");
+ }
+ }
+
+ public boolean isOpen() {
+ return true;
+ }
+
+ public void close() throws IOException {
+ }
+
+ public int write(ByteBuffer src) throws IOException {
+ int r = src.remaining();
+ byteBuffer.put(src);
+ return r;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/CastUtils.java b/isoparser/src/main/java/com/googlecode/mp4parser/util/CastUtils.java
new file mode 100644
index 0000000..2dd011a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/CastUtils.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.util;
+
+
+public class CastUtils {
+ /**
+ * Casts a long to an int. In many cases I use a long for a UInt32 but this cannot be used to allocate
+ * ByteBuffers or arrays since they restricted to <code>Integer.MAX_VALUE</code> this cast-method will throw
+ * a RuntimeException if the cast would cause a loss of information.
+ *
+ * @param l the long value
+ * @return the long value as int
+ */
+ public static int l2i(long l) {
+ if (l > Integer.MAX_VALUE || l < Integer.MIN_VALUE) {
+ throw new RuntimeException("A cast to int has gone wrong. Please contact the mp4parser discussion group (" + l + ")");
+ }
+ return (int) l;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/Math.java b/isoparser/src/main/java/com/googlecode/mp4parser/util/Math.java
new file mode 100644
index 0000000..27fd4b2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/Math.java
@@ -0,0 +1,30 @@
+package com.googlecode.mp4parser.util;
+
+public class Math {
+ public static long gcd(long a, long b) {
+ while (b > 0) {
+ long temp = b;
+ b = a % b; // % is remainder
+ a = temp;
+ }
+ return a;
+ }
+
+ public static int gcd(int a, int b) {
+ while (b > 0) {
+ int temp = b;
+ b = a % b; // % is remainder
+ a = temp;
+ }
+ return a;
+ }
+
+ public static long lcm(long a, long b) {
+ return a * (b / gcd(a, b));
+ }
+
+ public static int lcm(int a, int b) {
+ return a * (b / gcd(a, b));
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/Path.java b/isoparser/src/main/java/com/googlecode/mp4parser/util/Path.java
new file mode 100644
index 0000000..a6066c8
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/Path.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.util;
+
+
+import com.coremedia.iso.IsoFile;
+import com.coremedia.iso.boxes.Box;
+import com.coremedia.iso.boxes.ContainerBox;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Path {
+
+ private Path() {
+ }
+
+ static Pattern component = Pattern.compile("(....|\\.\\.)(\\[(.*)\\])?");
+
+ public static String createPath(Box box) {
+ return createPath(box, "");
+ }
+
+ private static String createPath(Box box, String path) {
+ if (box instanceof IsoFile) {
+ return path;
+ } else {
+ List<?> boxesOfBoxType = box.getParent().getBoxes(box.getClass());
+ int index = boxesOfBoxType.indexOf(box);
+ path = String.format("/%s[%d]", box.getType(), index) + path;
+
+ return createPath(box.getParent(), path);
+ }
+ }
+
+ public static Box getPath(Box box, String path) {
+ List<Box> all = getPaths(box, path);
+ return all.isEmpty() ? null : all.get(0);
+ }
+
+
+ public static List<Box> getPaths(Box box, String path) {
+ if (path.startsWith("/")) {
+ Box isoFile = box;
+ while (isoFile.getParent() != null) {
+ isoFile = isoFile.getParent();
+ }
+ assert isoFile instanceof IsoFile : isoFile.getType() + " has no parent";
+ return getPaths(isoFile, path.substring(1));
+ } else if (path.isEmpty()) {
+ return Collections.singletonList(box);
+ } else {
+ String later;
+ String now;
+ if (path.contains("/")) {
+ later = path.substring(path.indexOf('/') + 1);
+ now = path.substring(0, path.indexOf('/'));
+ } else {
+ now = path;
+ later = "";
+ }
+
+ Matcher m = component.matcher(now);
+ if (m.matches()) {
+ String type = m.group(1);
+ if ("..".equals(type)) {
+ return getPaths(box.getParent(), later);
+ } else {
+ int index = -1;
+ if (m.group(2) != null) {
+ // we have a specific index
+ String indexString = m.group(3);
+ index = Integer.parseInt(indexString);
+ }
+ List<Box> children = new LinkedList<Box>();
+ int currentIndex = 0;
+ for (Box box1 : ((ContainerBox) box).getBoxes()) {
+ if (box1.getType().matches(type)) {
+ if (index == -1 || index == currentIndex) {
+ children.addAll(getPaths(box1, later));
+ }
+ currentIndex++;
+ }
+ }
+ return children;
+ }
+ } else {
+ throw new RuntimeException(now + " is invalid path.");
+ }
+ }
+
+ }
+
+
+ public static boolean isContained(Box box, String path) {
+ assert path.startsWith("/") : "Absolute path required";
+ return getPaths(box, path).contains(box);
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/util/UUIDConverter.java b/isoparser/src/main/java/com/googlecode/mp4parser/util/UUIDConverter.java
new file mode 100644
index 0000000..635e4c1
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/util/UUIDConverter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.googlecode.mp4parser.util;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.UUID;
+
+/**
+ * UUID from/to byte array.
+ */
+public class UUIDConverter {
+ public static byte[] convert(UUID uuid) {
+
+ long msb = uuid.getMostSignificantBits();
+ long lsb = uuid.getLeastSignificantBits();
+ byte[] buffer = new byte[16];
+
+ for (int i = 0; i < 8; i++) {
+ buffer[i] = (byte) (msb >>> 8 * (7 - i));
+ }
+ for (int i = 8; i < 16; i++) {
+ buffer[i] = (byte) (lsb >>> 8 * (7 - i));
+ }
+
+ return buffer;
+
+ }
+
+ public static UUID convert(byte[] uuidBytes) {
+ ByteBuffer b = ByteBuffer.wrap(uuidBytes);
+ b.order(ByteOrder.BIG_ENDIAN);
+ return new UUID(b.getLong(), b.getLong());
+ }
+}