douque9982 2017-08-05 20:23
浏览 44
已采纳

从Go程序中读取媒体密钥

I am writing a media cross-platform distributed media player for use on my own network.

The current version has three/four parts:

  1. A NAS holding the audio files.
  2. A metadata server holding information about the files.
  3. A HTML/JS client that allows manipulation of the metadata server and queuing media for the:
  4. A player deamon.

My problem lies with part 4. The player has no UI, nor does it need one. It will be controlled via network commands from the client and by listening to the media keys on its current host.

The player daemon needs to work on both Windows and Linux, but I can't seem to figure out a way (any way) to read these keys on either OS. Most of the way I know to read the keyboard will not read these keys at all.

  • 写回答

1条回答 默认 最新

  • doudiejian5827 2017-08-07 13:10
    关注

    With the help of several commenters, I now have it all figured out.

    The Linux version is as follows:

    package main
    
    import (
        “bytes”
        “encoding/binary”
        “fmt”
        “os”
        “os/exec”
        “syscall”
    )
    
    // parses through the /proc/bus/input/devices file for keyboard devices.
    // Copied from `github.com/gearmover/keylogger` with trivial modification.
    func dumpDevices() ([]string, error) {
        cmd := exec.Command(“/bin/sh”, “-c”, “/bin/grep -E ‘Handlers|EV=’ /proc/bus/input/devices | /bin/grep -B1 ‘EV=120013’ | /bin/grep -Eo ‘event[0-9]+’”)
    
        output, err := cmd.Output()
        if err != nil {
            return nil, err
        }
    
        buf := bytes.NewBuffer(output)
    
        var devices []string
    
        for line, err := buf.ReadString(‘
    ’); err == nil; {
            devices = append(devices, “/dev/input/”+line[:len(line)-1])
    
            line, err = buf.ReadString(‘
    ’)
        }
    
        return devices, nil
    }
    
    // Using MS names, just because I don’t feel like looking up the Linux versions.
    var keys = map[uint16]string{
        0xa3: “VK_MEDIA_NEXT_TRACK”,
        0xa5: “VK_MEDIA_PREV_TRACK”,
        0xa6: “VK_MEDIA_STOP”,
        0xa4: “VK_MEDIA_PLAY_PAUSE”,
    }
    
    // Most of the code here comes from `github.com/gearmover/keylogger`.
    func main() {
        // drop privileges when executing other programs
        syscall.Setgid(65534)
        syscall.Setuid(65534)
    
        // dump our keyboard devices from /proc/bus/input/devices
        devices, err := dumpDevices()
        if err != nil {
            fmt.Println(err)
        }
        if len(devices) == 0 {
            fmt.Println(“No input devices found”)
            return
        }
    
        // bring back our root privs
        syscall.Setgid(0)
        syscall.Setuid(0)
    
        // Open the first keyboard device.
        input, err := os.OpenFile(devices[0], os.O_RDONLY, 0600)
        if err != nil {
            fmt.Println(err)
            return
        }
        defer input.Close()
    
        // Log media keys
        var buffer = make([]byte, 24)
        for {
            // read the input events as they come in
            n, err := input.Read(buffer)
            if err != nil {
                return
            }
    
            if n != 24 {
                fmt.Println(“Weird Input Event Size: “, n)
                continue
            }
    
            // parse the input event according to the <linux/input.h> header struct
            binary.LittleEndian.Uint64(buffer[0:8]) // Time stamp stuff I could care less about
            binary.LittleEndian.Uint64(buffer[8:16])
            etype := binary.LittleEndian.Uint16(buffer[16:18])        // Event Type. Always 1 for keyboard events
            code := binary.LittleEndian.Uint16(buffer[18:20])         // Key scan code
            value := int32(binary.LittleEndian.Uint32(buffer[20:24])) // press(1), release(0), or repeat(2)
    
            if etype == 1 && value == 1 && keys[code] != “” {
                // In a real application I would send a message here.
                fmt.Println(keys[code])
            }
        }
    }
    

    And the Windows version:

    package main
    
    import (
        “fmt”
        “syscall”
        “time”
    )
    
    var user32 = syscall.NewLazyDLL(“user32.dll”)
    var procGAKS = user32.NewProc(“GetAsyncKeyState”)
    
    // Key codes from MSDN
    var keys = [4]uint{
        0xb0, // VK_MEDIA_NEXT_TRACK
        0xb1, // VK_MEDIA_PREV_TRACK
        0xb2, // VK_MEDIA_STOP
        0xb3, // VK_MEDIA_PLAY_PAUSE
    }
    
    var names = [4]string{
        “VK_MEDIA_NEXT_TRACK”,
        “VK_MEDIA_PREV_TRACK”,
        “VK_MEDIA_STOP”,
        “VK_MEDIA_PLAY_PAUSE”,
    }
    
    func main() {
        fmt.Println(“Running…”)
    
        // Since I don’t want to trigger dozens of times for each key I need to track state.
        // I could check the bits of GAKS’ return value, but that is not reliable.
        down := [4]bool{false, false, false, false}
    
        for {
            time.Sleep(1 * time.Millisecond)
            for i, key := range keys {
                // val is not a simple boolean!
                // 0 means “not pressed” (also certain errors)
                // If LSB is set the key was just pressed (this may not be reliable)
                // If MSB is set the key is currently down.
                val, _, _ := procGAKS.Call(uintptr(key))
    
                // Turn a press into a transition and track key state.
                goingdown := false
                if int(val) != 0 && !down[i] {
                    goingdown = true
                    down[i] = true
                }
                if int(val) == 0 && down[i] {
                    down[i] = false
                }
                if goingdown {
                    // In a real application I would send a message here.
                    fmt.Println(names[i])
                }
            }
        }
    }
    

    The only "issue" is that the Linux version must be run as root. For me this is not a problem. If running as root is a problem I think there is a way that involves X11...

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 拟通过pc下指令到安卓系统,如果追求响应速度,尽可能无延迟,是不是用安卓模拟器会优于实体的安卓手机?如果是,可以快多少毫秒?
  • ¥20 神经网络Sequential name=sequential, built=False
  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
  • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)
  • ¥15 相敏解调 matlab
  • ¥15 求lingo代码和思路
  • ¥15 公交车和无人机协同运输
  • ¥15 stm32代码移植没反应