source: trunk/lua/gui_live.lua @ 238

Revision 238, 13.4 KB checked in by reyalp, 13 months ago (diff)

start removing old protocol

  • Property svn:eol-style set to native
Line 
1--[[
2 Copyright (C) 2010-2011 <reyalp (at) gmail dot com>
3
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License version 2 as
6  published by the Free Software Foundation.
7
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  GNU General Public License for more details.
12
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16]]
17--[[
18module for live view gui
19]]
20local stats=require('gui_live_stats')
21
22local m={
23        vp_par = 2, -- pixel aspect ratio for viewport 1:n, n=1,2
24        bm_par = 1, -- pixel aspect ratio for bitmap 1:n, n=1,2
25        vp_aspect_factor = 1, -- correction factor for height values when scaling for aspect
26--[[
27note - these are 'private' but exposed in the module for easier debugging
28container -- outermost widget
29icnv -- iup canvas
30vp_active -- viewport streaming selected
31bm_active -- bitmap streaming selected
32timer -- timer for fetching updates
33statslabel -- text for stats
34]]
35}
36
37local screen_aspects = {
38        [0]=4/3,
39        16/9,
40}
41
42function m.live_support()
43        local caps = guisys.caps()
44        return (caps.CD and caps.LIVEVIEW)
45end
46
47function m.get_current_frame_data()
48        if m.dump_replay then
49                return m.dump_replay_frame
50        end
51        if con.live then
52                return con.live.frame
53        end
54end
55
56function m.get_current_base_data()
57        if m.dump_replay then
58                return m.dump_replay_base
59        end
60        if con.live then
61                return con.live.base
62        end
63end
64
65local vp_toggle=iup.toggle{
66        title="Viewfinder",
67        action=function(self,state)
68                m.vp_active = (state == 1)
69        end,
70}
71
72local bm_toggle = iup.toggle{
73        title="UI Overlay",
74        action=function(self,state)
75                m.bm_active = (state == 1)
76        end,
77}
78
79local aspect_toggle = iup.toggle{
80        title="Scale for A/R",
81        value="ON",
82}
83                                       
84local function get_fb_selection()
85        local what=0
86        if m.vp_active then
87                what = 1
88        end
89        if m.bm_active then
90                what = what + 4
91                what = what + 8 -- palette TODO shouldn't request if we don't understand type, but palette type is in dynamic data
92        end
93        return what
94end
95
96--[[
97update canvas size from frame
98]]
99
100local function update_canvas_size2()
101        if not con.live2 then
102                return
103        end
104        local vp_w = con.live2.vp_logical_width/m.vp_par
105        local vp_h
106        if aspect_toggle.value == 'ON' then
107                vp_h = vp_w/screen_aspects[con.live2.lcd_aspect_ratio]
108                m.vp_aspect_factor = vp_h/con.live2.vp_logical_height
109        else
110                m.vp_aspect_factor = 1
111                vp_h = con.live2.vp_logical_height
112        end
113
114        local w,h = gui.parsesize(m.icnv.rastersize)
115       
116        local update
117        if w ~= vp_w then
118                update = true
119        end
120        if h ~= vp_h then
121                update = true
122        end
123        if update then
124                m.icnv.rastersize = vp_w.."x"..vp_h
125                iup.Refresh(m.container)
126                gui.resize_for_content()
127        end
128end
129
130local vp_par_toggle = iup.toggle{
131        title="Viewfinder 1:1",
132        action=function(self,state)
133                if state == 1 then
134                        m.vp_par = 1
135                else
136                        m.vp_par = 2
137                end
138        end,
139}
140
141local bm_par_toggle = iup.toggle{
142        title="Overlay 1:1",
143        value="1",
144        action=function(self,state)
145                if state == 1 then
146                        m.bm_par = 1
147                else
148                        m.bm_par = 2
149                end
150        end,
151}
152local bm_fit_toggle = iup.toggle{
153        title="Overlay fit",
154        value="ON",
155}
156
157local function update_should_run()
158        if not m.live_con_valid then
159                return false
160        end
161        if not con:is_connected() or m.tabs.value ~= m.container then
162                return false
163        end
164        return (m.vp_active or m.bm_active)
165end
166
167local last_frame_fields = {}
168local palette_size_for_type={
169        16*4,
170        16*4,
171        256*4,
172}
173local function update_frame_data2(frame)
174        local dirty
175        for i,f in ipairs(chdku.live2_field_names) do
176                local v = chdku.live2_get_frame_field(frame,f)
177                if v ~= last_frame_fields[f] then
178                        dirty = true
179                end
180        end
181        if dirty then
182                printf('update_frame_data: changed\n')
183                for i,f in ipairs(chdku.live2_field_names) do
184                        local v = chdku.live2_get_frame_field(frame,f)
185                        printf("%s:%s->%s\n",f,tostring(last_frame_fields[f]),v)
186                        last_frame_fields[f]=v
187                end
188                -- for big palettes this lags, should be an otpion
189                --[[
190                if last_frame_fields.palette_data_start > 0 then
191                        printf('palette:\n')
192                        local c=0
193
194                        local bytes = {frame:byte(last_frame_fields.palette_data_start+1,
195                                                                                last_frame_fields.palette_data_start+palette_size_for_type[last_frame_fields.palette_type])}
196                        for i,v in ipairs(bytes) do
197                                printf("0x%02x,",v)
198                                c = c + 1
199                                if c == 16 then
200                                        printf('\n')
201                                        c=0
202                                else
203                                        printf(' ')
204                                end
205                        end
206                end
207                ]]
208        end
209end
210
211-- TODO this is just to allow us to read/write a binary integer record size
212local dump_recsize = lbuf.new(4)
213
214--[[
215lbuf - optional lbuf to re-use, if possible
216fh - file handle
217returns (possibly new) lbuf or nil on eof
218]]
219local function read_dump_rec(lb,fh)
220        if not dump_recsize:fread(fh) then
221                return
222        end
223        local len = dump_recsize:get_u32()
224        if not lb or lb:len() ~= len then
225                lb = lbuf.new(len)
226        end
227        if lb:fread(fh) then -- on EOF, return nil
228                return lb
229        end
230end
231
232local function init_dump_replay()
233        m.dump_replay_file = io.open(m.dump_replay_filename,"rb")
234        if not m.dump_replay_file then
235                printf("failed to open dumpfile\n")
236                m.dump_replay = false
237                return
238        end
239        m.dump_replay_base = read_dump_rec(m.dump_replay_frame,m.dump_replay_file)
240        update_base_data(m.dump_replay_base)
241end
242
243local function end_dump_replay()
244        m.dump_replay_file:close()
245        m.dump_replay_file=nil
246        m.dump_replay_base=nil
247        m.dump_replay_frame=nil
248        stats:stop()
249end
250
251local function read_dump_frame()
252        stats:start()
253        stats:start_xfer()
254
255        local data = read_dump_rec(m.dump_replay_frame,m.dump_replay_file)
256        -- EOF, loop
257        if not data then
258                end_dump_replay()
259                init_dump_replay()
260                data = read_dump_rec(m.dump_replay_frame,m.dump_replay_file)
261        end
262        m.dump_replay_frame = data
263        update_frame_data(m.dump_replay_frame)
264        stats:end_xfer(m.dump_replay_frame:len())
265        -- TODO
266        update_canvas_size()
267end
268
269local function end_dump()
270        if con.live and con.live.dump_fh then
271                printf('%d bytes recorded to %s\n',tonumber(con.live.dump_size),tostring(con.live.dump_fn))
272                con:live_dump_end()
273        end
274end
275
276local function record_dump()
277        if not m.dump_active then
278                return
279        end
280        if not con.live.dump_fh then
281                local status,err = con:live_dump_start()
282                if not status then
283                        printf('error starting dump:%s\n',tostring(err))
284                        m.dump_active = false
285                        -- TODO update checkbox
286                        return
287                end
288                printf('recording to %s\n',con.live.dump_fn)
289        end
290        local status,err = con:live_dump_frame()
291        if not status then
292                printf('error dumping frame:%s\n',tostring(err))
293                end_dump()
294                m.dump_active = false
295        end
296end
297
298local function toggle_dump(ih,state)
299        m.dump_active = (state == 1)
300        -- TODO this should be called on disconnect etc
301        if not m.dumpactive then
302                end_dump()
303        end
304end
305
306local function toggle_play_dump(self,state)
307        if state == 1 then
308                local filedlg = iup.filedlg{
309                        dialogtype = "OPEN",
310                        title = "File to play", 
311                        filter = "*.lvdump", 
312                } 
313                filedlg:popup (iup.ANYWHERE, iup.ANYWHERE)
314
315                local status = filedlg.status
316                local value = filedlg.value
317                if status ~= "0" then
318                        printf('play dump canceled\n')
319                        self.value = "OFF"
320                        return
321                end
322                printf('playing %s\n',tostring(value))
323                m.dump_replay_filename = value
324                init_dump_replay()
325                m.dump_replay = true
326        else
327                end_dump_replay()
328                m.dump_replay = false
329        end
330end
331
332
333local function timer_action(self)
334        if update_should_run() then
335                stats:start()
336                local what=get_fb_selection()
337                if what == 0 then
338                        return
339                end
340                stats:start_xfer()
341                --local status,err = con:live_get_frame(what)
342                local status,err = con:live2_get_frame(what)
343                if not status then
344                        end_dump()
345                        printf('error getting frame: %s\n',tostring(err))
346                        gui.update_connection_status() -- update connection status on error, to prevent spamming
347                        stats:stop()
348                else
349                        --stats:end_xfer(con.live.frame:len())
350                        stats:end_xfer(con.live2._frame:len())
351                        update_frame_data2(con.live2._frame)
352                        --update_frame_data(con.live.frame)
353                        --record_dump()
354                        update_canvas_size2()
355                end
356                m.icnv:action()
357        elseif m.dump_replay then
358                read_dump_frame()
359                m.icnv:action()
360        else
361                stats:stop()
362        end
363        m.statslabel.title = stats:get()
364end
365
366local function init_timer(time)
367        if not time then
368                time = "100"
369        end 
370        if m.timer then
371                iup.Destroy(m.timer)
372        end
373        m.timer = iup.timer{ 
374                time = time,
375                action_cb = function()
376                        -- use xpcall so we don't get a popup every frame
377                        local cstatus,msg = xpcall(timer_action,util.err_traceback)
378                        if not cstatus then
379                                printf('live timer update error\n%s',tostring(msg))
380                                -- TODO could stop live updates here, for now just spam the console
381                        end
382                end,
383        }
384        m.update_run_state()
385end
386
387local function update_fps(val)
388        val = tonumber(val)
389        if val == 0 then
390                return
391        end
392        val = math.floor(1000/val)
393        if val ~= tonumber(m.timer.time) then
394                -- reset stats
395                stats:stop()
396                init_timer(val)
397        end
398end
399
400local function redraw_canvas(self)
401        if m.tabs.value ~= m.container then
402                return;
403        end
404        local ccnv = self.dccnv
405        stats:start_frame()
406        ccnv:Activate()
407        ccnv:Clear()
408        local lv = con.live2
409        if lv and lv._frame then
410                if m.vp_active then
411                        m.vp_img = liveimg.get_viewport2_pimg(m.vp_img,lv._frame,m.vp_par == 2)
412                        if m.vp_img then
413                                if aspect_toggle.value == "ON" then
414                                        m.vp_img:put_to_cd_canvas(ccnv,
415                                                lv.vp_buffer_logical_xoffset/m.vp_par,
416                                                (lv.vp_logical_height - lv.vp_visible_height - lv.vp_buffer_logical_yoffset)*m.vp_aspect_factor,
417                                                m.vp_img:width(),
418                                                m.vp_img:height()*m.vp_aspect_factor)
419                                else
420                                        m.vp_img:put_to_cd_canvas(ccnv,
421                                                lv.vp_buffer_logical_xoffset/m.vp_par,
422                                                lv.vp_logical_height - lv.vp_visible_height - lv.vp_buffer_logical_yoffset)
423                                end
424                        end
425                end
426                if m.bm_active then
427                        m.bm_img = liveimg.get_bitmap2_pimg(m.bm_img,lv._frame,m.bm_par == 2)
428                        if m.bm_img then
429                                if bm_fit_toggle.value == "ON" then
430                                        m.bm_img:blend_to_cd_canvas(ccnv, 0, 0, lv.vp_logical_width/m.vp_par, lv.vp_logical_height*m.vp_aspect_factor)
431                                else
432                                        m.bm_img:blend_to_cd_canvas(ccnv, 0, lv.vp_logical_height - lv.bm_visible_height)
433                                end
434                        else
435                                print('no bm')
436                        end
437                end
438        end
439        ccnv:Flush()
440        stats:end_frame()
441end
442
443function m.init()
444        if not m.live_support() then
445                return false
446        end
447        local icnv = iup.canvas{rastersize="360x240",border="NO",expand="NO"}
448        m.icnv = icnv
449        m.statslabel = iup.label{size="90x80",alignment="ALEFT:ATOP"}
450        m.container = iup.hbox{
451                iup.frame{
452                        icnv,
453                },
454                iup.vbox{
455                        iup.frame{
456                                iup.vbox{
457                                        vp_toggle,
458                                        bm_toggle,
459                                        vp_par_toggle,
460                                        bm_par_toggle,
461                                        bm_fit_toggle,
462                                        aspect_toggle,
463                                        iup.hbox{
464                                                iup.label{title="Target FPS"},
465                                                iup.text{
466                                                        spin="YES",
467                                                        spinmax="30",
468                                                        spinmin="1",
469                                                        spininc="1",
470                                                        value="10",
471                                                        action=function(self,c,newval)
472                                                                local v = tonumber(newval)
473                                                                local min = tonumber(self.spinmin)
474                                                                local max = tonumber(self.spinmax)
475                                                                if v and v >= min and v <= max then
476                                                                        self.value = tostring(v)
477                                                                        self.caretpos = string.len(tostring(v))
478                                                                        update_fps(self.value)
479                                                                end
480                                                                return iup.IGNORE
481                                                        end,
482                                                        spin_cb=function(self,newval)
483                                                                update_fps(newval)
484                                                        end
485                                                },
486                                        },
487                                },
488                                title="Stream"
489                        },
490                        iup.tabs{
491                                iup.vbox{
492                                        m.statslabel,
493                                        tabtitle="Statistics",
494                                },
495                                iup.vbox{
496                                        tabtitle="Debug",
497                                        iup.toggle{title="Dump to file",action=toggle_dump},
498                                        iup.toggle{title="Play from file",action=toggle_play_dump},
499                                        iup.button{
500                                                title="Quick dump",
501                                                action=function()
502                                                        add_status(cli:execute('dumpframes'))
503                                                end,
504                                        },
505                                },
506                        },
507                },
508                margin="4x4",
509                ngap="4"
510        }
511
512        function icnv:map_cb()
513                -- TODO UseContextPlus seems harmless if not built with plus support
514                if guisys.caps().CDPLUS then
515                        cd.UseContextPlus(true)
516                        printf("ContexIsPlus iup:%s cd:%s\n",tostring(cd.ContextIsPlus(cd.IUP)),tostring(cd.ContextIsPlus(cd.DBUFFER)))
517                end
518                self.ccnv = cd.CreateCanvas(cd.IUP,self)
519                self.dccnv = cd.CreateCanvas(cd.DBUFFER,self.ccnv)
520                if guisys.caps().CDPLUS then
521                        cd.UseContextPlus(false)
522                end
523                self.dccnv:SetBackground(cd.EncodeColor(32,32,32))
524        end
525
526        icnv.action=redraw_canvas
527
528        function icnv:unmap_cb()
529                self.dccnv:Kill()
530                self.ccnv:Kill()
531        end
532
533        --[[
534        function icnv:resize_cb(w,h)
535                print("Resize: Width="..w.."   Height="..h)
536        end
537        --]]
538        -- TODO - convenience meta table to access base and frame info. This should be bound to the lbuf somehow
539
540        local live_info_meta = {
541                __index=function(t,key)
542                        local base = m.get_current_base_data()
543                        local frame = m.get_current_frame_data()
544
545                        if base and chdku.live_base_map[key] then
546                                return chdku.live_get_base_field(base,key)
547                        end
548                        if frame and chdku.live_frame_map[key] then
549                                return chdku.live_get_frame_field(frame,key)
550                        end
551                end
552        }
553        m.li = {}
554        setmetatable(m.li,live_info_meta)
555
556        m.container_title='Live'
557end
558
559function m.set_tabs(tabs)
560        m.tabs = tabs
561end
562function m.get_container()
563        return m.container
564end
565function m.get_container_title()
566        return m.container_title
567end
568function m.on_connect_change(lcon)
569        m.live_con_valid = false
570        if con:is_connected() and con:live_is_api_compatible() then
571                m.live_con_valid = true
572        end
573end
574-- check whether we should be running, update timer
575function m.update_run_state(state)
576        if state == nil then
577                state = (m.tabs.value == m.container)
578        end
579        if state then
580                m.timer.run = "YES"
581                stats:start()
582        else
583                m.timer.run = "NO"
584                stats:stop()
585        end
586end
587function m.on_tab_change(new,old)
588        if not m.live_support() then
589                return
590        end
591        if new == m.container then
592                m.update_run_state(true)
593        else
594                m.update_run_state(false)
595        end
596end
597
598-- for anything that needs to be intialized when everything is started
599function m.on_dlg_run()
600        init_timer()
601end
602
603return m
Note: See TracBrowser for help on using the repository browser.