RK3588 camera2 支持4K录像

文章详细介绍了如何通过adbshell命令检查摄像头是否支持4K,然后追踪代码来发现4K视频质量被限制的原因,主要是由于isVideoQualitySupported函数中的条件判断。解决方案是修改media_profiles_V1_0.xml文件,增加对4K的支持,并更新编码器配置。经过修改和推送,设备成功支持4K录像并在设置中显示4K选项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 确认摄像头是否支持4K

指令 adb shell dumpsys media.camera | grep picture-size

adb shell 
rk3588_s:/ # dumpsys media.camera | grep picture-size                                                                                                                
    picture-size: 3840x2160
    picture-size-values: 3840x2160,2592x1944,1920x1080,1280x960,1280x720,640x480,320x240,176x144
rk3588_s:/ # 

可以看到当前摄像头是支持4K的

2、查看录像设置分辨率列表

可以看到目前最高支持480p分辨率,开始跟踪代码

3、跟踪列表加载代码

packages/apps/Camera2/src/com/android/camera/settings/CameraSettingsActivity.java文件中加载的list

private void setSummary(Preference preference) {
            if (!(preference instanceof ListPreference)) {
                return;
            }

            ListPreference listPreference = (ListPreference) preference;
            if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_BACK)) {
                setSummaryForSelection(mPictureSizes.backCameraSizes,
                        listPreference);
            } else if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_FRONT)) {
                setSummaryForSelection(mPictureSizes.frontCameraSizes,
                        listPreference);
            } else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_BACK)) {
                setSummaryForSelection(mPictureSizes.videoQualitiesBack.orNull(), listPreference);
            } else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_FRONT)) {
                setSummaryForSelection(mPictureSizes.videoQualitiesFront.orNull(), listPreference);
            } else if (listPreference.getKey().equals(Keys.KEY_MEDIA_SAVE_PATH)) {
                /*listPreference.setSummary(listPreference.getEntry());
                String value = listPreference.getValue();
                if (FLASH.equals(value))
                    Storage.DIRECTORY = Storage.DEFAULT_DIRECTORY;
                else if (SDCARD.equals(value)) {
                    String state = Environment.getStorageState(new File(Storage.EXTENAL_SD));
                    Log.i(TAG,"getSecondVolumeStorageState = " + state);
                    if (!Environment.MEDIA_MOUNTED.equalsIgnoreCase(state)) {
                        mSettingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL, Keys.KEY_MEDIA_SAVE_PATH);
                        restoreMediaSavePath();
                        Toast.makeText(getActivity(), R.string.external_sd_unmounted, Toast.LENGTH_SHORT).show();
                    } else {
                        Storage.DIRECTORY = Storage.EXTERNAL_DIRECTORY;
                    }
                }
                Log.i(TAG, "setSummary Storage.DIRECTORY = " + Storage.DIRECTORY);*/
            } else {
                listPreference.setSummary(listPreference.getEntry());
            }
        }

搜索KEY_VIDEO_QUALITY_BACK,加载list的值是mPictureSizes.videoQualitiesBack,继续查看videoQualitiesBack加载过程

packages/apps/Camera2/src/com/android/camera/settings/PictureSizeLoader.java

    private Optional<SelectedVideoQualities> computeQualitiesForCamera(
            CameraDeviceSelector facingSelector) {
        int cameraId = SettingsUtil.getCameraId(mCameraDeviceInfo, facingSelector);
        if (cameraId >= 0) {
            // This is guaranteed not to be null/absent.
            return Optional.of(SettingsUtil.getSelectedVideoQualities(cameraId));
        }
        return Optional.absent();
    }

SettingsUtil.getSelectedVideoQualities(cameraId)); 这个地方获取分辨率list

    static SelectedVideoQualities getSelectedVideoQualities(int cameraId) {
        if (sCachedSelectedVideoQualities.get(cameraId) != null) {
            return sCachedSelectedVideoQualities.get(cameraId);
        }

        // Go through the sizes in descending order, see if they are supported,
        // and set large/medium/small accordingly.
        // If no quality is supported at all, the first call to
        // getNextSupportedQuality will throw an exception.
        // If only one quality is supported, then all three selected qualities
        // will be the same.
        int largeIndex = getNextSupportedVideoQualityIndex(cameraId, -1);
        int mediumIndex = getNextSupportedVideoQualityIndex(cameraId, largeIndex);
        int smallIndex = getNextSupportedVideoQualityIndex(cameraId, mediumIndex);

        SelectedVideoQualities selectedQualities = new SelectedVideoQualities();
        selectedQualities.large = sVideoQualities[largeIndex];
        selectedQualities.medium = sVideoQualities[mediumIndex];
        selectedQualities.small = sVideoQualities[smallIndex];
        sCachedSelectedVideoQualities.put(cameraId, selectedQualities);
        return selectedQualities;
    }

largeIndex mediumIndex smallIndex 大 中 小 列表中的三个值,需要继续一层层的跟下去,getNextSupportedVideoQualityIndex里面有个判断把4K分辨率过滤掉了,这个判断需要打开

    private static boolean isVideoQualitySupported(int videoQuality) {
        // 4k is only supported on L or higher but some devices falsely report
        // to have support for it on K, see b/18172081.
        if (!ApiHelper.isLOrHigher() && videoQuality == CamcorderProfile.QUALITY_2160P) {
            return false;
        }
        return true;
    }

&& videoQuality == CamcorderProfile.QUALITY_2160P 这个条件判断去掉

getNextSupportedVideoQualityIndex是获取的接口,代码都贴出来太多,整个调用顺序好了。

getNextSupportedVideoQualityIndex -> hasProfile -> native_has_camcorder_profile -> android_media_MediaProfiles_native_has_camcorder_profile ->  sProfiles->hasCamcorderProfile -> getCamcorderProfileIndex

int MediaProfiles::getCamcorderProfileIndex(int cameraId, camcorder_quality quality) const
{
    int index = -1;
    for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
        if (mCamcorderProfiles[i]->mCameraId == cameraId &&
            mCamcorderProfiles[i]->mQuality == quality) {
            index = i;
            break;
        }
    }
    return index;
}

mCamcorderProfiles 加载过程就很重要了,MediaProfiles.cpp初始化的时候会去加载它

/*static*/ MediaProfiles*
MediaProfiles::getInstance()
{
    ALOGV("getInstance");
    Mutex::Autolock lock(sLock);
    if (!sIsInitialized) {
        char value[PROPERTY_VALUE_MAX];
        if (property_get("media.settings.xml", value, NULL) <= 0) {
            const char* xmlFile = nullptr;
            for (auto const& f : getXmlPaths()) {
                if (checkXmlFile(f)) {
                    xmlFile = f;
                    break;
                }
            }
            if (xmlFile == nullptr) {
                ALOGW("Could not find a validated xml file. "
                        "Using the default instance instead.");
                sInstance = createDefaultInstance();
            } else {
                sInstance = createInstanceFromXmlFile(xmlFile);
            }
        } else {
            sInstance = createInstanceFromXmlFile(value);
        }
        CHECK(sInstance != NULL);
        sInstance->checkAndAddRequiredProfilesIfNecessary();
        sIsInitialized = true;
    }

getprop 了下没有media.settings.xml,所有走 <= 0这条路了,getXmlPaths接口会去搜索product/etc/ odm/etc/ vendor/etc/ system/etc 路径下有没有media_profiles_V1_0.xml文件,查看后在vendor/etc有这个文件,那就是要修改这个xml文件。

4、解决方法

修改media_profiles_V1_0.xml

  #添加 注意区分camera0和camera1     
<EncoderProfile quality="2160p" fileFormat="mp4" duration="30">
            <Video codec="h264"
                   bitRate="6000000"
                   width="3840"
                   height="2160"
                   frameRate="30" />

            <Audio codec="aac"
                   bitRate="61000"
                   sampleRate="44100"
                   channels="1" />
</EncoderProfile>
    把h264编解码的最大分辨率改下,之前最大支持是1920
    <VideoEncoderCap name="h264" enabled="true"
        minBitRate="64000" maxBitRate="6000000"
        minFrameWidth="176" maxFrameWidth="3840"
        minFrameHeight="144" maxFrameHeight="2160"
        minFrameRate="1" maxFrameRate="30" />

改后后直接push到vendor/etc目录下,重启后验证ok,可以录4K的视频了。

设置界面也可以看到4K选项。我自己也添加了1920x1080,都是一样的方法。

红色标注是修改的地方,不能遗漏。

### RK3588 HDMI 默认输出 4K 配置方法及参数设置 对于RK3588平台,默认情况下HDMI输出4K分辨率的具体配置涉及到多个方面,包括内核驱动程序中的预设模式定义、屏参文件中对特定模式的支持以及通过命令行设定属性来确保正确解析和应用这些配置。 #### 修改内核源码支持自定义分辨率 在`kernel-5.10/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c`文件内的`dw_hdmi_connector_get_modes`函数用于获取可用的显示模式列表。为了使设备能够识别并采用定制化的高刷新率(如60Hz),可以在屏参中开启对应接口并将`hdmi0_mode`设置为特殊值(例如127)。这一步骤使得系统可以从预先定义好的一组非标准时序参数中选取合适的配置[^2]。 #### 屏幕参数调整 当涉及更具体的硬件层面适配时,RK3588允许将HDMI端口连接到内部PLL(Phase-Locked Loop)以获得更加精确的像素时钟频率分配。这意味着可以通过改变PLL的工作状态来满足不同分辨率需求下的DCLK要求,从而实现稳定可靠的视频信号传输[^3]。 #### 使用命令行设置分辨率 尽管上述更改可能已经足够让某些场景下达到预期效果,但在实际操作过程中发现直接编辑内核代码并非总是最有效的解决方案。因此,另一种更为简便的方法是利用Android系统的特性——即通过设置系统属性的方式来动态指定所需的屏幕分辨率及其相关参数: ```bash setprop persist.vendor.resolution.main 3840x2160@60-3840-4016-4104-4400-2160-2168-2178-2250-5 setprop vendor.display.timeline 1 ``` 此命令序列不仅指定了目标分辨率为3840×2160且帧率为60FPS,还包含了详细的水平同步脉冲宽度、前肩时间等细节信息,这些都是构建完整VESA定时协议所必需的部分[^4]。 综上所述,在RK3588平台上要实现默认状态下高质量的4K HDMI输出,既需要考虑底层架构上的优化也离不开高层级软件环境的支持。以上措施共同作用可帮助达成这一目的。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开开心心everyday

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值