flvtool2 1.0.6

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.
@@ -0,0 +1,297 @@
1
+ # Copyright (c) 2005 Norman Timmler (inlet media e.K., Hamburg, Germany)
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions
6
+ # are met:
7
+ # 1. Redistributions of source code must retain the above copyright
8
+ # notice, this list of conditions and the following disclaimer.
9
+ # 2. Redistributions in binary form must reproduce the above copyright
10
+ # notice, this list of conditions and the following disclaimer in the
11
+ # documentation and/or other materials provided with the distribution.
12
+ # 3. The name of the author may not be used to endorse or promote products
13
+ # derived from this software without specific prior written permission.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+
27
+ require 'flv'
28
+ require 'mixml'
29
+ require 'miyaml'
30
+
31
+ module FLVTool2
32
+
33
+ module Base
34
+
35
+ class << self
36
+
37
+ def execute!(options)
38
+
39
+ options[:commands].each do |command|
40
+ before_filter = "before_#{command.to_s}".intern
41
+ send(before_filter, options) if respond_to? before_filter
42
+ end
43
+
44
+ process_files(options) do |stream, in_path, out_path|
45
+ write_stream = false
46
+ options[:commands].each do |command|
47
+ write_stream = true if send( command, options, stream, in_path, out_path )
48
+ end
49
+ stream.write if write_stream && !options[:simulate]
50
+ end
51
+
52
+ options[:commands].each do |command|
53
+ after_filter = "after_#{command.to_s}".intern
54
+ send(after_filter, options) if respond_to? after_filter
55
+ end
56
+ end
57
+
58
+
59
+ def add(options, stream, in_path, out_path)
60
+ tag_structures = MiXML.parse( File.open( options[:tag_file], File::RDONLY ) { |file| file.readlines }.join )
61
+
62
+ add_tag = Proc.new do |data|
63
+
64
+ tag = FLV::FLVMetaTag.new
65
+
66
+ overwrite = ( data['overwrite'] && data['overwrite'].downcase ) == 'true' || false
67
+ data.delete( 'overwrite' )
68
+
69
+ tag.event = data['event'] || 'event'
70
+ data.delete( 'event' )
71
+
72
+ tag.timestamp = ( data['timestamp'] && data['timestamp'].to_i ) || 0
73
+ tag.timestamp = stream.find_nearest_keyframe_video_tag(tag.timestamp).timestamp if options[:keyframe_mode] && data['type'] == 'navigation'
74
+ data.delete( 'timestamp' )
75
+ data['time'] = tag.timestamp / 1000
76
+
77
+ tag.meta_data.merge!( data )
78
+
79
+ stream.add_tags( tag, false, overwrite )
80
+ end
81
+
82
+ tag_structures['tags'].each do |tag_name, value|
83
+ case tag_name
84
+ when 'metatag'
85
+ if value.class == Array
86
+ value.each { |_value| add_tag.call( _value ) }
87
+ else
88
+ add_tag.call( value )
89
+ end
90
+ else
91
+ end
92
+ end
93
+
94
+ return true
95
+ end
96
+
97
+
98
+ def debug(options, stream, in_path, out_path)
99
+
100
+ puts "---\n"
101
+ puts "path: #{in_path}"
102
+
103
+ unless stream.nil?
104
+ if options[:tag_number]
105
+ puts " tag_number: #{options[:tag_number]}"
106
+ puts " " + stream.tags[ options[:tag_number] - 1 ].inspect.join( "\n " )
107
+ else
108
+ stream.tags.each_with_index do |tag, index|
109
+ puts "##{index + 1} #{tag.info}"
110
+ end
111
+ end
112
+ end
113
+
114
+ return false
115
+ end
116
+
117
+
118
+ def cut(options, stream, in_path, out_path)
119
+
120
+ in_point = options[:keyframe_mode] ? stream.find_nearest_keyframe_video_tag(options[:in_point] || 0).timestamp : options[:in_point]
121
+ stream.cut( :in_point => in_point, :out_point => options[:out_point], :collapse => options[:collapse] )
122
+
123
+ return true
124
+ end
125
+
126
+
127
+ def update(options, stream, in_path, out_path)
128
+
129
+ if (
130
+ options[:preserve] &&
131
+ (
132
+ !stream.on_meta_data_tag ||
133
+ ( stream.on_meta_data_tag && stream.on_meta_data_tag.meta_data['metadatacreator'] != options[:metadatacreator] )
134
+ )
135
+ ) || !options[:preserve]
136
+
137
+ add_meta_data_tag( stream, options )
138
+
139
+ return true
140
+ else
141
+ puts 'Input file is FLV v1.1 yet. No update necessary.' if options[:verbose]
142
+
143
+ return false
144
+ end
145
+ end
146
+
147
+
148
+ def before_print(options)
149
+ puts "<?xml version=\"1.0\"?>\n<fileset>" if options[:xml]
150
+ end
151
+
152
+ def print(options, stream, in_path, out_path)
153
+
154
+ if options[:xml]
155
+ puts " <flv name=\"#{in_path}\">"
156
+ puts MiXML.dump( stream.on_meta_data_tag && stream.on_meta_data_tag.meta_data, 2 )
157
+ puts " </flv>"
158
+ else
159
+ puts MiYAML.dump( { in_path => ( stream.on_meta_data_tag && stream.on_meta_data_tag.meta_data ) } )
160
+ end
161
+
162
+ return false
163
+ end
164
+
165
+ def object_to_hash(object)
166
+ object.instance_variables.inject( {} ) do |hash, variable|
167
+ hash[variable.gsub('@', '')] = object.instance_variable_get( variable.intern )
168
+ hash
169
+ end
170
+ end
171
+
172
+ def after_print(options)
173
+ puts '</fileset>' if options[:xml]
174
+ end
175
+
176
+
177
+ def add_meta_data_tag(stream, options)
178
+ # add onLastSecond tag
179
+ onlastsecond = FLV::FLVMetaTag.new
180
+ onlastsecond.event = 'onLastSecond'
181
+ onlastsecond.timestamp = ((stream.duration - 1) * 1000).to_int
182
+ stream.add_tags(onlastsecond, false) if onlastsecond.timestamp >= 0
183
+
184
+ stream.add_meta_tag({ 'metadatacreator' => options[:metadatacreator], 'metadatadate' => Time.now }.merge(options[:metadata]))
185
+ unless options[:compatibility_mode]
186
+ stream.on_meta_data_tag.meta_data['duration'] += (stream.frame_sequence || 0) / 1000.0
187
+ end
188
+ end
189
+
190
+
191
+
192
+
193
+ def process_files(options)
194
+
195
+ if options[:in_pipe]
196
+
197
+ unless options[:omit_out] || options[:out_path].nil?
198
+ create_directories_for_path( options[:out_path] )
199
+ out_path = options[:out_path]
200
+ else
201
+ out_path = nil
202
+ end
203
+
204
+ begin
205
+ stream = open_stream( 'pipe', out_path )
206
+ yield stream, 'pipe', out_path
207
+ begin
208
+ stream.close
209
+ rescue
210
+ end
211
+
212
+ rescue Exception => e
213
+ show_exception(e, options)
214
+ end
215
+
216
+ else
217
+
218
+ if File.directory?( options[:in_path] )
219
+ pattern = options[:recursive] ? "#{File::SEPARATOR}**#{File::SEPARATOR}*.flv" : "#{File::SEPARATOR}*.flv"
220
+ file_names = Dir[options[:in_path] + pattern]
221
+ else
222
+ file_names = [options[:in_path]]
223
+ end
224
+
225
+ file_names.each do |in_path|
226
+
227
+ if options[:out_pipe]
228
+ out_path = 'pipe'
229
+ else
230
+ out_path = options[:out_path]
231
+ unless options[:out_path].nil?
232
+ out_path = in_path.gsub( options[:in_path], options[:out_path] )
233
+ create_directories_for_path( out_path )
234
+ end
235
+ end
236
+
237
+ begin
238
+ stream = open_stream( in_path, out_path, options[:stream_log] )
239
+ yield stream, in_path, out_path
240
+
241
+ begin
242
+ stream.close
243
+ rescue
244
+ end
245
+
246
+ rescue Exception => e
247
+ show_exception(e, options)
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ def open_stream(in_path, out_path = nil, stream_log = false)
254
+ attributes = (RUBY_PLATFORM =~ /win32/) ? File::BINARY : 0
255
+
256
+ if in_path == 'pipe'
257
+ in_stream = $stdin
258
+ elsif in_path == out_path || out_path.nil?
259
+ in_stream = File.open( in_path, File::RDWR|attributes )
260
+ else
261
+ in_stream = File.open( in_path, File::RDONLY|attributes )
262
+ end
263
+
264
+ if out_path == 'pipe' || ( in_path == 'pipe' && out_path.nil? )
265
+ out_stream = $stdout
266
+ elsif in_path != out_path && !out_path.nil?
267
+ out_stream = File.open( out_path, File::CREAT|File::WRONLY|attributes )
268
+ else
269
+ out_stream = nil
270
+ end
271
+
272
+ FLV::FLVStream.new( in_stream, out_stream, stream_log )
273
+ end
274
+
275
+ def create_directories_for_path(path_to_build)
276
+ parts = path_to_build.split(File::SEPARATOR)
277
+ parts.shift #removes '/' or 'c:\'
278
+ parts.pop # removes filename
279
+
280
+ parts.inject('') do |path, dir|
281
+ begin
282
+ path += File::SEPARATOR + dir
283
+ Dir.mkdir path
284
+ rescue Object => e
285
+ raise e unless File.directory?(path)
286
+ end
287
+ path
288
+ end
289
+ end
290
+
291
+ def show_exception(e, options)
292
+ puts "ERROR: #{e.message}\nERROR: #{e.backtrace.join("\nERROR: ")}"
293
+ puts "Skipping file #{options[:in_path]}\n" if options[:verbose]
294
+ end
295
+ end
296
+ end
297
+ end
@@ -0,0 +1,10 @@
1
+ module FLVTool2
2
+
3
+ PROGRAMM_VERSION = [1, 0, 6]
4
+ PROGRAMM_VERSION_EXTENSION = ''
5
+
6
+ def self.version
7
+ "#{PROGRAMM_VERSION.join('.')}#{PROGRAMM_VERSION_EXTENSION}"
8
+ end
9
+ end
10
+
@@ -0,0 +1,119 @@
1
+ # Copyright (c) 2005 Norman Timmler (inlet media e.K., Hamburg, Germany)
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions
6
+ # are met:
7
+ # 1. Redistributions of source code must retain the above copyright
8
+ # notice, this list of conditions and the following disclaimer.
9
+ # 2. Redistributions in binary form must reproduce the above copyright
10
+ # notice, this list of conditions and the following disclaimer in the
11
+ # documentation and/or other materials provided with the distribution.
12
+ # 3. The name of the author may not be used to endorse or promote products
13
+ # derived from this software without specific prior written permission.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ class MiXML
27
+
28
+ def self.parse(xml)
29
+ current = {}
30
+ xml.scan( /<([!\?].*?)>|<([^\/].*?)>|<\/(.+?)>|([^<>]*)/m ) do |ignore, open_tag, close_tag, cdata|
31
+ if open_tag
32
+ tag_name = open_tag.match( /(\w+)\s?/ )[1]
33
+
34
+ parameters = {}
35
+ open_tag.scan( /(\w+)=\"(.*?)\"/ ) { |key, value| parameters[key] = value }
36
+
37
+ parameters[:parent] = current
38
+
39
+ if current[tag_name].class == Array
40
+ current[tag_name] << parameters
41
+ current = current[tag_name].last
42
+ else
43
+ if current[tag_name]
44
+ current[tag_name] = [ current[tag_name], parameters ]
45
+ current = current[tag_name].last
46
+ else
47
+ current[tag_name] = parameters
48
+ current = current[tag_name]
49
+ end
50
+ end
51
+
52
+ elsif close_tag
53
+ parent = current[:parent]
54
+ current.delete( :parent )
55
+ current = parent unless parent.nil?
56
+ elsif cdata
57
+ cdata.strip!
58
+ current[:cdata] = cdata unless cdata.empty?
59
+ end
60
+ end
61
+
62
+ return normalize_cdata( current )
63
+ end
64
+
65
+ def self.normalize_cdata(branch)
66
+ if branch.class == Array
67
+ branch.collect do |item|
68
+ normalize_cdata( item )
69
+ end
70
+ elsif branch.class == Hash && branch[:cdata].nil?
71
+ branch.inject( {} ) do |hash, key_value|
72
+ key, value = key_value
73
+ value = normalize_cdata( value )
74
+ hash[key] = value
75
+ hash
76
+ end
77
+ else
78
+ if branch[:cdata] && branch.size == 1
79
+ branch[:cdata]
80
+ else
81
+ branch
82
+ end
83
+ end
84
+ end
85
+
86
+ def self.dump(object, indent = 0)
87
+ ' ' * indent << dump_object(object, indent).strip
88
+ end
89
+
90
+ def self.dump_object(object, indent = 0)
91
+ xml = ''
92
+
93
+ if object.class == Object
94
+ object = object.instance_variables.inject( {} ) { |hash, var| hash[var.gsub('@', '')] = object.instance_variable_get(var); hash }
95
+ end
96
+
97
+ case object
98
+ when Array
99
+ object.each do |value|
100
+ xml << indenter(indent) << "<value>" << dump_object( value, indent + 1 ) << "</value>"
101
+ end
102
+ xml << indenter(indent - 1)
103
+ when Hash
104
+ object.each do |key, value|
105
+ xml << indenter(indent) << "<#{key}>" << dump_object( value, indent + 1 ) << "</#{key}>"
106
+ end
107
+ xml << indenter(indent - 1)
108
+ else
109
+ xml << object.to_s
110
+ end
111
+
112
+ return xml
113
+ end
114
+
115
+ def self.indenter(indent = 0)
116
+ "\n" << ' ' * indent
117
+ end
118
+ end
119
+
@@ -0,0 +1,63 @@
1
+ # Copyright (c) 2005 Norman Timmler (inlet media e.K., Hamburg, Germany)
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions
6
+ # are met:
7
+ # 1. Redistributions of source code must retain the above copyright
8
+ # notice, this list of conditions and the following disclaimer.
9
+ # 2. Redistributions in binary form must reproduce the above copyright
10
+ # notice, this list of conditions and the following disclaimer in the
11
+ # documentation and/or other materials provided with the distribution.
12
+ # 3. The name of the author may not be used to endorse or promote products
13
+ # derived from this software without specific prior written permission.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ class MiYAML
27
+
28
+ def self.dump(object, options = {})
29
+ options = { :indent => 0, :boundaries => true }.merge( options )
30
+
31
+ out = (options[:boundaries] ? "---\n" : '')
32
+ out << dump_object(object, options[:indent]).strip
33
+ out << (options[:boundaries] ? "\n...\n" : '')
34
+ out
35
+ end
36
+
37
+ def self.dump_object(object, indent = 0)
38
+ yaml = ''
39
+
40
+ if object.class == Object
41
+ object = object.instance_variables.inject( {} ) { |hash, var| hash[var.gsub('@', '')] = object.instance_variable_get(var); hash }
42
+ end
43
+
44
+ case object
45
+ when Array
46
+ object.each do |value|
47
+ yaml << indenter(indent) << '- ' << dump_object( value, indent + 1 )
48
+ end
49
+ when Hash
50
+ object.each do |key, value|
51
+ yaml << indenter(indent) << "#{key}: " << dump_object( value, indent + 1 )
52
+ end
53
+ else
54
+ yaml << object.to_s
55
+ end
56
+
57
+ return yaml
58
+ end
59
+
60
+ def self.indenter(indent = 0)
61
+ "\n" << ' ' * indent
62
+ end
63
+ end