CamcorderProfile.videoCodec возвращает неправильное значение - PullRequest
10 голосов
/ 28 мая 2019

Согласно документам , вы можете использовать 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);

Ответы [ 2 ]

0 голосов
/ 06 июня 2019

Кажется, проблема в библиотеке. Позвольте мне объяснить ..

Посмотрев, как 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);
}

Похоже, что OpenCamera изменяет только качество видео с default / 0, если поддерживаемая ширина профиля составляет 1920, а высота равна 1080 - я думаю, это потому, что активность камеры всегда в альбомной ориентации:

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 , я изменил проект, чтобы реализовать только запись видео, и сделал доступным исходный код здесь .

0 голосов
/ 30 мая 2019

Похоже, что это связано с проблемой, обнаруженной в библиотеке CameraView https://github.com/natario1/CameraView/issues/467

Согласно документации Android, если используется старый android.hardware.camera, вы не можете доверять значению, возвращаемому API профиля видео.Та же проблема присутствует, если вы используете новый android.hardware.camera2 с режимом INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY.

При использовании API камеры 2 в режиме LEGACY (т.е. когда 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.

Если возвращаемый список не равен нулю, возвращаемый список будет содержать как минимум один размер, и один из размеров в возвращаемом списке должен быть передан в MediaRecorder.setVideoSize () дляприменение видеокамеры, если в качестве источника видео используется камера.В этом случае размер предварительного просмотра может отличаться от разрешения записанного видео во время записи видео.

Итак, возможно, нам следует проверить размеры видео и, если оно пустое, заблокировать предварительный просмотр.размер должен быть равен размеру записи.

...