Pipline Warning: gst-core-error-quark: A lot of buffers are being dropped. (13): gstbasesink.c(3003): gst_base_sink_is_too_late ():

Please provide complete information as applicable to your setup.

• Hardware Platform (Jetson / GPU) GPU
• DeepStream Version 6.3-docker
• JetPack Version (valid for Jetson only)
• TensorRT Version 8.5
• NVIDIA GPU Driver Version (valid for GPU only) 550.142
• Issue Type( questions, new requirements, bugs) questions
• How to reproduce the issue ? (This is for bugs. Including which sample app is using, the configuration files content, the command line used and other details for reproducing)

When my program uses the nvstreammux plugin to open multiple video streams (e.g., 13 video sources) and multiple programs open at the same time, the program warns:

Pipline Warning: gst-core-error-quark: A lot of buffers are being dropped. (13): gstbasesink.c(3003): gst_base_sink_is_too_late (): /GstPipeline:pipeline0/GstEglGlesSink:nvvideo-renderer: There may be a timestamping problem, or this computer is too slow.

Here’s the code I used to create the pipeline. Is there anything I can optimize:

 def create_pipeline(self):
        MyLogger.info("Creating Pipeline ")
        self.pipeline = Gst.Pipeline()
        self.is_live = False

        if not self.pipeline:
            MyLogger.error(" Unable to create Pipeline")
        MyLogger.info("Creating streamux ")

        self.streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
        if not self.streammux:
            MyLogger.error(" Unable to create NvStreamMux ")
            return False

        self.pipeline.add(self.streammux)
        valid_sources = []
        for i in range(self.number_sources):
            self.last_frame_time_dict[i] = time.time()
            MyLogger.info(f"Creating source_bin: {i}")
            uri_name = self.stream_paths[i]
            if uri_name.find("rtsp://") == 0:
                self.is_live = True
            self.source_bin = self.create_source_bin(i, uri_name)
            if self.source_bin is not None:
                valid_sources.append(self.source_bin)
                self.pipeline.add(self.source_bin)
            else:
                MyLogger.error(f"Skipping invalid source: {uri_name}")
                continue

            padname = "sink_%u" % i
            self.sinkpad = self.streammux.get_request_pad(padname)
            if not self.sinkpad:
                MyLogger.error("Unable to create sink pad bin")
                return False
            self.srcpad = self.source_bin.get_static_pad("src")
            if not self.srcpad:
                MyLogger.error("Unable to create src pad bin")
                return False
            self.srcpad.link(self.sinkpad)

        self.number_sources = len(valid_sources)  
        self.queue1 = Gst.ElementFactory.make("queue", "queue1")
        self.queue2 = Gst.ElementFactory.make("queue", "queue2")
        self.queue3 = Gst.ElementFactory.make("queue", "queue3")
        self.queue4 = Gst.ElementFactory.make("queue", "queue4")
        self.queue5 = Gst.ElementFactory.make("queue", "queue5")
        self.pipeline.add(self.queue1)
        self.pipeline.add(self.queue2)
        self.pipeline.add(self.queue3)
        self.pipeline.add(self.queue4)
        self.pipeline.add(self.queue5)

        self.nvdslogger = None
        self.transform = None

        MyLogger.info("Creating Pgie ")
        if self.requested_pgie is not None and (
                self.requested_pgie == 'nvinferserver' or self.requested_pgie == 'nvinferserver-grpc'):
            self.pgie = Gst.ElementFactory.make("nvinferserver", "primary-inference")
        elif self.requested_pgie is not None and self.requested_pgie == 'nvinfer':
            self.pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
        else:
            self.pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")

        if not self.pgie:
            MyLogger.error(f" Unable to create pgie :  {self.requested_pgie}")
            return False

        if self.disable_probe:
            MyLogger.info("Creating nvdslogger ")
            self.nvdslogger = Gst.ElementFactory.make("nvdslogger", "nvdslogger")

        MyLogger.info("Creating tiler ")
        self.tiler = Gst.ElementFactory.make("nvmultistreamtiler", "nvtiler")
        if not self.tiler:
            MyLogger.error(" Unable to create tiler")
            return False
        MyLogger.info("Creating nvvidconv")
        self.nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
        if not self.nvvidconv:
            MyLogger.error(" Unable to create nvvidconv")
            return False
        MyLogger.info("Creating nvosd")
        self.nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
        if not self.nvosd:
            MyLogger.error(" Unable to create nvosd")
            return False
        self.nvosd.set_property('process-mode', OSD_PROCESS_MODE)
        self.nvosd.set_property('display-text', OSD_DISPLAY_TEXT)

        if not int(MyConfigReader.cfg_dict["nvr"]["show"]):
            MyLogger.info("Creating Fakesink ")
            self.sink = Gst.ElementFactory.make("fakesink", "fakesink")
            self.sink.set_property('enable-last-sample', 0)
            self.sink.set_property('sync', 0)
        else:
            if is_aarch64():
                MyLogger.info("Creating transform")
                self.sink = Gst.ElementFactory.make("appsink", f"appsink")
            MyLogger.info("Creating EGLSink \n")
            self.sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
            self.sink.set_property('sync', 0)
        self.converter = Gst.ElementFactory.make("nvvideoconvert", f"converter2")
        self.capsfilter = Gst.ElementFactory.make("capsfilter", f"capsfilter")
        caps = Gst.Caps.from_string("video/x-raw(memory:NVMM), format=RGBA")
        mem_type = int(pyds.NVBUF_MEM_CUDA_UNIFIED)
        self.converter.set_property("nvbuf-memory-type", mem_type)
        self.tiler.set_property("nvbuf-memory-type", mem_type)
        self.capsfilter.set_property("caps", caps)

        if not self.sink:
            MyLogger.error(" Unable to create sink element")
            return False

        if self.is_live:
            MyLogger.info("At least one of the sources is live")
            self.streammux.set_property('live-source', 1)

        self.streammux.set_property('width', 1920)
        self.streammux.set_property('height', 1080)
        self.streammux.set_property('batch-size', self.number_sources)
        self.streammux.set_property('batched-push-timeout', 4000000)
        if self.requested_pgie == "nvinferserver" and self.config is not None:
            self.pgie.set_property('config-file-path', self.config)
        elif self.requested_pgie == "nvinferserver-grpc" and self.config is not None:
            self.pgie.set_property('config-file-path', self.config)
        elif self.requested_pgie == "nvinfer" and self.config is not None:
            self.pgie.set_property('config-file-path', self.config)
        else:
            self.pgie.set_property('config-file-path', self.config)
        pgie_batch_size = self.pgie.get_property("batch-size")
        if pgie_batch_size != self.number_sources:
            MyLogger.warning(
                f"WARNING: Overriding infer-config batch-size{pgie_batch_size}with number of sources{self.number_sources}")
            self.pgie.set_property("batch-size", self.number_sources)
        tiler_rows = int(math.sqrt(self.number_sources))
        tiler_columns = int(math.ceil((1.0 * self.number_sources) / tiler_rows))
        self.tiler.set_property("rows", tiler_rows)
        self.tiler.set_property("columns", tiler_columns)
        self.tiler.set_property("width", TILED_OUTPUT_WIDTH)
        self.tiler.set_property("height", TILED_OUTPUT_HEIGHT)
        self.sink.set_property("qos", 0)
        if int(MyConfigReader.cfg_dict["nvr"]["show"]):
            pass
        MyLogger.info("Adding elements to Pipeline")
        self.pipeline.add(self.pgie)
        self.pipeline.add(self.converter)
        self.pipeline.add(self.capsfilter)
        if self.nvdslogger:
            self.pipeline.add(self.nvdslogger)
        self.pipeline.add(self.tiler)
        self.pipeline.add(self.nvvidconv)
        self.pipeline.add(self.nvosd)
        if self.transform:
            self.pipeline.add(self.transform)
        if int(MyConfigReader.cfg_dict["nvr"]["show"]):
            pass
        self.pipeline.add(self.sink)
        MyLogger.info("Linking elements in the Pipeline \n")
        self.streammux.link(self.queue1)
        self.queue1.link(self.pgie)
        self.pgie.link(self.converter)
        self.converter.link(self.capsfilter)
        self.capsfilter.link(self.queue2)
        if self.nvdslogger:
            self.queue2.link(self.nvdslogger)
            self.nvdslogger.link(self.tiler)
        else:
            self.queue2.link(self.tiler)
        self.tiler.link(self.queue3)
        self.queue3.link(self.nvvidconv)
        self.nvvidconv.link(self.queue4)
        self.queue4.link(self.nvosd)
        if self.transform:
            self.nvosd.link(self.queue5)
            self.queue5.link(self.transform)
            self.transform.link(self.sink)
        else:
            self.nvosd.link(self.queue5)
            if int(MyConfigReader.cfg_dict["nvr"]["show"]):
                self.queue5.link(self.sink)
            else:
                self.queue5.link(self.sink)
        return True


