我正在使用在 Nvidia Jetson nano 上播放的 PyGST 和 Gst.parse_launch 编写 Gstreamer 管道。管道播放 4k 视频并在运行时淡入淡出视频。
我的问题是管道不会连续播放相同的视频。它会一个接一个地播放不同的视频。淡入淡出功能也运行良好。
这是问题的描述:
该管道第一次使用示例视频,例如 video_1.mp4。当我再次播放相同的视频时,它会卡在第一帧,停留在该帧并在几秒钟后退出该过程。
如果我播放 video_1.mp4,然后播放不同的视频,例如 video_2.mp4,那么管道将毫无问题地工作。如果在此之后我播放 video_1.mp4 那么它也会一直播放。
video_1.mp4 的持续时间为 10 秒。如果我在 5 秒处停止 video_1.mp4 并再次播放视频,将显示 video_1.mp4 的第一帧,它将在该帧等待 5 秒,然后在前一个管道停止的同一点继续解码视频.
我怀疑播放同一视频时缓冲区运行时间尚未重置为 0。我不确定为什么会发生这种情况,我将管道设置为 NULL,并尝试在视频完成后进行搜索位置 0 并刷新管道中的元素以将时钟时间重置回 0。也许是语法或我的方式我实施它是不正确的。
下面的这个线程创建管道
import gi
import time
gi.require_version('Gst', '1.0')
gi.require_version('GstPbutils', '1.0')
gi.require_version('GstController', '1.0')
from gi.repository import GObject, Gst, GstPbutils, GstController
from player_thread import PlayerThread
from threading import Timer
import logging
import time
Gst.debug_set_active(True)
Gst.debug_set_default_threshold(3)
# This imports all dependencies. We are working not only with the Gstreamer library but an additional sub-library called the Controller.
# The controller subsystem allows us to change element properties at runtime.
# This will come handy for changing the alpha property later.
class VideoPlayer():
def __init__(self, communicator:Communicator):
if communicator:
self.communicator = communicator
Gst.init(None)
GObject.threads_init()
# Pipeline overview
# mp4 file → Video decoded → Video converted to rgba → dumped into the compositor element (where we can control alpha)
# compositor element → converted to nicer colorspace → dumped into the Jetson device monitor
self.pipeline = Gst.parse_launch ("""
nvcompositor name=comp sink_0::alpha=1 ! video/x-raw(memory:NVMM),format=RGBA !
nvvidconv ! video/x-raw(memory:NVMM), format=NV12 ! autovideosink name=sinky uridecodebin name=vidsrc !
nvvidconv name=pipeconv ! video/x-raw(memory:NVMM),format=RGBA,pixel-aspect-ratio=1/1 ! queue ! comp.sink_0
""")
self.loop = GObject.MainLoop()
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect("message::eos", self.bus_call, self.loop)
self.bus.connect("message::error", self.bus_call, self.loop)
# self.discoverer = GstPbutils.Discoverer()
self.sink = self.pipeline.get_by_name('sinky')
self.convert = self.pipeline.get_by_name('pipeconv')
self.source = self.pipeline.get_by_name('vidsrc')
self.compositor = self.pipeline.get_by_name('comp')
self.source.connect("pad-added", self.on_pad_added)
self.running = False
self.play_thread = PlayerThread(self.pipeline, self.loop)
self.play_thread.start()
def get_alpha_controller(self, incoming_pad):
self.pad = incoming_pad
self.control_source = GstController.InterpolationControlSource()
self.control_source.set_property('mode', GstController.InterpolationMode.LINEAR)
self.control_bind = GstController.DirectControlBinding.new(self.pad, 'alpha', self.control_source)
self.pad.add_control_binding(self.control_bind)
return self.control_source
def fade_video_in(self):
self.compositor_sink_pad = self.compositor.get_static_pad('sink_0')
self.control_source = self.get_alpha_controller(self.compositor_sink_pad)
self.control_source.set(0*Gst.SECOND, 0)
self.control_source.set(2*Gst.SECOND, 1)
def fade_video_out(self):
self.pos = self.pipeline.query_position(Gst.Format.TIME).cur
self.control_source.set(self.pos, 1)
self.control_source.set(self.pos + 1*Gst.SECOND, 0)
def on_pad_added(self, src, new_pad):
print(
"Received new pad '{0:s}' from '{1:s}'".format(
new_pad.get_name(),
src.get_name()))
new_pad_caps = new_pad.get_current_caps()
new_pad_struct = new_pad_caps.get_structure(0)
new_pad_type = new_pad_struct.get_name()
if new_pad_type.startswith("video/x-raw"):
sink_pad = self.convert.get_static_pad("sink")
else:
print(
"It has type '{0:s}' which is not raw audio/video. Ignoring.".format(new_pad_type))
return
# if our converter is already linked, we have nothing to do here
# if(sink_pad.is_linked()):
# print("We are already linked. Ignoring.")
# return
# attempt the link
ret = new_pad.link(sink_pad)
# attach a probe for eos to detach
# new_pad.add_probe(Gst.PadProbeType.EVENT_DOWNSTREAM, self.probe_callback)
if not ret == Gst.PadLinkReturn.OK:
print("Type is '{0:s}}' but link failed".format(new_pad_type))
else:
print("Link succeeded (type '{0:s}')".format(new_pad_type))
return
def play_video(self, video_url, scene_name):
self.compositor.props.background = 1
self.running = True
self.scene_name = scene_name
self.video_url = video_url
print(self.video_url)
self.source.props.uri = self.video_url
self.sink.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0 * Gst.SECOND)
self.pipeline.set_state(Gst.State.READY)
self.pipeline.get_state(Gst.CLOCK_TIME_NONE)
self.pipeline.set_state(Gst.State.PAUSED)
# self.pipeline.seek(1.0,
# Gst.Format.TIME,
# Gst.SeekFlags.SEGMENT,
# Gst.SeekType.SET, 0,
# Gst.SeekType.NONE, 0
# )
# self.fade_video_in()
self.play_thread.start_playback()
def stop_video(self):
self.running = False
self.pipeline.set_state(Gst.State.NULL)
self.pipeline.set_state(Gst.State.READY)
self.play_thread.stop_playback()
self.source.props.uri = ""
# /self.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0.0 * Gst.SECOND )
# self.source.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0.0 * Gst.SECOND)
# self.sink.seek(1.0, Gst.FORMAT_BYTES, Gst.SEEK_FLAG_FLUSH,
# Gst.SEEK_TYPE_SET, 0 * Gst.SECOND,
# Gst.SEEK_TYPE_NONE, 0 * Gst.SECOND)
# self.pipeline.seek(1.0,
# Gst.Format.TIME,
# Gst.SeekFlags.SEGMENT,
# Gst.SeekType.SET, 0,
# Gst.SeekType.NONE, 0
# )
def bus_call(self, bus, message, loop):
t = message.type
if t == Gst.MessageType.EOS:
self.stop_video()
elif t == Gst.MessageType.ERROR:
print(message.parse_error())
elif message.type == Gst.MessageType.SEGMENT_DONE:
# self.fade_video_out()
pass
else:
# should not get here
print("ERROR: Unexpected message received")
return True
下面的另一个线程启动和停止管道。这是在单独的线程中完成的,因为 Gstreamer 通常以阻塞模式运行。
import gi
import time
gi.require_version('Gst', '1.0')
gi.require_version('GstPbutils', '1.0')
from gi.repository import GObject, Gst, GstPbutils
from threading import Thread, Timer
import logging
import time
"""
Class that plays the video in a separate, non-blocking thread
"""
class PlayerThread(Thread):
def __init__(self, pipeline, loop):
super(PlayerThread,self).__init__()
self.pipeline = pipeline
self.loop = loop
self.source = self.pipeline.get_by_name('vidsrc')
self.compositor = self.pipeline.get_by_name('comp')
def run(self):
self.pipeline.set_state(Gst.State.NULL)
self.loop.run()
def start_playback(self):
# self.pipeline.send_event(Gst.Event.new_flush_stop(True))
# self.pipeline.seek(1.0,
# Gst.Format.TIME,
# Gst.SeekFlags.SEGMENT,
# Gst.SeekType.SET, 0,
# Gst.SeekType.NONE, 0
# )
# self.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0 * Gst.SECOND)
self.pipeline.set_state(Gst.State.PLAYING)
# self.loop.run()
def stop_playback(self):
# self.pipeline.send_event(Gst.Event.new_eos())
# self.pipeline.send_event(Gst.Event.new_flush_start())
# self.pipeline.send_event(Gst.Event.new_flush_stop(True))
# self.pipeline.send_event(Gst.Event.new_seek())
self.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0.0 * Gst.SECOND )
# self.pipeline.set_state(Gst.State.NULL)
# self.pipeline.seek(1.0,
# Gst.Format.TIME,
# Gst.SeekFlags.FLUSH,
# Gst.SeekType.SET, 0,
# Gst.SeekType.NONE, 0
# )
# self.source.seek(1.0,
# Gst.Format.TIME,
# Gst.SeekFlags.FLUSH,
# Gst.SeekType.SET, 0,
# Gst.SeekType.NONE, 0)
def exit(self):
self.loop.quit()
这些是发生错误时的 Gstreamer 调试日志
0:05:51.707439047 1756 0x7f7800e540 WARN aggregator gstaggregator.c:1717:gst_aggregator_query_latency_unlocked:<comp> Latency query failed
0:05:51.709119977 1756 0x7f70007460 WARN basesrc gstbasesrc.c:3583:gst_base_src_start_complete:<source> pad not activated yet
0:05:51.709991980 1756 0x7f70007460 WARN basesrc gstbasesrc.c:3583:gst_base_src_start_complete:<source> pad not activated yet
sending message {"action": "STATE_CHANGE", "body": {"videoActiveVideo": "test"}}
0:05:51.714088392 1756 0x106e9630 WARN qtdemux qtdemux_types.c:233:qtdemux_type_get: unknown QuickTime node type pasp
0:05:51.714195686 1756 0x106e9630 WARN qtdemux qtdemux.c:3031:qtdemux_parse_trex:<qtdemux11> failed to find fragment defaults for stream 1
Opening in BLOCKING MODE
0:05:51.759452369 1756 0x106fae80 WARN v4l2 gstv4l2object.c:4447:gst_v4l2_object_probe_caps:<nvv4l2decoder11:src> Failed to probe pixel aspect ratio with VIDIOC_CROPCAP: Unknown error -1
0:05:51.759527839 1756 0x106fae80 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f64022090 Failed to determine interlace mode
0:05:51.759581956 1756 0x106fae80 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f64022090 Failed to determine interlace mode
0:05:51.759638728 1756 0x106fae80 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f64022090 Failed to determine interlace mode
NvMMLiteOpen : Block : BlockType = 261
NVMEDIA: Reading vendor.tegra.display-size : status: 6
NvMMLiteBlockCreate : Block : BlockType = 261
0:05:51.865431565 1756 0x106fae80 WARN v4l2 gstv4l2object.c:4447:gst_v4l2_object_probe_caps:<nvv4l2decoder11:src> Failed to probe pixel aspect ratio with VIDIOC_CROPCAP: Unknown error -1
0:05:51.865703395 1756 0x106fae80 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f64022090 Failed to determine interlace mode
0:05:51.865971579 1756 0x106fae80 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f64022090 Failed to determine interlace mode
0:05:51.866240232 1756 0x106fae80 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f64022090 Failed to determine interlace mode
Received new pad 'src_16' from 'vidsrc'
Link succeeded (type 'video/x-raw')
0:05:51.876923175 1756 0x106fae80 WARN v4l2videodec gstv4l2videodec.c:1673:gst_v4l2_video_dec_decide_allocation:<nvv4l2decoder11> Duration invalid, not setting latency
0:05:51.877534077 1756 0x106fae80 WARN v4l2bufferpool gstv4l2bufferpool.c:1065:gst_v4l2_buffer_pool_start:<nvv4l2decoder11:pool:src> Uncertain or not enough buffers, enabling copy threshold
0:05:51.883864038 1756 0x7f5c007850 WARN v4l2bufferpool gstv4l2bufferpool.c:1512:gst_v4l2_buffer_pool_dqbuf:<nvv4l2decoder11:pool:src> Driver should never set v4l2_buffer.field to ANY
0:05:51.891684769 1756 0x7f7800e540 FIXME basesink gstbasesink.c:3145:gst_base_sink_default_event:<sinky-actual-sink-nvoverlay> stream-start event without group-id. Consider implementing group-id handling in the upstream elements
0:05:51.893670863 1756 0x7f7800e540 WARN nvcompositor gstnvcompositor.c:980:gst_nvcompositor_negotiated_caps:<comp> Release old pool
这些是您播放不同视频并且管道重置后的日志(管道正常工作)
0:06:21.907924995 1756 0x7f580cc320 WARN aggregator gstaggregator.c:1717:gst_aggregator_query_latency_unlocked:<comp> Latency query failed
0:06:21.909268156 1756 0x7f70007460 WARN basesrc gstbasesrc.c:3583:gst_base_src_start_complete:<source> pad not activated yet
0:06:21.910099324 1756 0x7f70007460 WARN basesrc gstbasesrc.c:3583:gst_base_src_start_complete:<source> pad not activated yet
sending message {"action": "STATE_CHANGE", "body": {"videoActiveVideo": "test1"}}
0:06:21.913367433 1756 0x7f7800e720 WARN qtdemux qtdemux_types.c:233:qtdemux_type_get: unknown QuickTime node type gsst
0:06:21.914455326 1756 0x7f7800e720 WARN qtdemux qtdemux_types.c:233:qtdemux_type_get: unknown QuickTime node type gstd
0:06:21.914529130 1756 0x7f7800e720 WARN qtdemux qtdemux.c:3031:qtdemux_parse_trex:<qtdemux12> failed to find fragment defaults for stream 1
0:06:21.914677884 1756 0x7f7800e720 WARN qtdemux qtdemux.c:3031:qtdemux_parse_trex:<qtdemux12> failed to find fragment defaults for stream 2
Opening in BLOCKING MODE
0:06:21.964505314 1756 0x106ee320 WARN v4l2 gstv4l2object.c:4447:gst_v4l2_object_probe_caps:<nvv4l2decoder12:src> Failed to probe pixel aspect ratio with VIDIOC_CROPCAP: Unknown error -1
0:06:21.964574795 1756 0x106ee320 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f5802e8e0 Failed to determine interlace mode
0:06:21.964636619 1756 0x106ee320 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f5802e8e0 Failed to determine interlace mode
0:06:21.964687871 1756 0x106ee320 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f5802e8e0 Failed to determine interlace mode
NvMMLiteOpen : Block : BlockType = 261
NVMEDIA: Reading vendor.tegra.display-size : status: 6
NvMMLiteBlockCreate : Block : BlockType = 261
0:06:22.070629669 1756 0x106ee320 WARN v4l2 gstv4l2object.c:4447:gst_v4l2_object_probe_caps:<nvv4l2decoder12:src> Failed to probe pixel aspect ratio with VIDIOC_CROPCAP: Unknown error -1
0:06:22.070809622 1756 0x106ee320 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f5802e8e0 Failed to determine interlace mode
0:06:22.071008898 1756 0x106ee320 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f5802e8e0 Failed to determine interlace mode
0:06:22.071295416 1756 0x106ee320 WARN v4l2 gstv4l2object.c:2388:gst_v4l2_object_add_interlace_mode:0x7f5802e8e0 Failed to determine interlace mode
Received new pad 'src_17' from 'vidsrc'
Link succeeded (type 'video/x-raw')
Received new pad 'src_18' from 'vidsrc'
It has type 'audio/x-raw' which is not raw audio/video. Ignoring.
0:06:22.080439932 1756 0x106ee320 WARN v4l2videodec gstv4l2videodec.c:1673:gst_v4l2_video_dec_decide_allocation:<nvv4l2decoder12> Duration invalid, not setting latency
0:06:22.080866610 1756 0x106ee320 WARN v4l2bufferpool gstv4l2bufferpool.c:1065:gst_v4l2_buffer_pool_start:<nvv4l2decoder12:pool:src> Uncertain or not enough buffers, enabling copy threshold
0:06:22.085282770 1756 0x7f5c0078f0 WARN v4l2bufferpool gstv4l2bufferpool.c:1512:gst_v4l2_buffer_pool_dqbuf:<nvv4l2decoder12:pool:src> Driver should never set v4l2_buffer.field to ANY
0:06:22.091041154 1756 0x7f580cc320 FIXME basesink gstbasesink.c:3145:gst_base_sink_default_event:<sinky-actual-sink-nvoverlay> stream-start event without group-id. Consider implementing group-id handling in the upstream elements
0:06:22.098388747 1756 0x7f580cc320 WARN nvcompositor gstnvcompositor.c:980:gst_nvcompositor_negotiated_caps:<comp> Release old pool
0:06:22.108516102 1756 0x7f580cc320 ERROR omx gstomx.c:256:gst_omx_component_handle_messages:<sinky-actual-sink-nvoverlay> yuv420 port 0 was not flushing