将类文件对象传递给 ctypes 回调 [英] Passing file-like objects to ctypes callbacks

查看:46
本文介绍了将类文件对象传递给 ctypes 回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 LibVLC Python 绑定来播放内存流(Python 3.4、Windows 7、LibVLC 3.x).最终,我的目标是将数据馈送到 BytesIO 实例中,然后 VLC 将从中读取和播放.但目前,我决定编写一个快速脚本来尝试从文件流中读取.这是代码和回溯 - 说我对 ctypes 很陌生是轻描淡写的,所以有人知道我做错了什么吗?

I'm attempting to use the LibVLC Python bindings to play an in-memory stream (Python 3.4, Windows 7, LibVLC 3.x). Eventually, my aim is to feed data into a BytesIO instance which VLC will then read from and play. But for the moment, I decided to hack up a quick script to try reading from a file stream. Here's the code and traceback - to say I'm pretty new to ctypes would be an understatement so does anyone know what I'm doing wrong?

import ctypes
import io
import sys
import time

import vlc

MediaOpenCb = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_uint64))
MediaReadCb = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t)
MediaSeekCb = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_uint64)
MediaCloseCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)


def media_open_cb(opaque, data_pointer, size_pointer):
    data_pointer.value = opaque
    size_pointer.contents.value = sys.maxsize
    return 0


def media_read_cb(opaque, buffer, length):
    stream = ctypes.cast(opaque, ctypes.py_object).value
    new_data = stream.read(length.contents)
    buffer.contents.value = new_data
    return len(new_data)


def media_seek_cb(opaque, offset):
    stream = ctypes.cast(opaque, ctypes.py_object).value
    stream.seek(offset)
    return 0


def media_close_cb(opaque):
    stream = ctypes.cast(opaque, ctypes.py_object).value
    stream.close()


callbacks = {
    'open': MediaOpenCb(media_open_cb),
    'read': MediaReadCb(media_read_cb),
    'seek': MediaSeekCb(media_seek_cb),
    'close': MediaCloseCb(media_close_cb)
}


def main(path):
    stream = open(path, 'rb')
    instance = vlc.Instance()
    player = instance.media_player_new()
    media = instance.media_new_callbacks(callbacks['open'], callbacks['read'], callbacks['seek'], callbacks['close'], ctypes.byref(ctypes.py_object(stream)))
    player.set_media(media)
    player.play()

    while True:
        time.sleep(1)


if __name__ == '__main__':
    try:
        path = sys.argv[1]
    except IndexError:
        print('Usage: {0} <path>'.format(__file__))
        sys.exit(1)

    main(path)

[02f87cb0] imem demux error: Invalid get/release function pointers
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 234, in 'calling callback function'
  File "memory_stream.py", line 21, in media_read_cb
    stream = ctypes.cast(opaque, ctypes.py_object).value
ValueError: PyObject is NULL

重复上面的回溯,直到我杀死程序.

The above traceback is repeated until I kill the program.

推荐答案

NoneType 被传递给 media_read_cb,如回溯所示.代码中的问题似乎是 media_open_cb 函数.如果你在 media_new_callbacks 函数中用 None 替换这个回调,它不会被调用,并且 media_read_cb 将被调用适当的不透明指针.

A NoneType is passed to the media_read_cb as indicated by the traceback. The problem in the code seems to be the media_open_cb function. If you replace this callback with None in the media_new_callbacks function, it will not be called and media_read_cb will be called with the appropiate opaque pointer.

这个原因对我来说有点模糊.如果 open_cb 设置为 None,vlc 将调用其默认 open_cb,然后默认情况下将 size_pointer 设置为 maxsize 并将 data_pointer 设置为 opaque(与您的函数相同).显然,在设置指针值时,您的代码中出现了错误.我不知道如何解决这个问题,因为我也是 ctypes 的新手.

The reason for this is a bit obscure to me. If the open_cb is set to None, vlc will call its default open_cb, which will then set size_pointer to maxsize and data_pointer to opaque by default (which is identical to your function). Apparently, something in your code goes wrong when setting the value of the pointer. I do not know how to fix that as I am new to ctypes too.

当我运行您的代码时:

media = instance.media_new_callbacks(None, callbacks['read'], callbacks['seek'], callbacks['close'], ctypes.byref(ctypes.py_object(stream)))

media_read_cb 被成功调用.但是,python 然后在以下位置崩溃:

The media_read_cb is succesfully called. However, python then crashes at:

stream = ctypes.cast(opaque, ctypes.py_object).value

我也不知道如何解决这个问题,但有一个解决方法.您可以将流变量设置为全局变量,这样您就可以自己保留指针而不是依赖 ctypes 的东西.

I do not know how to solve this either, but there is a workaround. You could set the stream variable as a global variable, so you keep the pointer yourself instead of relying on the ctypes stuff.

写入缓冲区似乎也不起作用,因为缓冲区作为字符串传递给 media_read_cb.由于字符串在 python 中是不可变的,因此失败.解决方法是将 CFUNCTYPE 更改为包含 ctypes.POINTER 到 c_char 而不是普通的 c_char_p(python 中的字符串).然后,您可以通过迭代使用流中的字节填充内存区域.

Writing to the buffer also does not seem to work, as the buffer is passed as a string to the media_read_cb. Since strings are immutable in python, this fails. A workaround for this is to change the CFUNCTYPE to contain a ctypes.POINTER to c_char instead of plain c_char_p (string in python). You can then populate the memory area with the bytes from the stream through iteration.

应用这些更改后,您的代码如下所示:

Applying these changes, your code looks like this:

import ctypes
import io
import sys
import time

import vlc

MediaOpenCb = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_uint64))
MediaReadCb = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_void_p, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t)
MediaSeekCb = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_uint64)
MediaCloseCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)

stream=None

def media_open_cb(opaque, data_pointer, size_pointer):
    data_pointer.value = opaque
    size_pointer.contents.value = sys.maxsize
    return 0


def media_read_cb(opaque, buffer, length):
    new_data = stream.read(length)
    for i in range(len(new_data)):
        buffer[i]=new_data[i]
    return len(new_data)


def media_seek_cb(opaque, offset):
    stream.seek(offset)
    return 0


def media_close_cb(opaque):
    stream.close()


callbacks = {
    'open': MediaOpenCb(media_open_cb),
    'read': MediaReadCb(media_read_cb),
    'seek': MediaSeekCb(media_seek_cb),
    'close': MediaCloseCb(media_close_cb)
}

def main(path):
    global stream
    stream = open(path, 'rb')
    instance = vlc.Instance('-vvv')
    player = instance.media_player_new()
    media = instance.media_new_callbacks(None, callbacks['read'], callbacks['seek'], callbacks['close'], ctypes.byref(ctypes.py_object(stream)))
    player.set_media(media)
    player.play()

    while True:
        time.sleep(1)

if __name__ == '__main__':
    try:
        path = sys.argv[1]
    except IndexError:
        print('Usage: {0} <path>'.format(__file__))
        sys.exit(1)

    main(path)

它成功运行了!

当然,与其使用全局变量,不如将所有这些都包装在一个 Python 类中.

Of course, instead of using a global variable, it would be better to wrap all this inside a python class.

我想出了如何适当地设置 data_pointer.代码如下:

I figured out how to set the data_pointer appropiately. Here is the code:

import ctypes
import io
import sys
import time

import vlc

MediaOpenCb = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_uint64))
MediaReadCb = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_void_p, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t)
MediaSeekCb = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_uint64)
MediaCloseCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)


def media_open_cb(opaque, data_pointer, size_pointer):
    data_pointer.contents.value = opaque
    size_pointer.contents.value = sys.maxsize
    return 0


def media_read_cb(opaque, buffer, length):
    stream=ctypes.cast(opaque,ctypes.POINTER(ctypes.py_object)).contents.value
    new_data = stream.read(length)
    for i in range(len(new_data)):
        buffer[i]=new_data[i]
    return len(new_data)


def media_seek_cb(opaque, offset):
    stream=ctypes.cast(opaque,ctypes.POINTER(ctypes.py_object)).contents.value
    stream.seek(offset)
    return 0


def media_close_cb(opaque):
    stream=ctypes.cast(opaque,ctypes.POINTER(ctypes.py_object)).contents.value
    stream.close()


callbacks = {
    'open': MediaOpenCb(media_open_cb),
    'read': MediaReadCb(media_read_cb),
    'seek': MediaSeekCb(media_seek_cb),
    'close': MediaCloseCb(media_close_cb)
}

def main(path):
    stream = open(path, 'rb')
    instance = vlc.Instance()
    player = instance.media_player_new()
    media = instance.media_new_callbacks(callbacks['open'], callbacks['read'], callbacks['seek'], callbacks['close'], ctypes.cast(ctypes.pointer(ctypes.py_object(stream)), ctypes.c_void_p))
    player.set_media(media)
    player.play()

    while True:
        time.sleep(1)

if __name__ == '__main__':
    try:
        path = sys.argv[1]
    except IndexError:
        print('Usage: {0} <path>'.format(__file__))
        sys.exit(1)

    main(path)

这篇关于将类文件对象传递给 ctypes 回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