/camera/imageviewer

To get this branch, use:
bzr branch http://darksoft.org/webbzr/camera/imageviewer

« back to all changes in this revision

Viewing changes to player.rb

  • Committer: Suren A. Chilingaryan
  • Date: 2011-02-13 01:34:55 UTC
  • Revision ID: csa@dside.dyndns.org-20110213013455-7999955h7v4uf9m8
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/ruby
 
2
require 'fileutils'
 
3
 
 
4
require 'gtk2'
 
5
require 'gtkglext'
 
6
#require 'libglade2'
 
7
 
 
8
require 'RMagick'
 
9
include Magick
 
10
 
 
11
class Progress < Gtk::ProgressBar
 
12
 def initialize(parent, length, title)
 
13
    @length = length
 
14
    @iteration = 0
 
15
    super()
 
16
    #eventbox = Gtk::EventBox.new
 
17
    #eventbox.add(self)
 
18
    window = Gtk::Window.new(Gtk::Window::TOPLEVEL)
 
19
    window.title = title
 
20
    window.set_size_request(400, 30)
 
21
    window.transient_for=parent
 
22
    #window.add(eventbox)
 
23
    window.add(self)
 
24
    window.show_all
 
25
    window.modal = true
 
26
    window.deletable = false
 
27
    
 
28
    @window = window
 
29
 end
 
30
 
 
31
 def title=(title)
 
32
    @iteration = 0
 
33
    parent_window.title = title
 
34
 
 
35
    set_fraction(0)
 
36
#    if Gtk.events_pending?
 
37
#       while Gtk.main_iteration
 
38
#       end
 
39
#    end
 
40
 end
 
41
 
 
42
 def iteration()
 
43
    @iteration += 1
 
44
    set_fraction(Float(@iteration) / @length)
 
45
 
 
46
#    $block = true
 
47
#    while (Gtk.events_pending?)
 
48
#       puts 'new'
 
49
#       Gtk.main_iteration
 
50
#       puts 'done'
 
51
#    end
 
52
#    $block = false
 
53
 end
 
54
 
 
55
 def destroy
 
56
    @window.destroy
 
57
#    super
 
58
#    if Gtk.events_pending?
 
59
#       Gtk.main_iteration 
 
60
#    end
 
61
 end
 
62
end
 
63
 
 
64
class Player < Gtk::Builder
 
65
 attr_accessor :player
 
66
 
 
67
 def initialize(fps, width, height, images, real_frames)
 
68
    if images.class == Array
 
69
        @memory = true
 
70
        @images = images
 
71
        @writed  = false
 
72
    else
 
73
        @memory = false
 
74
        @images = Dir.glob(sprintf("saved_camera_images/%s/*.tif", images))
 
75
        @session = images
 
76
 
 
77
        pixbuf = Gdk::Pixbuf.new(@images[0])
 
78
        width = pixbuf.width
 
79
        height = pixbuf.height
 
80
        pixbuf = nil
 
81
    end
 
82
 
 
83
    @real_frames = real_frames
 
84
 
 
85
    size = @images.length * width * height / 1024 / 1024
 
86
    size = 1 if size < 1
 
87
    @size = (size < 4096)?sprintf("%i MB", size):sprintf("%i GB", (Float(size)/1024).round)
 
88
    
 
89
    @width = width
 
90
    @height = height
 
91
    @frame = 1
 
92
    @fps = fps
 
93
    @ms = 1000 / fps
 
94
    @playing = false
 
95
 
 
96
    super()
 
97
    
 
98
    add_from_file('player.glade')
 
99
    connect_signals { |handler| method(handler) }
 
100
 
 
101
    @player = get_object('player')
 
102
 
 
103
    if @memory then
 
104
        @player.title = sprintf("Current (%i frames, %s)", @images.length, @size)
 
105
    elsif @session =~ /^(\d{8}_\d{6})(\.(.+))?$/ then
 
106
        @player.title = sprintf("%s (%i frames, %s)", $2?$3:$1, @images.length, @size)
 
107
    else
 
108
        @player.title = sprintf("%s (%i frames, %s)", @session, @images.length, @size)
 
109
    end
 
110
 
 
111
    
 
112
    @area = get_object('draw')
 
