Move H.264 SPS VUI rewriting to FrameEncodeMetadataWriter.

Bug: webrtc:10559
Change-Id: I956287e71a47856cfb6dd807d9715d6ee2572f55
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/138263
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Commit-Queue: Mirta Dvornicic <mirtad@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28100}
diff --git a/api/video/encoded_image.h b/api/video/encoded_image.h
index 206ffb6..8bde5b3 100644
--- a/api/video/encoded_image.h
+++ b/api/video/encoded_image.h
@@ -93,6 +93,12 @@
     buffer_ = nullptr;
   }
 
+  void SetEncodedData(const rtc::CopyOnWriteBuffer& encoded_data) {
+    encoded_data_ = encoded_data;
+    size_ = encoded_data.size();
+    buffer_ = nullptr;
+  }
+
   uint8_t* data() { return buffer_ ? buffer_ : encoded_data_.data(); }
   const uint8_t* data() const {
     return buffer_ ? buffer_ : encoded_data_.cdata();
diff --git a/common_video/h264/sps_vui_rewriter.cc b/common_video/h264/sps_vui_rewriter.cc
index 850d29f..b6cb4ed 100644
--- a/common_video/h264/sps_vui_rewriter.cc
+++ b/common_video/h264/sps_vui_rewriter.cc
@@ -202,7 +202,7 @@
     size_t num_nalus,
     const size_t* nalu_offsets,
     const size_t* nalu_lengths,
-    rtc::Buffer* output_buffer,
+    rtc::CopyOnWriteBuffer* output_buffer,
     size_t* output_nalu_offsets,
     size_t* output_nalu_lengths) {
   // Allocate some extra space for potentially adding a missing VUI.
diff --git a/common_video/h264/sps_vui_rewriter.h b/common_video/h264/sps_vui_rewriter.h
index 99198f9..250b641 100644
--- a/common_video/h264/sps_vui_rewriter.h
+++ b/common_video/h264/sps_vui_rewriter.h
@@ -18,6 +18,7 @@
 #include "absl/types/optional.h"
 #include "common_video/h264/sps_parser.h"
 #include "rtc_base/buffer.h"
+#include "rtc_base/copy_on_write_buffer.h"
 
 namespace webrtc {
 
@@ -60,7 +61,7 @@
       size_t num_nalus,
       const size_t* nalu_offsets,
       const size_t* nalu_lengths,
-      rtc::Buffer* output_buffer,
+      rtc::CopyOnWriteBuffer* output_buffer,
       size_t* output_nalu_offsets,
       size_t* output_nalu_lengths);
 
diff --git a/common_video/h264/sps_vui_rewriter_unittest.cc b/common_video/h264/sps_vui_rewriter_unittest.cc
index 643271f..263bfef 100644
--- a/common_video/h264/sps_vui_rewriter_unittest.cc
+++ b/common_video/h264/sps_vui_rewriter_unittest.cc
@@ -209,7 +209,7 @@
   nalu_lengths[1] = sizeof(kIdr1);
   buffer.AppendData(kIdr1);
 
-  rtc::Buffer modified_buffer;
+  rtc::CopyOnWriteBuffer modified_buffer;
   size_t modified_nalu_offsets[kNumNalus];
   size_t modified_nalu_lengths[kNumNalus];
 
@@ -273,7 +273,7 @@
   expected_nalu_lengths[2] = sizeof(kIdr2);
   expected_buffer.AppendData(kIdr2);
 
-  rtc::Buffer modified_buffer;
+  rtc::CopyOnWriteBuffer modified_buffer;
   size_t modified_nalu_offsets[kNumNalus];
   size_t modified_nalu_lengths[kNumNalus];
 
diff --git a/modules/rtp_rtcp/source/rtp_format_h264.cc b/modules/rtp_rtcp/source/rtp_format_h264.cc
index e259599..0922935 100644
--- a/modules/rtp_rtcp/source/rtp_format_h264.cc
+++ b/modules/rtp_rtcp/source/rtp_format_h264.cc
@@ -76,26 +76,15 @@
     H264PacketizationMode packetization_mode,
     const RTPFragmentationHeader& fragmentation)
     : limits_(limits),
