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