diff --git a/libs/hwcodec/cpp/common/platform/linux/linux.cpp b/libs/hwcodec/cpp/common/platform/linux/linux.cpp index 94ccd566..a4b160d9 100644 --- a/libs/hwcodec/cpp/common/platform/linux/linux.cpp +++ b/libs/hwcodec/cpp/common/platform/linux/linux.cpp @@ -146,6 +146,67 @@ int linux_support_v4l2m2m() { return false; }; + auto is_qcom_platform = [&]() -> bool { + const char *platform_hints[] = { + "qcom", + "qualcomm", + "venus", + "iris", + "sc7280", + "qcm6490", + "qcs6490", + }; + + const char *platform_files[] = { + "/proc/device-tree/compatible", + "/proc/device-tree/model", + "/sys/firmware/devicetree/base/compatible", + "/sys/firmware/devicetree/base/model", + }; + + for (size_t i = 0; i < sizeof(platform_files) / sizeof(platform_files[0]); i++) { + std::string value; + if (read_text_file(platform_files[i], &value) && + contains_any(to_lower(value), platform_hints, + sizeof(platform_hints) / sizeof(platform_hints[0]))) { + return true; + } + } + + const char *video_nodes[] = { + "video0", + "video1", + "video2", + "video3", + "video10", + "video11", + "video32", + }; + const char *video_hints[] = { + "qcom-iris", + "qcom,", + "venus", + "iris", + }; + + for (size_t i = 0; i < sizeof(video_nodes) / sizeof(video_nodes[0]); i++) { + std::string name; + std::string modalias; + const std::string base = std::string("/sys/class/video4linux/") + video_nodes[i]; + if (read_text_file((base + "/name").c_str(), &name) && + contains_any(to_lower(name), video_hints, sizeof(video_hints) / sizeof(video_hints[0]))) { + return true; + } + if (read_text_file((base + "/device/modalias").c_str(), &modalias) && + contains_any(to_lower(modalias), video_hints, + sizeof(video_hints) / sizeof(video_hints[0]))) { + return true; + } + } + + return false; + }; + auto is_amlogic_platform = [&]() -> bool { const char *platform_hints[] = { "amlogic", @@ -210,7 +271,8 @@ int linux_support_v4l2m2m() { return false; }; - const bool amlogic_platform = is_amlogic_platform(); + const bool qcom_platform = is_qcom_platform(); + const bool amlogic_platform = !qcom_platform && is_amlogic_platform(); if (amlogic_platform && !v4l2m2m_allowed()) { LOG_WARN(std::string( "V4L2 M2M: skipped probe on Amlogic platform; set ONE_KVM_V4L2M2M_ALLOW=1 to enable")); diff --git a/libs/hwcodec/cpp/common/util.cpp b/libs/hwcodec/cpp/common/util.cpp index 282e5710..0894f4eb 100644 --- a/libs/hwcodec/cpp/common/util.cpp +++ b/libs/hwcodec/cpp/common/util.cpp @@ -4,6 +4,9 @@ extern "C" { } #include "util.h" +#include +#include +#include #include #include #include @@ -45,17 +48,46 @@ bool is_software_hevc(const std::string &name) { return true; } +bool is_qcom_iris_driver() { + const char *driver_path = "/sys/class/video4linux/video1/name"; + std::ifstream file(driver_path); + if (!file.is_open()) return false; + + std::string value; + std::getline(file, value, '\0'); + std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { + return static_cast(std::tolower(c)); + }); + return value.find("qcom-iris") != std::string::npos || + value.find("iris-encoder") != std::string::npos || + value.find("iris") != std::string::npos; +} + } // anonymous namespace namespace util_encode { +bool is_qcom_iris_platform() { + return is_qcom_iris_driver(); +} + +bool supports_forced_keyframe(const std::string &name) { + if (name.find("v4l2m2m") != std::string::npos && is_qcom_iris_platform()) { + return false; + } + return true; +} + void set_av_codec_ctx(AVCodecContext *c, const std::string &name, int kbs, int gop, int fps, int thread_count) { c->has_b_frames = 0; c->max_b_frames = 0; - if (gop > 0 && gop < std::numeric_limits::max()) { - c->gop_size = gop; - c->keyint_min = gop; // Match keyint_min to gop for consistent keyframe interval + const bool qcom_iris_v4l2 = + name.find("v4l2m2m") != std::string::npos && is_qcom_iris_platform(); + const int effective_gop = qcom_iris_v4l2 ? std::max(5, fps / 3) : gop; + if (effective_gop > 0 && effective_gop < std::numeric_limits::max()) { + c->gop_size = effective_gop; + c->keyint_min = effective_gop; // Match keyint_min to gop for consistent keyframe interval } else if (name.find("vaapi") != std::string::npos) { c->gop_size = fps > 0 ? fps : 30; // Default to 1 second keyframe interval c->keyint_min = c->gop_size; @@ -153,7 +185,8 @@ bool set_lantency_free(void *priv_data, const std::string &name) { av_err2str(ret)); // Not fatal } - if ((ret = av_opt_set_int(priv_data, "num_capture_buffers", 4, 0)) < 0) { + const int capture_buffers = is_qcom_iris_driver() ? 12 : 8; + if ((ret = av_opt_set_int(priv_data, "num_capture_buffers", capture_buffers, 0)) < 0) { LOG_DEBUG(std::string("v4l2m2m num_capture_buffers option is unavailable, ret = ") + av_err2str(ret)); // Not fatal diff --git a/libs/hwcodec/cpp/common/util.h b/libs/hwcodec/cpp/common/util.h index 0e5f4ee4..9a924692 100644 --- a/libs/hwcodec/cpp/common/util.h +++ b/libs/hwcodec/cpp/common/util.h @@ -9,6 +9,9 @@ extern "C" { namespace util_encode { +bool is_qcom_iris_platform(); +bool supports_forced_keyframe(const std::string &name); + void set_av_codec_ctx(AVCodecContext *c, const std::string &name, int kbs, int gop, int fps, int thread_count); bool set_lantency_free(void *priv_data, const std::string &name); diff --git a/libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_encode.cpp b/libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_encode.cpp index 18f32ee6..19ffd84b 100644 --- a/libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_encode.cpp +++ b/libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_encode.cpp @@ -379,12 +379,12 @@ private: frame->pts = ms; // Force keyframe if requested - if (force_keyframe_) { + if (force_keyframe_ && util_encode::supports_forced_keyframe(name_)) { frame->pict_type = AV_PICTURE_TYPE_I; - force_keyframe_ = false; } else { frame->pict_type = AV_PICTURE_TYPE_NONE; } + force_keyframe_ = false; ret = avcodec_send_frame(c_, frame); if (ret == AVERROR(EAGAIN)) {