0

我正在使用在 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
4

0 回答 0