host.lua
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:10k
源码类别:

midi

开发平台:

Unix_Linux

  1. --[==========================================================================[
  2.  host.lua: VLC Lua interface command line host module
  3. --[==========================================================================[
  4.  Copyright (C) 2007 the VideoLAN team
  5.  $Id$
  6.  Authors: Antoine Cellerier <dionoea at videolan dot org>
  7.  This program is free software; you can redistribute it and/or modify
  8.  it under the terms of the GNU General Public License as published by
  9.  the Free Software Foundation; either version 2 of the License, or
  10.  (at your option) any later version.
  11.  This program is distributed in the hope that it will be useful,
  12.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  GNU General Public License for more details.
  15.  You should have received a copy of the GNU General Public License
  16.  along with this program; if not, write to the Free Software
  17.  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  18. --]==========================================================================]
  19. --[==========================================================================[
  20. Example use:
  21.     require "host"
  22.     h = host.host()
  23.     -- Bypass any authentification
  24.     function on_password( client )
  25.         client:switch_status( host.status.read )
  26.     end
  27.     h.status_callbacks[host.status.password] = on_password
  28.     h:listen( "localhost:4212" )
  29.     h:listen( "*console" )
  30.     --or h:listen( { "localhost:4212", "*console" } )
  31.     -- The main loop
  32.     while not vlc.misc.should_die() do
  33.         -- accept new connections
  34.         h:accept()
  35.         -- select active clients
  36.         local write, read = h:select( 0.1 ) -- 0.1 is a timeout in seconds
  37.         -- handle clients in write mode
  38.         for _, client in pairs(write) do
  39.             client:send()
  40.             client.buffer = ""
  41.             client:switch_status( host.status.read )
  42.         end
  43.         -- handle clients in read mode
  44.         for _, client in pairs(read) do
  45.             local str = client:recv(1000)
  46.             str = string.gsub(str,"r?n$","")
  47.             client.buffer = "Got `"..str.."'.rn"
  48.             client:switch_status( host.status.write )
  49.         end
  50.     end
  51. For complete examples see existing VLC Lua interface modules (ie telnet.lua)
  52. --]==========================================================================]
  53. module("host",package.seeall)
  54. status = { init = 0, read = 1, write = 2, password = 3 }
  55. client_type = { net = 1, stdio = 2, fifo = 3 }
  56. function host()
  57.     -- private data
  58.     local clients = {}
  59.     local listeners = {}
  60.     local status_callbacks = {}
  61.     -- private data
  62.     local fds_read = vlc.net.fd_set_new()
  63.     local fds_write = vlc.net.fd_set_new()
  64.     -- private methods
  65.     local function client_accept( clients, listen )
  66.         local wait
  67.         if #clients == 0 then
  68.             wait = -1
  69.         else
  70.             wait = 0
  71.         end
  72.         return listen:accept( wait )
  73.     end
  74.     local function fd_client( client )
  75.         if client.status == status.read then
  76.             return client.rfd
  77.         else -- status.write
  78.             return client.wfd
  79.         end
  80.     end
  81.     local function send( client, data, len )
  82.         if len then
  83.             return vlc.net.send( client.wfd, data, len )
  84.         else
  85.             return vlc.net.send( client.wfd, data or client.buffer )
  86.         end
  87.     end
  88.     local function recv( client, len )
  89.         if len then
  90.             return vlc.net.recv( client.rfd, len )
  91.         else
  92.             return vlc.net.recv( client.rfd )
  93.         end
  94.     end
  95.     local function write( client, data )
  96.         return vlc.net.write( client.wfd, data or client.buffer )
  97.     end
  98.     local function read( client, len )
  99.         if len then
  100.             return vlc.net.read( client.rfd, len )
  101.         else
  102.             return vlc.net.read( client.rfd )
  103.         end
  104.     end
  105.     local function del_client( client )
  106.         if client.type == client_type.stdio then
  107.             client:send( "Cannot delete stdin/stdout client.n" )
  108.             return
  109.         end
  110.         for i, c in pairs(clients) do
  111.             if c == client then
  112.                 if client.type == client_type.net then
  113.                     if client.wfd ~= client.rfd then
  114.                         vlc.net.close( client.rfd )
  115.                     end
  116.                     vlc.net.close( client.wfd )
  117.                 end
  118.                 clients[i] = nil
  119.                 return
  120.             end
  121.         end
  122.         vlc.msg.err("couldn't find client to remove.")
  123.     end
  124.     
  125.     local function switch_status( client, s )
  126.         if client.status == s then return end
  127.         client.status = s
  128.         if status_callbacks[s] then
  129.             status_callbacks[s]( client )
  130.         end
  131.     end
  132.     -- append a line to a client's (output) buffer
  133.     local function append( client, string )
  134.         client.buffer = client.buffer .. string .. "rn"
  135.     end
  136.     local function new_client( h, fd, wfd, t )
  137.         if fd < 0 then return end
  138.         local w, r
  139.         if t == client_type.net then
  140.             w = send
  141.             r = recv
  142.         else if t == client_type.stdio or t == client_type.fifo then
  143.             w = write
  144.             r = read
  145.         else
  146.             error("Unknown client type", t )
  147.         end end
  148.         local client = { -- data
  149.                          rfd = fd,
  150.                          wfd = wfd or fd,
  151.                          status = status.init,
  152.                          buffer = "",
  153.                          type = t,
  154.                          -- methods
  155.                          fd = fd_client,
  156.                          send = w,
  157.                          recv = r,
  158.                          del = del_client,
  159.                          switch_status = switch_status,
  160.                          append = append,
  161.                        }
  162.         client:send( "VLC media player "..vlc.misc.version().."n" )
  163.         table.insert(clients, client)
  164.         client:switch_status(status.password)
  165.     end
  166.     function filter_client( fd, status, status2 )
  167.         local l = 0
  168.         fd:zero()
  169.         for _, client in pairs(clients) do
  170.             if client.status == status or client.status == status2 then
  171.                 fd:set( client:fd() )
  172.                 l = math.max( l, client:fd() )
  173.             end
  174.         end
  175.         return l
  176.     end
  177.     -- public methods
  178.     local function _listen_tcp( h, host, port )
  179.         if listeners.tcp and listeners.tcp[host]
  180.                          and listeners.tcp[host][port] then
  181.             error("Already listening on tcp host `"..host..":"..tostring(port).."'")
  182.         end
  183.         if not listeners.tcp then
  184.             listeners.tcp = {}
  185.         end
  186.         if not listeners.tcp[host] then
  187.             listeners.tcp[host] = {}
  188.         end
  189.         local listener = vlc.net.listen_tcp( host, port )
  190.         listeners.tcp[host][port] = listener
  191.         if not listeners.tcp.list then
  192.             -- FIXME: if host == "list" we'll have a problem
  193.             listeners.tcp.list = {}
  194.             local m = { __mode = "v" } -- week values
  195.             setmetatable( listeners.tcp.list, m )
  196.         end
  197.         table.insert( listeners.tcp.list, listener )
  198.     end
  199.     local function _listen_stdio( h )
  200.         
  201.         if listeners.stdio then
  202.             error("Already listening on stdio")
  203.         end
  204.         new_client( h, 0, 1, client_type.stdio )
  205.         listeners.stdio = true
  206.     end
  207.     local function _listen( h, url )
  208.         if type(url)==type({}) then
  209.             for _,u in pairs(url) do
  210.                 h:listen( u )
  211.             end
  212.         else
  213.             vlc.msg.info( "Listening on host ""..url..""." )
  214.             if url == "*console" then
  215.                 h:listen_stdio()
  216.             else
  217.                 u = vlc.net.url_parse( url )
  218.                 h:listen_tcp( u.host, u.port )
  219.             end
  220.         end
  221.     end
  222.     local function _accept( h )
  223.         if listeners.tcp then
  224.             local wait
  225.             if #clients == 0 and not listeners.stdio and #listeners.tcp.list == 1 then
  226.                 wait = -1 -- blocking
  227.             else
  228.                 wait = 0
  229.             end
  230.             for _, listener in pairs(listeners.tcp.list) do
  231.                 local fd = listener:accept( wait )
  232.                 new_client( h, fd, fd, client_type.net )
  233.             end
  234.         end
  235.     end
  236.     local function _select( h, timeout )
  237.         local nfds = math.max( filter_client( fds_read, status.read, status.password ),
  238.                                filter_client( fds_write, status.write ) ) + 1
  239.         local ret = vlc.net.select( nfds, fds_read, fds_write,
  240.                                     timeout or 0.5 )
  241.         local wclients = {}
  242.         local rclients = {}
  243.         if ret > 0 then
  244.             for _, client in pairs(clients) do
  245.                 if fds_write:isset( client:fd() ) then
  246.                     table.insert(wclients,client)
  247.                 end
  248.                 if fds_read:isset( client:fd() ) then
  249.                     table.insert(rclients,client)
  250.                 end
  251.             end
  252.         end
  253.         return wclients, rclients
  254.     end
  255.     local function destructor( h )
  256.         print "destructor"
  257.         for _,client in pairs(clients) do
  258.             client:send("Shutting down.")
  259.             if client.type == client_type.tcp then
  260.                 if client.wfd ~= client.rfd then
  261.                     vlc.net.close(client.rfd)
  262.                 end
  263.                 vlc.net.close(client.wfd)
  264.             end
  265.         end
  266.     end
  267.     local function _broadcast( h, msg )
  268.         for _,client in pairs(clients) do
  269.             client:send( msg )
  270.         end
  271.     end
  272.     -- the instance
  273.     local h = { -- data
  274.                 status_callbacks = status_callbacks,
  275.                 -- methods
  276.                 listen = _listen,
  277.                 listen_tcp = _listen_tcp,
  278.                 listen_stdio = _listen_stdio,
  279.                 accept = _accept,
  280.                 select = _select,
  281.                 broadcast = _broadcast,
  282.               }
  283.     -- the metatable
  284.     local m = { -- data
  285.                 __metatable = "Nothing to see here. Move along.",
  286.                 -- methods
  287.                 __gc = destructor,
  288.               }
  289.     setmetatable( h, m )
  290.     return h
  291. end