113
    @area.set_gl_capability(Gdk::GLConfig.new(Gdk::GLConfig::MODE_DEPTH | Gdk::GLConfig::MODE_DOUBLE | Gdk::GLConfig::MODE_RGB))
 
114
    @area.set_size_request(width, height)
 
115
    
 
116
    Gtk::Drag.source_set(@area, 
 
117
        Gdk::Window::BUTTON1_MASK, 
 
118
        [["frame-number", Gtk::Drag::TARGET_SAME_APP, 1]],
 
119
        Gdk::DragContext::ACTION_COPY
 
120
    )
 
121
 
 
122
    from = get_object('from')
 
123
    to = get_object('to')
 
124
 
 
125
    @input_field = false
 
126
    @area.signal_connect("button-press-event") { |w, e|
 
127
        if (e.event_type == Gdk::Event::BUTTON2_PRESS) then
 
128
            widget = @input_field?to:from
 
129
            widget.text = sprintf("%i", @framesel.value)
 
130
            @input_field = !@input_field
 
131
        end
 
132
    }
 
133
 
 
134
    [from, to].each { |widget|
 
135
        Gtk::Drag.dest_set(widget,
 
136
            Gtk::Drag::DEST_DEFAULT_MOTION | Gtk::Drag::DEST_DEFAULT_HIGHLIGHT,
 
137
            [["frame-number", Gtk::Drag::TARGET_SAME_APP, 1]], 
 
138
            Gdk::DragContext::ACTION_COPY)
 
139
 
 
140
        widget.signal_connect("drag-drop") { |w, dc, x, y, time|
 
141
            w.text = sprintf("%i", @framesel.value)
 
142
        }
 
143
    }
 
144
 
 
145
    @player.visible = true
 
146
    
 
147
    @region = Gdk::Rectangle.new(0, 0, width, height)
 
148
    
 
149
    @framesel = get_object('frame')
 
150
    @framesel.set_range(1, @images.length)
 
151
    @framesel.value = 1
 
152
 
 
153
    frame_value_changed_cb(@framesel)
 
154
 end
 
155
 
 
156
# Player window callbacks
 
157
 def frame_value_changed_cb(widget)
 
158
    @frame = widget.value
 
159
    @area.window.invalidate(@region, false)
 
160
 end
 
161
 
 
162
 def frame_format_value_cb(widget, digits)
 
163
    val = widget.value
 
164
    return @real_frames?sprintf('%i (%i)', val, @real_frames[val - 1]):sprintf('%i', val)
 
165
 end
 
166
 
 
167
 def play_clicked_cb(widget)
 
168
    get_object('play').sensitive = false
 
169
    @play = true
 
170
    @playing = true
 
171
    
 
172
    @frame = 1 if @frame == @images.length
 
173
    
 
174
    GLib::Timeout.add(@ms) {
 
175
        if (@play) and (@frame < @images.length) then
 
176
            @framesel.value = @frame + 1
 
177
            true
 
178
        else
 
179
            get_object('play').sensitive = true
 
180
            @playing = false
 
181
            false
 
182
        end
 
183
    }
 
184
    
 
185
 end
 
186
 
 
187
 def forward_clicked_cb(widget)
 
188
    if (@frame < @images.length) then
 
189
        @framesel.value = @frame + 1
 
190
    else
 
191
        @framesel.value = 0
 
192
    end
 
193
 end
 
194
 
 
195
 def back_clicked_cb(widget)
 
196
    if (@frame > 0) then
 
197
        @framesel.value = @frame - 1
 
198
    else
 
199
        @framesel.value = @images.length - 1
 
200
    end
 
201
 end
 
202
 
 
203
 def pause_clicked_cb(widget)
 
204
    @play = false
 
205
 end
 
206
 
 
207
 def draw_expose_event_cb(area, event)
 
208
    if @memory then
 
209
        Gdk::RGB.draw_gray_image(area.window, area.style.fg_gc(area.state), 0, 0, @width, @height, Gdk::RGB::DITHER_NORMAL, @images[@frame - 1], @width)
 
210
    else
 
211
#       image = ImageList.new(@images[@frame - 1]).first
 
212
        pixbuf = Gdk::Pixbuf.new(@images[@frame - 1], @width, @height)
 
213
        @area.window.draw_pixbuf(nil, pixbuf, 0, 0, 0, 0, @width, @height, Gdk::RGB::DITHER_NORMAL, 0, 0)
 
