1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.video.internal.compat.quirk;
18 
19 import static androidx.camera.core.impl.utils.TransformUtils.rectToSize;
20 import static androidx.camera.core.impl.utils.TransformUtils.rotateSize;
21 
22 import static java.util.Collections.singletonList;
23 
24 import android.graphics.Rect;
25 import android.os.Build;
26 import android.util.Size;
27 
28 import androidx.camera.core.impl.Quirk;
29 import androidx.camera.video.internal.encoder.VideoEncoderInfo;
30 
31 import org.jspecify.annotations.NonNull;
32 import org.jspecify.annotations.Nullable;
33 
34 import java.util.Collections;
35 import java.util.HashSet;
36 import java.util.Set;
37 
38 /**
39  * <p>QuirkSummary
40  *     Bug Id: 318036483
41  *     Description: On MotoC, resolution 720x1280 will crash the media server while encoding. The
42  *                  workaround is to adjust the resolution according to encoder's alignment.
43  *                  E.g. the workaround will adjust 720x1280 to 720x1264 when the height
44  *                  alignment is 16.
45  *     Device(s): MotoC
46  */
47 public class SizeCannotEncodeVideoQuirk implements Quirk {
48 
load()49     static boolean load() {
50         return isMotoC();
51     }
52 
isMotoC()53     private static boolean isMotoC() {
54         return "motorola".equalsIgnoreCase(Build.BRAND) && "moto c".equalsIgnoreCase(Build.MODEL);
55     }
56 
getProblematicSizes()57     private static @NonNull Set<Size> getProblematicSizes() {
58         if (isMotoC()) {
59             return new HashSet<>(singletonList(new Size(720, 1280)));
60         }
61         return Collections.emptySet();
62     }
63 
64     /** Checks if the size is the problematic size for encoding. */
isProblematicEncodeSize(@onNull Size size)65     public boolean isProblematicEncodeSize(@NonNull Size size) {
66         return getProblematicSizes().contains(size);
67     }
68 
69     /**
70      * Adjusts the input crop rect if its size is the problematic size for encoding. Otherwise
71      * returns the original crop rect.
72      *
73      * @param cropRectWithoutRotation the input crop rect without rotation involved.
74      * @param rotationDegrees         the rotation degrees that should apply to the input crop rect.
75      * @param videoEncoderInfo        the video encoder info.
76      * @return the adjusted crop rect.
77      */
adjustCropRectForProblematicEncodeSize( @onNull Rect cropRectWithoutRotation, int rotationDegrees, @Nullable VideoEncoderInfo videoEncoderInfo)78     public @NonNull Rect adjustCropRectForProblematicEncodeSize(
79             @NonNull Rect cropRectWithoutRotation, int rotationDegrees,
80             @Nullable VideoEncoderInfo videoEncoderInfo) {
81         Size sizeToEncode = rotateSize(rectToSize(cropRectWithoutRotation), rotationDegrees);
82         if (!isProblematicEncodeSize(sizeToEncode)) {
83             return cropRectWithoutRotation;
84         }
85         int halfAlignment =
86                 videoEncoderInfo != null ? videoEncoderInfo.getHeightAlignment() / 2 : 8;
87         Rect rectToAdjust = new Rect(cropRectWithoutRotation);
88         // Adjust the rect from the center of the height side and keep the same orientation.
89         // E.g. On MotoC, l:0,t:0,r:1280,b:720 -> l:8,t:0,r:1272,b:720
90         if (cropRectWithoutRotation.width() == sizeToEncode.getHeight()) {
91             rectToAdjust.left += halfAlignment;
92             rectToAdjust.right -= halfAlignment;
93         } else {
94             rectToAdjust.top += halfAlignment;
95             rectToAdjust.bottom -= halfAlignment;
96         }
97         return rectToAdjust;
98     }
99 }
100