简体   繁体   English

python gstreamer播放多个视频流

[英]python gstreamer play multiple video streams

I'm participating in an art project which includes remotely playing videos. 我正在参与一个包括远程播放视频的艺术项目。 I've implemented a simple python application with a HTTP server and a gstreamer video player. 我已经实现了一个带有HTTP服务器和gstreamer视频播放器的简单python应用程序。 I'm able to catch a http request and change the video which is currently playing, but I'd like to just add the new video in the same window and continue playing two videos simultaniously. 我能够捕获一个http请求并更改当前正在播放的视频,但我想在同一窗口中添加新视频并继续同时播放两个视频。

I've used playbin2 to play videos, but I think it can only play one uri at the time. 我用playbin2来播放视频,但我认为它当时只能玩一个uri。 I've tried to find other solutions which could play several videos at the same time, but no use... 我试图找到可以同时播放多个视频的其他解决方案,但没有用...

Could anyone please post a simple example of playing multiple streams at the same time, or give me some pointers to the documentation or other resources?? 任何人都可以发布一个同时播放多个流的简单示例,或者给我一些指向文档或其他资源的指针?

Thanks in advance!! 提前致谢!!

PS. PS。 Here's the code I wrote: the VideoPlayer class initializes the stream and the the playCurrent function switches the currently played video - I'd like that function just to add the new video to the stream. 这是我写的代码:VideoPlayer类初始化流,playCurrent函数切换当前播放的视频 - 我希望该功能只是将新视频添加到流中。

#!/usr/bin/python

import threading
import time
import BaseHTTPServer
from BaseHTTPServer import HTTPServer
from urlparse import urlparse, parse_qs
from os import path
import gst
import gtk



HOST_NAME = 'localhost' # !!!REMEMBER TO CHANGE THIS!!!
PORT_NUMBER = 9000 # Maybe set this to 9000.

#################################################################
# VIDEO DICTIONARY
# Manages the video database
#################################################################

# VideoDictionary class
#################################################################
# This class allows to access the video database
# used by the video player - for best performance, it's a native
# python dictionary
class VideoDictionary():

    # declaring filenames
    filename = path.join(path.dirname(path.abspath(__file__)), 'large.mp4')
    filename_02 = path.join(path.dirname(path.abspath(__file__)), '01.avi')

    # declaring uris
    uri = 'file://' + filename
    uri_02 = 'file://' + filename_02

    # combining it all into a dictionary
    videoDict = {}
    videoDict["01"] = uri
    videoDict["02"] = uri_02

    # setting the current video
    currentVideo = "01"

#################################################################
# VIDEO DICTIONARY END
#################################################################



#################################################################
# VIDEO PLAYER
# Manages all the video playing
#################################################################

# VideoPlayer class
#################################################################
# This class initializes the GST pipe context and it
# handles different events related to video stream playing
class VideoPlayer(object, VideoDictionary):

    VideoDictionary = ""

    def __init__(self, VideoDictionary):
        self.VideoDictionary = VideoDictionary        
        self.window = gtk.Window()
        self.window.connect('destroy', self.quit)
        self.window.set_default_size(1024, 768)

        self.drawingarea = gtk.DrawingArea()
        self.window.add(self.drawingarea)

        # Create GStreamer pipeline
        self.pipeline = gst.Pipeline()

        # Create bus to get events from GStreamer pipeline
        self.bus = self.pipeline.get_bus()

        # This is needed to make the video output in our DrawingArea:
        self.bus.enable_sync_message_emission()
        self.bus.connect('sync-message::element', self.on_sync_message)

        # Create GStreamer elements
        self.playbin = gst.element_factory_make('playbin2')

        # Add playbin2 to the pipeline
        self.pipeline.add(self.playbin)
        self.window.show_all()
        self.xid = self.drawingarea.window.xid
        print('DEBUG INFO: player initialization finished')

    def playCurrent(self):
        print('DEBUG INFO: getting running video ')
        print(self.VideoDictionary.currentVideo)
        self.pipeline.set_state(gst.STATE_READY)
        self.playbin.set_property('uri', self.VideoDictionary.videoDict[self.VideoDictionary.currentVideo])
        self.pipeline.set_state(gst.STATE_PLAYING)

    def quit(self, window):
        print('DEBUG INFO: quitting player')
        self.pipeline.set_state(gst.STATE_NULL)
        gtk.main_quit()

    def on_sync_message(self, bus, msg):
        if msg.structure.get_name() == 'prepare-xwindow-id':
            msg.src.set_property('force-aspect-ratio', True)
            msg.src.set_xwindow_id(self.xid)

    def on_eos(self, bus, msg):
        print('DEBUG INFO: EOS detected')
        print('on_eos(): seeking to start of video')
        self.pipeline.seek_simple(
            gst.FORMAT_TIME,        
            gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT,
            0L
        )

    def on_error(self, bus, msg):
        print('DEBUG INFO: error detected')
        print('on_error():', msg.parse_error())

#################################################################
# VIDEO PLAYER END
#################################################################




#################################################################
# HTTP SERVER
# implements the http listener in a separate thread
# the listener plays the videos depending on the 
# received parameters in the GET request 
#################################################################

