如何在GStreamer Tee直播分支添加缩放,同时保留原分辨率录制?
解决方案
完全可以实现你的需求:保持录制流为4K/1080p原分辨率,同时对直播分支的流进行缩放处理。需要注意的是,当前从tee分流的是编码后的H265流,无法直接缩放,必须先解码为原始视频帧,完成缩放后再重新编码,才能用于直播传输。
修改后的代码示例
import sys import gi gi.require_version('Gst', '1.0') from gi.repository import Gst Gst.init(None) cameraPipeline = Gst.parse_launch( " XXXCAMERASRC\ ! video/x-h265,width=3840,height=2160,framerate=25/1,stream-format=byte-stream,bitrate=10000000,profile=high,bitrate-control-mode=vbr\ ! h265parse\ ! tee name=t\ ! queue\ ! splitmuxsink\ location=/mnt/media/output.mp4\ max-size-bytes=1000000000 " ) t = cameraPipeline.get_by_name("t") cameraPipeline.set_state(Gst.State.PLAYING) liveBranch = {} def liveView(host="10.42.0.10", port=5000): if len(liveBranch) == 0: print("Starting live view") # 创建直播分支所需元素:解码→格式转换→缩放→重新编码→RTP打包→UDP发送 q = Gst.ElementFactory.make("queue", "liveQueue") decoder = Gst.ElementFactory.make("avdec_h265", "h265_decoder") videoconvert = Gst.ElementFactory.make("videoconvert", "video_convert") videoscale = Gst.ElementFactory.make("videoscale", "video_scale") encoder = Gst.ElementFactory.make("x265enc", "h265_encoder") rtppay = Gst.ElementFactory.make("rtph265pay", "rtpPay") udpsink = Gst.ElementFactory.make("udpsink", "udpSink") # 设置UDP目标地址和端口 udpsink.set_property("host", host) udpsink.set_property("port", port) # 设置编码器参数(直播用低码率、低延迟) encoder.set_property("bitrate", 2000000) encoder.set_property("speed-preset", "ultrafast") encoder.set_property("tune", "zerolatency") # 定义缩放后的分辨率caps scale_caps = Gst.Caps.from_string("video/x-raw,width=1280,height=720") # 检查元素创建是否成功 elements = [q, decoder, videoconvert, videoscale, encoder, rtppay, udpsink] for e in elements: if not e: print(f"Failed to create element: {e.get_name() if e else 'unknown'}") sys.exit(1) cameraPipeline.add(e) e.sync_state_with_parent() # 链接元素:queue → decoder → videoconvert → videoscale → encoder → rtppay → udpsink q.link(decoder) decoder.link(videoconvert) videoconvert.link_filtered(videoscale, scale_caps) videoscale.link(encoder) encoder.link(rtppay) rtppay.link(udpsink) # 链接tee的pad到直播分支的queue pad = t.get_request_pad("src_%u") pad.link(q.get_static_pad("sink")) # 保存分支元素,方便后续销毁 liveBranch.update({ "q": q, "decoder": decoder, "videoconvert": videoconvert, "videoscale": videoscale, "encoder": encoder, "rtppay": rtppay, "udpsink": udpsink, "pad": pad }) print("Live view branch created and linked") print(f"Live view started at {host}:{port} (scaled to 1280x720)") else: print("Live view already started")
关键说明
- 录制分支:保持原有的流程,直接从
tee分流后写入splitmuxsink,确保录制的视频是4K/1080p原分辨率。 - 直播分支流程:
avdec_h265:将H265编码流解码为原始视频帧。videoconvert:处理颜色空间转换,确保后续元素能正常接收视频数据。videoscale:配合video/x-rawcaps设置,将视频缩放到指定分辨率(示例中为1280x720)。x265enc:重新编码为H265流,设置zerolatency调优参数保证直播低延迟,同时降低码率减少带宽占用。- 后续的
rtph265pay和udpsink保持原有逻辑,完成RTP打包和UDP传输。
动态销毁分支的补充(可选)
如果需要动态关闭直播分支,可以添加如下函数,用于释放元素和pad:
def stopLiveView(): if liveBranch: print("Stopping live view") # 断开tee的pad链接 pad = liveBranch["pad"] pad.unlink(liveBranch["q"].get_static_pad("sink")) t.release_request_pad(pad) # 逐个移除并销毁分支元素 for elem_name in liveBranch: if elem_name != "pad": elem = liveBranch[elem_name] cameraPipeline.remove(elem) elem.set_state(Gst.State.NULL) liveBranch.clear() print("Live view branch stopped and cleaned up") else: print("Live view is not running")
内容的提问来源于stack exchange,提问作者Muhammad Haziq
相关产品推荐
相关产品推荐

