mp4-rb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +24 -0
  3. data/Gemfile.lock +149 -0
  4. data/README.md +195 -0
  5. data/Rakefile +8 -0
  6. data/bin/build_mp4_xlsx.rb +35 -0
  7. data/bin/divide_mp4_to_segments.rb +52 -0
  8. data/lib/m4s/binary/ftyp.rb +11 -0
  9. data/lib/m4s/binary/mdat.rb +0 -0
  10. data/lib/m4s/binary/mfhd.rb +18 -0
  11. data/lib/m4s/binary/mvhd.rb +39 -0
  12. data/lib/m4s/binary/sidx.rb +38 -0
  13. data/lib/m4s/binary/styp.rb +17 -0
  14. data/lib/m4s/binary/tfdt.rb +25 -0
  15. data/lib/m4s/binary/tfhd.rb +38 -0
  16. data/lib/m4s/binary/trex.rb +25 -0
  17. data/lib/m4s/binary/trun.rb +30 -0
  18. data/lib/m4s/dash/manifest.rb +149 -0
  19. data/lib/m4s/layout/atom.rb +44 -0
  20. data/lib/m4s/layout/builder.rb +32 -0
  21. data/lib/m4s/layout/container.rb +59 -0
  22. data/lib/m4s/layout/ctts.rb +20 -0
  23. data/lib/m4s/layout/dinf.rb +17 -0
  24. data/lib/m4s/layout/elst.rb +33 -0
  25. data/lib/m4s/layout/ftyp.rb +21 -0
  26. data/lib/m4s/layout/hdlr.rb +22 -0
  27. data/lib/m4s/layout/init_mp4_builder.rb +57 -0
  28. data/lib/m4s/layout/mdat.rb +61 -0
  29. data/lib/m4s/layout/mdhd.rb +25 -0
  30. data/lib/m4s/layout/mfhd.rb +17 -0
  31. data/lib/m4s/layout/mvhd.rb +32 -0
  32. data/lib/m4s/layout/sidx.rb +36 -0
  33. data/lib/m4s/layout/stco.rb +21 -0
  34. data/lib/m4s/layout/stsc.rb +20 -0
  35. data/lib/m4s/layout/stsd.rb +17 -0
  36. data/lib/m4s/layout/stss.rb +20 -0
  37. data/lib/m4s/layout/stsz.rb +19 -0
  38. data/lib/m4s/layout/stts.rb +20 -0
  39. data/lib/m4s/layout/styp.rb +17 -0
  40. data/lib/m4s/layout/tfdt.rb +21 -0
  41. data/lib/m4s/layout/tfhd.rb +17 -0
  42. data/lib/m4s/layout/tkhd.rb +34 -0
  43. data/lib/m4s/layout/trex.rb +33 -0
  44. data/lib/m4s/layout/trun.rb +89 -0
  45. data/lib/m4s/layout/vsmhd.rb +51 -0
  46. data/lib/m4s/m3u8/general_playlist.rb +52 -0
  47. data/lib/m4s/m3u8/media_playlist.rb +32 -0
  48. data/lib/mp4/binary/avc1.rb +98 -0
  49. data/lib/mp4/binary/box_head.rb +8 -0
  50. data/lib/mp4/binary/co64.rb +11 -0
  51. data/lib/mp4/binary/codec.rb +8 -0
  52. data/lib/mp4/binary/ctts.rb +14 -0
  53. data/lib/mp4/binary/ctts_reader.rb +5 -0
  54. data/lib/mp4/binary/dinf.rb +5 -0
  55. data/lib/mp4/binary/dref.rb +5 -0
  56. data/lib/mp4/binary/elst.rb +16 -0
  57. data/lib/mp4/binary/fixed_point16.rb +19 -0
  58. data/lib/mp4/binary/fixed_point8.rb +19 -0
  59. data/lib/mp4/binary/ftyp.rb +9 -0
  60. data/lib/mp4/binary/gmhd.rb +5 -0
  61. data/lib/mp4/binary/hdlr.rb +13 -0
  62. data/lib/mp4/binary/hev1.rb +73 -0
  63. data/lib/mp4/binary/hmhd.rb +14 -0
  64. data/lib/mp4/binary/iods.rb +5 -0
  65. data/lib/mp4/binary/mdhd.rb +18 -0
  66. data/lib/mp4/binary/mp4a.rb +76 -0
  67. data/lib/mp4/binary/mvhd.rb +39 -0
  68. data/lib/mp4/binary/nmhd.rb +8 -0
  69. data/lib/mp4/binary/saio.rb +25 -0
  70. data/lib/mp4/binary/saiz.rb +17 -0
  71. data/lib/mp4/binary/sbgp.rb +5 -0
  72. data/lib/mp4/binary/sdtp.rb +18 -0
  73. data/lib/mp4/binary/senc.rb +12 -0
  74. data/lib/mp4/binary/sgpd.rb +5 -0
  75. data/lib/mp4/binary/smhd.rb +11 -0
  76. data/lib/mp4/binary/stco.rb +13 -0
  77. data/lib/mp4/binary/stco_reader.rb +5 -0
  78. data/lib/mp4/binary/sthd.rb +8 -0
  79. data/lib/mp4/binary/stsc.rb +15 -0
  80. data/lib/mp4/binary/stsc_reader.rb +5 -0
  81. data/lib/mp4/binary/stsd.rb +5 -0
  82. data/lib/mp4/binary/stsd_atom.rb +11 -0
  83. data/lib/mp4/binary/stsd_reader.rb +5 -0
  84. data/lib/mp4/binary/stss.rb +13 -0
  85. data/lib/mp4/binary/stss_reader.rb +5 -0
  86. data/lib/mp4/binary/stsz.rb +18 -0
  87. data/lib/mp4/binary/stsz_reader.rb +5 -0
  88. data/lib/mp4/binary/stts.rb +14 -0
  89. data/lib/mp4/binary/stts_reader.rb +5 -0
  90. data/lib/mp4/binary/tkhd.rb +46 -0
  91. data/lib/mp4/binary/udta.rb +5 -0
  92. data/lib/mp4/binary/vmhd.rb +11 -0
  93. data/lib/mp4/cache/memory.rb +328 -0
  94. data/lib/mp4/cache/sqlite.rb +348 -0
  95. data/lib/mp4/cache.rb +13 -0
  96. data/lib/mp4/constants.rb +87 -0
  97. data/lib/mp4/ext/refinements.rb +19 -0
  98. data/lib/mp4/layout/atom.rb +47 -0
  99. data/lib/mp4/layout/base.rb +10 -0
  100. data/lib/mp4/layout/builder.rb +16 -0
  101. data/lib/mp4/layout/container.rb +59 -0
  102. data/lib/mp4/layout/ctts.rb +35 -0
  103. data/lib/mp4/layout/dinf.rb +15 -0
  104. data/lib/mp4/layout/elst.rb +33 -0
  105. data/lib/mp4/layout/free.rb +20 -0
  106. data/lib/mp4/layout/ftyp.rb +21 -0
  107. data/lib/mp4/layout/hdlr.rb +20 -0
  108. data/lib/mp4/layout/mdat.rb +33 -0
  109. data/lib/mp4/layout/mdhd.rb +27 -0
  110. data/lib/mp4/layout/moov.rb +58 -0
  111. data/lib/mp4/layout/mvhd.rb +46 -0
  112. data/lib/mp4/layout/stco.rb +37 -0
  113. data/lib/mp4/layout/stsc.rb +27 -0
  114. data/lib/mp4/layout/stsd.rb +15 -0
  115. data/lib/mp4/layout/stss.rb +22 -0
  116. data/lib/mp4/layout/stsz.rb +39 -0
  117. data/lib/mp4/layout/stts.rb +35 -0
  118. data/lib/mp4/layout/tkhd.rb +45 -0
  119. data/lib/mp4/layout/vsmhd.rb +49 -0
  120. data/lib/mp4/layout.rb +3 -0
  121. data/lib/mp4/metadata/memoize/ftyp.rb +13 -0
  122. data/lib/mp4/metadata/memoize/stbl/binary_stsd.rb +13 -0
  123. data/lib/mp4/metadata/memoize/stbl/chunks.rb +46 -0
  124. data/lib/mp4/metadata/memoize/stbl/codec.rb +82 -0
  125. data/lib/mp4/metadata/memoize/stbl/composition_offset_calculator.rb +31 -0
  126. data/lib/mp4/metadata/memoize/stbl/delta_calculator.rb +29 -0
  127. data/lib/mp4/metadata/memoize/stbl/sample_to_chunk_calculator.rb +63 -0
  128. data/lib/mp4/metadata/memoize/stbl/samples.rb +80 -0
  129. data/lib/mp4/metadata/memoize/track.rb +98 -0
  130. data/lib/mp4/metadata/memoize/tracks.rb +11 -0
  131. data/lib/mp4/metadata/service.rb +30 -0
  132. data/lib/mp4/metadata/summary.rb +11 -0
  133. data/lib/mp4/nolan/service/sound.rb +36 -0
  134. data/lib/mp4/nolan/service/track.rb +9 -0
  135. data/lib/mp4/nolan/service/video.rb +35 -0
  136. data/lib/mp4/nolan/service.rb +84 -0
  137. data/lib/mp4/nolan.rb +3 -0
  138. data/lib/mp4/parser/main_boxes.rb +43 -0
  139. data/lib/mp4/parser/moov.rb +40 -0
  140. data/lib/mp4/parser/service.rb +47 -0
  141. data/lib/mp4/parser/track.rb +56 -0
  142. data/lib/mp4/rb/version.rb +7 -0
  143. data/lib/mp4/rb.rb +10 -0
  144. data/lib/mp4/source/base.rb +20 -0
  145. data/lib/mp4/source/local.rb +79 -0
  146. data/lib/mp4/source/s3.rb +118 -0
  147. data/lib/mp4/source/url.rb +130 -0
  148. data/lib/mp4/source.rb +23 -0
  149. data/lib/mp4/structure/service.rb +13 -0
  150. data/lib/mp4/xlsx/builder.rb +44 -0
  151. data/lib/mp4.rb +222 -0
  152. data/mp4-rb.gemspec +68 -0
  153. data/sig/mp4/rb.rbs +6 -0
  154. data/todo.txt +4 -0
  155. metadata +323 -0
