[Merge-148] Cherry pick "Move the NullVideoDecoder into a separate file and target."

Original change's description:
> Move the NullVideoDecoder into a separate file and target.
>
> This is necessary because the Chromium video decoder factory needs to
> be able to instantiate it for fallback purposes (e.g. Decode error
> causing the HW to no longer be available).
>
> Bug: chromium:500960863
> Change-Id: I724a5f28aa0a0a615972dd49eebde701c3b7ec90
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/462243
> Reviewed-by: Evan Shrubsole <eshr@webrtc.org>
> Commit-Queue: Henrik Boström <hbos@webrtc.org>
> Cr-Commit-Position: refs/heads/main@{#47366}

Bug: chromium:500960863
Change-Id: I724a5f28aa0a0a615972dd49eebde701c3b7ec90
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/464521
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@webrtc.org>
Cr-Commit-Position: refs/branch-heads/7778@{#1}
Cr-Branched-From: ca896b7ffef011bbf6957c99d413c5aac602c99f-refs/heads/main@{#47319}
diff --git a/video/BUILD.gn b/video/BUILD.gn
index f7f773e..766f6b2 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -73,6 +73,7 @@
     ":frame_cadence_adapter",
     ":frame_decode_scheduler",
     ":frame_dumping_decoder",
+    ":null_video_decoder",
     ":task_queue_frame_decode_scheduler",
     ":unique_timestamp_counter",
     ":video_stream_buffer_controller",
@@ -191,6 +192,23 @@
   }
 }
 
+rtc_library("null_video_decoder") {
+  visibility = [ "*" ]
+
+  sources = [
+    "null_video_decoder.cc",
+    "null_video_decoder.h",
+  ]
+
+  deps = [
+    "../api/video:encoded_image",
+    "../api/video_codecs:video_codecs_api",
+    "../modules/video_coding:video_codec_interface",
+    "../rtc_base:logging",
+    "../rtc_base/system:rtc_export",
+  ]
+}
+
 rtc_library("frame_dumping_decoder") {
   visibility = [ "*" ]
 
diff --git a/video/null_video_decoder.cc b/video/null_video_decoder.cc
new file mode 100644
index 0000000..3ce5812
--- /dev/null
+++ b/video/null_video_decoder.cc
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2026 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "video/null_video_decoder.h"
+
+#include <cstdint>
+
+#include "api/video/encoded_image.h"
+#include "api/video_codecs/video_decoder.h"
+#include "modules/video_coding/include/video_error_codes.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+bool NullVideoDecoder::Configure(const Settings& settings) {
+  RTC_LOG(LS_ERROR) << "Can't initialize NullVideoDecoder.";
+  return true;
+}
+
+int32_t NullVideoDecoder::Decode(const EncodedImage& input_image,
+                                 int64_t render_time_ms) {
+  RTC_LOG(LS_ERROR) << "The NullVideoDecoder doesn't support decoding.";
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t NullVideoDecoder::RegisterDecodeCompleteCallback(
+    DecodedImageCallback* callback) {
+  RTC_LOG(LS_ERROR)
+      << "Can't register decode complete callback on NullVideoDecoder.";
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t NullVideoDecoder::Release() {
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+const char* NullVideoDecoder::ImplementationName() const {
+  return "NullVideoDecoder";
+}
+
+}  // namespace webrtc
diff --git a/video/null_video_decoder.h b/video/null_video_decoder.h
new file mode 100644
index 0000000..3ed049c
--- /dev/null
+++ b/video/null_video_decoder.h
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2026 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef VIDEO_NULL_VIDEO_DECODER_H_
+#define VIDEO_NULL_VIDEO_DECODER_H_
+
+#include <cstdint>
+
+#include "api/video/encoded_image.h"
+#include "api/video_codecs/video_decoder.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// The decoder used when there is no real decoder implementation available.
+class RTC_EXPORT NullVideoDecoder : public VideoDecoder {
+ public:
+  // These are NO-OPs except for RTC_LOG lines.
+  bool Configure(const Settings& settings) override;
+  int32_t Decode(const EncodedImage& input_image,
+                 int64_t render_time_ms) override;
+  int32_t RegisterDecodeCompleteCallback(
+      DecodedImageCallback* callback) override;
+  int32_t Release() override;
+
+  // This is exposed in getStats() as "decoderImplementation".
+  const char* ImplementationName() const override;
+};
+
+}  // namespace webrtc
+
+#endif  // VIDEO_NULL_VIDEO_DECODER_H_
diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc
index 8e3f6a8..79624b6 100644
--- a/video/video_receive_stream2.cc
+++ b/video/video_receive_stream2.cc
@@ -88,6 +88,7 @@
 #include "video/decode_synchronizer.h"
 #include "video/frame_decode_scheduler.h"
 #include "video/frame_dumping_decoder.h"
+#include "video/null_video_decoder.h"
 #include "video/receive_statistics_proxy.h"
 #include "video/render/incoming_video_stream.h"
 #include "video/task_queue_frame_decode_scheduler.h"
@@ -170,33 +171,6 @@
   return RenderResolution(320, 180);
 }
 
-// Video decoder class to be used for unknown codecs. Doesn't support decoding
-// but logs messages to LS_ERROR.
-class NullVideoDecoder : public VideoDecoder {
- public:
-  bool Configure(const Settings& settings) override {
-    RTC_LOG(LS_ERROR) << "Can't initialize NullVideoDecoder.";
-    return true;
-  }
-
-  int32_t Decode(const EncodedImage& input_image,
-                 int64_t render_time_ms) override {
-    RTC_LOG(LS_ERROR) << "The NullVideoDecoder doesn't support decoding.";
-    return WEBRTC_VIDEO_CODEC_OK;
-  }
-
-  int32_t RegisterDecodeCompleteCallback(
-      DecodedImageCallback* callback) override {
-    RTC_LOG(LS_ERROR)
-        << "Can't register decode complete callback on NullVideoDecoder.";
-    return WEBRTC_VIDEO_CODEC_OK;
-  }
-
-  int32_t Release() override { return WEBRTC_VIDEO_CODEC_OK; }
-
-  const char* ImplementationName() const override { return "NullVideoDecoder"; }
-};
-
 bool IsKeyFrameAndUnspecifiedResolution(const EncodedFrame& frame) {
   return frame.IsKey() && frame.EncodedImage()._encodedWidth == 0 &&
          frame.EncodedImage()._encodedHeight == 0;
@@ -555,10 +529,8 @@
                "VideoReceiveStream2::CreateAndRegisterExternalDecoder");
   std::unique_ptr<VideoDecoder> video_decoder =
       config_.decoder_factory->Create(env_, decoder.video_format);
-  // If we still have no valid decoder, we have to create a "Null" decoder
-  // that ignores all calls. The reason we can get into this state is that the
-  // old decoder factory interface doesn't have a way to query supported
-  // codecs.
+  // The factory can end up in this state either if the format is not supported
+  // or because a creation step failed, e.g. HW is unavailable.
   if (!video_decoder) {
     video_decoder = std::make_unique<NullVideoDecoder>();
   }