2016-02-26 84 views
1

我試圖使用LibVLC Python綁定來播放內存流(Python 3.4,Windows 7,LibVLC 3.x)。最終,我的目標是將數據提供給一個BytesIO實例,然後VLC將讀取並播放它。但目前,我決定修改一個快速腳本來嘗試從文件流中讀取數據。這裏的代碼和回溯 - 說我對ctypes很陌生會是輕描淡寫,所以有人知道我做錯了什麼?將文件類對象傳遞給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 

上述追蹤被重複,直到我殺死程序。

回答

1

根據回溯指示,將一個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) 
+0

我想「謝謝你!」評論通常在這裏附近引起,但是這真棒。我已經放棄了希望,我會永遠解決這個問題,或者這會得到回答。我將把它包裝到一個類中,並嘗試使用它來加載一個HLS塊的隊列。如果我可以不止一次地讚揚你,我會。 –

+0

不客氣。我偶然發現了這個問題,因爲我有類似的問題。 另請注意,libvlc不喜歡收到比它所要求的更少的字節。所以,在media_read_cb函數中等待更多數據是一個好方法(我認爲它會使事情崩潰)。 – Munchhausen

+0

任何想法,爲什麼我一直看到錯誤:[03527320] imem demux error:開始播放時無效的獲取/釋放函數指針?媒體仍然很好。 –