@@ -3396,6 +3396,38 @@ def _get_decoder_description(self, codec: str, fallback: str) -> Tuple[str, bool
33963396 parts.append(f"{key}={value}")
33973397
33983398 return " ".join(parts), using_hw
3399+
3400+ def _get_v4l2sink_decoder_description(
3401+ self,
3402+ codec: str,
3403+ fallbacks: Tuple[str, ...],
3404+ allow_hw: bool = True,
3405+ ) -> Tuple[str, bool]:
3406+ """Return a decoder fragment for viewer V4L2 output."""
3407+ primary_fallback = fallbacks[0]
3408+ disable_hw = getattr(self, "disable_hw_decoder", False) or not allow_hw
3409+ force_hw = getattr(self, "_force_hw_decoder", False) and allow_hw
3410+ factory_name, properties, using_hw = select_preferred_decoder(
3411+ codec,
3412+ primary_fallback,
3413+ disable_hw=disable_hw,
3414+ force_hw=force_hw,
3415+ )
3416+
3417+ if not using_hw:
3418+ for candidate in fallbacks:
3419+ if gst_element_available(candidate):
3420+ factory_name = candidate
3421+ properties = {}
3422+ break
3423+
3424+ parts = [factory_name]
3425+ for key, value in properties.items():
3426+ if isinstance(value, bool):
3427+ value = "true" if value else "false"
3428+ parts.append(f"{key}={value}")
3429+
3430+ return " ".join(parts), using_hw
33993431
34003432 def setup_ice_servers(self, webrtc):
34013433 """Configure ICE servers including default VDO.Ninja TURN servers"""
@@ -6628,28 +6660,55 @@ def _attach_v4l2sink_video_stream(self, pad: Gst.Pad, caps_name: str) -> Optiona
66286660 elif "AV1" in caps_upper:
66296661 codec_type = "AV1"
66306662
6663+ def build_v4l2sink_output_chain(using_hw_decoder: bool = False) -> str:
6664+ if using_hw_decoder and gst_element_available("nvvidconv"):
6665+ return (
6666+ "nvvidconv ! video/x-raw,format=NV12 ! "
6667+ "videoconvert ! videoscale ! videorate ! "
6668+ f"video/x-raw,format={self.v4l2sink_format},width=(int){self.v4l2sink_width},"
6669+ f"height=(int){self.v4l2sink_height},framerate=(fraction){self.v4l2sink_fps}/1 ! "
6670+ f"identity name=v4l2sink_identity_{pad.get_name()}"
6671+ )
6672+ return (
6673+ "videoconvert ! videoscale ! videorate ! "
6674+ f"video/x-raw,format={self.v4l2sink_format},width=(int){self.v4l2sink_width},"
6675+ f"height=(int){self.v4l2sink_height},framerate=(fraction){self.v4l2sink_fps}/1 ! "
6676+ f"identity name=v4l2sink_identity_{pad.get_name()}"
6677+ )
6678+
6679+ def get_v4l2sink_decoder(codec: str, fallbacks: Tuple[str, ...]) -> Tuple[str, bool]:
6680+ decoder_desc, using_hw_decoder = self._get_v4l2sink_decoder_description(codec, fallbacks)
6681+ if using_hw_decoder and not gst_element_available("nvvidconv"):
6682+ printwarn(
6683+ f"V4L2 sink hardware {codec} decoder requires `nvvidconv`; "
6684+ "falling back to software decoding."
6685+ )
6686+ decoder_desc, using_hw_decoder = self._get_v4l2sink_decoder_description(
6687+ codec,
6688+ fallbacks,
6689+ allow_hw=False,
6690+ )
6691+ return decoder_desc, using_hw_decoder
6692+
66316693 fallback_pipeline = (
66326694 "queue ! decodebin ! "
6633- "videoconvert ! videoscale ! videorate ! "
6634- f"video/x-raw,format={self.v4l2sink_format},width=(int){self.v4l2sink_width},"
6635- f"height=(int){self.v4l2sink_height},framerate=(fraction){self.v4l2sink_fps}/1 ! "
6636- f"identity name=v4l2sink_identity_{pad.get_name()}"
6695+ f"{build_v4l2sink_output_chain()}"
66376696 )
66386697
66396698 pipeline_desc = None
66406699 if codec_type == "VP8":
6700+ decoder_desc, using_hw_decoder = get_v4l2sink_decoder("VP8", ("vp8dec",))
66416701 pipeline_desc = (
6642- "queue ! rtpvp8depay ! vp8dec ! videoconvert ! videoscale ! videorate ! "
6643- f"video/x-raw,format={self.v4l2sink_format},width=(int){self.v4l2sink_width},"
6644- f"height=(int){self.v4l2sink_height},framerate=(fraction){self.v4l2sink_fps}/1 ! "
6645- f"identity name=v4l2sink_identity_{pad.get_name()}"
6702+ "queue ! rtpvp8depay ! "
6703+ f"{decoder_desc} ! "
6704+ f"{build_v4l2sink_output_chain(using_hw_decoder)}"
66466705 )
66476706 elif codec_type == "H264":
6707+ decoder_desc, using_hw_decoder = get_v4l2sink_decoder("H264", ("openh264dec", "avdec_h264"))
66486708 pipeline_desc = (
6649- "queue ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! videorate ! "
6650- f"video/x-raw,format={self.v4l2sink_format},width=(int){self.v4l2sink_width},"
6651- f"height=(int){self.v4l2sink_height},framerate=(fraction){self.v4l2sink_fps}/1 ! "
6652- f"identity name=v4l2sink_identity_{pad.get_name()}"
6709+ "queue ! rtph264depay ! h264parse ! "
6710+ f"{decoder_desc} ! "
6711+ f"{build_v4l2sink_output_chain(using_hw_decoder)}"
66536712 )
66546713 elif codec_type == "VP9":
66556714 if not gst_element_available("rtpvp9depay"):
@@ -6658,11 +6717,11 @@ def _attach_v4l2sink_video_stream(self, pad: Gst.Pad, caps_name: str) -> Optiona
66586717 )
66596718 pipeline_desc = fallback_pipeline
66606719 else:
6720+ decoder_desc, using_hw_decoder = get_v4l2sink_decoder("VP9", ("vp9dec",))
66616721 pipeline_desc = (
6662- "queue ! rtpvp9depay ! vp9dec ! videoconvert ! videoscale ! videorate ! "
6663- f"video/x-raw,format={self.v4l2sink_format},width=(int){self.v4l2sink_width},"
6664- f"height=(int){self.v4l2sink_height},framerate=(fraction){self.v4l2sink_fps}/1 ! "
6665- f"identity name=v4l2sink_identity_{pad.get_name()}"
6722+ "queue ! rtpvp9depay ! "
6723+ f"{decoder_desc} ! "
6724+ f"{build_v4l2sink_output_chain(using_hw_decoder)}"
66666725 )
66676726 elif codec_type == "AV1":
66686727 if not gst_element_available("rtpav1depay") or not gst_element_available("av1parse"):
@@ -6671,11 +6730,11 @@ def _attach_v4l2sink_video_stream(self, pad: Gst.Pad, caps_name: str) -> Optiona
66716730 )
66726731 pipeline_desc = fallback_pipeline
66736732 else:
6733+ decoder_desc, using_hw_decoder = get_v4l2sink_decoder("AV1", ("av1dec",))
66746734 pipeline_desc = (
6675- "queue ! rtpav1depay ! av1parse ! av1dec ! videoconvert ! videoscale ! videorate ! "
6676- f"video/x-raw,format={self.v4l2sink_format},width=(int){self.v4l2sink_width},"
6677- f"height=(int){self.v4l2sink_height},framerate=(fraction){self.v4l2sink_fps}/1 ! "
6678- f"identity name=v4l2sink_identity_{pad.get_name()}"
6735+ "queue ! rtpav1depay ! av1parse ! "
6736+ f"{decoder_desc} ! "
6737+ f"{build_v4l2sink_output_chain(using_hw_decoder)}"
66796738 )
66806739 else:
66816740 printc(f"Unsupported video codec for V4L2 sink: {caps_name}", "F70")
0 commit comments