【问题标题】:CamcorderProfile.videoCodec returning wrong valueCamcorderProfile.videoCodec 返回错误值
【发布时间】:2019-10-13 23:48:56
【问题描述】:

根据docs,您可以使用CamcorderProfile获取设备默认的视频编解码格式,然后将其设置为MediaRecorder,如下所示:

CamcorderProfile mProfile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH);

//

mMediaRecorder.setVideoEncoder(mProfile.videoCodec);

但由于某种原因,它返回了错误的格式。

我正在使用CameraView 库,在FullVideoRecorder 类中定义了以下内容:

switch (mResult.getVideoCodec()) {
    case H_263: mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); break;
    case H_264: mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); break;
    case DEVICE_DEFAULT: mMediaRecorder.setVideoEncoder(mProfile.videoCodec); break;
} 

当我将视频编码器设置为 H_263 时,我遇到问题的设备工作得非常好,但由于某种原因,当我将其设置为默认值时它崩溃 - 在这种情况下,默认值意味着 CamcorderProfile 应该选择设备默认的视频编解码格式。


我的问题:

CamcorderProfile.videoCodec 有什么原因会返回错误的值吗?如何解决?


编辑 - 添加更多信息

我执行了以下操作以确保 CamcoderProfile 是否返回错误值:

//In onCreate
CamcorderProfile camcorderProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);

//getVideoCodec method below
String profileCodec = getVideoCodec(camcorderProfile.videoCodec);    

//Log the result I get
Log.e("Video Codec =", profileCodec);


private String getVideoCodec(int videoCodec){
    switch(videoCodec){
        case MediaRecorder.VideoEncoder.H263:
            return "H263";
        case MediaRecorder.VideoEncoder.H264:
            return "H264";
        case MediaRecorder.VideoEncoder.MPEG_4_SP:
            return "MPEG_4_SP";
        case MediaRecorder.VideoEncoder.DEFAULT:
            return "DEFAULT";
        default:
            return "unknown";
    }
}

在我的日志中我得到Video Codec = H264,但这是不正确的,它应该返回Video Codec = H263


如果我将以下内容传递给MediaRecorder,它将完美运行:

mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);

但不是当我设置以下任何一项时:

mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
mMediaRecorder.setVideoEncoder(mProfile.videoCodec);

