bmff 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +183 -0
- data/Rakefile +9 -0
- data/bmff.gemspec +25 -0
- data/lib/bmff/binary_accessor.rb +186 -0
- data/lib/bmff/box/audio_sample_entry.rb +16 -0
- data/lib/bmff/box/base.rb +66 -0
- data/lib/bmff/box/bit_rate.rb +14 -0
- data/lib/bmff/box/chunk_large_offset.rb +16 -0
- data/lib/bmff/box/chunk_offset.rb +16 -0
- data/lib/bmff/box/clean_aperture.rb +20 -0
- data/lib/bmff/box/colour_information.rb +27 -0
- data/lib/bmff/box/compact_sample_size.rb +32 -0
- data/lib/bmff/box/composition_offset.rb +25 -0
- data/lib/bmff/box/composition_to_decode.rb +17 -0
- data/lib/bmff/box/container.rb +70 -0
- data/lib/bmff/box/copyright.rb +13 -0
- data/lib/bmff/box/data_entry_url.rb +12 -0
- data/lib/bmff/box/data_entry_urn.rb +13 -0
- data/lib/bmff/box/data_information.rb +12 -0
- data/lib/bmff/box/data_reference.rb +14 -0
- data/lib/bmff/box/degradation_priority.rb +19 -0
- data/lib/bmff/box/edit.rb +12 -0
- data/lib/bmff/box/edit_list.rb +27 -0
- data/lib/bmff/box/file_type.rb +17 -0
- data/lib/bmff/box/free_space.rb +6 -0
- data/lib/bmff/box/full.rb +11 -0
- data/lib/bmff/box/handler.rb +15 -0
- data/lib/bmff/box/hint_media_header.rb +16 -0
- data/lib/bmff/box/hint_sample_entry.rb +14 -0
- data/lib/bmff/box/level_assignment.rb +35 -0
- data/lib/bmff/box/map.rb +15 -0
- data/lib/bmff/box/media.rb +12 -0
- data/lib/bmff/box/media_data.rb +6 -0
- data/lib/bmff/box/media_header.rb +25 -0
- data/lib/bmff/box/media_information.rb +12 -0
- data/lib/bmff/box/meta_data_sample_entry.rb +5 -0
- data/lib/bmff/box/movie.rb +12 -0
- data/lib/bmff/box/movie_extends.rb +12 -0
- data/lib/bmff/box/movie_extends_header.rb +16 -0
- data/lib/bmff/box/movie_fragment.rb +12 -0
- data/lib/bmff/box/movie_fragment_header.rb +12 -0
- data/lib/bmff/box/movie_fragment_random_access.rb +12 -0
- data/lib/bmff/box/movie_fragment_random_access_offset.rb +12 -0
- data/lib/bmff/box/movie_header.rb +36 -0
- data/lib/bmff/box/null_media_header.rb +6 -0
- data/lib/bmff/box/padding_bits.rb +23 -0
- data/lib/bmff/box/pixel_aspect_ratio.rb +13 -0
- data/lib/bmff/box/progressive_download_info.rb +17 -0
- data/lib/bmff/box/sample_auxiliary_information_offsets.rb +24 -0
- data/lib/bmff/box/sample_auxiliary_information_sizes.rb +23 -0
- data/lib/bmff/box/sample_dependency_type.rb +26 -0
- data/lib/bmff/box/sample_description.rb +27 -0
- data/lib/bmff/box/sample_entry.rb +15 -0
- data/lib/bmff/box/sample_size.rb +19 -0
- data/lib/bmff/box/sample_table.rb +12 -0
- data/lib/bmff/box/sample_to_chunk.rb +20 -0
- data/lib/bmff/box/shadow_sync_sample.rb +18 -0
- data/lib/bmff/box/sound_media_header.rb +13 -0
- data/lib/bmff/box/sub_sample_information.rb +41 -0
- data/lib/bmff/box/sync_sample.rb +16 -0
- data/lib/bmff/box/text_meta_data_sample_entry.rb +18 -0
- data/lib/bmff/box/time_to_sample.rb +18 -0
- data/lib/bmff/box/track.rb +12 -0
- data/lib/bmff/box/track_extends.rb +17 -0
- data/lib/bmff/box/track_fragment.rb +12 -0
- data/lib/bmff/box/track_fragment_base_media_decode_time.rb +16 -0
- data/lib/bmff/box/track_fragment_header.rb +18 -0
- data/lib/bmff/box/track_fragment_random_access.rb +54 -0
- data/lib/bmff/box/track_group.rb +12 -0
- data/lib/bmff/box/track_group_type.rb +12 -0
- data/lib/bmff/box/track_header.rb +36 -0
- data/lib/bmff/box/track_reference.rb +12 -0
- data/lib/bmff/box/track_reference_type.rb +15 -0
- data/lib/bmff/box/track_run.rb +31 -0
- data/lib/bmff/box/track_selection.rb +16 -0
- data/lib/bmff/box/unknown.rb +5 -0
- data/lib/bmff/box/uri.rb +12 -0
- data/lib/bmff/box/uri_init.rb +15 -0
- data/lib/bmff/box/uri_meta_sample_entry.rb +24 -0
- data/lib/bmff/box/user_data.rb +12 -0
- data/lib/bmff/box/video_media_header.rb +13 -0
- data/lib/bmff/box/visual_sample_entry.rb +37 -0
- data/lib/bmff/box/xml_meta_data_sample_entry.rb +19 -0
- data/lib/bmff/box.rb +115 -0
- data/lib/bmff/file_container.rb +20 -0
- data/lib/bmff/version.rb +3 -0
- data/lib/bmff.rb +7 -0
- data/test/bmff/box/test_audio_sample_entry.rb +46 -0
- data/test/bmff/box/test_base.rb +37 -0
- data/test/bmff/box/test_bit_rate.rb +30 -0
- data/test/bmff/box/test_chunk_large_offset.rb +35 -0
- data/test/bmff/box/test_chunk_offset.rb +35 -0
- data/test/bmff/box/test_clean_aperture.rb +40 -0
- data/test/bmff/box/test_colour_information.rb +73 -0
- data/test/bmff/box/test_compact_sample_size.rb +99 -0
- data/test/bmff/box/test_composition_offset.rb +64 -0
- data/test/bmff/box/test_composition_to_decode.rb +38 -0
- data/test/bmff/box/test_copyright.rb +55 -0
- data/test/bmff/box/test_data_entry_url.rb +30 -0
- data/test/bmff/box/test_data_entry_urn.rb +32 -0
- data/test/bmff/box/test_data_information.rb +26 -0
- data/test/bmff/box/test_data_reference.rb +58 -0
- data/test/bmff/box/test_degradation_priority.rb +62 -0
- data/test/bmff/box/test_edit.rb +26 -0
- data/test/bmff/box/test_edit_list.rb +72 -0
- data/test/bmff/box/test_file_type.rb +30 -0
- data/test/bmff/box/test_free_space.rb +42 -0
- data/test/bmff/box/test_handler.rb +67 -0
- data/test/bmff/box/test_hint_media_header.rb +38 -0
- data/test/bmff/box/test_hint_sample_entry.rb +38 -0
- data/test/bmff/box/test_level_assignment.rb +50 -0
- data/test/bmff/box/test_media.rb +26 -0
- data/test/bmff/box/test_media_data.rb +25 -0
- data/test/bmff/box/test_media_header.rb +72 -0
- data/test/bmff/box/test_media_information.rb +26 -0
- data/test/bmff/box/test_movie.rb +26 -0
- data/test/bmff/box/test_movie_extends.rb +26 -0
- data/test/bmff/box/test_movie_extends_header.rb +52 -0
- data/test/bmff/box/test_movie_fragment.rb +26 -0
- data/test/bmff/box/test_movie_fragment_header.rb +30 -0
- data/test/bmff/box/test_movie_fragment_random_access.rb +26 -0
- data/test/bmff/box/test_movie_fragment_random_access_offset.rb +30 -0
- data/test/bmff/box/test_movie_header.rb +110 -0
- data/test/bmff/box/test_null_media_header.rb +28 -0
- data/test/bmff/box/test_padding_bits.rb +38 -0
- data/test/bmff/box/test_pixel_aspect_ratio.rb +28 -0
- data/test/bmff/box/test_progressive_download_info.rb +36 -0
- data/test/bmff/box/test_sample_auxiliary_information_offsets.rb +94 -0
- data/test/bmff/box/test_sample_auxiliary_information_sizes.rb +70 -0
- data/test/bmff/box/test_sample_dependency_type.rb +70 -0
- data/test/bmff/box/test_sample_description.rb +355 -0
- data/test/bmff/box/test_sample_size.rb +61 -0
- data/test/bmff/box/test_sample_table.rb +26 -0
- data/test/bmff/box/test_sample_to_chunk.rb +39 -0
- data/test/bmff/box/test_shadow_sync_sample.rb +36 -0
- data/test/bmff/box/test_sound_media_header.rb +32 -0
- data/test/bmff/box/test_sub_sample_information.rb +90 -0
- data/test/bmff/box/test_sync_sample.rb +34 -0
- data/test/bmff/box/test_text_meta_data_sample_entry.rb +49 -0
- data/test/bmff/box/test_time_to_sample.rb +36 -0
- data/test/bmff/box/test_track.rb +26 -0
- data/test/bmff/box/test_track_extends.rb +38 -0
- data/test/bmff/box/test_track_fragment.rb +26 -0
- data/test/bmff/box/test_track_fragment_base_media_decode_time.rb +52 -0
- data/test/bmff/box/test_track_fragment_header.rb +200 -0
- data/test/bmff/box/test_track_fragment_random_access.rb +96 -0
- data/test/bmff/box/test_track_group.rb +26 -0
- data/test/bmff/box/test_track_group_type.rb +30 -0
- data/test/bmff/box/test_track_header.rb +118 -0
- data/test/bmff/box/test_track_reference.rb +26 -0
- data/test/bmff/box/test_track_reference_type.rb +100 -0
- data/test/bmff/box/test_track_run.rb +282 -0
- data/test/bmff/box/test_track_selection.rb +33 -0
- data/test/bmff/box/test_uri.rb +30 -0
- data/test/bmff/box/test_uri_init.rb +32 -0
- data/test/bmff/box/test_uri_meta_sample_entry.rb +74 -0
- data/test/bmff/box/test_user_data.rb +26 -0
- data/test/bmff/box/test_video_media_header.rb +34 -0
- data/test/bmff/box/test_visual_sample_entry.rb +95 -0
- data/test/bmff/box/test_xml_meta_data_sample_entry.rb +51 -0
- data/test/bmff/test_binary_accessor.rb +805 -0
- data/test/bmff/test_file_container.rb +58 -0
- data/test/minitest_helper.rb +7 -0
- metadata +323 -0
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
.DS_Store
|
19
|
+
.DS_Store?
|
20
|
+
._*
|
21
|
+
.Spotlight-V100
|
22
|
+
.Trashes
|
23
|
+
Icon?
|
24
|
+
ehthumbs.db
|
25
|
+
Thumbs.db
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Takayuki Ogiso
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
# bmff
|
2
|
+
|
3
|
+
This gem library is an ISO Base Media File Format (BMFF) parser.
|
4
|
+
You can parse BMFF file.
|
5
|
+
And you may be able to parse a file related to BMFF like MP4.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'bmff'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install bmff
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Dump Box Structure
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require "bmff"
|
27
|
+
|
28
|
+
def print_box(box, level = 0)
|
29
|
+
puts "#{' ' * level}#{box.type} #{box.class}"
|
30
|
+
if box.container?
|
31
|
+
box.children.each do |child|
|
32
|
+
print_box(child, level + 1)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
open("/path/to/video.mp4", "rb:ascii-8bit") do |f|
|
38
|
+
file_container = BMFF::FileContainer.parse(f)
|
39
|
+
file_container.boxes.each do |box|
|
40
|
+
print_box(box)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
### Get Video Duration from Media Header Box
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
require "bmff"
|
49
|
+
|
50
|
+
open("/path/to/video.mp4", "rb:ascii-8bit") do |f|
|
51
|
+
file_container = BMFF::FileContainer.parse(f)
|
52
|
+
media_header = file_container.select_descendants(BMFF::Box::MediaHeader).first
|
53
|
+
if media_header
|
54
|
+
puts media_header.duration / media_header.timescale.to_f
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
### Get Each Fragment Duration
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
require "bmff"
|
63
|
+
|
64
|
+
open("/path/to/video.ismv", "rb:ascii-8bit") do |f|
|
65
|
+
file_container = BMFF::FileContainer.parse(f)
|
66
|
+
file_container.select_descendants(BMFF::Box::TrackRun).each do |track_run|
|
67
|
+
puts track_run.sample_duration.inject {|result, item| result + item}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
## Progress
|
73
|
+
|
74
|
+
### ISO/IEC 14496-12:2012
|
75
|
+
|
76
|
+
|Box Name | Type | Status |
|
77
|
+
|:--------------------------------------------|:--------------:|:---------:|
|
78
|
+
|File Type Box | ftyp |OK | \ 4.3
|
79
|
+
|Media Data Box | mdat |OK | \ 8.1.1
|
80
|
+
|Free Space Box | free, skip |OK | \ 8.1.2
|
81
|
+
|Progressive Download Information Box | pdin |OK | \ 8.1.3
|
82
|
+
|Movie Box | moov |OK | \ 8.2.1
|
83
|
+
|Movie Header Box | mvhd |OK | \ 8.2.2
|
84
|
+
|Track Box | trak |OK | \ 8.3.1
|
85
|
+
|Track Header Box | tkhd |OK | \ 8.3.2
|
86
|
+
|Track Reference Box | tref |OK | \ 8.3.3
|
87
|
+
|Track Group Box | trgr |OK | \ 8.3.4
|
88
|
+
|Media Box | mdia |OK | \ 8.4.1
|
89
|
+
|Media Header Box | mdhd |OK | \ 8.4.2
|
90
|
+
|Handler Reference Box | hdlr |OK | \ 8.4.3
|
91
|
+
|Media Information Box | minf |OK | \ 8.4.4
|
92
|
+
|Video Media Header Box | vmhd |OK | \ 8.4.5.2
|
93
|
+
|Sound Media Header Box | smhd |OK | \ 8.4.5.3
|
94
|
+
|Hint Media Header Box | hmhd |OK | \ 8.4.5.4
|
95
|
+
|Null Media Header Box | nmhd |OK | \ 8.4.5.5
|
96
|
+
|Sample Table Box | stbl |OK | \ 8.5.1
|
97
|
+
|Sample Description Box | stsd |OK | \ 8.5.2
|
98
|
+
|Degradation Priority Box | stdp |OK | \ 8.5.3
|
99
|
+
|Decoding Time to Sample Box | stts |OK | \ 8.6.1.2
|
100
|
+
|Composition Time to Sample Box | ctts |OK | \ 8.6.1.3
|
101
|
+
|Composition to Decode Box | cslg |OK | \ 8.6.1.4
|
102
|
+
|Sync Sample Box | stss |OK | \ 8.6.2
|
103
|
+
|Shadow Sync Sample Box | stsh |OK | \ 8.6.3
|
104
|
+
|Independent and Disposable Samples Box | sdtp |OK | \ 8.6.4
|
105
|
+
|Edit Box | edts |OK | \ 8.6.5
|
106
|
+
|Edit List Box | elst |OK | \ 8.6.6
|
107
|
+
|Data Information Box | dinf |OK | \ 8.7.1
|
108
|
+
|Data Reference Box |url , urn , dref|OK | \ 8.7.2
|
109
|
+
|Sample Size Box | stsz |OK | \ 8.7.3.2
|
110
|
+
|Compact Sample Size Box | stz2 |OK | \ 8.7.3.3
|
111
|
+
|Sample to Chunk Box | stsc |OK | \ 8.7.4
|
112
|
+
|Chunk Offset Box | stco, co64 |OK | \ 8.7.5
|
113
|
+
|Padding Bits Box | padb |OK | \ 8.7.6
|
114
|
+
|Sub-Sample Information Box | subs |OK | \ 8.7.7
|
115
|
+
|Sample Auxiliary Information Sizes Box | saiz |OK | \ 8.7.8
|
116
|
+
|Sample Auxiliary Information Offsets Box | saio |OK | \ 8.7.9
|
117
|
+
|Movie Extends Box | mvex |OK | \ 8.8.1
|
118
|
+
|Movie Extends Header Box | mehd |OK | \ 8.8.2
|
119
|
+
|Track Extends Box | trex |OK | \ 8.8.3
|
120
|
+
|Movie Fragment Box | moof |OK | \ 8.8.4
|
121
|
+
|Movie Fragment Header Box | mfhd |OK | \ 8.8.5
|
122
|
+
|Track Fragment Box | traf |OK | \ 8.8.6
|
123
|
+
|Track Fragment Header Box | tfhd |OK | \ 8.8.7
|
124
|
+
|Track Fragment Run Box | trun |OK | \ 8.8.8
|
125
|
+
|Movie Fragment Random Access Box | mfra |OK | \ 8.8.9
|
126
|
+
|Track Fragment Random Access Box | tfra |OK | \ 8.8.10
|
127
|
+
|Movie Fragment Random Access Offset Box | mfro |OK | \ 8.8.11
|
128
|
+
|Track fragment decode time | tfdt |OK | \ 8.8.12
|
129
|
+
|Level Assignment Box | leva |OK | \ 8.8.13
|
130
|
+
|Sample to Group Box | sbgp |Not yet | \ 8.9.2
|
131
|
+
|Sample Group Description Box | sgpd |Not yet | \ 8.9.3
|
132
|
+
|User Data Box | udta |OK | \ 8.10.1
|
133
|
+
|Copyright Box | cprt |OK | \ 8.10.2
|
134
|
+
|Track Selection Box | tsel |OK | \ 8.10.3
|
135
|
+
|The Meta Box | meta |Not yet | \ 8.11.1
|
136
|
+
|XML Box | xml |Not yet | \ 8.11.2
|
137
|
+
|Binary XML Box | bxml |Not yet | \ 8.11.2
|
138
|
+
|The Item Location Box | iloc |Not yet | \ 8.11.3
|
139
|
+
|Primary Item Box | pitm |Not yet | \ 8.11.4
|
140
|
+
|Item Protection Box | ipro |Not yet | \ 8.11.5
|
141
|
+
|Item Information Box | iinf |Not yet | \ 8.11.6
|
142
|
+
|Additional Metadata Container Box | meco |Not yet | \ 8.11.7
|
143
|
+
|Metabox Relation Box | mere |Not yet | \ 8.11.8
|
144
|
+
|Item Data Box | idat |Not yet | \ 8.11.11
|
145
|
+
|Item Reference Box | iref |Not yet | \ 8.11.12
|
146
|
+
|Protection Scheme Information Box | sinf |Not yet | \ 8.12.1
|
147
|
+
|Original Format Box | frma |Not yet | \ 8.12.2
|
148
|
+
|Scheme Type Box | schm |Not yet | \ 8.12.5
|
149
|
+
|Scheme Information Box | schi |Not yet | \ 8.12.6
|
150
|
+
|FD Item Information Box | fiin |Not yet | \ 8.13.2
|
151
|
+
|File Partition Box | fpar |Not yet | \ 8.13.3
|
152
|
+
|FEC Reservoir Box | fecr |Not yet | \ 8.13.4
|
153
|
+
|FD Session Group Box | segr |Not yet | \ 8.13.5
|
154
|
+
|Group ID to Name Box | gitn |Not yet | \ 8.13.6
|
155
|
+
|File Reservoir Box | fire |Not yet | \ 8.13.7
|
156
|
+
|Sub Track Box | strk |Not yet | \ 8.14.3
|
157
|
+
|Sub Track Information Box | stri |Not yet | \ 8.14.4
|
158
|
+
|Sub Track Definition Box | strd |Not yet | \ 8.14.5
|
159
|
+
|Sub Track Sample Group Box | stsg |Not yet | \ 8.14.6
|
160
|
+
|Restricted Scheme Information Box | rinf |Not yet | \ 8.15.3
|
161
|
+
|Stereo Video Box | stvi |Not yet | \ 8.15.4.2
|
162
|
+
|Segment Type Box | styp |Not yet | \ 8.16.2
|
163
|
+
|Segment Index Box | sidx |Not yet | \ 8.16.3
|
164
|
+
|Subsegment Index Box | ssix |Not yet | \ 8.16.4
|
165
|
+
|Producer Reference Time Box | prft |Not yet | \ 8.16.5
|
166
|
+
|
167
|
+
|
168
|
+
## Contributing
|
169
|
+
|
170
|
+
1. Fork it ( http://github.com/zuku/bmff/fork )
|
171
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
172
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
173
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
174
|
+
5. Create new Pull Request
|
175
|
+
|
176
|
+
## License
|
177
|
+
|
178
|
+
Copyright (c) 2014 Takayuki Ogiso.
|
179
|
+
|
180
|
+
This library (except audio-visual contents) is released under the MIT license.
|
181
|
+
See LICENSE.txt.
|
182
|
+
|
183
|
+
The audio-visual contents are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/.
|
data/Rakefile
ADDED
data/bmff.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'bmff/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "bmff"
|
8
|
+
spec.version = BMFF::VERSION
|
9
|
+
spec.authors = ["Takayuki Ogiso"]
|
10
|
+
spec.email = ["gomatofu@gmail.com"]
|
11
|
+
spec.summary = %q{ISO BMFF Parser}
|
12
|
+
spec.description = %q{ISO Base Media File Format file parser.}
|
13
|
+
spec.homepage = "https://github.com/zuku/bmff"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").delete_if{|x| %r|/assets/| =~ x }
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.required_ruby_version = '>= 1.9.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
module BMFF::BinaryAccessor
|
5
|
+
BYTE_ORDER = "\xFE\xFF".unpack("s").first == 0xFEFF ? :be : :le
|
6
|
+
|
7
|
+
# UTF-8, Shift_JIS or ASCII-8BIT (fallback)
|
8
|
+
STRING_ENCODINGS = %w(UTF-8 Shift_JIS ASCII-8BIT)
|
9
|
+
|
10
|
+
def get_int8
|
11
|
+
_read(1).unpack("c").first
|
12
|
+
end
|
13
|
+
|
14
|
+
def write_int8(num)
|
15
|
+
expected_int(num, -128, 127)
|
16
|
+
write([num].pack("c"))
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_uint8
|
20
|
+
_read(1).unpack("C").first
|
21
|
+
end
|
22
|
+
|
23
|
+
def write_uint8(num)
|
24
|
+
expected_int(num, 0, 255)
|
25
|
+
write([num].pack("C"))
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_int16
|
29
|
+
flip_byte_if_needed(_read(2)).unpack("s").first
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_int16(num)
|
33
|
+
expected_int(num, -32768, 32767)
|
34
|
+
write(flip_byte_if_needed([num].pack("s")))
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_uint16
|
38
|
+
_read(2).unpack("n").first
|
39
|
+
end
|
40
|
+
|
41
|
+
def write_uint16(num)
|
42
|
+
expected_int(num, 0, 65535)
|
43
|
+
write([num].pack("n"))
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_uint24
|
47
|
+
(get_uint8 << 16) | get_uint16
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_uint24(num)
|
51
|
+
expected_int(num, 0, 16777215)
|
52
|
+
write_uint8(num >> 16)
|
53
|
+
write_uint16(num & 0xFFFF)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_int32
|
57
|
+
flip_byte_if_needed(_read(4)).unpack("l").first
|
58
|
+
end
|
59
|
+
|
60
|
+
def write_int32(num)
|
61
|
+
expected_int(num, -2147483648, 2147483647)
|
62
|
+
write(flip_byte_if_needed([num].pack("l")))
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_uint32
|
66
|
+
_read(4).unpack("N").first
|
67
|
+
end
|
68
|
+
|
69
|
+
def write_uint32(num)
|
70
|
+
expected_int(num, 0, 4294967295)
|
71
|
+
write([num].pack("N"))
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_int64
|
75
|
+
b1 = flip_byte_if_needed(_read(4)).unpack("l").first
|
76
|
+
b2 = _read(4).unpack("N").first
|
77
|
+
(b1 << 32) | b2
|
78
|
+
end
|
79
|
+
|
80
|
+
def write_int64(num)
|
81
|
+
expected_int(num, -9223372036854775808, 9223372036854775807)
|
82
|
+
b1 = flip_byte_if_needed([num >> 32].pack("l"))
|
83
|
+
b2 = [num & 0xFFFFFFFF].pack("N")
|
84
|
+
write(b1 + b2)
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_uint64
|
88
|
+
b1, b2 = _read(8).unpack("N2")
|
89
|
+
(b1 << 32) | b2
|
90
|
+
end
|
91
|
+
|
92
|
+
def write_uint64(num)
|
93
|
+
expected_int(num, 0, 18446744073709551615)
|
94
|
+
write([num >> 32, num & 0xFFFFFFFF].pack("N2"))
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_ascii(size)
|
98
|
+
_read(size).unpack("a*").first
|
99
|
+
end
|
100
|
+
|
101
|
+
def write_ascii(ascii)
|
102
|
+
raise TypeError unless ascii.kind_of?(String)
|
103
|
+
write([ascii].pack("a*"))
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_byte(size = 1)
|
107
|
+
_read(size)
|
108
|
+
end
|
109
|
+
|
110
|
+
def write_byte(byte)
|
111
|
+
raise TypeError unless byte.kind_of?(String)
|
112
|
+
write(byte)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Null-terminated string
|
116
|
+
# An encoding of this string is maybe UTF-8.
|
117
|
+
# Other encodings are possible.
|
118
|
+
# (e.g. Apple Media Handler outputs non UTF-8 string)
|
119
|
+
def get_null_terminated_string(max_byte = nil)
|
120
|
+
buffer = ""
|
121
|
+
read_byte = 0
|
122
|
+
until eof?
|
123
|
+
b = read(1)
|
124
|
+
read_byte += 1
|
125
|
+
break if b == "\x00"
|
126
|
+
buffer << b
|
127
|
+
break if max_byte && read_byte >= max_byte
|
128
|
+
end
|
129
|
+
STRING_ENCODINGS.each do |encoding|
|
130
|
+
buffer.force_encoding(encoding)
|
131
|
+
break if buffer.valid_encoding?
|
132
|
+
end
|
133
|
+
buffer
|
134
|
+
end
|
135
|
+
|
136
|
+
def write_null_terminated_string(str)
|
137
|
+
raise TypeError unless str.kind_of?(String)
|
138
|
+
terminator = str.end_with?("\x00") ? "" : "\x00"
|
139
|
+
write(str + terminator)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Return ISO 639-2/T code
|
143
|
+
# Each character is compressed into 5-bit width.
|
144
|
+
# The bit 5 and 6 values are always 1. The bit 7 value is always 0.
|
145
|
+
def get_iso639_2_language
|
146
|
+
lang = get_uint16
|
147
|
+
c1 = (lang >> 10) & 0x1F | 0x60
|
148
|
+
c2 = (lang >> 5) & 0x1F | 0x60
|
149
|
+
c3 = lang & 0x1F | 0x60
|
150
|
+
sprintf("%c%c%c", c1, c2, c3)
|
151
|
+
end
|
152
|
+
|
153
|
+
def get_uuid
|
154
|
+
# TODO: create and return UUID type.
|
155
|
+
_read(16)
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
def flip_byte_if_needed(data)
|
160
|
+
if BYTE_ORDER == :le
|
161
|
+
return data.force_encoding("ascii-8bit").reverse
|
162
|
+
end
|
163
|
+
return data
|
164
|
+
end
|
165
|
+
|
166
|
+
def _read(size)
|
167
|
+
raise TypeError unless size.kind_of?(Integer)
|
168
|
+
raise RangeError if size <= 0
|
169
|
+
data = read(size)
|
170
|
+
raise EOFError unless data
|
171
|
+
raise EOFError unless data.bytesize == size
|
172
|
+
data
|
173
|
+
end
|
174
|
+
|
175
|
+
def expected_int(val, min, max)
|
176
|
+
if val.kind_of?(Integer)
|
177
|
+
if val >= min && val <= max
|
178
|
+
return true
|
179
|
+
else
|
180
|
+
raise RangeError
|
181
|
+
end
|
182
|
+
else
|
183
|
+
raise TypeError
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
class BMFF::Box::AudioSampleEntry < BMFF::Box::SampleEntry
|
5
|
+
attr_accessor :reserved2, :channelcount, :samplesize, :pre_defined, :reserved3, :samplerate
|
6
|
+
|
7
|
+
def parse_data
|
8
|
+
super
|
9
|
+
@reserved2 = [io.get_uint32, io.get_uint32]
|
10
|
+
@channelcount = io.get_uint16
|
11
|
+
@samplesize = io.get_uint16
|
12
|
+
@pre_defined = io.get_uint16
|
13
|
+
@reserved3 = io.get_uint16
|
14
|
+
@samplerate = io.get_uint32
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
class BMFF::Box::Base
|
5
|
+
attr_accessor :size, :type, :largesize, :usertype,
|
6
|
+
:io, :offset, :parent
|
7
|
+
|
8
|
+
def self.register_box(*boxtypes)
|
9
|
+
boxtypes.each do |boxtype|
|
10
|
+
BMFF::Box::Map.register_box(boxtype, self)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def actual_size
|
15
|
+
return largesize if size == 1
|
16
|
+
return nil if size == 0
|
17
|
+
return size
|
18
|
+
end
|
19
|
+
|
20
|
+
def remaining_size
|
21
|
+
if actual_size
|
22
|
+
return (offset + actual_size) - io.pos
|
23
|
+
end
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# end of box?
|
28
|
+
def eob?
|
29
|
+
if actual_size
|
30
|
+
return io.pos >= offset + actual_size
|
31
|
+
else
|
32
|
+
return io.eof?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def seek_to_end
|
37
|
+
if actual_size
|
38
|
+
io.pos = offset + actual_size
|
39
|
+
else
|
40
|
+
io.seek(0, IO::SEEK_END)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse
|
45
|
+
parse_data
|
46
|
+
if actual_size
|
47
|
+
if io.pos > offset + actual_size
|
48
|
+
raise RangeError, "Given box size is smaller than expected."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
seek_to_end
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_data
|
55
|
+
if size == 1
|
56
|
+
@largesize = io.get_uint64
|
57
|
+
end
|
58
|
+
if type == 'uuid'
|
59
|
+
@usertype = io.get_uuid
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def container?
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
class BMFF::Box::BitRate < BMFF::Box::Base
|
5
|
+
attr_accessor :buffer_size_db, :max_bitrate, :avg_bitrate
|
6
|
+
register_box "btrt"
|
7
|
+
|
8
|
+
def parse_data
|
9
|
+
super
|
10
|
+
@buffer_size_db = io.get_uint32
|
11
|
+
@max_bitrate = io.get_uint32
|
12
|
+
@avg_bitrate = io.get_uint32
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
class BMFF::Box::ChunkLargeOffset < BMFF::Box::Full
|
5
|
+
attr_accessor :entry_count, :chunk_offset
|
6
|
+
register_box "co64"
|
7
|
+
|
8
|
+
def parse_data
|
9
|
+
super
|
10
|
+
@entry_count = io.get_uint32
|
11
|
+
@chunk_offset = []
|
12
|
+
@entry_count.times do
|
13
|
+
@chunk_offset << io.get_uint64
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
class BMFF::Box::ChunkOffset < BMFF::Box::Full
|
5
|
+
attr_accessor :entry_count, :chunk_offset
|
6
|
+
register_box "stco"
|
7
|
+
|
8
|
+
def parse_data
|
9
|
+
super
|
10
|
+
@entry_count = io.get_uint32
|
11
|
+
@chunk_offset = []
|
12
|
+
@entry_count.times do
|
13
|
+
@chunk_offset << io.get_uint32
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
class BMFF::Box::CleanAperture < BMFF::Box::Base
|
5
|
+
attr_accessor :clean_aperture_width_n, :clean_aperture_width_d, :clean_aperture_height_n, :clean_aperture_height_d,
|
6
|
+
:horiz_off_n, :horiz_off_d, :vert_off_n, :vert_off_d
|
7
|
+
register_box "clap"
|
8
|
+
|
9
|
+
def parse_data
|
10
|
+
super
|
11
|
+
@clean_aperture_width_n = io.get_uint32
|
12
|
+
@clean_aperture_width_d = io.get_uint32
|
13
|
+
@clean_aperture_height_n = io.get_uint32
|
14
|
+
@clean_aperture_height_d = io.get_uint32
|
15
|
+
@horiz_off_n = io.get_uint32
|
16
|
+
@horiz_off_d = io.get_uint32
|
17
|
+
@vert_off_n = io.get_uint32
|
18
|
+
@vert_off_d = io.get_uint32
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
class BMFF::Box::ColourInformation < BMFF::Box::Base
|
5
|
+
attr_accessor :colour_type,
|
6
|
+
:colour_primaries, :transfer_characteristics, :matrix_coefficients, :full_range_flag, :reserved1,
|
7
|
+
:icc_profile
|
8
|
+
register_box "colr"
|
9
|
+
|
10
|
+
def parse_data
|
11
|
+
super
|
12
|
+
@colour_type = io.get_ascii(4)
|
13
|
+
case @colour_type
|
14
|
+
when "nclx"
|
15
|
+
@colour_primaries = io.get_uint16
|
16
|
+
@transfer_characteristics = io.get_uint16
|
17
|
+
@matrix_coefficients = io.get_uint16
|
18
|
+
tmp = io.get_uint8
|
19
|
+
@full_range_flag = (tmp & 0x80) > 0
|
20
|
+
@reserved1 = tmp & 0x7F
|
21
|
+
when "rICC"
|
22
|
+
@icc_profile = :restricted
|
23
|
+
when "prof"
|
24
|
+
@icc_profile = :unrestricted
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
|
3
|
+
|
4
|
+
class BMFF::Box::CompactSampleSize < BMFF::Box::Full
|
5
|
+
attr_accessor :reserved1, :field_size, :sample_count, :entry_size
|
6
|
+
register_box "stz2"
|
7
|
+
|
8
|
+
def parse_data
|
9
|
+
super
|
10
|
+
@reserved1 = io.get_uint24
|
11
|
+
@field_size = io.get_uint8
|
12
|
+
@sample_count = io.get_uint32
|
13
|
+
@entry_size = []
|
14
|
+
i = 0
|
15
|
+
while i < @sample_count
|
16
|
+
case @field_size
|
17
|
+
when 4
|
18
|
+
tmp = io.get_uint8
|
19
|
+
@entry_size << (tmp >> 4)
|
20
|
+
i += 1
|
21
|
+
@entry_size << (tmp & 0x0F) if i < @sample_count
|
22
|
+
when 8
|
23
|
+
@entry_size << io.get_uint8
|
24
|
+
when 16
|
25
|
+
@entry_size << io.get_uint16
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Unsupported field_size"
|
28
|
+
end
|
29
|
+
i += 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|