The units here are “us”. You are setting 4s to the nvstreammux, this might cause the warning. Please configure this parameter the appropriate value according to your stream.

Should I make the timeout larger

No. You can try to make the timeout smaller, just the 1000000/fps. Like if the fps of your stream is 25, you can set that to 40000.

When I open multiple camera streams with nvstreammux plugin, the camera picture will appear blank screen, like the camera stream decoding problem, this is why.

Is the fps difference between these cameras large? You can refer to our FAQ to learn how to set the parameters for the multiple rtsp streams.

Streams placed in the same nvstreammux plugin have the same direct frame rate, and I don’t get a blurb when I open one pipeline, but when I run multiple pipelines to play multiple streams at the same time, this problem occurs

So you are using multiple pipelines, and each pipeline with multiple rtsp sources, is that right?
Can you find out how many pipelines there are when problems occurs? And you can add the GST_DEBUG=5 in front of your command to open more log and attach the whole log.
Also what do you mean about get a blurb?

So you are using multiple pipelines , and each pipeline with multiple rtsp sources , is that right? YES
Can you find out how many pipelines there are when problems occurs? About four pipelines
Also what do you mean about get a blurb ? I mean the phenomenon of frame skipping and blank screen

That might be a heavy loading issue. Could you attach the GPU and CPU loading when you run into this issue?

