Воспроизведение аудио и видео с конвейером в Gstreamer

Есть ли способ создать конвейер, который будет воспроизводить любой видеофайл (который также будет содержать звук)? Я пробовал связывать такие элементы, как:

filesrc -> decodebin

вместе с

queue -> audioconvert -> autoaudiosink

и

queue -> autovideoconvert -> autovideosink

Это вызывает две проблемы:

  1. queue нельзя связать с autovideoconvert.
  2. Я понятия не имею, как реализовать пэд с событием "pad-added", особенно когда конвейер поддерживает и аудио, и видео.

Я хотел бы знать, как это сделать без необходимости gst.parse_launch. Кроме того, я хочу, чтобы сводная линия работала с любым форматом, который я использую (например, playbin), но не могу использовать playbin, так как мне нужно будет связать другие элементы (level и volume).

В качестве альтернативы, есть ли способ подключить элементы (например, level) к игровой корзине?


person D K    schedule 18.11.2011    source источник


Ответы (5)


Я создал пример видеопроигрывателя, в котором используются описанные вами элементы.

Он должен показать вам, как динамически подключать пэды друг к другу.

'''
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()
person joar    schedule 21.11.2011
comment
Отлично, но у меня остался еще один вопрос: new-decoded-pad устарел. Как использовать pad-added? - person D K; 21.11.2011
comment
Спасибо за наводку, я обновил код, но еще не успел его протестировать, хотя он должен быть очень похожим - просто еще один метод. - person joar; 21.11.2011
comment
Это работает, но вы должны поставить if not sink_pad.is_linked(): перед связыванием колодок. Кроме того, я проверил это только со звуком, и он никогда не воспроизводится. Ты знаешь почему? - person D K; 21.11.2011
comment
Возможно, видео часть конвейера приостанавливает все это. Вы можете пропустить связывание видео и аудио элементов в зависимости от того, содержит ли исходный файл аудио или видео. - person joar; 21.11.2011
comment
Есть ли способ сделать это без реконструкции всего трубопровода? Если я просто add видеоэлементы в конвейер, не подключая их, все равно будет та же проблема. - person D K; 22.11.2011
comment
Полезные ресурсы для отладки GStreamer; gstreamer.freedesktop.org/data/doc/ gstreamer/head/gstreamer/ - person joar; 22.11.2011
comment
Я решил использовать Discoverer, чтобы определить, содержит ли медиафайл видео, и создать видео- или аудиоконвейер на основе результата. Я отмечу этот вопрос как решенный, но если есть лучший способ сделать такой динамический конвейер, пожалуйста, дайте мне знать. Спасибо за твою помощь! - person D K; 23.11.2011
comment
Хороший выбор на Discoverer. Я действительно не знаю, есть ли более простые способы. Я видел кое-что про автозаглушки на некоторых элементах, но еще не вникал в них - пожалуйста :) - person joar; 23.11.2011

queue не является исходным элементом, вам нужно иметь либо uridecodebin, либо decodebin, либо что-то подобное в качестве исходного элемента.

Это пример конвейера в формате 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

При необходимости вы можете добавить элемент queue между uridecodebin и audioconvert.


Обновлять

Я могу сделать то, что вы описываете, используя следующую команду 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
person joar    schedule 19.11.2011
comment
Полезно, но на самом деле это не ответ на мой вопрос. Я обновил схему пайплайна, чтобы было понятнее. - person D K; 20.11.2011
comment
Я вижу, у меня есть идея, как это решить, к сожалению, у меня сейчас нет времени. Пожалуйста, добавьте комментарий после этого, чтобы я получил уведомление, и я отвечу, как только окажусь перед своим компьютером. - person joar; 20.11.2011
comment
Ладно, добавил новый раздел, если вы не используете формат gst-launch, скажите мне, и я помогу вам сделать это с gst.element_factory_make(). - person joar; 20.11.2011
comment
Не могли бы вы помочь мне сделать это с gst.element_factory_make(), пожалуйста? Кроме того, что именно делает dmux.? Связывает ли он стоки с декобином? - person D K; 21.11.2011

В качестве альтернативы вы уже можете использовать GStreamer 1.0.

Там вы найдете новые свойства audio-filter и video-filter, который можно использовать для подключения элементов (таких как level) к плейбину .

С Python GObject Introspection это можно сделать так же просто, как:

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

playbin = Gst.ElementFactory.make("playbin")
playbin.props.audio_filter = level
person tynn    schedule 13.06.2015

Я уверен, что вы уже реализовали это. Но для других, кто смотрит на ваш вопрос, мой ответ может помочь. Код выглядит следующим образом

 #!/usr/bin/python
 import pygst
 pygst.require('0.10')
 import gst

 import pygtk
 pygtk.require('2.0')
 import gtk

 # this is very important, without this, callbacks from gstreamer thread
 # will messed our program up


 def on_new_decoded_pad(dbin, pad, islast):
     structure_name = pad.get_caps()[0].get_name()
     decode = pad.get_parent()
     pipeline = decode.get_parent()
     if structure_name.startswith("video"):
            queuev = pipeline.get_by_name('queuev')
        decode.link(queuev)
     if structure_name.startswith("audio"):
            queuea = pipeline.get_by_name('queuea')
    print queuea
        decode.link(queuea)


 def main():
     pipeline = gst.Pipeline('pipleline')

     filesrc = gst.element_factory_make("filesrc", "filesrc")
     filesrc.set_property('location', '/home/thothadri/Videos/nuclear.avi')

     decode = gst.element_factory_make("decodebin", "decode")

     queuev = gst.element_factory_make("queue", "queuev")

     sink = gst.element_factory_make("autovideosink", "sink")

     queuea = gst.element_factory_make("queue", "queuea")

     convert = gst.element_factory_make('audioconvert', 'convert')

     sink_audio = gst.element_factory_make("autoaudiosink", "sink_audio")

     pipeline.add(filesrc,decode,queuev,queuea,convert,sink,sink_audio)

     gst.element_link_many(filesrc, decode)
     gst.element_link_many(queuev,sink)
     gst.element_link_many(queuea,convert,sink_audio)

     decode.connect("new-decoded-pad", on_new_decoded_pad)

     pipeline.set_state(gst.STATE_PLAYING)



 main()
 gtk.gdk.threads_init()
 gtk.main()  
person Thothadri Rajesh    schedule 16.08.2012

Конвейер для воспроизведения аудио и видео на локальном компьютере:

gst-launch-1.0 -v filesrc location=random-file.mpeg ! decodebin name=demux demux. ! очередь ! видео конвертировать ! xvimagesink демультиплексор. ! очередь ! аудиоконвертер! импульсный сток

мы можем использовать autovideosink вместо xvimagesink.

person RAHUL PACHAURI    schedule 24.05.2021