214
    end
 
215
 end
 
216
 
 
217
 def save_images(name, progress)
 
218
    if @memory and not @writed then
 
219
        geometry = Magick::Geometry.new(@width, @height, nil, nil, Magick::AspectGeometry)
 
220
 
 
221
        time = Time.now
 
222
        basename = (name =~ /([^\/]+)\.avi$/i)?$1:name
 
223
        @session = sprintf("%02i%02i%02i_%02i%02i%02i.", time.year, time.month, time.day, time.hour, time.min, time.sec) + basename
 
224
        FileUtils::mkdir_p('saved_camera_images/' + @session)
 
225
        
 
226
        key = 0
 
227
        @images.each { |data|
 
228
            img = Image.from_blob(data) {
 
229
                self.format = "GRAY"
 
230
                self.depth = 8
 
231
                self.size = geometry
 
232
            }[0]
 
233
                
 
234
            key += 1    
 
235
            img.write(sprintf("saved_camera_images/%s/PIC%09i.tif", @session, key))
 
236
            img.destroy!
 
237
 
 
238
            progress.iteration if progress
 
239
        }        
 
240
 
 
241
        @writed = true
 
242
    end
 
243
 end
 
244
 
 
245
 def save(name = nil)
 
246
    if @memory and not @writed then
 
247
        if not name then
 