Why is it that I have less memory and graphics card power consumption and less cpu usage, but my graphics card utilization is particularly high?Both of the following cases will blank screen

I open a pipeline of 6 code streams, a total of 4 programs, gpu and cpu usage is as follows:


I open a pipeline of 14 code streams, a total of 1 program, gpu and cpu usage are as follows:



Below is the code I used to create the pipeline:

def decodebin_child_added(self, child_proxy, Object, name, user_data):
        MyLogger.info(f"Decodebin child added:{name}")
        if (name.find("decodebin") != -1):
            Object.connect("child-added", self.decodebin_child_added, user_data)

        if "nvv" in name.lower():  # 匹配 nvv4l2decoder, nvv4l2h264dec 等
            if Object.find_property("gpu_id") is not None:
                Object.set_property("gpu_id", GPU_ID)  # 与下游组件(如 nvstreammux)一致
                MyLogger.info(f"设置 {name} 的 GPU_ID 为 1")

        #todo
        # if "source" in name:
        #     source_element = child_proxy.get_by_name("source")
        #     if source_element.find_property('drop-on-latency') != None:
        #         Object.set_property("drop-on-latency", True)

    def create_pipeline(self):
        MyLogger.info("Creating Pipeline ")
        self.pipeline = Gst.Pipeline()
        self.is_live = False

        if not self.pipeline:
            MyLogger.error(" Unable to create Pipeline")
        MyLogger.info("Creating streamux ")

        # Create nvstreammux instance to form batches from one or more sources.
        self.streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
        if not self.streammux:
            MyLogger.error(" Unable to create NvStreamMux ")
            return False
        self.streammux.set_property('batched-push-timeout', 40000)
        self.streammux.set_property("gpu_id", GPU_ID)

        self.pipeline.add(self.streammux)
        valid_sources = []
        for i in range(self.number_sources):
            self.last_frame_time_dict[i] = time.time()
            MyLogger.info(f"Creating source_bin: {i}")
            uri_name = self.stream_paths[i]
            if uri_name.find("rtsp://") == 0:
                self.is_live = True
            # todo
            self.source_bin = self.create_source_bin(i, uri_name)
            # if not self.source_bin:
            #     MyLogger.error("Unable to create source bin")
            #     return False
            # self.pipeline.add(self.source_bin)
            if self.source_bin is not None:
                # valid_sources.append(source_bin)
                # ½« source_bin Ìí¼Óµ½Ö÷ Pipeline ÖÐ
                valid_sources.append(self.source_bin)
                self.pipeline.add(self.source_bin)
            else:
                MyLogger.error(f"Skipping invalid source: {uri_name}")
                continue

            padname = "sink_%u" % i
            self.sinkpad = self.streammux.get_request_pad(padname)
            if not self.sinkpad:
                MyLogger.error("Unable to create sink pad bin")
                return False
            self.srcpad = self.source_bin.get_static_pad("src")
            if not self.srcpad:
                MyLogger.error("Unable to create src pad bin")
                return False
            self.srcpad.link(self.sinkpad)

        self.number_sources = len(valid_sources)
        # self.streammux.set_property("batch-size", self.number_sources)
        # self.pgie.set_property("batch-size", self.number_sources)
        self.queue1 = Gst.ElementFactory.make("queue", "queue1")
        self.queue2 = Gst.ElementFactory.make("queue", "queue2")
        self.queue3 = Gst.ElementFactory.make("queue", "queue3")
        self.queue4 = Gst.ElementFactory.make("queue", "queue4")
        self.queue5 = Gst.ElementFactory.make("queue", "queue5")
        self.queue6 = Gst.ElementFactory.make("queue", "queue6")
        self.queue7 = Gst.ElementFactory.make("queue", "queue7")
        # self.queue8 = Gst.ElementFactory.make("queue", "queue8")
        self.pipeline.add(self.queue1)
        self.pipeline.add(self.queue2)
        self.pipeline.add(self.queue3)
        self.pipeline.add(self.queue4)
        self.pipeline.add(self.queue5)
        self.pipeline.add(self.queue6)
        self.pipeline.add(self.queue7)
        # self.pipeline.add(self.queue8)

        self.nvdslogger = None
        self.transform = None

        MyLogger.info("Creating Pgie ")
        if self.requested_pgie is not None and (
                self.requested_pgie == 'nvinferserver' or self.requested_pgie == 'nvinferserver-grpc'):
            self.pgie = Gst.ElementFactory.make("nvinferserver", "primary-inference")
        elif self.requested_pgie is not None and self.requested_pgie == 'nvinfer':
            self.pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
        else:
            self.pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")

        if not self.pgie:
            MyLogger.error(f" Unable to create pgie :  {self.requested_pgie}")
            return False

        if self.disable_probe:
            # Use nvdslogger for perf measurement instead of probe function
            MyLogger.info("Creating nvdslogger ")
            self.nvdslogger = Gst.ElementFactory.make("nvdslogger", "nvdslogger")
            self.nvdslogger.set_property("gpu_id", GPU_ID)

        MyLogger.info("Creating tiler ")
        self.tiler = Gst.ElementFactory.make("nvmultistreamtiler", "nvtiler")
        self.tiler.set_property("gpu_id", GPU_ID)
        if not self.tiler:
            MyLogger.error(" Unable to create tiler")
            return False
        MyLogger.info("Creating nvvidconv")
        self.nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
        self.nvvidconv.set_property("gpu_id", GPU_ID)
        if not self.nvvidconv:
            MyLogger.error(" Unable to create nvvidconv")
            return False
        MyLogger.info("Creating nvosd")
        self.nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
        self.nvosd.set_property("gpu_id", GPU_ID)
        if not self.nvosd:
            MyLogger.error(" Unable to create nvosd")
            return False
        self.nvosd.set_property('process-mode', OSD_PROCESS_MODE)
        self.nvosd.set_property('display-text', OSD_DISPLAY_TEXT)

        if not int(MyConfigReader.cfg_dict["nvr"]["show"]):
            MyLogger.info("Creating Fakesink ")
            self.sink = Gst.ElementFactory.make("fakesink", "fakesink")
            self.sink.set_property('enable-last-sample', 0)
            self.sink.set_property('sync', 0)
        else:
            if is_aarch64():
                MyLogger.info("Creating transform")
                # self.transform = Gst.ElementFactory.make("nvegltransform", "nvegl-transform")
                # if not self.transform:
                #     MyLogger.error(" Unable to create transform")
                self.sink = Gst.ElementFactory.make("appsink", f"appsink")
            MyLogger.info("Creating EGLSink \n")
            self.sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
            self.sink.set_property('sync', 0)
            self.sink.set_property("gpu_id", GPU_ID)
            # self.sink = Gst.ElementFactory.make("appsink", f"appsink1")

        self.converter = Gst.ElementFactory.make("nvvideoconvert", f"converter2")
        self.converter.set_property("gpu_id", GPU_ID)
        self.capsfilter = Gst.ElementFactory.make("capsfilter", f"capsfilter")
        caps = Gst.Caps.from_string("video/x-raw(memory:NVMM), format=RGBA")
        # self.converter.set_property("nvbuf-memory-type", 0)
        mem_type = int(pyds.NVBUF_MEM_CUDA_UNIFIED)
        self.converter.set_property("nvbuf-memory-type", mem_type)
        self.tiler.set_property("nvbuf-memory-type", mem_type)
        self.capsfilter.set_property("caps", caps)

        if not self.sink:
            MyLogger.error(" Unable to create sink element")
            return False

        if self.is_live:
            MyLogger.info("At least one of the sources is live")
            self.streammux.set_property('live-source', 1)

        self.streammux.set_property('width', 1920)
        self.streammux.set_property('height', 1080)

        self.streammux.set_property('batch-size', self.number_sources)

        if self.requested_pgie == "nvinferserver" and self.config is not None:
            self.pgie.set_property('config-file-path', self.config)
        elif self.requested_pgie == "nvinferserver-grpc" and self.config is not None:
            self.pgie.set_property('config-file-path', self.config)
        elif self.requested_pgie == "nvinfer" and self.config is not None:
            self.pgie.set_property('config-file-path', self.config)
        else:
            # todo
            self.pgie.set_property('config-file-path', self.config)
        pgie_batch_size = self.pgie.get_property("batch-size")
        if pgie_batch_size != self.number_sources:
            # print( pgie_batch_size, ":", self.number_sources)
            MyLogger.warning(
                f"WARNING: Overriding infer-config batch-size{pgie_batch_size}with number of sources{self.number_sources}")
            self.pgie.set_property("batch-size", self.number_sources)
        tiler_rows = int(math.sqrt(self.number_sources))
        tiler_columns = int(math.ceil((1.0 * self.number_sources) / tiler_rows))
        self.tiler.set_property("rows", tiler_rows)
        self.tiler.set_property("columns", tiler_columns)
        self.tiler.set_property("width", TILED_OUTPUT_WIDTH)
        self.tiler.set_property("height", TILED_OUTPUT_HEIGHT)
        # todo
        self.sink.set_property("qos", 1)
        if int(MyConfigReader.cfg_dict["nvr"]["show"]):
            # todo
            # self.sink.set_property("emit-signals", True)
            # self.sink.set_property("sync", False)
            # self.sink.set_property("max-buffers", 50)
            # self.sink.set_property("drop", True)
            # self.sink.connect("new-sample", self.on_new_sample)
            pass

        MyLogger.info("Adding elements to Pipeline")
        self.pipeline.add(self.pgie)
        # todo
        self.pipeline.add(self.converter)
        self.pipeline.add(self.capsfilter)
        if self.nvdslogger:
            self.pipeline.add(self.nvdslogger)
        self.pipeline.add(self.tiler)
        self.pipeline.add(self.nvvidconv)
        self.pipeline.add(self.nvosd)
        if self.transform:
            self.pipeline.add(self.transform)

        if int(MyConfigReader.cfg_dict["nvr"]["show"]):
            pass

        # self.pipeline.add(self.encoder)
        # self.pipeline.add(self.parser)
        self.pipeline.add(self.sink)

        MyLogger.info("Linking elements in the Pipeline \n")
        self.streammux.link(self.queue1)
        self.queue1.link(self.pgie)
        self.pgie.link(self.queue2)
        self.queue2.link(self.converter)
        self.converter.link(self.queue3)
        self.queue3.link(self.capsfilter)
        self.capsfilter.link(self.queue4)
        if self.nvdslogger:
            self.queue4.link(self.nvdslogger)
            self.nvdslogger.link(self.tiler)
        else:
            self.queue4.link(self.tiler)
        self.tiler.link(self.queue5)
        self.queue5.link(self.nvvidconv)
        self.nvvidconv.link(self.queue6)
        self.queue6.link(self.nvosd)
        if self.transform:
            self.nvosd.link(self.queue7)
            self.queue7.link(self.transform)
            self.transform.link(self.sink)
        else:
            self.nvosd.link(self.queue7)
            if int(MyConfigReader.cfg_dict["nvr"]["show"]):
                # self.queue5.link(self.converter)
                # self.converter.link(self.capsfilter)
                # self.capsfilter.link(self.sink)
                self.queue7.link(self.sink)

            else:
                self.queue7.link(self.sink)
        return True

    def create_source_bin(self, index, uri):
        MyLogger.info("Creating source bin")
        if not self.check_uri_valid(uri):
            MyLogger.error(f"URI is invalid or unreachable: {uri}", )
            return None  # Ö±½Ó·µ»Ø None ±íʾ´´½¨Ê§°Ü

        # Create a source GstBin to abstract this bin's content from the rest of the
        # pipeline
        bin_name = "source-bin-%02d" % index
        MyLogger.info(bin_name)
        nbin = Gst.Bin.new(bin_name)
        if not nbin:
            MyLogger.error(" Unable to create source bin")

        if file_loop:
            # use nvurisrcbin to enable file-loop
            uri_decode_bin = Gst.ElementFactory.make("nvurisrcbin", "uri-decode-bin")
            uri_decode_bin.set_property("file-loop", 1)
            MyLogger.info("nvurisrcbin")
        else:
            uri_decode_bin = Gst.ElementFactory.make("uridecodebin", "uri-decode-bin")
            MyLogger.info("uridecodebin")
        if not uri_decode_bin:
            sys.stderr.write(" Unable to create uri decode bin \n")
        # We set the input uri to the source element
        uri_decode_bin.set_property("uri", uri)
        # Connect to the "pad-added" signal of the decodebin which generates a
        # callback once a new pad for raw data has beed created by the decodebin
        # todo
        uri_decode_bin.connect("pad-added", self.cb_newpad, nbin)
        self.source_bins[index] = nbin
        uri_decode_bin.connect("child-added", self.decodebin_child_added, nbin)

        Gst.Bin.add(nbin, uri_decode_bin)
        bin_pad = nbin.add_pad(Gst.GhostPad.new_no_target("src", Gst.PadDirection.SRC))
        if not bin_pad:
            sys.stderr.write(" Failed to add ghost pad in source bin \n")
            return None
        return nbin

My cpu usage is low. Is there something I can offload to the cpu?

No. Because you have multiple pipelines, the nvinfer in each pipeline uses GPU to reason. It cannot offload to the cpu.
In your scenario, there is no other way to solve the problem of insufficient GPU loading issue.
If you just need to change sources, we suggest you can refer to our runtime_source_add_delete.

Wouldn’t it be better if I delegated the decoding and other work to the cpu and only the inference work to the gpu

Yes, it would be better, but they do not solve the fundamental problem. The one that uses the most GPU resources is nvinfer.
In your scenario, the usage of GPU can only be reduced by reducing the number of your pipelines.

But I also have this problem when I only have one pipeline.
Secondly, how can I modify the code to decode the stream using the CPU, In addition to selecting Gst.ElementFactory.make("uridecodebin", "uri-decode-bin") instead of Gst.ElementFactory.make("nvurisrcbin", "uri-decode-bin")

This means that your model inference is beyond the load of your GPU. You can try to set the interval=10 to reduce the loading. Please refer to our Gst-nvinfer Property Group Supported Keys to learn how to setup that.

You should build the entire decoding pipeline yourself. Please refer to our deepstream-test1 to learn how to build the decoding pipeline yourself.