【问题讨论】:

    标签: java android android-camera android-mediarecorder


    【解决方案1】:

    它看起来与在 CameraView 库中发现的问题有关 https://github.com/natario1/CameraView/issues/467

    根据 Android 文档,如果使用旧的 android.hardware.camera,则您无法信任视频配置文件 API 返回的值。 如果您在 INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY 模式下使用新的 android.hardware.camera2,也会出现同样的问题。

    在 LEGACY 模式下使用 Camera 2 API 时(即当 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL 设置为 CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY 时),对于不受支持的分辨率,hasProfile(int) 可能会返回 true。为确保在 LEGACY 模式下支持给定的分辨率,CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP 中给出的配置必须包含支持的输出大小中的分辨率。

    camcorder.hasProfile 是测试给定质量级别的给定摄像机是否存在摄像机配置文件的方法。

    所以在使用帧速率和分辨率之前,必须对其进行检查。

    可以使用 getSupportedVideoSizes、getSupportedPreviewSizes、getSupportedPreviewFpsRange 方法检索支持的值

    getSupportedVideoSizes 获取 MediaRecorder 可以使用的支持的视频帧大小。

    如果返回的列表不为空,则返回的列表将包含至少一个 Size,并且如果使用摄像头作为视频源,则返回列表中的其中一个尺寸必须传递给 MediaRecorder.setVideoSize() 用于摄像机应用程序。在这种情况下,预览的大小可能与视频录制过程中录制视频的分辨率不同。

    所以,也许我们应该检查视频大小,如果它是空的,则将预览大小锁定为等于录制大小。

    【讨论】:

    • 我发布了那个问题。
    • 很高兴知道...现在我正在仔细查看库代码
    • 是的,我做到了......这个问题可能与编解码器 - 分辨率 - 帧率之间的某些设备特定的不兼容问题有关。我们可以尝试修改库以使用整个配置文件来设置记录器。可以使用 setProfile 方法完成
    • 我的意思是 mProfile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH);然后是 mMediaRecorder.setProfile(mProfile) 但这只是在黑暗中拍摄,因为图书馆在我的像素上没有任何问题
    • 错误代码 -19 与 MediaRecorder.setVideoSize 设置的视频大小有关。您可以使用 camera.getParameters().getSupportedVideoSizes() 查看相机支持的分辨率吗?和 camera.getParameters().getSupportedPreviewSizes();?您的分辨率是否支持?
    【解决方案2】:

    看来问题出在库上。让我解释一下..

    看了OpenCamera如何实现他们的相机后,我注意到他们首先检查camCoderProfile是否有CamcorderProfile.QUALITY...,然后设置配置文件并传递配置文件的大小,如下所示:

    private void initialiseVideoQuality() {
        int cameraId = camera_controller.getCameraId();
        List<Integer> profiles = new ArrayList<>();
        List<VideoQualityHandler.Dimension2D> dimensions = new ArrayList<>();
    
        if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH) ) {
            CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH);
            profiles.add(CamcorderProfile.QUALITY_HIGH);
            dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
        }
        if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) {
            if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_2160P) ) {
                CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_2160P);
                profiles.add(CamcorderProfile.QUALITY_2160P);
                dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
            }
        }
        if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_1080P) ) {
            CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_1080P);
            profiles.add(CamcorderProfile.QUALITY_1080P);
            dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
        }
        if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_720P) ) {
            CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_720P);
            profiles.add(CamcorderProfile.QUALITY_720P);
            dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
        }
        if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_480P) ) {
            CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_480P);
            profiles.add(CamcorderProfile.QUALITY_480P);
            dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
        }
        if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_CIF) ) {
            CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_CIF);
            profiles.add(CamcorderProfile.QUALITY_CIF);
            dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
        }
        if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_QVGA) ) {
            CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_QVGA);
            profiles.add(CamcorderProfile.QUALITY_QVGA);
            dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
        }
        if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_QCIF) ) {
            CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_QCIF);
            profiles.add(CamcorderProfile.QUALITY_QCIF);
            dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
        }
        if( CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_LOW) ) {
            CamcorderProfile profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW);
            profiles.add(CamcorderProfile.QUALITY_LOW);
            dimensions.add(new VideoQualityHandler.Dimension2D(profile.videoFrameWidth, profile.videoFrameHeight));
        }
        this.video_quality_handler.initialiseVideoQualityFromProfiles(profiles, dimensions);
    }
    

    如果支持的配置文件宽度为 1920 且高度为 1080,OpenCamera 似乎仅将视频质量从 default/0 更改 - 我认为这是因为相机活动始终处于横向:

    if( video_quality_handler.getCurrentVideoQualityIndex() == -1 && video_quality_handler.getSupportedVideoQuality().size() > 0 ) {
    video_quality_handler.setCurrentVideoQualityIndex(0); // start with highest quality
    
    //If I log video_quality_handler.getSupportedVideoQuality() here, I get:
    //[1, 5_r1440x1080, 5, 4_r960x720, 4_r800x450, 4, 7_r640x480, 7_r480x320, 7_r352x288, 7, 2]
    //With 1 being QUALITY_HIGH
    //https://developer.android.com/reference/android/media/CamcorderProfile.html#constants_2
    
    for(int i=0;i<video_quality_handler.getSupportedVideoQuality().size();i++) {
        CamcorderProfile profile = getCamcorderProfile(video_quality_handler.getSupportedVideoQuality().get(i));
            if( profile.videoFrameWidth == 1920 && profile.videoFrameHeight == 1080 ) {
                video_quality_handler.setCurrentVideoQualityIndex(i);
                break;
            }
        }
    }
    
    private CamcorderProfile getCamcorderProfile(String quality) {
        if( camera_controller == null ) {
            //Camera is not opened
            return CamcorderProfile.get(0, CamcorderProfile.QUALITY_HIGH);
        }
    
        int cameraId = camera_controller.getCameraId();
        CamcorderProfile camcorder_profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH); // default
            try {
                String profile_string = quality;
                int index = profile_string.indexOf('_');
                if( index != -1 ) {
                    profile_string = quality.substring(0, index);
                }
                int profile = Integer.parseInt(profile_string);
                camcorder_profile = CamcorderProfile.get(cameraId, profile);
                if( index != -1 && index+1 < quality.length() ) {
                    String override_string = quality.substring(index+1);
                        if( override_string.charAt(0) == 'r' && override_string.length() >= 4 ) {
                            index = override_string.indexOf('x');
                            if( index == -1 ) {
                                Log.d(TAG, "override_string invalid format, can't find x");
                            }
                            else {
                                String resolution_w_s = override_string.substring(1, index); // skip first 'r'
                                String resolution_h_s = override_string.substring(index+1);
                                // copy to local variable first, so that if we fail to parse height, we don't set the width either
                                int resolution_w = Integer.parseInt(resolution_w_s);
                                int resolution_h = Integer.parseInt(resolution_h_s);
                                camcorder_profile.videoFrameWidth = resolution_w;
                                camcorder_profile.videoFrameHeight = resolution_h;
                            }
                        }
                        else {
                            Log.d(TAG, "unknown override_string initial code, or otherwise invalid format");
                        }
                    }
                }
                catch(NumberFormatException e) {
                    e.printStackTrace();
                }
            return camcorder_profile;
        }
    }
    

    现在,我将使用与 OpenCamera 相同的实现。由于它是在GPLv3 下获得许可的,因此我将项目更改为仅实现视频录制并提供源代码here

    【讨论】:

      猜你喜欢
      • 2019-02-03
      • 2015-06-19
      • 2014-01-16
      • 2020-10-05
      • 2015-11-19
      • 2020-05-27
      • 2017-04-16
      • 2017-08-26
      • 2019-10-25
      相关资源
      最近更新 更多