加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

python gstreamer播放多个视频流

发布时间:2020-12-20 13:33:12 所属栏目:Python 来源:网络整理
导读:我正在参与一个包括远程播放视频的艺术项目.我已经实现了一个带有HTTP服务器和gstreamer视频播放器的简单 python应用程序.我能够捕获一个http请求并更改当前正在播放的视频,但我想在同一窗口中添加新视频并继续同时播放两个视频. 我用playbin2来播放视频,但
我正在参与一个包括远程播放视频的艺术项目.我已经实现了一个带有HTTP服务器和gstreamer视频播放器的简单 python应用程序.我能够捕获一个http请求并更改当前正在播放的视频,但我想在同一窗口中添加新视频并继续同时播放两个视频.

我用playbin2来播放视频,但我认为它当时只能玩一个uri.我试图找到可以同时播放多个视频的其他解决方案,但没有用…

任何人都可以发布一个同时播放多个流的简单示例,或者给我一些指向文档或其他资源的指针?

提前致谢!!

PS.这是我写的代码: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,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,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()

解决方法

这是一个同时播放多个视频流的代码的简单示例.

它适用于Python 2和3,它使用标准的Python GUI(Tk)和Gstreamer 1.0.因此它应该是可移植的,但我只在Ubuntu 16.04下进行了测试.

(ffmpeg fork libav在Ubuntu 14.04下创建了问题,这似乎是在16.04下解决的.请注意,除了gstreamer1.0-plugins- *之外,还需要gstreamer1.0-libav包.)

代码配置为在列中创建八个帧,并将Gstreamer播放器与它们中的每一个相关联.您需要提供一个(最多八个)有效本地视频文件名列表作为您保存它的文件的参数(比如multivid.py),如下所示:

$python3 multivid.py video1.webm video2.mp4

声道简单地混合在一起.你可能想改变它.

我的解决方案不涉及远程播放,但您已经解决了这个问题.

我之前在video files in tkinter的另一个问题的答案中发布了相同的代码,其中问题没有要求同时流.因此,这里更合适.

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()

如果将句柄保存到播放器(例如在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)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读