-      modified_buffer_(new rtc::Buffer()),
       num_packets_left_(0) {
   // Guard against uninitialized memory in packetization_mode.
   RTC_CHECK(packetization_mode == H264PacketizationMode::NonInterleaved ||
             packetization_mode == H264PacketizationMode::SingleNalUnit);
 
-  RTPFragmentationHeader modified_fragmentation;
-  modified_fragmentation.CopyFrom(fragmentation);
-
-  SpsVuiRewriter::ParseOutgoingBitstreamAndRewriteSps(
-      payload, fragmentation.fragmentationVectorSize,
-      fragmentation.fragmentationOffset, fragmentation.fragmentationLength,
-      modified_buffer_.get(), modified_fragmentation.fragmentationOffset,
-      modified_fragmentation.fragmentationLength);
-
-  for (size_t i = 0; i < modified_fragmentation.fragmentationVectorSize; ++i) {
-    const uint8_t* fragment = modified_buffer_->data() +
-                              modified_fragmentation.fragmentationOffset[i];
-    const size_t fragment_length =
-        modified_fragmentation.fragmentationLength[i];
+  for (size_t i = 0; i < fragmentation.fragmentationVectorSize; ++i) {
+    const uint8_t* fragment =
+        payload.data() + fragmentation.fragmentationOffset[i];
+    const size_t fragment_length = fragmentation.fragmentationLength[i];
     input_fragments_.push_back(Fragment(fragment, fragment_length));
   }
 
diff --git a/modules/rtp_rtcp/source/rtp_format_h264.h b/modules/rtp_rtcp/source/rtp_format_h264.h
index e27189d..3a51359 100644
--- a/modules/rtp_rtcp/source/rtp_format_h264.h
+++ b/modules/rtp_rtcp/source/rtp_format_h264.h
@@ -90,7 +90,6 @@
   void NextFragmentPacket(RtpPacketToSend* rtp_packet);
 
   const PayloadSizeLimits limits_;
-  std::unique_ptr<rtc::Buffer> modified_buffer_;
   size_t num_packets_left_;
   std::deque<Fragment> input_fragments_;
   std::queue<PacketUnit> packets_;
diff --git a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
index 6253d58..484dbcb 100644
--- a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
@@ -497,7 +497,6 @@
   EXPECT_THAT(packets, IsEmpty());
 }
 
-const uint8_t kStartSequence[] = {0x00, 0x00, 0x00, 0x01};
 const uint8_t kOriginalSps[] = {kSps, 0x00, 0x00, 0x03, 0x03,
                                 0xF4, 0x05, 0x03, 0xC7, 0xC0};
 const uint8_t kRewrittenSps[] = {kSps, 0x00, 0x00, 0x03, 0x03, 0xF4, 0x05, 0x03,
@@ -505,79 +504,6 @@
 const uint8_t kIdrOne[] = {kIdr, 0xFF, 0x00, 0x00, 0x04};
 const uint8_t kIdrTwo[] = {kIdr, 0xFF, 0x00, 0x11};
 
-class RtpPacketizerH264TestSpsRewriting : public ::testing::Test {
- public:
-  void SetUp() override {
-    fragmentation_header_.VerifyAndAllocateFragmentationHeader(3);
-    fragmentation_header_.fragmentationVectorSize = 3;
-    in_buffer_.AppendData(kStartSequence);
-
-    fragmentation_header_.fragmentationOffset[0] = in_buffer_.size();
-    fragmentation_header_.fragmentationLength[0] = sizeof(kOriginalSps);
-    in_buffer_.AppendData(kOriginalSps);
-
-    fragmentation_header_.fragmentationOffset[1] = in_buffer_.size();
-    fragmentation_header_.fragmentationLength[1] = sizeof(kIdrOne);
-    in_buffer_.AppendData(kIdrOne);
-
-    fragmentation_header_.fragmentationOffset[2] = in_buffer_.size();
-    fragmentation_header_.fragmentationLength[2] = sizeof(kIdrTwo);
-    in_buffer_.AppendData(kIdrTwo);
-  }
-
- protected:
-  rtc::Buffer in_buffer_;
-  RTPFragmentationHeader fragmentation_header_;
-};
-
-TEST_F(RtpPacketizerH264TestSpsRewriting, FuASps) {
-  const size_t kHeaderOverhead = kFuAHeaderSize + 1;
-
-  // Set size to fragment SPS into two FU-A packets.
-  RtpPacketizer::PayloadSizeLimits limits;
-  limits.max_payload_len = sizeof(kOriginalSps) - 2 + kHeaderOverhead;
-  RtpPacketizerH264 packetizer(in_buffer_, limits,
-                               H264PacketizationMode::NonInterleaved,
-                               fragmentation_header_);
-  std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
-
-  size_t offset = H264::kNaluTypeSize;
-  size_t length = packets[0].payload_size() - kFuAHeaderSize;
-  EXPECT_THAT(packets[0].payload().subview(kFuAHeaderSize),
-              ElementsAreArray(&kRewrittenSps[offset], length));
-  offset += length;
-
-  length = packets[1].payload_size() - kFuAHeaderSize;
-  EXPECT_THAT(packets[1].payload().subview(kFuAHeaderSize),
-              ElementsAreArray(&kRewrittenSps[offset], length));
-  offset += length;
-
-  EXPECT_EQ(offset, sizeof(kRewrittenSps));
-}
-
-TEST_F(RtpPacketizerH264TestSpsRewriting, StapASps) {
-  const size_t kHeaderOverhead = kFuAHeaderSize + 1;
-  const size_t kExpectedTotalSize = H264::kNaluTypeSize +  // Stap-A type.
-                                    sizeof(kRewrittenSps) + sizeof(kIdrOne) +
-                                    sizeof(kIdrTwo) + (kLengthFieldLength * 3);
-
-  // Set size to include SPS and the rest of the packets in a Stap-A package.
-  RtpPacketizer::PayloadSizeLimits limits;
-  limits.max_payload_len = kExpectedTotalSize + kHeaderOverhead;
-
-  RtpPacketizerH264 packetizer(in_buffer_, limits,
-                               H264PacketizationMode::NonInterleaved,
-                               fragmentation_header_);
-  std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
-
-  ASSERT_THAT(packets, SizeIs(1));
-  EXPECT_EQ(packets[0].payload_size(), kExpectedTotalSize);
-  EXPECT_THAT(
-      packets[0].payload().subview(H264::kNaluTypeSize + kLengthFieldLength,
-                                   sizeof(kRewrittenSps)),
-      ElementsAreArray(kRewrittenSps));
-}
-
 struct H264ParsedPayload : public RtpDepacketizer::ParsedPayload {
   RTPVideoHeaderH264& h264() {
     return absl::get<RTPVideoHeaderH264>(video.video_type_header);
diff --git a/video/frame_encode_metadata_writer.cc b/video/frame_encode_metadata_writer.cc
index 4b5fabb..999ca74 100644
--- a/video/frame_encode_metadata_writer.cc
+++ b/video/frame_encode_metadata_writer.cc
@@ -12,8 +12,11 @@
 
 #include <algorithm>
 
+#include "absl/memory/memory.h"
+#include "common_video/h264/sps_vui_rewriter.h"
 #include "modules/include/module_common_types_public.h"
 #include "modules/video_coding/include/video_coding_defines.h"
+#include "rtc_base/copy_on_write_buffer.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/time_utils.h"
 
@@ -183,6 +186,35 @@
   }
 }
 
+std::unique_ptr<RTPFragmentationHeader>
+FrameEncodeMetadataWriter::UpdateBitstream(
+    const CodecSpecificInfo* codec_specific_info,
+    const RTPFragmentationHeader* fragmentation,
+    EncodedImage* encoded_image) {
+  if (!codec_specific_info ||
+      codec_specific_info->codecType != kVideoCodecH264 || !fragmentation ||
+      encoded_image->_frameType != VideoFrameType::kVideoFrameKey) {
+    return nullptr;
+  }
+
+  rtc::CopyOnWriteBuffer modified_buffer;
+  std::unique_ptr<RTPFragmentationHeader> modified_fragmentation =
+      absl::make_unique<RTPFragmentationHeader>();
+  modified_fragmentation->CopyFrom(*fragmentation);
+
+  // Make sure that the data is not copied if owned by EncodedImage.
+  const EncodedImage& buffer = *encoded_image;
+  SpsVuiRewriter::ParseOutgoingBitstreamAndRewriteSps(
+      buffer, fragmentation->fragmentationVectorSize,
+      fragmentation->fragmentationOffset, fragmentation->fragmentationLength,
+      &modified_buffer, modified_fragmentation->fragmentationOffset,
+      modified_fragmentation->fragmentationLength);
+
+  encoded_image->SetEncodedData(modified_buffer);
+
+  return modified_fragmentation;
+}
+
 void FrameEncodeMetadataWriter::Reset() {
   rtc::CritScope cs(&lock_);
   timing_frames_info_.clear();
diff --git a/video/frame_encode_metadata_writer.h b/video/frame_encode_metadata_writer.h
index c1ffcd9..467c859 100644
--- a/video/frame_encode_metadata_writer.h
+++ b/video/frame_encode_metadata_writer.h
@@ -12,12 +12,14 @@
 #define VIDEO_FRAME_ENCODE_METADATA_WRITER_H_
 
 #include <list>
+#include <memory>
 #include <vector>
 
 #include "absl/types/optional.h"
 #include "api/video/encoded_image.h"
 #include "api/video_codecs/video_codec.h"
 #include "api/video_codecs/video_encoder.h"
+#include "modules/video_coding/include/video_codec_interface.h"
 #include "rtc_base/critical_section.h"
 
 namespace webrtc {
@@ -34,6 +36,12 @@
   void OnEncodeStarted(const VideoFrame& frame);
 
   void FillTimingInfo(size_t simulcast_svc_idx, EncodedImage* encoded_image);
+
+  std::unique_ptr<RTPFragmentationHeader> UpdateBitstream(
+      const CodecSpecificInfo* codec_specific_info,
+      const RTPFragmentationHeader* fragmentation,
+      EncodedImage* encoded_image);
+
   void Reset();
 
  private:
diff --git a/video/frame_encode_metadata_writer_unittest.cc b/video/frame_encode_metadata_writer_unittest.cc
index dcb870b..373b911 100644
--- a/video/frame_encode_metadata_writer_unittest.cc
+++ b/video/frame_encode_metadata_writer_unittest.cc
@@ -16,9 +16,11 @@
 #include "api/video/i420_buffer.h"
 #include "api/video/video_frame.h"
 #include "api/video/video_timing.h"
+#include "common_video/h264/h264_common.h"
 #include "common_video/test/utilities.h"
 #include "modules/video_coding/include/video_coding_defines.h"
 #include "rtc_base/time_utils.h"
+#include "test/gmock.h"
 #include "test/gtest.h"
 
 namespace webrtc {
@@ -125,7 +127,7 @@
 }
 }  // namespace
 
-TEST(FrameEncodeTimerTest, MarksTimingFramesPeriodicallyTogether) {
+TEST(FrameEncodeMetadataWriterTest, MarksTimingFramesPeriodicallyTogether) {
   const int64_t kDelayMs = 29;
   const size_t kMinFrameSize = 10;
   const size_t kMaxFrameSize = 20;
@@ -169,7 +171,7 @@
   }
 }
 
-TEST(FrameEncodeTimerTest, MarksOutliers) {
+TEST(FrameEncodeMetadataWriterTest, MarksOutliers) {
   const int64_t kDelayMs = 29;
   const size_t kMinFrameSize = 2495;
   const size_t kMaxFrameSize = 2505;
@@ -191,7 +193,7 @@
   }
 }
 
-TEST(FrameEncodeTimerTest, NoTimingFrameIfNoEncodeStartTime) {
+TEST(FrameEncodeMetadataWriterTest, NoTimingFrameIfNoEncodeStartTime) {
   int64_t timestamp = 1;
   constexpr size_t kFrameSize = 500;
   EncodedImage image;
@@ -228,7 +230,8 @@
   EXPECT_FALSE(IsTimingFrame(image));
 }
 
-TEST(FrameEncodeTimerTest, AdjustsCaptureTimeForInternalSourceEncoder) {
+TEST(FrameEncodeMetadataWriterTest,
+     AdjustsCaptureTimeForInternalSourceEncoder) {
   const int64_t kEncodeStartDelayMs = 2;
   const int64_t kEncodeFinishDelayMs = 10;
   constexpr size_t kFrameSize = 500;
@@ -273,7 +276,7 @@
               1);
 }
 
-TEST(FrameEncodeTimerTest, NotifiesAboutDroppedFrames) {
+TEST(FrameEncodeMetadataWriterTest, NotifiesAboutDroppedFrames) {
   const int64_t kTimestampMs1 = 47721840;
   const int64_t kTimestampMs2 = 47721850;
   const int64_t kTimestampMs3 = 47721860;
@@ -332,7 +335,7 @@
   EXPECT_EQ(1u, sink.GetNumFramesDropped());
 }
 
-TEST(FrameEncodeTimerTest, RestoresCaptureTimestamps) {
+TEST(FrameEncodeMetadataWriterTest, RestoresCaptureTimestamps) {
   EncodedImage image;
   const int64_t kTimestampMs = 123456;
   FakeEncodedImageCallback sink;
@@ -357,7 +360,7 @@
   EXPECT_EQ(kTimestampMs, image.capture_time_ms_);
 }
 
-TEST(FrameEncodeTimerTest, CopiesRotation) {
+TEST(FrameEncodeMetadataWriterTest, CopiesRotation) {
   EncodedImage image;
   const int64_t kTimestampMs = 123456;
   FakeEncodedImageCallback sink;
@@ -381,7 +384,7 @@
   EXPECT_EQ(kVideoRotation_180, image.rotation_);
 }
 
-TEST(FrameEncodeTimerTest, SetsContentType) {
+TEST(FrameEncodeMetadataWriterTest, SetsContentType) {
   EncodedImage image;
   const int64_t kTimestampMs = 123456;
   FakeEncodedImageCallback sink;
@@ -407,7 +410,7 @@
   EXPECT_EQ(VideoContentType::SCREENSHARE, image.content_type_);
 }
 
-TEST(FrameEncodeTimerTest, CopiesColorSpace) {
+TEST(FrameEncodeMetadataWriterTest, CopiesColorSpace) {
   EncodedImage image;
   const int64_t kTimestampMs = 123456;
   FakeEncodedImageCallback sink;
@@ -434,5 +437,86 @@
   EXPECT_EQ(color_space, *image.ColorSpace());
 }
 
+TEST(FrameEncodeMetadataWriterTest, DoesNotRewriteBitstreamWithoutCodecInfo) {
+  uint8_t buffer[] = {1, 2, 3};
+  EncodedImage image(buffer, sizeof(buffer), sizeof(buffer));
+  const RTPFragmentationHeader fragmentation;
+
+  FakeEncodedImageCallback sink;
+  FrameEncodeMetadataWriter encode_metadata_writer(&sink);
+  EXPECT_EQ(
+      encode_metadata_writer.UpdateBitstream(nullptr, &fragmentation, &image),
+      nullptr);
+  EXPECT_EQ(image.data(), buffer);
+  EXPECT_EQ(image.size(), sizeof(buffer));
+}
+
+TEST(FrameEncodeMetadataWriterTest, DoesNotRewriteVp8Bitstream) {
+  uint8_t buffer[] = {1, 2, 3};
+  EncodedImage image(buffer, sizeof(buffer), sizeof(buffer));
+  CodecSpecificInfo codec_specific_info;
+  codec_specific_info.codecType = kVideoCodecVP8;
+  const RTPFragmentationHeader fragmentation;
+
+  FakeEncodedImageCallback sink;
+  FrameEncodeMetadataWriter encode_metadata_writer(&sink);
+  EXPECT_EQ(encode_metadata_writer.UpdateBitstream(&codec_specific_info,
+                                                   &fragmentation, &image),
+            nullptr);
+  EXPECT_EQ(image.data(), buffer);
+  EXPECT_EQ(image.size(), sizeof(buffer));
+}
+
+TEST(FrameEncodeMetadataWriterTest,
+     DoesNotRewriteH264BitstreamWithoutFragmentation) {
+  uint8_t buffer[] = {1, 2, 3};
+  EncodedImage image(buffer, sizeof(buffer), sizeof(buffer));
+  CodecSpecificInfo codec_specific_info;
+  codec_specific_info.codecType = kVideoCodecH264;
+
+  FakeEncodedImageCallback sink;
+  FrameEncodeMetadataWriter encode_metadata_writer(&sink);
+  EXPECT_EQ(encode_metadata_writer.UpdateBitstream(&codec_specific_info,
+                                                   nullptr, &image),
+            nullptr);
+  EXPECT_EQ(image.data(), buffer);
+  EXPECT_EQ(image.size(), sizeof(buffer));
+}
+
+TEST(FrameEncodeMetadataWriterTest, RewritesH264BitstreamWithNonOptimalSps) {
+  uint8_t original_sps[] = {0,    0,    0,    1,    H264::NaluType::kSps,
+                            0x00, 0x00, 0x03, 0x03, 0xF4,
+                            0x05, 0x03, 0xC7, 0xC0};
+  const uint8_t kRewrittenSps[] = {0,    0,    0,    1,    H264::NaluType::kSps,
+                                   0x00, 0x00, 0x03, 0x03, 0xF4,
+                                   0x05, 0x03, 0xC7, 0xE0, 0x1B,
+                                   0x41, 0x10, 0x8D, 0x00};
+
+  EncodedImage image(original_sps, sizeof(original_sps), sizeof(original_sps));
+  image._frameType = VideoFrameType::kVideoFrameKey;
+
+  CodecSpecificInfo codec_specific_info;
+  codec_specific_info.codecType = kVideoCodecH264;
+
+  RTPFragmentationHeader fragmentation;
+  fragmentation.VerifyAndAllocateFragmentationHeader(1);
+  fragmentation.fragmentationOffset[0] = 4;
+  fragmentation.fragmentationLength[0] = sizeof(original_sps) - 4;
+
+  FakeEncodedImageCallback sink;
+  FrameEncodeMetadataWriter encode_metadata_writer(&sink);
+  std::unique_ptr<RTPFragmentationHeader> modified_fragmentation =
+      encode_metadata_writer.UpdateBitstream(&codec_specific_info,
+                                             &fragmentation, &image);
+
+  ASSERT_NE(modified_fragmentation, nullptr);
+  EXPECT_THAT(std::vector<uint8_t>(image.data(), image.data() + image.size()),
+              testing::ElementsAreArray(kRewrittenSps));
+  ASSERT_THAT(modified_fragmentation->fragmentationVectorSize, 1U);
+  EXPECT_EQ(modified_fragmentation->fragmentationOffset[0], 4U);
+  EXPECT_EQ(modified_fragmentation->fragmentationLength[0],
+            sizeof(kRewrittenSps) - 4);
+}
+
 }  // namespace test
 }  // namespace webrtc
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 579c144..013ad8f 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -1412,6 +1412,10 @@
 
   frame_encode_metadata_writer_.FillTimingInfo(spatial_idx, &image_copy);
 
+  std::unique_ptr<RTPFragmentationHeader> fragmentation_copy =
+      frame_encode_metadata_writer_.UpdateBitstream(codec_specific_info,
+                                                    fragmentation, &image_copy);
+
   // Piggyback ALR experiment group id and simulcast id into the content type.
   const uint8_t experiment_id =
       experiment_groups_[videocontenttypehelpers::IsScreenshare(
@@ -1487,7 +1491,7 @@
 
   EncodedImageCallback::Result result = sink_->OnEncodedImage(
       image_copy, codec_info_copy ? codec_info_copy.get() : codec_specific_info,
-      fragmentation);
+      fragmentation_copy ? fragmentation_copy.get() : fragmentation);
 
   // We are only interested in propagating the meta-data about the image, not
   // encoded data itself, to the post encode function. Since we cannot be sure
diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc
index 19451ec..d20025d 100644
--- a/video/video_stream_encoder_unittest.cc
+++ b/video/video_stream_encoder_unittest.cc
@@ -15,12 +15,14 @@
 #include <memory>
 #include <utility>
 
+#include "absl/memory/memory.h"
 #include "api/task_queue/default_task_queue_factory.h"
 #include "api/video/builtin_video_bitrate_allocator_factory.h"
 #include "api/video/i420_buffer.h"
 #include "api/video/video_bitrate_allocation.h"
 #include "api/video_codecs/vp8_temporal_layers.h"
 #include "api/video_codecs/vp8_temporal_layers_factory.h"
+#include "common_video/h264/h264_common.h"
 #include "media/base/video_adapter.h"
 #include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
 #include "modules/video_coding/utility/default_video_bitrate_allocator.h"
@@ -58,6 +60,11 @@
 const int kDefaultFramerate = 30;
 const int64_t kFrameIntervalMs = rtc::kNumMillisecsPerSec / kDefaultFramerate;
 
+uint8_t optimal_sps[] = {0,    0,    0,    1,    H264::NaluType::kSps,
+                         0x00, 0x00, 0x03, 0x03, 0xF4,
+                         0x05, 0x03, 0xC7, 0xE0, 0x1B,
+                         0x41, 0x10, 0x8D, 0x00};
+
 class TestBuffer : public webrtc::I420Buffer {
  public:
   TestBuffer(rtc::Event* event, int width, int height)
@@ -658,6 +665,14 @@
       encoded_image_callback_->OnEncodedImage(image, nullptr, nullptr);
     }
 
+    void InjectEncodedImage(const EncodedImage& image,
+                            const CodecSpecificInfo* codec_specific_info,
+                            const RTPFragmentationHeader* fragmentation) {
+      rtc::CritScope lock(&local_crit_sect_);
+      encoded_image_callback_->OnEncodedImage(image, codec_specific_info,
+                                              fragmentation);
+    }
+
     void ExpectNullFrame() {
       rtc::CritScope lock(&local_crit_sect_);
       expect_null_frame_ = true;
@@ -855,6 +870,16 @@
       return last_capture_time_ms_;
     }
 
+    std::vector<uint8_t> GetLastEncodedImageData() {
+      rtc::CritScope lock(&crit_);
+      return std::move(last_encoded_image_data_);
+    }
+
+    RTPFragmentationHeader GetLastFragmentation() {
+      rtc::CritScope lock(&crit_);
+      return std::move(last_fragmentation_);
+    }
+
    private:
     Result OnEncodedImage(
         const EncodedImage& encoded_image,
@@ -862,6 +887,11 @@
         const RTPFragmentationHeader* fragmentation) override {
       rtc::CritScope lock(&crit_);
       EXPECT_TRUE(expect_frames_);
+      last_encoded_image_data_ = std::vector<uint8_t>(
+          encoded_image.data(), encoded_image.data() + encoded_image.size());
+      if (fragmentation) {
+        last_fragmentation_.CopyFrom(*fragmentation);
+      }
       uint32_t timestamp = encoded_image.Timestamp();
       if (last_timestamp_ != timestamp) {
         num_received_layers_ = 1;
@@ -890,6 +920,8 @@
     rtc::CriticalSection crit_;
     TestEncoder* test_encoder_;
     rtc::Event encoded_frame_event_;
+    std::vector<uint8_t> last_encoded_image_data_;
+    RTPFragmentationHeader last_fragmentation_;
     uint32_t last_timestamp_ = 0;
     int64_t last_capture_time_ms_ = 0;
     uint32_t last_height_ = 0;
@@ -3826,4 +3858,67 @@
 
   video_stream_encoder_->Stop();
 }
+
+TEST_F(VideoStreamEncoderTest, DoesNotRewriteH264BitstreamWithOptimalSps) {
+  // Configure internal source factory and setup test again.
+  encoder_factory_.SetHasInternalSource(true);
+  ResetEncoder("H264", 1, 1, 1, false);
+
+  EncodedImage image(optimal_sps, sizeof(optimal_sps), sizeof(optimal_sps));
+  image._frameType = VideoFrameType::kVideoFrameKey;
+
+  CodecSpecificInfo codec_specific_info;
+  codec_specific_info.codecType = kVideoCodecH264;
+
+  RTPFragmentationHeader fragmentation;
+  fragmentation.VerifyAndAllocateFragmentationHeader(1);
+  fragmentation.fragmentationOffset[0] = 4;
+  fragmentation.fragmentationLength[0] = sizeof(optimal_sps) - 4;
+
+  fake_encoder_.InjectEncodedImage(image, &codec_specific_info, &fragmentation);
+  EXPECT_TRUE(sink_.WaitForFrame(kDefaultTimeoutMs));
+
+  EXPECT_THAT(sink_.GetLastEncodedImageData(),
+              testing::ElementsAreArray(optimal_sps));
+  RTPFragmentationHeader last_fragmentation = sink_.GetLastFragmentation();
+  ASSERT_THAT(last_fragmentation.fragmentationVectorSize, 1U);
+  EXPECT_EQ(last_fragmentation.fragmentationOffset[0], 4U);
+  EXPECT_EQ(last_fragmentation.fragmentationLength[0], sizeof(optimal_sps) - 4);
+
+  video_stream_encoder_->Stop();
+}
+
+TEST_F(VideoStreamEncoderTest, RewritesH264BitstreamWithNonOptimalSps) {
+  uint8_t original_sps[] = {0,    0,    0,    1,    H264::NaluType::kSps,
+                            0x00, 0x00, 0x03, 0x03, 0xF4,
+                            0x05, 0x03, 0xC7, 0xC0};
+
+  // Configure internal source factory and setup test again.
+  encoder_factory_.SetHasInternalSource(true);
+  ResetEncoder("H264", 1, 1, 1, false);
+
+  EncodedImage image(original_sps, sizeof(original_sps), sizeof(original_sps));
+  image._frameType = VideoFrameType::kVideoFrameKey;
+
+  CodecSpecificInfo codec_specific_info;
+  codec_specific_info.codecType = kVideoCodecH264;
+
+  RTPFragmentationHeader fragmentation;
+  fragmentation.VerifyAndAllocateFragmentationHeader(1);
+  fragmentation.fragmentationOffset[0] = 4;
+  fragmentation.fragmentationLength[0] = sizeof(original_sps) - 4;
+
+  fake_encoder_.InjectEncodedImage(image, &codec_specific_info, &fragmentation);
+  EXPECT_TRUE(sink_.WaitForFrame(kDefaultTimeoutMs));
+
+  EXPECT_THAT(sink_.GetLastEncodedImageData(),
+              testing::ElementsAreArray(optimal_sps));
+  RTPFragmentationHeader last_fragmentation = sink_.GetLastFragmentation();
+  ASSERT_THAT(last_fragmentation.fragmentationVectorSize, 1U);
+  EXPECT_EQ(last_fragmentation.fragmentationOffset[0], 4U);
+  EXPECT_EQ(last_fragmentation.fragmentationLength[0], sizeof(optimal_sps) - 4);
+
+  video_stream_encoder_->Stop();
+}
+
 }  // namespace webrtc