@@ -0,0 +1,87 @@
1
+ module MP4::Constants
2
+ GENERIC_HEADER_SIZE = 8
3
+
4
+ TRAK_ATOMS = [
5
+ TKHD_ATOM = 'tkhd'.freeze,
6
+ TREF_ATOM = 'tref'.freeze,
7
+ TRGR_ATOM = 'trgr'.freeze,
8
+ ELST_ATOM = 'elst'.freeze,
9
+ META_ATOM = 'meta'.freeze,
10
+ MDHD_ATOM = 'mdhd'.freeze,
11
+ HDLR_ATOM = 'hdlr'.freeze,
12
+ ELNG_ATOM = 'elng'.freeze,
13
+ # minf
14
+ VMHD_ATOM = 'vmhd'.freeze,
15
+ SMHD_ATOM = 'smhd'.freeze,
16
+ HMHD_ATOM = 'hmhd'.freeze,
17
+ STHD_ATOM = 'sthd'.freeze,
18
+ NMHD_ATOM = 'nmhd'.freeze,
19
+ DINF_ATOM = 'dinf'.freeze, # TODO
20
+ # 'dref', # TODO
21
+ # stbl
22
+ STSD_ATOM = 'stsd'.freeze,
23
+ STTS_ATOM = 'stts'.freeze,
24
+ CTTS_ATOM = 'ctts'.freeze,
25
+ CSLG_ATOM = 'cslg'.freeze,
26
+ STSC_ATOM = 'stsc'.freeze,
27
+ STSZ_ATOM = 'stsz'.freeze,
28
+ STZ2_ATOM = 'stz2'.freeze,
29
+ STCO_ATOM = 'stco'.freeze,
30
+ CO64_ATOM = 'co64'.freeze,
31
+ STSS_ATOM = 'stss'.freeze,
32
+ STSH_ATOM = 'stsh'.freeze,
33
+ PADB_ATOM = 'padb'.freeze,
34
+ STDP_ATOM = 'stdp'.freeze,
35
+ SDTP_ATOM = 'sdtp'.freeze,
36
+ SBGP_ATOM = 'sbgp'.freeze,
37
+ # 'sgpd',
38
+ SUBS_ATOM = 'subs'.freeze,
39
+ SAIZ_ATOM = 'saiz'.freeze,
40
+ SAIO_ATOM = 'saio'.freeze,
41
+ ].freeze
42
+
43
+ CACHED_ATOMS = %w[dinf stbl stsd].freeze
44
+
45
+ PARSERS = {
46
+ 'mvhd' => '::MP4::Binary::Mvhd',
47
+ 'iods' => '::MP4::Binary::Iods',
48
+ 'tkhd' => '::MP4::Binary::Tkhd',
49
+ 'tref' => '::MP4::Binary::Tref',
50
+ 'trgr' => '::MP4::Binary::Trgr',
51
+ 'elst' => '::MP4::Binary::Elst',
52
+ 'meta' => '::MP4::Binary::Meta',
53
+ 'mdhd' => '::MP4::Binary::Mdhd',
54
+ 'hdlr' => '::MP4::Binary::Hdlr',
55
+ 'elng' => '::MP4::Binary::Elng',
56
+ # minf
57
+ 'vmhd' => '::MP4::Binary::Vmhd',
58
+ 'smhd' => '::MP4::Binary::Smhd',
59
+ 'hmhd' => '::MP4::Binary::Hmhd',
60
+ 'sthd' => '::MP4::Binary::Sthd',
61
+ 'nmhd' => '::MP4::Binary::Nmhd',
62
+ 'dinf' => '::MP4::Binary::Dinf',
63
+ # stbl
64
+ 'stsd' => '::MP4::Binary::Stsd',
65
+ 'stts' => '::MP4::Binary::Stts',
66
+ 'ctts' => '::MP4::Binary::Ctts',
67
+ 'cslg' => '::MP4::Binary::Cslg',
68
+ 'stsc' => '::MP4::Binary::Stsc',
69
+ 'stsz' => '::MP4::Binary::Stsz',
70
+ 'stz2' => '::MP4::Binary::Stz2',
71
+ 'stco' => '::MP4::Binary::Stco',
72
+ 'co64' => '::MP4::Binary::Co64',
73
+ 'stss' => '::MP4::Binary::Stss',
74
+ 'stsh' => '::MP4::Binary::Stsh',
75
+ 'padb' => '::MP4::Binary::Padb',
76
+ 'stdp' => '::MP4::Binary::Stdp',
77
+ 'sdtp' => '::MP4::Binary::Sdtp',
78
+ 'gmhd' => '::MP4::Binary::Gmhd',
79
+ 'sgpd' => '::MP4::Binary::Sgpd', # TODO: add support
80
+ 'sbgp' => '::MP4::Binary::Sbgp', # TODO: add support
81
+ 'subs' => '::MP4::Binary::Subs',
82
+ 'saiz' => '::MP4::Binary::Saiz',
83
+ 'saio' => '::MP4::Binary::Saio',
84
+ 'senc' => '::MP4::Binary::Senc',
85
+ 'udta' => '::MP4::Binary::Udta',
86
+ }.freeze
87
+ end
@@ -0,0 +1,19 @@
1
+ module MP4::Ext
2
+ module Refinements
3
+ refine Hash do
4
+ def dig!(*keys)
5
+ val = self
6
+ keys.each { |key| val = val.fetch(key) }
7
+ val
8
+ end
9
+ end
10
+
11
+ refine Array do
12
+ def dig!(*keys)
13
+ val = self
14
+ keys.each { |key| val = val.fetch(key) }
15
+ val
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ module MP4::Layout
2
+ class Atom
3
+ attr_accessor :cache, :media_cache, :track_id
4
+
5
+ def self.generate(cache, media_cache, opts = {})
6
+ new(cache, media_cache, opts).generate
7
+ end
8
+
9
+ def initialize(cache, media_cache, opts = {})
10
+ @cache = cache
11
+ @media_cache = media_cache
12
+ @track_id = opts.fetch(:track_id, nil)
13
+ end
14
+
15
+ def generate
16
+ [
17
+ *header,
18
+ binary_content,
19
+ ].join
20
+ end
21
+
22
+ private
23
+
24
+ def header
25
+ [
26
+ BinData::Uint32be.new(size).to_binary_s,
27
+ BinData::String.new(name).to_binary_s,
28
+ ]
29
+ end
30
+
31
+ def name
32
+ raise NotImplementedError.new(message: "#{__callee__} not implemented")
33
+ end
34
+
35
+ def size
36
+ ::MP4::Constants::GENERIC_HEADER_SIZE + binary_content.size
37
+ end
38
+
39
+ def binary_content
40
+ raise NotImplementedError.new(message: "#{__callee__} not implemented")
41
+ end
42
+
43
+ def track
44
+ @track ||= media_cache.track(track_id)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,10 @@
1
+ module MP4::Layout::Base
2
+ private
3
+
4
+ def header
5
+ [
6
+ BinData::Uint32be.new(size).to_binary_s,
7
+ BinData::String.new(name).to_binary_s,
8
+ ]
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module MP4::Layout
2
+ module Builder
3
+ module_function
4
+
5
+ def call(cache, media_cache, source)
6
+ container = Container.new(root: true)
7
+
8
+ container.add_atom Ftyp.generate(cache, media_cache)
9
+ container.add_atom Free.generate(cache, media_cache, size: container.size)
10
+ container.add_atom Mdat.generate(cache, media_cache, source: source)
11
+ container.add_atom Moov.generate(cache, media_cache)
12
+
13
+ container
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,59 @@
1
+ module MP4::Layout
2
+ class Container
3
+ # include ::Layout::Base
4
+
5
+ attr_accessor :root, :name, :descendants
6
+
7
+ def initialize(root: false, name: nil)
8
+ @root = root
9
+ @name = name
10
+ @descendants ||= []
11
+ end
12
+
13
+ def generate
14
+ return binary_descendants if root
15
+
16
+ [
17
+ BinData::Uint32be.new(size).to_binary_s,
18
+ BinData::String.new(name).to_binary_s,
19
+ binary_descendants,
20
+ ].join
21
+ end
22
+
23
+ def binary_descendants
24
+ descendants.join
25
+ end
26
+
27
+ def size
28
+ ::MP4::Constants::GENERIC_HEADER_SIZE + binary_descendants.size
29
+ end
30
+
31
+ def add_container(name, &block)
32
+ container = self.class.new(name: name)
33
+
34
+ block.call(container)
35
+
36
+ @descendants << container.generate
37
+
38
+ container
39
+ end
40
+
41
+ def add_atom(atom)
42
+ @descendants << atom
43
+ atom
44
+ end
45
+
46
+ def add_mdat(mp4, source)
47
+ @descendants << ::Layout::Mdat.new(mp4: mp4, source: source)
48
+ end
49
+
50
+ def pack
51
+ return descendants.map(&:pack).join if root
52
+
53
+ [
54
+ *header,
55
+ descendants.map(&:pack),
56
+ ].join
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,35 @@
1
+ module MP4::Layout
2
+ class Ctts < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'ctts'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Ctts.new(
13
+ entry_count: generate_entries_list.size,
14
+ entries_list: generate_entries_list,
15
+ ).to_binary_s
16
+ end
17
+
18
+ def generate_entries_list
19
+ return @generate_entries_list if defined?(@generate_entries_list)
20
+
21
+ buffer = []
22
+ media_cache.sample_c_delta_pairs(track_id).each do |row|
23
+ c_delta = row.fetch('c_delta')
24
+ latest = buffer.last
25
+ if latest && latest[:composition_offset] == c_delta
26
+ latest[:sample_count] += 1
27
+ else
28
+ buffer << { sample_count: 1, composition_offset: c_delta }
29
+ end
30
+ end
31
+
32
+ @generate_entries_list = buffer
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ module MP4::Layout
2
+ class Dinf < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'dinf'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Dinf.new(content: cache.dinf_content(track_id)).to_binary_s
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ module MP4::Layout
2
+ class Elst < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'elst'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Elst.new(
13
+ version: 0,
14
+ flags: [0, 0, 0],
15
+ entries_count: 1,
16
+ matrix: [compile_matrix],
17
+ ).to_binary_s
18
+ end
19
+
20
+ def compile_matrix
21
+ {
22
+ segment_duration: calculate_duration,
23
+ media_time: 0,
24
+ media_rate: 1,
25
+ }
26
+ end
27
+
28
+ def calculate_duration
29
+ seconds = media_cache.track_sample_delta_sum(track_id) / track.fetch('timescale').to_f
30
+ (seconds.round(3) * MP4::Layout::Mvhd::DEFAULT_TIMESCALE).to_i
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ module MP4::Layout
2
+ class Free < Atom
3
+
4
+ def initialize(_cache, _media_cache, opts = {})
5
+ @size = opts.fetch(:size)
6
+ end
7
+
8
+ def name
9
+ 'free'
10
+ end
11
+
12
+ def binary_content
13
+ @binary_content ||= Array.new(50 - @size - MP4::Constants::GENERIC_HEADER_SIZE) { "\x00" }.join
14
+ end
15
+
16
+ def memoized_ftyp
17
+ @memoized_ftyp ||= @cache.ftyp
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module MP4::Layout
2
+ class Ftyp < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ def name
6
+ 'ftyp'
7
+ end
8
+
9
+ def binary_content
10
+ @binary_content ||= MP4::Binary::Ftyp.new(
11
+ major_brand: memoized_ftyp.fetch('major_brand'),
12
+ minor_version: memoized_ftyp.fetch('minor_version'),
13
+ compatible_brands: memoized_ftyp.fetch('compatible_brands'),
14
+ ).to_binary_s
15
+ end
16
+
17
+ def memoized_ftyp
18
+ @memoized_ftyp ||= cache.ftyp
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ module MP4::Layout
2
+ class Hdlr < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'hdlr'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Hdlr.new(
13
+ version: 0,
14
+ flags: [0, 0, 0],
15
+ handler_type: track.fetch('handler_type'),
16
+ handler_name: track.fetch('handler_name'),
17
+ ).to_binary_s
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ module MP4::Layout
2
+ class Mdat < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ attr_reader :source
6
+
7
+ def initialize(cache, media_cache, opts = {})
8
+ super(cache, media_cache, opts)
9
+ @source = opts.fetch(:source)
10
+ end
11
+
12
+ private
13
+
14
+ def name
15
+ 'mdat'
16
+ end
17
+
18
+ def binary_content
19
+ return @binary_content if defined?(@binary_content)
20
+
21
+ buffer = StringIO.new
22
+ source.reset_seek
23
+
24
+ media_cache.each_sample_ordered_across_tracks do |chunk|
25
+ source.set_seek chunk.fetch('offset')
26
+ buffer.write source.read(chunk.fetch('size'))
27
+ end
28
+
29
+ buffer.seek(0)
30
+ @binary_content = BinData::String.new(buffer.read).to_binary_s
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ module MP4::Layout
2
+ class Mdhd < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'mdhd'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Mdhd.new(
13
+ version: 0,
14
+ flags: [0, 0, 0],
15
+ creation_time: 0,
16
+ modification_time: 0,
17
+ timescale: track.fetch('timescale'),
18
+ duration: calculate_duration,
19
+ language: [0, 0, 0],
20
+ ).to_binary_s
21
+ end
22
+
23
+ def calculate_duration
24
+ media_cache.track_sample_delta_sum(track_id)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,58 @@
1
+ module MP4::Layout
2
+ module Moov
3
+ extend self
4
+
5
+ def generate(cache, media_cache)
6
+ moov = Container.new(name: 'moov')
7
+ moov.add_atom Mvhd.generate(cache, media_cache)
8
+
9
+ media_cache.tracks_asc.each do |track|
10
+ moov.add_container(:trak) { |trak| build_trak(cache, media_cache, trak, track) }
11
+ end
12
+
13
+ moov.generate
14
+ end
15
+
16
+ private
17
+
18
+ def build_trak(cache, media_cache, trak, track)
19
+ track_id = track.fetch('track_id')
20
+
21
+ trak.add_atom Tkhd.generate(cache, media_cache, track_id: track_id)
22
+ trak.add_container(:edts) { |edts| edts.add_atom Elst.generate(cache, media_cache, track_id: track_id) }
23
+ trak.add_container(:mdia) { |mdia| build_mdia(cache, media_cache, mdia, track) }
24
+ end
25
+
26
+ def build_mdia(cache, media_cache, mdia, track)
27
+ track_id = track.fetch('track_id')
28
+
29
+ mdia.add_atom Mdhd.generate(cache, media_cache, track_id: track_id)
30
+ mdia.add_atom Hdlr.generate(cache, media_cache, track_id: track_id)
31
+ mdia.add_container(:minf) { |minf| build_minf(cache, media_cache, minf, track) }
32
+ end
33
+
34
+ def build_minf(cache, media_cache, minf, track)
35
+ track_id = track.fetch('track_id')
36
+
37
+ minf.add_atom VSmhd.generate(cache, media_cache, track_id: track_id)
38
+ minf.add_atom Dinf.generate(cache, media_cache, track_id: track_id)
39
+ minf.add_container(:stbl) { |stbl| build_stbl(cache, media_cache, stbl, track) }
40
+ end
41
+
42
+ def build_stbl(cache, media_cache, stbl, track)
43
+ track_id = track.fetch('track_id')
44
+
45
+ stbl.add_atom Stsd.generate(cache, media_cache, track_id: track_id)
46
+ stbl.add_atom Stts.generate(cache, media_cache, track_id: track_id)
47
+
48
+ if track['handler_type'] == 'vide'
49
+ stbl.add_atom Stss.generate(cache, media_cache, track_id: track_id)
50
+ stbl.add_atom Ctts.generate(cache, media_cache, track_id: track_id) if track['have_composition_time'].to_i.nonzero?
51
+ end
52
+
53
+ stbl.add_atom Stsc.generate(cache, media_cache, track_id: track_id)
54
+ stbl.add_atom Stsz.generate(cache, media_cache, track_id: track_id)
55
+ stbl.add_atom Stco.generate(cache, media_cache, track_id: track_id)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,46 @@
1
+ module MP4::Layout
2
+ class Mvhd < Atom
3
+ DEFAULT_TIMESCALE = 1000
4
+
5
+ using ::MP4::Ext::Refinements
6
+
7
+ private
8
+
9
+ def name
10
+ 'mvhd'
11
+ end
12
+
13
+ def binary_content
14
+ @binary_content ||= MP4::Binary::Mvhd.new(
15
+ version: 0,
16
+ flags: [0, 0, 0],
17
+ creation_time: 0,
18
+ modification_time: 0,
19
+ timescale: calculate_timescale,
20
+ duration: calculate_duration,
21
+ rate: 1.0,
22
+ volume: 1.0,
23
+ matrix: [65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824],
24
+ next_track_id: calculate_next_track_id,
25
+ ).to_binary_s
26
+ end
27
+
28
+ def calculate_timescale
29
+ DEFAULT_TIMESCALE
30
+ end
31
+
32
+ def calculate_duration
33
+ durations = media_cache.tracks.map do |tr|
34
+ tid = tr.fetch('track_id')
35
+ track_timescale = cache.track(tid).fetch('timescale').to_f
36
+ seconds = media_cache.track_sample_delta_sum(tid) / track_timescale
37
+ (seconds.round(3) * calculate_timescale).to_i
38
+ end
39
+ durations.max.to_i.nonzero? || DEFAULT_TIMESCALE
40
+ end
41
+
42
+ def calculate_next_track_id
43
+ cache.next_track_id
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,37 @@
1
+ module MP4::Layout
2
+ class Stco < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'stco'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Stco.new(
13
+ chunks_count: generate_offsets_list.size,
14
+ offsets: generate_offsets_list,
15
+ ).to_binary_s
16
+ end
17
+
18
+ def generate_offsets_list
19
+ return @generate_offsets_list if defined?(@generate_offsets_list)
20
+
21
+ offset_counter = 50
22
+ buffer = []
23
+
24
+ media_cache.each_sample_ordered_across_tracks do |sample|
25
+ buffer << offset_counter if sample.fetch('track_id') == track_id
26
+ offset_counter += sample.fetch('size')
27
+ end
28
+
29
+ @generate_offsets_list = buffer
30
+ end
31
+
32
+ def offset_start_point
33
+ @offset_start_point ||= media_cache.min_sample_offset
34
+ end
35
+ end
36
+ end
37
+ # 794164
@@ -0,0 +1,27 @@
1
+ module MP4::Layout
2
+ class Stsc < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'stsc'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Stsc.new(
13
+ chunk_count: sample_to_chunk_table.size,
14
+ table: sample_to_chunk_table,
15
+ ).to_binary_s
16
+ end
17
+
18
+ def sample_to_chunk_table
19
+ return @sample_to_chunk_table if defined?(@sample_to_chunk_table)
20
+
21
+ chunk_indices = media_cache.sample_chunk_indices(track_id)
22
+ @sample_to_chunk_table = chunk_indices.each_with_index.map do |_chunk_index, i|
23
+ { first_chunk: i + 1, samples_per_chunk: 1, sample_description_index: 1 }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module MP4::Layout
2
+ class Stsd < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'stsd'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Stsd.new(content: cache.stsd_content(track_id)).to_binary_s
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ module MP4::Layout
2
+ class Stss < Atom
3
+ using ::MP4::Ext::Refinements
4
+
5
+ private
6
+
7
+ def name
8
+ 'stss'
9
+ end
10
+
11
+ def binary_content
12
+ @binary_content ||= MP4::Binary::Stss.new(
13
+ sync_samples_count: generate_sync_sample_list.size,
14
+ sync_sample: generate_sync_sample_list,
15
+ ).to_binary_s
16
+ end
17
+
18
+ def generate_sync_sample_list
19
+ @generate_sync_sample_list ||= media_cache.sync_sample_indices(track_id)
20
+ end
21
+ end
22
+ end