我正在使用GStreammer Python 3绑定来构建一个应用程序,该应用程序在录制在线流式视频时可以录制并可以在屏幕上播放。我正在使用Streamlink来获取m3u8和/或流。用户可能会添加奇怪的源,所以我的原始想法是使用可以检测源格式并自动对其进行解码的方法。为此,我发现结合使用Streamlink和GStreammer's Playbin是最好的方法。
Streamlink文档中的以下代码允许播放任何m3u8流:
#!/usr/bin/env python
from __future__ import print_function
import sys
import gi
gi.require_version("Gst", "1.0")
from gi.repository import GObject as gobject, Gst as gst
from streamlink import Streamlink, StreamError, PluginError, NoPluginError
def exit(msg):
print(msg, file=sys.stderr)
sys.exit()
class StreamlinkPlayer(object):
def __init__(self):
self.fd = None
self.mainloop = gobject.MainLoop()
# This creates a playbin pipeline and using the appsrc source
# we can feed it our stream data
self.pipeline = gst.ElementFactory.make("playbin", None)
self.pipeline.set_property("uri", "appsrc://")
# When the playbin creates the appsrc source it will call
# this callback and allow us to configure it
self.pipeline.connect("source-setup", self.on_source_setup)
# Creates a bus and set callbacks to receive errors
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect("message::eos", self.on_eos)
self.bus.connect("message::error", self.on_error)
def exit(self, msg):
self.stop()
exit(msg)
def stop(self):
# Stop playback and exit mainloop
self.pipeline.set_state(gst.State.NULL)
self.mainloop.quit()
# Close the stream
if self.fd:
self.fd.close()
def play(self, stream):
# Attempt to open the stream
try:
self.fd = stream.open()
except StreamError as err:
self.exit("Failed to open stream: {0}".format(err))
print(vars(self.pipeline))
# Start playback
self.pipeline.set_state(gst.State.PLAYING)
self.mainloop.run()
def on_source_setup(self, element, source):
# When this callback is called the appsrc expects
# us to feed it more data
source.connect("need-data", self.on_source_need_data)
def on_source_need_data(self, source, length):
# Attempt to read data from the stream
try:
data = self.fd.read(length)
except IOError as err:
self.exit("Failed to read data from stream: {0}".format(err))
# If data is empty it's the end of stream
if not data:
source.emit("end-of-stream")
return
# Convert the Python bytes into a GStreamer Buffer
# and then push it to the appsrc
buf = gst.Buffer.new_wrapped(data)
source.emit("push-buffer", buf)
def on_eos(self, bus, msg):
# Stop playback on end of stream
self.stop()
def on_error(self, bus, msg):
# Print error message and exit on error
error = msg.parse_error()[1]
self.exit(error)
def main():
if len(sys.argv) < 3:
exit("Usage: {0} <url> <quality>".format(sys.argv[0]))
# Initialize and check GStreamer version
gi.require_version("Gst", "1.0")
gobject.threads_init()
gst.init(None)
# Collect arguments
url = sys.argv[1]
quality = sys.argv[2]
# Create the Streamlink session
streamlink = Streamlink()
# Enable logging
streamlink.set_loglevel("info")
streamlink.set_logoutput(sys.stdout)
# Attempt to fetch streams
try:
streams = streamlink.streams(url)
except NoPluginError:
exit("Streamlink is unable to handle the URL '{0}'".format(url))
except PluginError as err:
exit("Plugin error: {0}".format(err))
if not streams:
exit("No streams found on URL '{0}'".format(url))
# Look for specified stream
if quality not in streams:
exit("Unable to find '{0}' stream on URL '{1}'".format(quality, url))
# We found the stream
stream = streams[quality]
# Create the player and start playback
player = StreamlinkPlayer()
# Blocks until playback is done
player.play(stream)
if __name__ == "__main__":
main()
用法:
TestStreamlink.py https://www.mitele.es/directo/divinity best
例如,您可以用一个抽搐来更改mitele URL。
我已经查看了gst文档(GStreammer API reference),并且我认为用来保存文件的最好的方法是“ filesink bin”,但是,我不知道如何对playbin的输出进行分割在录制和播放之间。我也不知道怎么只录音。
我是Python的新手,但我必须使用该语言才能使用它,因此对我来说非常方便。 据我所知,方法是创建一个“ tee”以将下载的流克隆到两个不同的管道中,一个用于将标准GUI输出连接到标准键盘(这是可选的播放流,仅用于此)进行调试),另一个连接到“ bin”,“ filesink”。然后启用和禁用实时播放只是删除/添加打击垫的问题。但是,我已经咨询过this tutorial,因为我说我是Python和GST的新手,所以我对它不熟悉:S
顺便说一句,能够录制远比现场播放更重要,因此任何有关如何集成playbin和filesink(用于mp4录制,就是说随机音频/视频)格式的想法都足够了。顺便说一句:我不仅要录制音频/视频(mp4),而且还要在另一个过程中处理音频,所以我的想法是同时录制包含视频+音频的mp4和仅包含音频的mp3。 mp3可能会使用RAM或存储在磁盘中的MP3发送到另一个应用程序以供以后读取,这是更方便的方法。