2

我正在尝试编写一个 Gstreamer Python 程序来控制我家中的整个家庭音频系统。基本前提是我将有多种不同的来源选择(Pandora、MP3、谷歌音乐等),并且能够在我家的不同“区域”中播放它们。我已经让它可以动态地将一个区域添加到当前正在播放的管道中,但是当我尝试删除一个区域时,音频会停止在所有区域中播放。这是相关代码,如有需要我可以发布更多:

基本设置:

def __init__(self, username, password, zones=[]):
    # initialize the player
    self.player = gst.element_factory_make('playbin2', 'pandora_player')
    fakesink = gst.element_factory_make('fakesink', 'fakesink')
    self.player.set_property('video-sink', fakesink)

    # enable progressive download (GST_PLAY_FLAG_DOWNLOAD)
    self.player.props.flags |= (1 << 7)

    # create bin
    teebin = gst.element_factory_make('bin', 'master')
    tee = gst.element_factory_make('tee', 'tee')
    teebin.add(tee)
    ghost_pad = gst.GhostPad('sink', tee.get_pad('sink'))
    teebin.add_pad(ghost_pad)

    # set bin as audio sink
    self.player.set_property('audio-sink', teebin)

    # set volume
    self.player.set_property('volume', 0.01)

    bus = self.player.get_bus()
    bus.add_signal_watch()
    bus.connect('message', self.on_message)

    # make everything accessible
    self.tee = tee
    self.teebin = teebin


def add_zone(self, zone_id):
    # create out first audio output device
    zone_name = 'zone_{0}'.format(zone_id)
    device_name = 'mono{0}'.format(zone_id)
    bin_name = 'bin_{0}'.format(zone_id)
    queue_name = 'q_{0}'.format(zone_id)

    # wrap everything in a convenient zone object
    zone = gst.element_factory_make('bin', bin_name)

    # handle sending to the proper sound device
    zone_device = gst.element_factory_make('alsasink', zone_name)
    zone_device.set_property('device', device_name)

    # create a queue to handle asynchronous playback
    zone_queue = gst.element_factory_make('queue', queue_name)
    zone.add(zone_queue, zone_device)
    zone_queue.link(zone_device)

    # add sink into element
    zone_ghost = gst.GhostPad('sink', zone_queue.get_pad('sink'))
    zone.add_pad(zone_ghost)

    self.zones[zone_id] = zone

    self.teebin.add(zone)
    zone.sync_state_with_parent()
    self.tee.link(zone)

def remove_zone(self, zone_id):
    # get zone
    zone = self.zones[zone_id]

    # get src pad that is sending audio
    pad = zone.get_pad('sink').get_peer()

    # block src pad
    pad.set_blocked(True)

    # set zone state null
    zone.set_state(gst.STATE_NULL)

    # unlink and remove zone
    self.tee.unlink(zone)
    self.teebin.remove(zone)

    # remove zone reference
    del self.zones[zone_id]
4

1 回答 1

0

可能您的整个管道都进入了暂停状态。

尝试按照文档的步骤进行操作

http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-dynamic-pipelines.html

我们不能只取消 element2 的 sinkpad 与 element1 的 source pad 的链接,因为这会使 element1 的 source pad 处于未链接状态,并且在将数据推送到 source pad 时会导致管道中的流错误。该技术是在我们将 element2 更改为 element4 之前阻止来自 element1 的源焊盘的数据流,然后恢复数据流,如以下步骤所示:

用阻塞焊盘探针阻塞 element1 的源焊盘。当 pad 被阻塞时,将调用探针回调。

在块回调内部,element1 和 element2 之间没有任何流动,并且在解除阻塞之前没有任何流动。

取消链接 element1 和 element2。

确保数据已从 element2 中清除。某些元素可能会在内部保留一些数据,您需要确保不会通过强制将其从 element2 中丢失数据。您可以通过将 EOS 推入 element2 来做到这一点,如下所示:

在 element2 的源焊盘上放置一个事件探测器。

将 EOS 发送到 element2 的 sinkpad。这确保了 element2 中的所有数据都被强制输出。

等待 EOS 事件出现在 element2 的源板上。收到 EOS 后,将其丢弃并移除事件探针。

取消链接 element2 和 element3。您现在还可以从管道中删除 element2 并将状态设置为 NULL。

将 element4 添加到管道(如果尚未添加)。链接 element4 和 element3。链接 element1 和 element4。

确保 element4 与管道中的其余元素处于相同状态。在它可以接收缓冲区和事件之前,它应该至少处于 PAUSED 状态。

解锁 element1 的源焊盘探针。这将使新数据进入 element4 并继续流式传输。

上述算法在源垫被阻塞时起作用,即当管道中有数据流时。如果没有数据流,也没有必要改变元素(只是还没有),所以这个算法也可以在 PAUSED 状态下使用。

于 2013-06-10T17:18:23.493 回答