Hello,
I’m writing a Gstreamer pipeline using PyGST and Gst.parse_launch. The pipeline plays 4k video and fades the videos in and out at runtime.
My issue is the pipeline wont play the same video consecutively. It will play different videos one after another just fine. The fade functionality is also working well.
Here is a description of the problem:
The pipeline works the first time with an example video such as video_1.mp4. When I play the same video again it will get stuck on the first frame, stay on that frame and exit the process after a few seconds.
If I play video_1.mp4 and then play a different video like video_2.mp4 then the pipeline will work with no issues. If after this I play back video_1.mp4 then it will also play all the way through.
video_1.mp4 has a duration of 10 seconds. If I stop video_1.mp4 at 5 seconds and play the video again, the first frame of video_1.mp4 will display, it will wait at that frame for 5 seconds and then continue decoding the video at the same point where the previous pipeline left off.
My suspicion is the buffer running time has not been reset back to 0 when the same video plays. Im unsure of why this happens, I set the pipeline to NULL and I have tried doing a seek position 0 and flushing the elements in the pipeline after the video finishes to reset the clock time back to 0. Perhaps the syntax or the way i’m implementing it is incorrect.
This thread below creates the pipeline
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
class VideoPlayer():
def __init__(self):
Gst.init(None)
GObject.threads_init()
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.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.pipeline.set_state(Gst.State.NULL)
self.loop.run()
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)
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.set_state(Gst.State.PLAYING)
def stop_video(self):
self.running = False
self.pipeline.set_state(Gst.State.NULL)
self.pipeline.set_state(Gst.State.READY)
self.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0.0 * Gst.SECOND )
self.source.props.uri = ""
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
def exit(self):
self.loop.quit()