248
            fs = Gtk::Dialog.new("Please select a name", @player, Gtk::Dialog::DESTROY_WITH_PARENT,
 
249
                [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
 
250
                [ Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT ]
 
251
            )
 
252
 
 
253
            entry = Gtk::Entry.new      
 
254
            fs.vbox.add(entry)
 
255
            fs.show_all
 
256
        
 
257
            response = fs.run
 
258
            name = entry.text if response == Gtk::Dialog::RESPONSE_ACCEPT
 
259
            fs.destroy
 
260
        end
 
261
        
 
262
        if name then 
 
263
            progress = Progress.new(@player, @images.length, sprintf("Saving %i images (%s)", @images.length, @size))
 
264
        
 
265
            thr = Thread.start {
 
266
                save_images(name, progress)
 
267
 
 
268
                Gtk.main_quit
 
269
            }
 
270
            Gtk.main
 
271
            thr.join
 
272
        
 
273
            progress.destroy
 
274
        end
 
275
    end
 
276
 end
 
277
 
 
278
 def save_clicked_cb(widget)
 
279
    save
 
280
 end
 
281
 
 
282
 def video_clicked_cb(widget)
 
283
    FileUtils::mkdir_p("saved_camera_videos")
 
284
 
 
285
    filter = Gtk::FileFilter.new
 
286
    filter.name = "Video Files"
 
287
    filter.add_pattern('*.avi')
 
288
    
 
289
    fs = Gtk::FileChooserDialog.new("Please select a file to save", nil, Gtk::FileChooser::ACTION_SAVE, nil, 
 
290
        [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
 
291
        [ Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT ]
 
292
    )
 
293
    fs.current_folder = 'saved_camera_videos'
 
294
    fs.add_filter(filter)
 
295
 
 
296
    response = fs.run
 
297
    name = fs.filename
 
298
    fs.destroy
 
299
 
 
300
    if response == Gtk::Dialog::RESPONSE_ACCEPT
 
301
        name = name + '.avi' if name !~ /\.avi$/i
 
302
 
 
303
        progress = Progress.new(@player, @images.length, sprintf("Saving %i images (%s)", @images.length, @size))
 
304
        
 
305
        thr = Thread.start {
 
306
            save_images(name, progress)
 
307
        
 
308
            FileUtils::mkdir_p("temporary_camera_images/" + @session)
 
309
        
 
310
            progress.title = sprintf("Converting %i images (%s)", @images.length, @size)
 
311
            
 
312
            Dir.glob(sprintf("saved_camera_images/%s/*.tif", @session)) { |image_name|
 
313
                output = image_name.sub(/saved_camera_images/, "temporary_camera_images")
 
314
                output.sub!(/tif$/, "bmp")
 
315
            
 
316
                image = Magick::ImageList.new(image_name)
 
317
                image.write(output)
 
318
                image.destroy!
 
319
            
 
320
                progress.iteration
 
321
            }
 
322
            
 
323
            progress.title = sprintf("Encoding %i images (%s)", @images.length, @size)
 
324
            mplayer = open("|mencoder mf://temporary_camera_images/#{@session}/*.bmp -mf fps=#{@fps}:type=bmp -ovc x264 -x264encopts bitrate=3000 -o #{name} 2>/dev/null") { |pipe|
 
325
                pipe.each("r") { |line|
 
326
                    progress.set_fraction(Float($1)/100) if line =~ /Pos:[^(]*\(\s*(\d+)%\)/
 
327
                }
 
328
                
 
329
            }
 
330
            
 
331
            progress.title = "Cleaning temporary files"
 
332
            FileUtils::remove_dir("temporary_camera_images/" + @session)
 
333
            
 
334
            #system "./encode.sh \"#{name}\" \"#{@session}\" #{@fps}"
 
335
            Gtk.main_quit
 
336
        }
 
337
        Gtk.main
 
338
        thr.join
 
339
        
 
340
        progress.destroy
 
341
    end
 
342
 end
 
343
 
 
344
 def cut_clicked_cb(widget)
 
345
    begin
 
346
        from = Integer(get_object('from').text)
 
347
    rescue 
 
348
        from = 0
 
349
    end
 
350
        
 
351
    begin
 
352
        to = Integer(get_object('to').text)
 
353
    rescue 
 
354
        to = @images.length - 1
 
355
    end
 
356
        
 
357
    from = 0 if (from < 0)
 
358
    to = @images.length -1 if (to <= 0) or (to >= @images.length)
 
359
 
 
360
    if @memory then
 
361
        Player.new(@fps, @width, @height, @images[from..to], (@real_frames)?(@real_frames[from..to]):nil)
 
362
    else
 
363
        name = nil
 
364
        
 
365
        fs = Gtk::Dialog.new("Please select a name", @player, Gtk::Dialog::DESTROY_WITH_PARENT,
 
366
            [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
 
367
            [ Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT ]
 
368
        )
 
369
 
 
370
        entry = Gtk::Entry.new
 
371
        fs.vbox.add(entry)
 
372
        fs.show_all
 
373
        
 
374
        response = fs.run
 
375
        name = entry.text if response == Gtk::Dialog::RESPONSE_ACCEPT
 
376
        fs.destroy
 
377
 
 
378
        time = Time.now
 
379
        if name then
 
380
            session = sprintf("%02i%02i%02i_%02i%02i%02i.", time.year, time.month, time.day, time.hour, time.min, time.sec) + name
 
381
        else
 
382
            session = sprintf("%02i%02i%02i_%02i%02i%02i", time.year, time.month, time.day, time.hour, time.min, time.sec)
 
383
        end
 
384
            
 
385
        FileUtils::mkdir_p('saved_camera_images/' + session)
 
386
        
 
387
        key = 0
 
388
        @images[from..to].each { |src|
 
389
            key += 1
 
390
            dst = sprintf("saved_camera_images/%s/PIC%09i.tif", session, key)
 
391
            FileUtils::ln(src, dst)
 
392
        }
 
393
        
 
394
        Player.new(@fps, @width, @height, session, (@real_frames)?(@real_frames[from..to]):nil)
 
395
    end
 
396
 end
 
397
 
 
398
 def gtk_widget_delete(widget, ev)
 
399
    @play = false
 
400
 
 
401
    while @playing 
 
402
        Gtk.main_iteration      
 
403
    end
 
404
 
 
405
    # Generally we should somehow clean complete object, do it later
 
406
    @images = nil
 
407
    @real_frames = nil
 
408
    GC.start
 
409
    
 
410
#    @player.destroy
 
411
 
 
412
    return false
 
413
 end
 
414
 
 
415
 def gtk_widget_destroy(widget)
 
416
 end
 
417
end
 
418
 
 
419
if $*.length > 0 then
 
420
    player = Player.new(25, -1, -1, $*[0], nil)
 
421
    
 
422
    #metaclass = class << player; self; end
 
423
    #metaclass.send(:define_method, :gtk_widget_destroy) { Gtk.main_quit }
 
424
    #player.gtk_widget_destroy(nil)
 
425
 
 
426
    player.player.signal_connect('destroy') { Gtk.main_quit }
 
427
    Gtk.main
 
428
end