在Gstreamer中使用管道播放音频和视频

9 投票
5 回答
18077 浏览
提问于 2025-04-17 06:36

有没有办法创建一个管道,可以播放任何视频文件(包括音频)呢?我试过把一些元素连接起来,比如:

filesrc -> decodebin

还有

queue -> audioconvert -> autoaudiosink

queue -> autovideoconvert -> autovideosink

这样做会遇到两个问题:

  1. 一个 queue 不能和 autovideoconvert 连接。
  2. 我不知道怎么用 "pad-added" 事件来实现一个 pad,特别是当这个管道同时支持音频和视频的时候。

我想知道怎么做到这一点,而不需要用到 gst.parse_launch。另外,我希望这个管道能处理我输入的任何格式(就像 playbin 一样),但我不能使用 playbin,因为我需要连接其他元素(levelvolume)。

另外,有没有办法把元素(比如 level)连接到 playbin 上呢?

5 个回答

1

另外,现在你可以使用 GStreamer 1.0 了。

在这里,你会发现新的属性 audio-filtervideo-filter,这些可以用来把一些元素(比如 level)连接到一个播放组件上。

使用 Python 的 GObject 反射,这样做会变得非常简单:

level = Gst.ElementFactory.make('level')

playbin = Gst.ElementFactory.make("playbin")
playbin.props.audio_filter = level
4

queue 不是一个源元素,你需要使用 uridecodebindecodebin 或类似的东西作为源元素。

这是一个使用 gst-launch 格式的示例管道。

uridecodebin \
    uri="file:///home/joar/Dropbox/Music/04 - Deadmau5 - Clockwork (Jonas Steur Remix).mp3" \
! audioconvert ! autoaudiosink

这意味着在这个管道中有:

  • uridecodebin - 这是一个解码单元,能够解码任何与 GStreamer 兼容的源文件,uri 属性设置为 file:///home/joar/Dropbox/Music/04 - Deadmau5 - Clockwork (Jonas Steur Remix).mp3
  • audioconvert - 用于在不同音频格式之间转换音频。
  • autoaudiosink - 自动选择音频输出。

如果需要,你可以在 uridecodebinaudioconvert 之间添加一个 queue 元素。


更新

我可以使用以下 gst-launch 命令来实现你所描述的功能。

gst-launch-0.10 filesrc \
    location="/home/joar/Dropbox/Skrillex vs. Adele - Set Fire to Everybody.mov" \
! decodebin name=dmux \
dmux. ! queue ! audioconvert ! autoaudiosink \
dmux. ! queue ! autovideoconvert ! autovideosink
3

我做了一个示例视频播放器,里面用到了你提到的那些元素。

这个示例可以教你如何动态地把这些按钮连接在一起。

'''
Copyright (c) 2011 Joar Wandborg <http://wandborg.se>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

---

- A response to http://stackoverflow.com/questions/8187257/play-audio-and-video-with-a-pipeline-in-gstreamer-python/8197837
- Like it? Buy me a beer! https://flattr.com/thing/422997/Joar-Wandborg
'''

import gst
import gobject
gobject.threads_init()
import logging


logging.basicConfig()

_log = logging.getLogger(__name__)
_log.setLevel(logging.DEBUG)


class VideoPlayer(object):
    '''
    Simple video player
    '''

    source_file = None

    def __init__(self, **kwargs):
        self.loop = gobject.MainLoop()

        if kwargs.get('src'):
            self.source_file = kwargs.get('src')

        self.__setup()

    def run(self):
        self.loop.run()

    def stop(self):
        self.loop.quit()

    def __setup(self):
        _log.info('Setting up VideoPlayer...')
        self.__setup_pipeline()
        _log.info('Set up')

    def __setup_pipeline(self):
        self.pipeline = gst.Pipeline('video-player-pipeline')

        # Source element
        self.filesrc = gst.element_factory_make('filesrc')
        self.filesrc.set_property('location', self.source_file)
        self.pipeline.add(self.filesrc)

        # Demuxer
        self.decoder = gst.element_factory_make('decodebin2')
        self.decoder.connect('pad-added', self.__on_decoded_pad)
        self.pipeline.add(self.decoder)

        # Video elements
        self.videoqueue = gst.element_factory_make('queue', 'videoqueue')
        self.pipeline.add(self.videoqueue)

        self.autovideoconvert = gst.element_factory_make('autovideoconvert')
        self.pipeline.add(self.autovideoconvert)

        self.autovideosink = gst.element_factory_make('autovideosink')
        self.pipeline.add(self.autovideosink)

        # Audio elements
        self.audioqueue = gst.element_factory_make('queue', 'audioqueue')
        self.pipeline.add(self.audioqueue)

        self.audioconvert = gst.element_factory_make('audioconvert')
        self.pipeline.add(self.audioconvert)

        self.autoaudiosink = gst.element_factory_make('autoaudiosink')
        self.pipeline.add(self.autoaudiosink)

        self.progressreport = gst.element_factory_make('progressreport')
        self.progressreport.set_property('update-freq', 1)
        self.pipeline.add(self.progressreport)

        # Link source and demuxer
        linkres = gst.element_link_many(
            self.filesrc,
            self.decoder)

        if not linkres:
            _log.error('Could not link source & demuxer elements!\n{0}'.format(
                    linkres))

        linkres = gst.element_link_many(
            self.audioqueue,
            self.audioconvert,
            self.autoaudiosink)

        if not linkres:
            _log.error('Could not link audio elements!\n{0}'.format(
                    linkres))

        linkres = gst.element_link_many(
            self.videoqueue,
            self.progressreport,
            self.autovideoconvert,
            self.autovideosink)

        if not linkres:
            _log.error('Could not link video elements!\n{0}'.format(
                    linkres))

        self.bus = self.pipeline.get_bus()
        self.bus.add_signal_watch()
        self.bus.connect('message', self.__on_message)

        self.pipeline.set_state(gst.STATE_PLAYING)

    def __on_decoded_pad(self, pad, data):
        _log.debug('on_decoded_pad: {0}'.format(pad))

        if pad.get_caps()[0].to_string().startswith('audio'):
            pad.link(self.audioqueue.get_pad('sink'))
        else:
            pad.link(self.videoqueue.get_pad('sink'))

    def __on_message(self, bus, message):
        _log.debug(' - MESSAGE: {0}'.format(message))
        

if __name__ == '__main__':
    player = VideoPlayer(
        src='/home/joar/Videos/big_buck_bunny_1080p_stereo.avi')

    player.run()

撰写回答