简体   繁体   中英

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. 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.

I've used playbin2 to play videos, but I think it can only play one uri at the time. 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. 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.

#!/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. It should therefore be portable, but I only tested it under 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-*.)

The code is configured to create eight frames in a column, and to associate a Gstreamer player with each of them. 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:

$ 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. 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[index].set_state(Gst.State.NULL)
player_list[index].set_property('uri', 'file://%s' % fileName)
player_list[index].set_state(Gst.State.PLAYING)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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