diff --git a/common/Android.bp b/common/Android.bp
index cde85d023dea412e8b95e869376700efa8c65b53..6b571c9f19663b9965c74df995d5aec89f4d865f 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -20,6 +20,7 @@ cc_library {
         "EncodeHelpers.cpp",
         "FormatConverter.cpp",
         "Fourcc.cpp",
+        "H264.cpp",
         "H264NalParser.cpp",
         "HEVCNalParser.cpp",
         "NalParser.cpp",
diff --git a/common/H264.cpp b/common/H264.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bc969ed56d478c91f02b0ab9e8b9c719beb52b9e
--- /dev/null
+++ b/common/H264.cpp
@@ -0,0 +1,30 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <v4l2_codec2/common/H264.h>
+
+#include <log/log.h>
+
+namespace android {
+
+uint32_t maxFramerateForLevelH264(C2Config::level_t level, const ui::Size& videoSize) {
+    uint32_t maxFramerate = std::numeric_limits<uint32_t>::max();
+
+    bool found = false;
+    for (const H264LevelLimits& limit : kH264Limits) {
+        if (limit.level != level) continue;
+
+        uint64_t frameSizeMB =
+                static_cast<uint64_t>((videoSize.width + 15) / 16) * ((videoSize.height + 15) / 16);
+        maxFramerate = limit.maxMBPS / frameSizeMB;
+        found = true;
+        break;
+    }
+
+    if (!found) ALOGW("%s - failed to find matching H264 level=%d", __func__, level);
+
+    return maxFramerate;
+}
+
+}  // namespace android
diff --git a/common/include/v4l2_codec2/common/H264.h b/common/include/v4l2_codec2/common/H264.h
new file mode 100644
index 0000000000000000000000000000000000000000..18770efe9ee37bd40bcedad6b2c883551cdb3640
--- /dev/null
+++ b/common/include/v4l2_codec2/common/H264.h
@@ -0,0 +1,51 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_V4L2_CODEC2_COMMON_H264_H
+#define ANDROID_V4L2_CODEC2_COMMON_H264_H
+
+#include <C2Config.h>
+
+#include <ui/Size.h>
+
+namespace android {
+
+// Table A-1 in spec
+struct H264LevelLimits {
+    C2Config::level_t level;
+    float maxMBPS;   // max macroblock processing rate in macroblocks per second
+    uint64_t maxFS;  // max frame size in macroblocks
+    uint32_t maxBR;  // max video bitrate in bits per second
+};
+
+constexpr H264LevelLimits kH264Limits[] = {
+        {C2Config::LEVEL_AVC_1, 1485, 99, 64000},
+        {C2Config::LEVEL_AVC_1B, 1485, 99, 128000},
+        {C2Config::LEVEL_AVC_1_1, 3000, 396, 192000},
+        {C2Config::LEVEL_AVC_1_2, 6000, 396, 384000},
+        {C2Config::LEVEL_AVC_1_3, 11880, 396, 768000},
+        {C2Config::LEVEL_AVC_2, 11880, 396, 2000000},
+        {C2Config::LEVEL_AVC_2_1, 19800, 792, 4000000},
+        {C2Config::LEVEL_AVC_2_2, 20250, 1620, 4000000},
+        {C2Config::LEVEL_AVC_3, 40500, 1620, 10000000},
+        {C2Config::LEVEL_AVC_3_1, 108000, 3600, 14000000},
+        {C2Config::LEVEL_AVC_3_2, 216000, 5120, 20000000},
+        {C2Config::LEVEL_AVC_4, 245760, 8192, 20000000},
+        {C2Config::LEVEL_AVC_4_1, 245760, 8192, 50000000},
+        {C2Config::LEVEL_AVC_4_2, 522240, 8704, 50000000},
+        {C2Config::LEVEL_AVC_5, 589824, 22080, 135000000},
+        {C2Config::LEVEL_AVC_5_1, 983040, 36864, 240000000},
+        {C2Config::LEVEL_AVC_5_2, 2073600, 36864, 240000000},
+};
+
+uint32_t maxFramerateForLevelH264(C2Config::level_t level, const ui::Size& videoSize);
+
+inline bool isH264Profile(C2Config::profile_t profile) {
+    return (profile >= C2Config::PROFILE_AVC_BASELINE &&
+            profile <= C2Config::PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH);
+}
+
+}  // namespace android
+
+#endif  // ANDROID_V4L2_CODEC2_COMMON_H264_H
diff --git a/components/EncodeComponent.cpp b/components/EncodeComponent.cpp
index 0c7d0445109339110e4b00427f42c440890916ea..5d1d64a6ed32acae52470f76bea23049caaac7d3 100644
--- a/components/EncodeComponent.cpp
+++ b/components/EncodeComponent.cpp
@@ -25,6 +25,7 @@
 
 #include <v4l2_codec2/common/EncodeHelpers.h>
 #include <v4l2_codec2/common/FormatConverter.h>
+#include <v4l2_codec2/common/H264.h>
 #include <v4l2_codec2/components/BitstreamBuffer.h>
 #include <v4l2_codec2/components/EncodeInterface.h>
 #include <v4l2_codec2/components/VideoEncoder.h>
@@ -679,6 +680,13 @@ bool EncodeComponent::updateEncodingParameters() {
         }
     }
 