# HttpHandler class
#################################################################
# uses global variables to operate videos
class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(self):
        # initialize the currently played video
        global VideoDictionary
        print('DEBUG INFO: GET running playCurrent')
        if VideoDictionary.currentVideo == "01":
            VideoDictionary.currentVideo = "02"
        else:
            VideoDictionary.currentVideo = "01"

        # play the video we have just set        
        global player
        player.playCurrent()        

# HttpThread class
#################################################################
# initializes the http listener in a separate thread
class HttpThread (threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        gtk.gdk.threads_enter()
        server_class = BaseHTTPServer.HTTPServer
        httpd = server_class((HOST_NAME, PORT_NUMBER), HttpHandler)
        print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            pass
        httpd.server_close()
        print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)
        gtk.gdk.threads_leave()
        return

#################################################################
# HTTP SERVER END
#################################################################

if __name__ == '__main__':
    VideoDictionary = VideoDictionary()
    player = VideoPlayer(VideoDictionary)
    gtk.gdk.threads_init()
    thread2 = HttpThread()
    thread2.run()
    gtk.gdk.threads_enter()
    gtk.main()
    gtk.gdk.threads_leave()

Here is a simple example of code that plays multiple video streams at the same time. 这是一个同时播放多个视频流的代码的简单示例。

It works with Python 2 and 3 and it uses the standard Python GUI (Tk) and Gstreamer 1.0. 它适用于Python 2和3,它使用标准的Python GUI(Tk)和Gstreamer 1.0。 It should therefore be portable, but I only tested it under Ubuntu 16.04. 因此它应该是可移植的,但我只在Ubuntu 16.04下进行了测试。

(The ffmpeg fork libav created problems under Ubuntu 14.04, which seem to be solved under 16.04. Note that you need the package gstreamer1.0-libav in addition to gstreamer1.0-plugins-*.) (ffmpeg fork libav在Ubuntu 14.04下创建了问题,这似乎是在16.04下解决的。请注意,除了gstreamer1.0-plugins- *之外,还需要gstreamer1.0-libav包。)

The code is configured to create eight frames in a column, and to associate a Gstreamer player with each of them. 代码配置为在列中创建八个帧,并将Gstreamer播放器与它们中的每一个相关联。 You are required to give a list of (up to eight) valid local video file names as arguments to the file into which you saved it (say multivid.py), like this: 您需要提供一个(最多八个)有效本地视频文件名列表作为您保存它的文件的参数(比如multivid.py),如下所示:

$ python3 multivid.py video1.webm video2.mp4

The sound channels are simply mixed together. 声道简单地混合在一起。 You probably want to change this. 你可能想改变它。

My solution does not address remote playing, but you have already solved that part. 我的解决方案不涉及远程播放,但您已经解决了这个问题。

I previously posted the same code in an answer to another question on video files in tkinter , where the question did not ask for simultaneous streams. 我之前在tkinter中关于视频文件的另一个问题的答案中发布了相同的代码,其中问题没有要求同时流。 It is therefore more appropriate here. 因此,这里更合适。

import sys
import os

if sys.version_info[0] < 3:
    import Tkinter as tkinter
else:
    import tkinter

import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst, GObject

# Needed for set_window_handle():
gi.require_version('GstVideo', '1.0')
from gi.repository import GstVideo

def set_frame_handle(bus, message, frame_id):
    if not message.get_structure() is None:
        if message.get_structure().get_name() == 'prepare-window-handle':
            display_frame = message.src
            display_frame.set_property('force-aspect-ratio', True)
            display_frame.set_window_handle(frame_id)

NUMBER_OF_FRAMES = 8 # with more frames than arguments, videos are repeated
relative_height = 1 / float(NUMBER_OF_FRAMES)

# Only argument number checked, not validity.
number_of_file_names_given = len(sys.argv) - 1
if number_of_file_names_given < 1:
    print('Give at least one video file name.')
    sys.exit()
if number_of_file_names_given < NUMBER_OF_FRAMES:
    print('Up to', NUMBER_OF_FRAMES, 'video file names can be given.')
file_names = list()
for index in range(number_of_file_names_given):
    file_names.append(sys.argv[index + 1])

window = tkinter.Tk()
window.title("Multiple videos in a column using Tk and GStreamer 1.0")
window.geometry('480x960')

Gst.init(None)
GObject.threads_init()

for number in range(NUMBER_OF_FRAMES):
    display_frame = tkinter.Frame(window, bg='')
    relative_y = number * relative_height
    display_frame.place(relx = 0, rely = relative_y,
            anchor = tkinter.NW, relwidth = 1, relheight = relative_height)
    frame_id = display_frame.winfo_id()

    player = Gst.ElementFactory.make('playbin', None)
    fullname = os.path.abspath(file_names[number % len(file_names)])
    player.set_property('uri', 'file://%s' % fullname)
    player.set_state(Gst.State.PLAYING)

    bus = player.get_bus()
    bus.enable_sync_message_emission()
    bus.connect('sync-message::element', set_frame_handle, frame_id)

window.mainloop()

If you save the handles to the players (say in player_list), you can later change the uri that it is playing in one of them like this: 如果将句柄保存到播放器(例如在player_list中),您可以稍后更改其中一个播放的uri,如下所示:

player_list[index].set_state(Gst.State.NULL)
player_list[index].set_property('uri', 'file://%s' % fileName)
player_list[index].set_state(Gst.State.PLAYING)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM