其他分享
首页 > 其他分享> > 将类文件对象传递给ctypes回调

将类文件对象传递给ctypes回调

作者:互联网

我正在尝试使用LibVLC Python绑定来播放内存流(Python 3.4,Windows 7,LibVLC 3.x).最终,我的目标是将数据馈入BytesIO实例,然后VLC将读取并播放.但是目前,我决定破解一个快速脚本以尝试从文件流中读取.这是代码和追溯-说我对ctypes相当陌生,那么有人知道我在做什么错吗?

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

重复上面的回溯,直到我终止该程序.

解决方法:

如回溯所示,将NoneType传递给media_read_cb.
代码中的问题似乎是media_open_cb函数.
如果在media_new_callbacks函数中将此回调替换为None,则不会调用该回调,并且会使用适当的不透明指针来调用media_read_cb.

这对我来说有点晦涩.如果open_cb设置为None,vlc将调用其默认的open_cb,默认情况下,它将将size_pointer设置为maxsize,将data_pointer设置为不透明(与您的功能相同).显然,在设置指针的值时,代码中的某些地方出了问题.我也不知道如何解决这个问题,因为我也是ctypes的新手.

当我使用以下代码运行您的代码时:

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

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

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

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

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

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

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

编辑:我想出了如何正确设置data_pointer.这是代码:

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)

标签:libvlc,python,python-3-x
来源: https://codeday.me/bug/20191009/1880607.html