+    C2Config::profile_t outputProfile = mInterface->getOutputProfile();
+    if (isH264Profile(outputProfile)) {
+        C2Config::level_t outputLevel = mInterface->getOutputLevel();
+        ui::Size inputSize = mInterface->getInputVisibleSize();
+        mMaxFramerate = maxFramerateForLevelH264(outputLevel, inputSize);
+    }
+
     return true;
 }
 
@@ -711,6 +719,18 @@ bool EncodeComponent::encode(C2ConstGraphicBlock block, uint64_t index, int64_t
         int64_t newFramerate = std::max(
                 static_cast<int64_t>(std::round(1000000.0 / (timestamp - *mLastFrameTime))),
                 static_cast<int64_t>(1LL));
+        // Clients using input surface may exceed the maximum allowed framerate for the given
+        // profile. One of such examples is android.media.codec.cts.MediaCodecTest#testAbruptStop.
+        // To mitigate that, value is clamped to the maximum framerate for the given level and
+        // current frame size.
+        // See: b/362902868
+        if (newFramerate > mMaxFramerate) {
+            ALOGW("Frames are coming too fast - new framerate (%" PRIi64
+                  ") would exceed the maximum value (%" PRIu32 ")",
+                  newFramerate, mMaxFramerate);
+            newFramerate = mMaxFramerate;
+        }
+
         if (abs(mFramerate - newFramerate) > kMaxFramerateDiff) {
             ALOGV("Adjusting framerate to %" PRId64 " based on frame timestamps", newFramerate);
             mInterface->setFramerate(static_cast<uint32_t>(newFramerate));
diff --git a/components/EncodeInterface.cpp b/components/EncodeInterface.cpp
index 9d7d81f7433b3a9f8a4ec007a209efd50a75007b..12152e6f747198b2058268efd1ccd92826de67a8 100644
--- a/components/EncodeInterface.cpp
+++ b/components/EncodeInterface.cpp
@@ -17,6 +17,7 @@
 #include <utils/Log.h>
 
 #include <v4l2_codec2/common/Common.h>
+#include <v4l2_codec2/common/H264.h>
 #include <v4l2_codec2/common/VideoTypes.h>
 
 using android::hardware::graphics::common::V1_0::BufferUsage;
@@ -65,33 +66,6 @@ C2R EncodeInterface::H264ProfileLevelSetter(bool /*mayBlock*/,
         }
     }
 
-    // Table A-1 in spec
-    struct LevelLimits {
-        C2Config::level_t level;
-        float maxMBPS;   // max macroblock processing rate in macroblocks per second
-        uint64_t maxFS;  // max frame size in macroblocks
-        uint32_t maxBR;  // max video bitrate in bits per second
-    };
-    constexpr LevelLimits kLimits[] = {
-            {C2Config::LEVEL_AVC_1, 1485, 99, 64000},
-            {C2Config::LEVEL_AVC_1B, 1485, 99, 128000},
-            {C2Config::LEVEL_AVC_1_1, 3000, 396, 192000},
-            {C2Config::LEVEL_AVC_1_2, 6000, 396, 384000},
-            {C2Config::LEVEL_AVC_1_3, 11880, 396, 768000},
-            {C2Config::LEVEL_AVC_2, 11880, 396, 2000000},
-            {C2Config::LEVEL_AVC_2_1, 19800, 792, 4000000},
-            {C2Config::LEVEL_AVC_2_2, 20250, 1620, 4000000},
-            {C2Config::LEVEL_AVC_3, 40500, 1620, 10000000},
-            {C2Config::LEVEL_AVC_3_1, 108000, 3600, 14000000},
-            {C2Config::LEVEL_AVC_3_2, 216000, 5120, 20000000},
-            {C2Config::LEVEL_AVC_4, 245760, 8192, 20000000},
-            {C2Config::LEVEL_AVC_4_1, 245760, 8192, 50000000},
-            {C2Config::LEVEL_AVC_4_2, 522240, 8704, 50000000},
-            {C2Config::LEVEL_AVC_5, 589824, 22080, 135000000},
-            {C2Config::LEVEL_AVC_5_1, 983040, 36864, 240000000},
-            {C2Config::LEVEL_AVC_5_2, 2073600, 36864, 240000000},
-    };
-
     uint64_t targetFS =
             static_cast<uint64_t>((videoSize.v.width + 15) / 16) * ((videoSize.v.height + 15) / 16);
     float targetMBPS = static_cast<float>(targetFS) * frameRate.v.value;
@@ -107,7 +81,7 @@ C2R EncodeInterface::H264ProfileLevelSetter(bool /*mayBlock*/,
 
     bool found = false;
     bool needsUpdate = !info.F(info.v.level).supportsAtAll(info.v.level);
-    for (const LevelLimits& limit : kLimits) {
+    for (const H264LevelLimits& limit : kH264Limits) {
         if (!info.F(info.v.level).supportsAtAll(limit.level)) {
             continue;
         }
diff --git a/components/include/v4l2_codec2/components/EncodeComponent.h b/components/include/v4l2_codec2/components/EncodeComponent.h
index 81c8c6d28966adc4b2133f748437207090222e10..2d40dfff423a8457431cd84c119e824fbff3a94e 100644
--- a/components/include/v4l2_codec2/components/EncodeComponent.h
+++ b/components/include/v4l2_codec2/components/EncodeComponent.h
@@ -6,6 +6,7 @@
 #define ANDROID_V4L2_CODEC2_COMPONENTS_ENCODE_COMPONENT_H
 
 #include <atomic>
+#include <limits>
 #include <memory>
 #include <optional>
 #include <unordered_map>
@@ -164,6 +165,8 @@ protected:
     C2Config::bitrate_mode_t mBitrateMode = C2Config::BITRATE_CONST;
     // The framerate currently configured on the v4l2 device.
     uint32_t mFramerate = 0;
+    // Maximum valid framerate for current output level and input frame size.
+    uint32_t mMaxFramerate = std::numeric_limits<uint32_t>::max();
     // The timestamp of the last frame encoded, used to dynamically adjust the framerate.
     std::optional<int64_t> mLastFrameTime;
 
diff --git a/v4l2/V4L2EncodeComponent.cpp b/v4l2/V4L2EncodeComponent.cpp
index 6d8f037bb84f3d8a057945d9a62a3f6ee82dd7da..e3b9b14543bbdcb710adf79397c4b7cbbd0bebdc 100644
--- a/v4l2/V4L2EncodeComponent.cpp
+++ b/v4l2/V4L2EncodeComponent.cpp
@@ -11,21 +11,13 @@
 
 #include <cutils/properties.h>
 
+#include <v4l2_codec2/common/H264.h>
 #include <v4l2_codec2/components/BitstreamBuffer.h>
 #include <v4l2_codec2/components/EncodeInterface.h>
 #include <v4l2_codec2/v4l2/V4L2Encoder.h>
 
 namespace android {
 
-namespace {
-
-// Check whether the specified |profile| is an H.264 profile.
-bool IsH264Profile(C2Config::profile_t profile) {
-    return (profile >= C2Config::PROFILE_AVC_BASELINE &&
-            profile <= C2Config::PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH);
-}
-}  // namespace
-
 // static
 std::atomic<int32_t> V4L2EncodeComponent::sConcurrentInstances = 0;
 
@@ -74,10 +66,10 @@ bool V4L2EncodeComponent::initializeEncoder() {
     C2Config::profile_t outputProfile = mInterface->getOutputProfile();
 
     // CSD only needs to be extracted when using an H.264 profile.
-    mExtractCSD = IsH264Profile(outputProfile);
+    mExtractCSD = isH264Profile(outputProfile);
 
     std::optional<uint8_t> h264Level;
-    if (IsH264Profile(outputProfile)) {
+    if (isH264Profile(outputProfile)) {
         h264Level = c2LevelToV4L2Level(mInterface->getOutputLevel());
     }
 
@@ -120,4 +112,4 @@ bool V4L2EncodeComponent::initializeEncoder() {
     return true;
 }
 
-}  // namespace android
\ No newline at end of file
+}  // namespace android