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

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

报告相同问题?

悬赏问题

  • ¥20 如何通过代码传输视频到亚马逊平台
  • ¥15 php查询mysql数据库并显示至下拉列表中
  • ¥15 freertos下使用外部中断失效
  • ¥15 输入的char字符转为int类型,不是对应的ascall码,如何才能使之转换为对应ascall码?或者使输入的char字符可以正常与其他字符比较?
  • ¥15 devserver配置完 启动服务 无法访问static上的资源
  • ¥15 解决websocket跟c#客户端通信
  • ¥30 Python调用dll文件输出Nan重置dll状态
  • ¥15 浮动div的高度控制问题。
  • ¥66 换电脑后应用程序报错
  • ¥50 array数据同步问题