1*b7c941bbSAndroid Build Coastguard Worker /*
2*b7c941bbSAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*b7c941bbSAndroid Build Coastguard Worker  *
4*b7c941bbSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*b7c941bbSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*b7c941bbSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*b7c941bbSAndroid Build Coastguard Worker  *
8*b7c941bbSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*b7c941bbSAndroid Build Coastguard Worker  *
10*b7c941bbSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*b7c941bbSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*b7c941bbSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b7c941bbSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*b7c941bbSAndroid Build Coastguard Worker  * limitations under the License.
15*b7c941bbSAndroid Build Coastguard Worker  */
16*b7c941bbSAndroid Build Coastguard Worker 
17*b7c941bbSAndroid Build Coastguard Worker package android.videoencodingquality.cts;
18*b7c941bbSAndroid Build Coastguard Worker 
19*b7c941bbSAndroid Build Coastguard Worker import static com.android.media.videoquality.bdrate.BdRateMain.verifyBdRate;
20*b7c941bbSAndroid Build Coastguard Worker 
21*b7c941bbSAndroid Build Coastguard Worker import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters;
22*b7c941bbSAndroid Build Coastguard Worker import android.cts.host.utils.DeviceJUnit4Parameterized;
23*b7c941bbSAndroid Build Coastguard Worker import android.platform.test.annotations.AppModeFull;
24*b7c941bbSAndroid Build Coastguard Worker 
25*b7c941bbSAndroid Build Coastguard Worker import com.android.compatibility.common.util.CddTest;
26*b7c941bbSAndroid Build Coastguard Worker import com.android.ddmlib.IDevice;
27*b7c941bbSAndroid Build Coastguard Worker import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
28*b7c941bbSAndroid Build Coastguard Worker import com.android.ddmlib.testrunner.TestResult.TestStatus;
29*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.config.Option;
30*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.config.OptionClass;
31*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.device.DeviceNotAvailableException;
32*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.device.ITestDevice;
33*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.log.LogUtil;
34*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.result.CollectingTestListener;
35*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.result.TestDescription;
36*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.result.TestResult;
37*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.result.TestRunResult;
38*b7c941bbSAndroid Build Coastguard Worker import com.android.tradefed.testtype.IDeviceTest;
39*b7c941bbSAndroid Build Coastguard Worker 
40*b7c941bbSAndroid Build Coastguard Worker import org.json.JSONArray;
41*b7c941bbSAndroid Build Coastguard Worker import org.json.JSONObject;
42*b7c941bbSAndroid Build Coastguard Worker import org.junit.Assert;
43*b7c941bbSAndroid Build Coastguard Worker import org.junit.Assume;
44*b7c941bbSAndroid Build Coastguard Worker import org.junit.Test;
45*b7c941bbSAndroid Build Coastguard Worker import org.junit.runner.RunWith;
46*b7c941bbSAndroid Build Coastguard Worker import org.junit.runners.Parameterized;
47*b7c941bbSAndroid Build Coastguard Worker import org.junit.runners.Parameterized.UseParametersRunnerFactory;
48*b7c941bbSAndroid Build Coastguard Worker 
49*b7c941bbSAndroid Build Coastguard Worker import java.io.BufferedReader;
50*b7c941bbSAndroid Build Coastguard Worker import java.io.File;
51*b7c941bbSAndroid Build Coastguard Worker import java.io.FileReader;
52*b7c941bbSAndroid Build Coastguard Worker import java.io.FileWriter;
53*b7c941bbSAndroid Build Coastguard Worker import java.io.IOException;
54*b7c941bbSAndroid Build Coastguard Worker import java.io.InputStreamReader;
55*b7c941bbSAndroid Build Coastguard Worker import java.nio.charset.StandardCharsets;
56*b7c941bbSAndroid Build Coastguard Worker import java.nio.file.Files;
57*b7c941bbSAndroid Build Coastguard Worker import java.nio.file.Paths;
58*b7c941bbSAndroid Build Coastguard Worker import java.util.ArrayList;
59*b7c941bbSAndroid Build Coastguard Worker import java.util.Arrays;
60*b7c941bbSAndroid Build Coastguard Worker import java.util.List;
61*b7c941bbSAndroid Build Coastguard Worker import java.util.Map;
62*b7c941bbSAndroid Build Coastguard Worker import java.util.concurrent.TimeUnit;
63*b7c941bbSAndroid Build Coastguard Worker import java.util.concurrent.locks.Condition;
64*b7c941bbSAndroid Build Coastguard Worker import java.util.concurrent.locks.Lock;
65*b7c941bbSAndroid Build Coastguard Worker import java.util.concurrent.locks.ReentrantLock;
66*b7c941bbSAndroid Build Coastguard Worker 
67*b7c941bbSAndroid Build Coastguard Worker import javax.annotation.Nullable;
68*b7c941bbSAndroid Build Coastguard Worker 
69*b7c941bbSAndroid Build Coastguard Worker /**
70*b7c941bbSAndroid Build Coastguard Worker  * This class constitutes host-part of video encoding quality test (go/pc14-veq). This test is
71*b7c941bbSAndroid Build Coastguard Worker  * aimed towards benchmarking encoders on the target device.
72*b7c941bbSAndroid Build Coastguard Worker  * <p>
73*b7c941bbSAndroid Build Coastguard Worker  * Video encoding quality test quantifies encoders on the test device by encoding a set of clips
74*b7c941bbSAndroid Build Coastguard Worker  * at various configurations. The encoded output is analysed for vmaf and compared against
75*b7c941bbSAndroid Build Coastguard Worker  * reference. This entire process is not carried on the device. The host side of the test
76*b7c941bbSAndroid Build Coastguard Worker  * prepares the test environment by installing a VideoEncodingApp on the device. It also pushes
77*b7c941bbSAndroid Build Coastguard Worker  * the test vectors and test configurations on to the device. The VideoEncodingApp transcodes the
78*b7c941bbSAndroid Build Coastguard Worker  * input clips basing on the configurations shared. The host side of the test then pulls output
79*b7c941bbSAndroid Build Coastguard Worker  * files from the device and analyses for vmaf. These values are compared against reference using
80*b7c941bbSAndroid Build Coastguard Worker  * Bjontegaard metric.
81*b7c941bbSAndroid Build Coastguard Worker  **/
82*b7c941bbSAndroid Build Coastguard Worker @AppModeFull(reason = "Instant apps cannot access the SD card")
83*b7c941bbSAndroid Build Coastguard Worker @RunWith(DeviceJUnit4Parameterized.class)
84*b7c941bbSAndroid Build Coastguard Worker @UseParametersRunnerFactory(DeviceJUnit4ClassRunnerWithParameters.RunnerFactory.class)
85*b7c941bbSAndroid Build Coastguard Worker @OptionClass(alias = "pc-veq-test")
86*b7c941bbSAndroid Build Coastguard Worker public class CtsVideoEncodingQualityHostTest implements IDeviceTest {
87*b7c941bbSAndroid Build Coastguard Worker     private static final String RES_URL =
88*b7c941bbSAndroid Build Coastguard Worker             "https://storage.googleapis.com/android_media/cts/hostsidetests/pc14_veq/veqtests-1_4.tar.gz";
89*b7c941bbSAndroid Build Coastguard Worker 
90*b7c941bbSAndroid Build Coastguard Worker     // variables related to host-side of the test
91*b7c941bbSAndroid Build Coastguard Worker     private static final int MEDIA_PERFORMANCE_CLASS_14 = 34;
92*b7c941bbSAndroid Build Coastguard Worker     private static final int MINIMUM_VALID_SDK = 31;
93*b7c941bbSAndroid Build Coastguard Worker             // test is not valid before sdk 31, aka Android 12, aka Android S
94*b7c941bbSAndroid Build Coastguard Worker 
95*b7c941bbSAndroid Build Coastguard Worker     private static final Lock sLock = new ReentrantLock();
96*b7c941bbSAndroid Build Coastguard Worker     private static final Condition sCondition = sLock.newCondition();
97*b7c941bbSAndroid Build Coastguard Worker     private static boolean sIsTestSetUpDone = false;
98*b7c941bbSAndroid Build Coastguard Worker             // install apk, push necessary resources to device to run the test. lock/condition
99*b7c941bbSAndroid Build Coastguard Worker             // pair is to keep setupTestEnv() thread safe
100*b7c941bbSAndroid Build Coastguard Worker     private static File sHostWorkDir;
101*b7c941bbSAndroid Build Coastguard Worker     private static int sMpc;
102*b7c941bbSAndroid Build Coastguard Worker 
103*b7c941bbSAndroid Build Coastguard Worker     // Variables related to device-side of the test. These need to kept in sync with definitions of
104*b7c941bbSAndroid Build Coastguard Worker     // VideoEncodingApp.apk
105*b7c941bbSAndroid Build Coastguard Worker     private static final String DEVICE_SIDE_TEST_PACKAGE = "android.videoencoding.app";
106*b7c941bbSAndroid Build Coastguard Worker     private static final String DEVICE_SIDE_TEST_CLASS =
107*b7c941bbSAndroid Build Coastguard Worker             "android.videoencoding.app.VideoTranscoderTest";
108*b7c941bbSAndroid Build Coastguard Worker     private static final String RUNNER = "androidx.test.runner.AndroidJUnitRunner";
109*b7c941bbSAndroid Build Coastguard Worker     private static final String TEST_CONFIG_INST_ARGS_KEY = "conf-json";
110*b7c941bbSAndroid Build Coastguard Worker     private static final long DEFAULT_SHELL_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5);
111*b7c941bbSAndroid Build Coastguard Worker     private static final String TEST_TIMEOUT_INST_ARGS_KEY = "timeout_msec";
112*b7c941bbSAndroid Build Coastguard Worker     private static final long DEFAULT_TEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(3);
113*b7c941bbSAndroid Build Coastguard Worker 
114*b7c941bbSAndroid Build Coastguard Worker     // local variables related to host-side of the test
115*b7c941bbSAndroid Build Coastguard Worker     private final String mJsonName;
116*b7c941bbSAndroid Build Coastguard Worker     private ITestDevice mDevice;
117*b7c941bbSAndroid Build Coastguard Worker 
118*b7c941bbSAndroid Build Coastguard Worker     @Option(name = "force-to-run", description = "Force to run the test even if the device is not"
119*b7c941bbSAndroid Build Coastguard Worker             + " a right performance class device.")
120*b7c941bbSAndroid Build Coastguard Worker     private boolean mForceToRun = false;
121*b7c941bbSAndroid Build Coastguard Worker 
122*b7c941bbSAndroid Build Coastguard Worker     @Option(name = "skip-avc", description = "Skip avc encoder testing")
123*b7c941bbSAndroid Build Coastguard Worker     private boolean mSkipAvc = false;
124*b7c941bbSAndroid Build Coastguard Worker 
125*b7c941bbSAndroid Build Coastguard Worker     @Option(name = "skip-hevc", description = "Skip hevc encoder testing")
126*b7c941bbSAndroid Build Coastguard Worker     private boolean mSkipHevc = false;
127*b7c941bbSAndroid Build Coastguard Worker 
128*b7c941bbSAndroid Build Coastguard Worker     @Option(name = "skip-p", description = "Skip P only testing")
129*b7c941bbSAndroid Build Coastguard Worker     private boolean mSkipP = false;
130*b7c941bbSAndroid Build Coastguard Worker 
131*b7c941bbSAndroid Build Coastguard Worker     @Option(name = "skip-b", description = "Skip B frame testing")
132*b7c941bbSAndroid Build Coastguard Worker     private boolean mSkipB = false;
133*b7c941bbSAndroid Build Coastguard Worker 
134*b7c941bbSAndroid Build Coastguard Worker     @Option(name = "reset", description = "Start with a fresh directory.")
135*b7c941bbSAndroid Build Coastguard Worker     private boolean mReset = false;
136*b7c941bbSAndroid Build Coastguard Worker 
137*b7c941bbSAndroid Build Coastguard Worker     @Option(name = "quick-check", description = "Run a quick check.")
138*b7c941bbSAndroid Build Coastguard Worker     private boolean mQuickCheck = false;
139*b7c941bbSAndroid Build Coastguard Worker 
CtsVideoEncodingQualityHostTest(String jsonName, @SuppressWarnings("unused") String testLabel)140*b7c941bbSAndroid Build Coastguard Worker     public CtsVideoEncodingQualityHostTest(String jsonName,
141*b7c941bbSAndroid Build Coastguard Worker             @SuppressWarnings("unused") String testLabel) {
142*b7c941bbSAndroid Build Coastguard Worker         mJsonName = jsonName;
143*b7c941bbSAndroid Build Coastguard Worker     }
144*b7c941bbSAndroid Build Coastguard Worker 
145*b7c941bbSAndroid Build Coastguard Worker     private static final List<Object[]> AVC_VBR_B0_PARAMS = Arrays.asList(new Object[][]{
146*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json",
147*b7c941bbSAndroid Build Coastguard Worker                     "Beach_SO04_CRW02_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"},
148*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0"
149*b7c941bbSAndroid Build Coastguard Worker                     + ".json",
150*b7c941bbSAndroid Build Coastguard Worker                     "BirthdayHalfway_SI17_CRUW03_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"},
151*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p"
152*b7c941bbSAndroid Build Coastguard Worker                     + "-30fps_hw_avc_vbr_b0.json",
153*b7c941bbSAndroid Build Coastguard Worker                     "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_"
154*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b0"},
155*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json",
156*b7c941bbSAndroid Build Coastguard Worker                     "Waterfall_SO05_CRW01_P_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"},
157*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json"
158*b7c941bbSAndroid Build Coastguard Worker                     , "SelfieFamily_SF14_CF01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"},
159*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json",
160*b7c941bbSAndroid Build Coastguard Worker                     "River_SO03_CRW01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"},
161*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0"
162*b7c941bbSAndroid Build Coastguard Worker                     + ".json",
163*b7c941bbSAndroid Build Coastguard Worker                     "SelfieGroupGarden_SF15_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"},
164*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json"
165*b7c941bbSAndroid Build Coastguard Worker                     , "ConcertNear_SI10_CRW01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"},
166*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR"
167*b7c941bbSAndroid Build Coastguard Worker                     + "-1080p-30fps_hw_avc_vbr_b0.json",
168*b7c941bbSAndroid Build Coastguard Worker                     "SelfieCoupleCitySocialMedia_SS02_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_"
169*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b0"}});
170*b7c941bbSAndroid Build Coastguard Worker 
171*b7c941bbSAndroid Build Coastguard Worker     private static final List<Object[]> AVC_VBR_B3_PARAMS = Arrays.asList(new Object[][]{
172*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json",
173*b7c941bbSAndroid Build Coastguard Worker                     "Beach_SO04_CRW02_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"},
174*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3"
175*b7c941bbSAndroid Build Coastguard Worker                     + ".json",
176*b7c941bbSAndroid Build Coastguard Worker                     "BirthdayHalfway_SI17_CRUW03_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"},
177*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p"
178*b7c941bbSAndroid Build Coastguard Worker                     + "-30fps_hw_avc_vbr_b3.json",
179*b7c941bbSAndroid Build Coastguard Worker                     "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_"
180*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b3"},
181*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json",
182*b7c941bbSAndroid Build Coastguard Worker                     "Waterfall_SO05_CRW01_P_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"},
183*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json"
184*b7c941bbSAndroid Build Coastguard Worker                     , "SelfieFamily_SF14_CF01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"},
185*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json",
186*b7c941bbSAndroid Build Coastguard Worker                     "River_SO03_CRW01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"},
187*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3"
188*b7c941bbSAndroid Build Coastguard Worker                     + ".json",
189*b7c941bbSAndroid Build Coastguard Worker                     "SelfieGroupGarden_SF15_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"},
190*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json"
191*b7c941bbSAndroid Build Coastguard Worker                     , "ConcertNear_SI10_CRW01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"},
192*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR"
193*b7c941bbSAndroid Build Coastguard Worker                     + "-1080p-30fps_hw_avc_vbr_b3.json",
194*b7c941bbSAndroid Build Coastguard Worker                     "SelfieCoupleCitySocialMedia_SS02_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_"
195*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b3"}});
196*b7c941bbSAndroid Build Coastguard Worker 
197*b7c941bbSAndroid Build Coastguard Worker     private static final List<Object[]> HEVC_VBR_B0_PARAMS = Arrays.asList(new Object[][]{
198*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json",
199*b7c941bbSAndroid Build Coastguard Worker                     "Beach_SO04_CRW02_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"},
200*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0"
201*b7c941bbSAndroid Build Coastguard Worker                     + ".json",
202*b7c941bbSAndroid Build Coastguard Worker                     "BirthdayHalfway_SI17_CRUW03_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"},
203*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p"
204*b7c941bbSAndroid Build Coastguard Worker                     + "-30fps_hw_hevc_vbr_b0.json",
205*b7c941bbSAndroid Build Coastguard Worker                     "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_"
206*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b0"},
207*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json",
208*b7c941bbSAndroid Build Coastguard Worker                     "Waterfall_SO05_CRW01_P_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"},
209*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json"
210*b7c941bbSAndroid Build Coastguard Worker                     , "SelfieFamily_SF14_CF01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"},
211*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json",
212*b7c941bbSAndroid Build Coastguard Worker                     "River_SO03_CRW01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"},
213*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0"
214*b7c941bbSAndroid Build Coastguard Worker                     + ".json",
215*b7c941bbSAndroid Build Coastguard Worker                     "SelfieGroupGarden_SF15_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"},
216*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json"
217*b7c941bbSAndroid Build Coastguard Worker                     , "ConcertNear_SI10_CRW01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"},
218*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR"
219*b7c941bbSAndroid Build Coastguard Worker                     + "-1080p-30fps_hw_hevc_vbr_b0.json",
220*b7c941bbSAndroid Build Coastguard Worker                     "SelfieCoupleCitySocialMedia_SS02_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_"
221*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b0"}});
222*b7c941bbSAndroid Build Coastguard Worker 
223*b7c941bbSAndroid Build Coastguard Worker     private static final List<Object[]> HEVC_VBR_B3_PARAMS = Arrays.asList(new Object[][]{
224*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json",
225*b7c941bbSAndroid Build Coastguard Worker                     "Beach_SO04_CRW02_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"},
226*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3"
227*b7c941bbSAndroid Build Coastguard Worker                     + ".json",
228*b7c941bbSAndroid Build Coastguard Worker                     "BirthdayHalfway_SI17_CRUW03_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"},
229*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p"
230*b7c941bbSAndroid Build Coastguard Worker                     + "-30fps_hw_hevc_vbr_b3.json",
231*b7c941bbSAndroid Build Coastguard Worker                     "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_"
232*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b3"},
233*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json",
234*b7c941bbSAndroid Build Coastguard Worker                     "Waterfall_SO05_CRW01_P_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"},
235*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json"
236*b7c941bbSAndroid Build Coastguard Worker                     , "SelfieFamily_SF14_CF01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"},
237*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json",
238*b7c941bbSAndroid Build Coastguard Worker                     "River_SO03_CRW01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"},
239*b7c941bbSAndroid Build Coastguard Worker             // Abnormal curve, not monotonically increasing.
240*b7c941bbSAndroid Build Coastguard Worker             /*{"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3"
241*b7c941bbSAndroid Build Coastguard Worker                     + ".json",
242*b7c941bbSAndroid Build Coastguard Worker                     "SelfieGroupGarden_SF15_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"},*/
243*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json"
244*b7c941bbSAndroid Build Coastguard Worker                     , "ConcertNear_SI10_CRW01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"},
245*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR"
246*b7c941bbSAndroid Build Coastguard Worker                     + "-1080p-30fps_hw_hevc_vbr_b3.json",
247*b7c941bbSAndroid Build Coastguard Worker                     "SelfieCoupleCitySocialMedia_SS02_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_"
248*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b3"}});
249*b7c941bbSAndroid Build Coastguard Worker 
250*b7c941bbSAndroid Build Coastguard Worker     private static final List<Object[]> QUICK_RUN_PARAMS = Arrays.asList(new Object[][]{
251*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p"
252*b7c941bbSAndroid Build Coastguard Worker                     + "-30fps_hw_avc_vbr_b0.json",
253*b7c941bbSAndroid Build Coastguard Worker                     "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_" +
254*b7c941bbSAndroid Build Coastguard Worker                             "vbr_b0"},
255*b7c941bbSAndroid Build Coastguard Worker             {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p"
256*b7c941bbSAndroid Build Coastguard Worker                     + "-30fps_hw_hevc_vbr_b0.json",
257*b7c941bbSAndroid Build Coastguard Worker                     "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_"
258*b7c941bbSAndroid Build Coastguard Worker                             + "vbr_b0"}});
259*b7c941bbSAndroid Build Coastguard Worker 
260*b7c941bbSAndroid Build Coastguard Worker     @Parameterized.Parameters(name = "{index}_{1}")
input()261*b7c941bbSAndroid Build Coastguard Worker     public static List<Object[]> input() {
262*b7c941bbSAndroid Build Coastguard Worker         final List<Object[]> args = new ArrayList<>();
263*b7c941bbSAndroid Build Coastguard Worker         args.addAll(AVC_VBR_B0_PARAMS);
264*b7c941bbSAndroid Build Coastguard Worker         args.addAll(AVC_VBR_B3_PARAMS);
265*b7c941bbSAndroid Build Coastguard Worker         args.addAll(HEVC_VBR_B0_PARAMS);
266*b7c941bbSAndroid Build Coastguard Worker         args.addAll(HEVC_VBR_B3_PARAMS);
267*b7c941bbSAndroid Build Coastguard Worker         return args;
268*b7c941bbSAndroid Build Coastguard Worker     }
269*b7c941bbSAndroid Build Coastguard Worker 
270*b7c941bbSAndroid Build Coastguard Worker     @Override
setDevice(ITestDevice device)271*b7c941bbSAndroid Build Coastguard Worker     public void setDevice(ITestDevice device) {
272*b7c941bbSAndroid Build Coastguard Worker         mDevice = device;
273*b7c941bbSAndroid Build Coastguard Worker     }
274*b7c941bbSAndroid Build Coastguard Worker 
275*b7c941bbSAndroid Build Coastguard Worker     @Override
getDevice()276*b7c941bbSAndroid Build Coastguard Worker     public ITestDevice getDevice() {
277*b7c941bbSAndroid Build Coastguard Worker         return mDevice;
278*b7c941bbSAndroid Build Coastguard Worker     }
279*b7c941bbSAndroid Build Coastguard Worker 
280*b7c941bbSAndroid Build Coastguard Worker     /**
281*b7c941bbSAndroid Build Coastguard Worker      * Sets up the necessary environment for the video encoding quality test.
282*b7c941bbSAndroid Build Coastguard Worker      */
setupTestEnv()283*b7c941bbSAndroid Build Coastguard Worker     public void setupTestEnv() throws Exception {
284*b7c941bbSAndroid Build Coastguard Worker         String sdkAsString = getDevice().getProperty("ro.build.version.sdk");
285*b7c941bbSAndroid Build Coastguard Worker         int sdk = Integer.parseInt(sdkAsString);
286*b7c941bbSAndroid Build Coastguard Worker         Assume.assumeTrue("Test requires sdk >= " + MINIMUM_VALID_SDK
287*b7c941bbSAndroid Build Coastguard Worker                 + " test device has sdk = " + sdk, sdk >= MINIMUM_VALID_SDK);
288*b7c941bbSAndroid Build Coastguard Worker 
289*b7c941bbSAndroid Build Coastguard Worker         String pcAsString = getDevice().getProperty("ro.odm.build.media_performance_class");
290*b7c941bbSAndroid Build Coastguard Worker         try {
291*b7c941bbSAndroid Build Coastguard Worker             sMpc = Integer.parseInt("0" + pcAsString);
292*b7c941bbSAndroid Build Coastguard Worker         } catch (Exception e) {
293*b7c941bbSAndroid Build Coastguard Worker             LogUtil.CLog.i("Invalid pcAsString: " + pcAsString + ", exception: " + e);
294*b7c941bbSAndroid Build Coastguard Worker         }
295*b7c941bbSAndroid Build Coastguard Worker 
296*b7c941bbSAndroid Build Coastguard Worker         Assume.assumeTrue("Performance class advertised by the test device is less than "
297*b7c941bbSAndroid Build Coastguard Worker                 + MEDIA_PERFORMANCE_CLASS_14, mForceToRun || sMpc >= MEDIA_PERFORMANCE_CLASS_14
298*b7c941bbSAndroid Build Coastguard Worker                 || (sMpc == 0 && sdk >= 34 /* Build.VERSION_CODES.UPSIDE_DOWN_CAKE */));
299*b7c941bbSAndroid Build Coastguard Worker 
300*b7c941bbSAndroid Build Coastguard Worker         Assert.assertTrue("Failed to install package on device : " + DEVICE_SIDE_TEST_PACKAGE,
301*b7c941bbSAndroid Build Coastguard Worker                 getDevice().isPackageInstalled(DEVICE_SIDE_TEST_PACKAGE));
302*b7c941bbSAndroid Build Coastguard Worker 
303*b7c941bbSAndroid Build Coastguard Worker         // set up host-side working directory
304*b7c941bbSAndroid Build Coastguard Worker         String tmpBase = System.getProperty("java.io.tmpdir");
305*b7c941bbSAndroid Build Coastguard Worker         String dirName = "CtsVideoEncodingQualityHostTest_" + getDevice().getSerialNumber();
306*b7c941bbSAndroid Build Coastguard Worker         String tmpDir = tmpBase + "/" + dirName;
307*b7c941bbSAndroid Build Coastguard Worker         LogUtil.CLog.i("tmpBase= " + tmpBase + " tmpDir =" + tmpDir);
308*b7c941bbSAndroid Build Coastguard Worker         sHostWorkDir = new File(tmpDir);
309*b7c941bbSAndroid Build Coastguard Worker         if (mReset || sHostWorkDir.isFile()) {
310*b7c941bbSAndroid Build Coastguard Worker             File cwd = new File(".");
311*b7c941bbSAndroid Build Coastguard Worker             runCommand("rm -rf " + tmpDir, cwd);
312*b7c941bbSAndroid Build Coastguard Worker         }
313*b7c941bbSAndroid Build Coastguard Worker         try {
314*b7c941bbSAndroid Build Coastguard Worker             if (!sHostWorkDir.isDirectory()) {
315*b7c941bbSAndroid Build Coastguard Worker                 Assert.assertTrue("Failed to create directory : " + sHostWorkDir.getAbsolutePath(),
316*b7c941bbSAndroid Build Coastguard Worker                         sHostWorkDir.mkdirs());
317*b7c941bbSAndroid Build Coastguard Worker             }
318*b7c941bbSAndroid Build Coastguard Worker         } catch (SecurityException e) {
319*b7c941bbSAndroid Build Coastguard Worker             LogUtil.CLog.e("Unable to establish temp directory " + sHostWorkDir.getPath());
320*b7c941bbSAndroid Build Coastguard Worker         }
321*b7c941bbSAndroid Build Coastguard Worker 
322*b7c941bbSAndroid Build Coastguard Worker         // Clean up output folders before starting the test
323*b7c941bbSAndroid Build Coastguard Worker         runCommand("rm -rf " + "output_*", sHostWorkDir);
324*b7c941bbSAndroid Build Coastguard Worker 
325*b7c941bbSAndroid Build Coastguard Worker         // Download the test suite tar file.
326*b7c941bbSAndroid Build Coastguard Worker         downloadFile(RES_URL, sHostWorkDir);
327*b7c941bbSAndroid Build Coastguard Worker 
328*b7c941bbSAndroid Build Coastguard Worker         // Unpack the test suite tar file.
329*b7c941bbSAndroid Build Coastguard Worker         String fileName = RES_URL.substring(RES_URL.lastIndexOf('/') + 1);
330*b7c941bbSAndroid Build Coastguard Worker         int result = runCommand("tar xvzf " + fileName, sHostWorkDir);
331*b7c941bbSAndroid Build Coastguard Worker         Assert.assertEquals("Failed to untar " + fileName, 0, result);
332*b7c941bbSAndroid Build Coastguard Worker 
333*b7c941bbSAndroid Build Coastguard Worker         // Push input files to device
334*b7c941bbSAndroid Build Coastguard Worker         String deviceInDir = getDevice().getMountPoint(IDevice.MNT_EXTERNAL_STORAGE)
335*b7c941bbSAndroid Build Coastguard Worker                 + "/veq/input/";
336*b7c941bbSAndroid Build Coastguard Worker         String deviceJsonDir = deviceInDir + "json/";
337*b7c941bbSAndroid Build Coastguard Worker         String deviceSamplesDir = deviceInDir + "samples/";
338*b7c941bbSAndroid Build Coastguard Worker         Assert.assertNotNull("Failed to create directory " + deviceJsonDir + " on device ",
339*b7c941bbSAndroid Build Coastguard Worker                 getDevice().executeAdbCommand("shell", "mkdir", "-p", deviceJsonDir));
340*b7c941bbSAndroid Build Coastguard Worker         Assert.assertNotNull("Failed to create directory " + deviceSamplesDir + " on device ",
341*b7c941bbSAndroid Build Coastguard Worker                 getDevice().executeAdbCommand("shell", "mkdir", "-p", deviceSamplesDir));
342*b7c941bbSAndroid Build Coastguard Worker         Assert.assertTrue("Failed to push json files to " + deviceJsonDir + " on device ",
343*b7c941bbSAndroid Build Coastguard Worker                 getDevice().pushDir(new File(sHostWorkDir.getPath() + "/json/"), deviceJsonDir));
344*b7c941bbSAndroid Build Coastguard Worker         Assert.assertTrue("Failed to push mp4 files to " + deviceSamplesDir + " on device ",
345*b7c941bbSAndroid Build Coastguard Worker                 getDevice().pushDir(new File(sHostWorkDir.getPath() + "/samples/"),
346*b7c941bbSAndroid Build Coastguard Worker                         deviceSamplesDir));
347*b7c941bbSAndroid Build Coastguard Worker 
348*b7c941bbSAndroid Build Coastguard Worker         sIsTestSetUpDone = true;
349*b7c941bbSAndroid Build Coastguard Worker     }
350*b7c941bbSAndroid Build Coastguard Worker 
containsJson(String jsonName, List<Object[]> params)351*b7c941bbSAndroid Build Coastguard Worker     public static boolean containsJson(String jsonName, List<Object[]> params) {
352*b7c941bbSAndroid Build Coastguard Worker         for (Object[] param : params) {
353*b7c941bbSAndroid Build Coastguard Worker             if (param[0].equals(jsonName)) {
354*b7c941bbSAndroid Build Coastguard Worker                 return true;
355*b7c941bbSAndroid Build Coastguard Worker             }
356*b7c941bbSAndroid Build Coastguard Worker         }
357*b7c941bbSAndroid Build Coastguard Worker         return false;
358*b7c941bbSAndroid Build Coastguard Worker     }
359*b7c941bbSAndroid Build Coastguard Worker 
360*b7c941bbSAndroid Build Coastguard Worker     /**
361*b7c941bbSAndroid Build Coastguard Worker      * Verify the video encoding quality requirements for the performance class 14 devices.
362*b7c941bbSAndroid Build Coastguard Worker      */
363*b7c941bbSAndroid Build Coastguard Worker     @CddTest(requirements = {"2.2.7.1/5.8/H-1-1"})
364*b7c941bbSAndroid Build Coastguard Worker     @Test
testEncoding()365*b7c941bbSAndroid Build Coastguard Worker     public void testEncoding() throws Exception {
366*b7c941bbSAndroid Build Coastguard Worker         Assume.assumeFalse("Skipping due to quick run mode",
367*b7c941bbSAndroid Build Coastguard Worker                 mQuickCheck && !containsJson(mJsonName, QUICK_RUN_PARAMS));
368*b7c941bbSAndroid Build Coastguard Worker         Assume.assumeFalse("Skipping avc encoder tests",
369*b7c941bbSAndroid Build Coastguard Worker                 mSkipAvc && (containsJson(mJsonName, AVC_VBR_B0_PARAMS) || containsJson(mJsonName,
370*b7c941bbSAndroid Build Coastguard Worker                         AVC_VBR_B3_PARAMS)));
371*b7c941bbSAndroid Build Coastguard Worker         Assume.assumeFalse("Skipping hevc encoder tests",
372*b7c941bbSAndroid Build Coastguard Worker                 mSkipHevc && (containsJson(mJsonName, HEVC_VBR_B0_PARAMS) || containsJson(mJsonName,
373*b7c941bbSAndroid Build Coastguard Worker                         HEVC_VBR_B3_PARAMS)));
374*b7c941bbSAndroid Build Coastguard Worker         Assume.assumeFalse("Skipping b-frame tests",
375*b7c941bbSAndroid Build Coastguard Worker                 mSkipB && (containsJson(mJsonName, AVC_VBR_B3_PARAMS) || containsJson(mJsonName,
376*b7c941bbSAndroid Build Coastguard Worker                         HEVC_VBR_B3_PARAMS)));
377*b7c941bbSAndroid Build Coastguard Worker         Assume.assumeFalse("Skipping non b-frame tests",
378*b7c941bbSAndroid Build Coastguard Worker                 mSkipP && (containsJson(mJsonName, AVC_VBR_B0_PARAMS) || containsJson(mJsonName,
379*b7c941bbSAndroid Build Coastguard Worker                         HEVC_VBR_B0_PARAMS)));
380*b7c941bbSAndroid Build Coastguard Worker 
381*b7c941bbSAndroid Build Coastguard Worker         // set up test environment
382*b7c941bbSAndroid Build Coastguard Worker         sLock.lock();
383*b7c941bbSAndroid Build Coastguard Worker         try {
384*b7c941bbSAndroid Build Coastguard Worker             if (!sIsTestSetUpDone) setupTestEnv();
385*b7c941bbSAndroid Build Coastguard Worker             sCondition.signalAll();
386*b7c941bbSAndroid Build Coastguard Worker         } finally {
387*b7c941bbSAndroid Build Coastguard Worker             sLock.unlock();
388*b7c941bbSAndroid Build Coastguard Worker         }
389*b7c941bbSAndroid Build Coastguard Worker 
390*b7c941bbSAndroid Build Coastguard Worker         // transcode input
391*b7c941bbSAndroid Build Coastguard Worker         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, DEVICE_SIDE_TEST_CLASS, "testTranscode");
392*b7c941bbSAndroid Build Coastguard Worker 
393*b7c941bbSAndroid Build Coastguard Worker         // copy the encoded output from the device to the host.
394*b7c941bbSAndroid Build Coastguard Worker         String outDir = "output_" + mJsonName.substring(0, mJsonName.indexOf('.'));
395*b7c941bbSAndroid Build Coastguard Worker         File outHostPath = new File(sHostWorkDir, outDir);
396*b7c941bbSAndroid Build Coastguard Worker         try {
397*b7c941bbSAndroid Build Coastguard Worker             if (!outHostPath.isDirectory()) {
398*b7c941bbSAndroid Build Coastguard Worker                 Assert.assertTrue("Failed to create directory : " + outHostPath.getAbsolutePath(),
399*b7c941bbSAndroid Build Coastguard Worker                         outHostPath.mkdirs());
400*b7c941bbSAndroid Build Coastguard Worker             }
401*b7c941bbSAndroid Build Coastguard Worker         } catch (SecurityException e) {
402*b7c941bbSAndroid Build Coastguard Worker             LogUtil.CLog.e("Unable to establish output host directory : " + outHostPath.getPath());
403*b7c941bbSAndroid Build Coastguard Worker         }
404*b7c941bbSAndroid Build Coastguard Worker         String outDevPath = getDevice().getMountPoint(IDevice.MNT_EXTERNAL_STORAGE) + "/veq/output/"
405*b7c941bbSAndroid Build Coastguard Worker                 + outDir;
406*b7c941bbSAndroid Build Coastguard Worker         Assert.assertTrue("Failed to pull mp4 files from " + outDevPath
407*b7c941bbSAndroid Build Coastguard Worker                 + " to " + outHostPath.getPath(), getDevice().pullDir(outDevPath, outHostPath));
408*b7c941bbSAndroid Build Coastguard Worker         getDevice().deleteFile(outDevPath);
409*b7c941bbSAndroid Build Coastguard Worker 
410*b7c941bbSAndroid Build Coastguard Worker         // Parse json file
411*b7c941bbSAndroid Build Coastguard Worker         String jsonPath = sHostWorkDir.getPath() + "/json/" + mJsonName;
412*b7c941bbSAndroid Build Coastguard Worker         String jsonString =
413*b7c941bbSAndroid Build Coastguard Worker                 new String(Files.readAllBytes(Paths.get(jsonPath)), StandardCharsets.UTF_8);
414*b7c941bbSAndroid Build Coastguard Worker         JSONArray jsonArray = new JSONArray(jsonString);
415*b7c941bbSAndroid Build Coastguard Worker         JSONObject obj = jsonArray.getJSONObject(0);
416*b7c941bbSAndroid Build Coastguard Worker         String refFileName = obj.getString("RefFileName");
417*b7c941bbSAndroid Build Coastguard Worker         int fps = obj.getInt("FrameRate");
418*b7c941bbSAndroid Build Coastguard Worker         int frameCount = obj.getInt("FrameCount");
419*b7c941bbSAndroid Build Coastguard Worker         int clipDuration = frameCount / fps;
420*b7c941bbSAndroid Build Coastguard Worker 
421*b7c941bbSAndroid Build Coastguard Worker         // Compute Vmaf
422*b7c941bbSAndroid Build Coastguard Worker         try (FileWriter writer = new FileWriter(outHostPath.getPath() + "/" + "all_vmafs.txt")) {
423*b7c941bbSAndroid Build Coastguard Worker             JSONArray codecConfigs = obj.getJSONArray("CodecConfigs");
424*b7c941bbSAndroid Build Coastguard Worker             int th = Runtime.getRuntime().availableProcessors() / 2;
425*b7c941bbSAndroid Build Coastguard Worker             th = Math.min(Math.max(1, th), 8);
426*b7c941bbSAndroid Build Coastguard Worker             String filter =
427*b7c941bbSAndroid Build Coastguard Worker                     "[0:v]setpts=PTS-STARTPTS[reference];[1:v]setpts=PTS-STARTPTS[distorted];"
428*b7c941bbSAndroid Build Coastguard Worker                             + "[distorted][reference]libvmaf=feature=name=psnr:model=version"
429*b7c941bbSAndroid Build Coastguard Worker                             + "=vmaf_v0.6.1:n_threads=" + th;
430*b7c941bbSAndroid Build Coastguard Worker             for (int i = 0; i < codecConfigs.length(); i++) {
431*b7c941bbSAndroid Build Coastguard Worker                 JSONObject codecConfig = codecConfigs.getJSONObject(i);
432*b7c941bbSAndroid Build Coastguard Worker                 String outputName = codecConfig.getString("EncodedFileName");
433*b7c941bbSAndroid Build Coastguard Worker                 outputName = outputName.substring(0, outputName.lastIndexOf("."));
434*b7c941bbSAndroid Build Coastguard Worker                 String outputVmafPath = outDir + "/" + outputName + ".txt";
435*b7c941bbSAndroid Build Coastguard Worker                 String cmd = "./bin/ffmpeg";
436*b7c941bbSAndroid Build Coastguard Worker                 cmd += " -hide_banner";
437*b7c941bbSAndroid Build Coastguard Worker                 cmd += " -r " + fps;
438*b7c941bbSAndroid Build Coastguard Worker                 cmd += " -i " + "samples/" + refFileName + " -an"; // reference video
439*b7c941bbSAndroid Build Coastguard Worker                 cmd += " -r " + fps;
440*b7c941bbSAndroid Build Coastguard Worker                 cmd += " -i " + outDir + "/" + outputName + ".mp4" + " -an"; // distorted video
441*b7c941bbSAndroid Build Coastguard Worker                 cmd += " -filter_complex " + "\"" + filter + "\"";
442*b7c941bbSAndroid Build Coastguard Worker                 cmd += " -f null -";
443*b7c941bbSAndroid Build Coastguard Worker                 cmd += " > " + outputVmafPath + " 2>&1";
444*b7c941bbSAndroid Build Coastguard Worker                 LogUtil.CLog.i("ffmpeg command : " + cmd);
445*b7c941bbSAndroid Build Coastguard Worker                 int result = runCommand(cmd, sHostWorkDir);
446*b7c941bbSAndroid Build Coastguard Worker                 if (sMpc >= MEDIA_PERFORMANCE_CLASS_14) {
447*b7c941bbSAndroid Build Coastguard Worker                     Assert.assertEquals("Encountered error during vmaf computation.", 0, result);
448*b7c941bbSAndroid Build Coastguard Worker                 } else {
449*b7c941bbSAndroid Build Coastguard Worker                     Assume.assumeTrue("Encountered error during vmaf computation but the "
450*b7c941bbSAndroid Build Coastguard Worker                             + "test device does not advertise performance class", result == 0);
451*b7c941bbSAndroid Build Coastguard Worker                 }
452*b7c941bbSAndroid Build Coastguard Worker                 String vmafLine = "";
453*b7c941bbSAndroid Build Coastguard Worker                 try (BufferedReader reader = new BufferedReader(
454*b7c941bbSAndroid Build Coastguard Worker                         new FileReader(sHostWorkDir.getPath() + "/" + outputVmafPath))) {
455*b7c941bbSAndroid Build Coastguard Worker                     String token = "VMAF score: ";
456*b7c941bbSAndroid Build Coastguard Worker                     String line;
457*b7c941bbSAndroid Build Coastguard Worker                     while ((line = reader.readLine()) != null) {
458*b7c941bbSAndroid Build Coastguard Worker                         if (line.contains(token)) {
459*b7c941bbSAndroid Build Coastguard Worker                             line = line.substring(line.indexOf(token));
460*b7c941bbSAndroid Build Coastguard Worker                             vmafLine = "VMAF score = " + line.substring(token.length());
461*b7c941bbSAndroid Build Coastguard Worker                             LogUtil.CLog.i(vmafLine);
462*b7c941bbSAndroid Build Coastguard Worker                             break;
463*b7c941bbSAndroid Build Coastguard Worker                         }
464*b7c941bbSAndroid Build Coastguard Worker                     }
465*b7c941bbSAndroid Build Coastguard Worker                 } catch (IOException e) {
466*b7c941bbSAndroid Build Coastguard Worker                     throw new AssertionError("Unexpected IOException: " + e.getMessage());
467*b7c941bbSAndroid Build Coastguard Worker                 }
468*b7c941bbSAndroid Build Coastguard Worker 
469*b7c941bbSAndroid Build Coastguard Worker                 writer.write(vmafLine + "\n");
470*b7c941bbSAndroid Build Coastguard Worker                 writer.write("Y4M file = " + refFileName + "\n");
471*b7c941bbSAndroid Build Coastguard Worker                 writer.write("MP4 file = " + refFileName + "\n");
472*b7c941bbSAndroid Build Coastguard Worker                 File file = new File(outHostPath + "/" + outputName + ".mp4");
473*b7c941bbSAndroid Build Coastguard Worker                 Assert.assertTrue("output file from device missing", file.exists());
474*b7c941bbSAndroid Build Coastguard Worker                 long fileSize = file.length();
475*b7c941bbSAndroid Build Coastguard Worker                 writer.write("Filesize = " + fileSize + "\n");
476*b7c941bbSAndroid Build Coastguard Worker                 writer.write("FPS = " + fps + "\n");
477*b7c941bbSAndroid Build Coastguard Worker                 writer.write("FRAME_COUNT = " + frameCount + "\n");
478*b7c941bbSAndroid Build Coastguard Worker                 writer.write("CLIP_DURATION = " + clipDuration + "\n");
479*b7c941bbSAndroid Build Coastguard Worker                 long totalBits = fileSize * 8;
480*b7c941bbSAndroid Build Coastguard Worker                 long totalBits_kbps = totalBits / 1000;
481*b7c941bbSAndroid Build Coastguard Worker                 long bitrate_kbps = totalBits_kbps / clipDuration;
482*b7c941bbSAndroid Build Coastguard Worker                 writer.write("Bitrate kbps = " + bitrate_kbps + "\n");
483*b7c941bbSAndroid Build Coastguard Worker             }
484*b7c941bbSAndroid Build Coastguard Worker         } catch (IOException e) {
485*b7c941bbSAndroid Build Coastguard Worker             throw new AssertionError("Unexpected IOException: " + e.getMessage());
486*b7c941bbSAndroid Build Coastguard Worker         }
487*b7c941bbSAndroid Build Coastguard Worker 
488*b7c941bbSAndroid Build Coastguard Worker         // bd rate verification
489*b7c941bbSAndroid Build Coastguard Worker         String refJsonFilePath = sHostWorkDir.getPath() + "/json/" + mJsonName;
490*b7c941bbSAndroid Build Coastguard Worker         String testVmafFilePath = sHostWorkDir.getPath() + "/" + outDir + "/" + "all_vmafs.txt";
491*b7c941bbSAndroid Build Coastguard Worker         String resultFilePath = sHostWorkDir.getPath() + "/" + outDir + "/result.txt";
492*b7c941bbSAndroid Build Coastguard Worker         int result = verifyBdRate(refJsonFilePath, testVmafFilePath, resultFilePath);
493*b7c941bbSAndroid Build Coastguard Worker         if (sMpc >= MEDIA_PERFORMANCE_CLASS_14) {
494*b7c941bbSAndroid Build Coastguard Worker             Assert.assertEquals("bd rate validation failed.", 0, result);
495*b7c941bbSAndroid Build Coastguard Worker         } else {
496*b7c941bbSAndroid Build Coastguard Worker             Assume.assumeTrue("bd rate validation failed but the test device does not "
497*b7c941bbSAndroid Build Coastguard Worker                     + "advertise performance class", result == 0);
498*b7c941bbSAndroid Build Coastguard Worker         }
499*b7c941bbSAndroid Build Coastguard Worker         LogUtil.CLog.i("Finished executing the process.");
500*b7c941bbSAndroid Build Coastguard Worker     }
501*b7c941bbSAndroid Build Coastguard Worker 
runCommand(String command, File dir)502*b7c941bbSAndroid Build Coastguard Worker     private int runCommand(String command, File dir) throws IOException, InterruptedException {
503*b7c941bbSAndroid Build Coastguard Worker         Process p = new ProcessBuilder("/bin/sh", "-c", command)
504*b7c941bbSAndroid Build Coastguard Worker                 .directory(dir)
505*b7c941bbSAndroid Build Coastguard Worker                 .redirectErrorStream(true)
506*b7c941bbSAndroid Build Coastguard Worker                 .redirectOutput(ProcessBuilder.Redirect.INHERIT)
507*b7c941bbSAndroid Build Coastguard Worker                 .start();
508*b7c941bbSAndroid Build Coastguard Worker 
509*b7c941bbSAndroid Build Coastguard Worker         BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
510*b7c941bbSAndroid Build Coastguard Worker         BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
511*b7c941bbSAndroid Build Coastguard Worker         String line;
512*b7c941bbSAndroid Build Coastguard Worker         while ((line = stdInput.readLine()) != null || (line = stdError.readLine()) != null) {
513*b7c941bbSAndroid Build Coastguard Worker             LogUtil.CLog.i(line + "\n");
514*b7c941bbSAndroid Build Coastguard Worker         }
515*b7c941bbSAndroid Build Coastguard Worker         return p.waitFor();
516*b7c941bbSAndroid Build Coastguard Worker     }
517*b7c941bbSAndroid Build Coastguard Worker 
518*b7c941bbSAndroid Build Coastguard Worker     // Download the indicated file (within the base_url folder) to our desired destination
519*b7c941bbSAndroid Build Coastguard Worker     // simple caching -- if file exists, we do not re-download
downloadFile(String url, File destDir)520*b7c941bbSAndroid Build Coastguard Worker     private void downloadFile(String url, File destDir) {
521*b7c941bbSAndroid Build Coastguard Worker         String fileName = url.substring(RES_URL.lastIndexOf('/') + 1);
522*b7c941bbSAndroid Build Coastguard Worker         File destination = new File(destDir, fileName);
523*b7c941bbSAndroid Build Coastguard Worker 
524*b7c941bbSAndroid Build Coastguard Worker         // save bandwidth, also allows a user to manually preload files
525*b7c941bbSAndroid Build Coastguard Worker         LogUtil.CLog.i("Do we already have a copy of file " + destination.getPath());
526*b7c941bbSAndroid Build Coastguard Worker         if (destination.isFile()) {
527*b7c941bbSAndroid Build Coastguard Worker             LogUtil.CLog.i("Skipping re-download of file " + destination.getPath());
528*b7c941bbSAndroid Build Coastguard Worker             return;
529*b7c941bbSAndroid Build Coastguard Worker         }
530*b7c941bbSAndroid Build Coastguard Worker 
531*b7c941bbSAndroid Build Coastguard Worker         String cmd = "wget -O " + destination.getPath() + " " + url;
532*b7c941bbSAndroid Build Coastguard Worker         LogUtil.CLog.i("wget_cmd = " + cmd);
533*b7c941bbSAndroid Build Coastguard Worker 
534*b7c941bbSAndroid Build Coastguard Worker         int result = 0;
535*b7c941bbSAndroid Build Coastguard Worker         try {
536*b7c941bbSAndroid Build Coastguard Worker             result = runCommand(cmd, destDir);
537*b7c941bbSAndroid Build Coastguard Worker         } catch (IOException e) {
538*b7c941bbSAndroid Build Coastguard Worker             result = -2;
539*b7c941bbSAndroid Build Coastguard Worker         } catch (InterruptedException e) {
540*b7c941bbSAndroid Build Coastguard Worker             result = -3;
541*b7c941bbSAndroid Build Coastguard Worker         }
542*b7c941bbSAndroid Build Coastguard Worker         Assert.assertEquals("download file failed.\n", 0, result);
543*b7c941bbSAndroid Build Coastguard Worker     }
544*b7c941bbSAndroid Build Coastguard Worker 
runDeviceTests(String pkgName, @Nullable String testClassName, @Nullable String testMethodName)545*b7c941bbSAndroid Build Coastguard Worker     private void runDeviceTests(String pkgName, @Nullable String testClassName,
546*b7c941bbSAndroid Build Coastguard Worker             @Nullable String testMethodName) throws DeviceNotAvailableException {
547*b7c941bbSAndroid Build Coastguard Worker         RemoteAndroidTestRunner testRunner = getTestRunner(pkgName, testClassName, testMethodName);
548*b7c941bbSAndroid Build Coastguard Worker         CollectingTestListener listener = new CollectingTestListener();
549*b7c941bbSAndroid Build Coastguard Worker         Assert.assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
550*b7c941bbSAndroid Build Coastguard Worker         assertTestsPassed(listener.getCurrentRunResults());
551*b7c941bbSAndroid Build Coastguard Worker     }
552*b7c941bbSAndroid Build Coastguard Worker 
getTestRunner(String pkgName, String testClassName, String testMethodName)553*b7c941bbSAndroid Build Coastguard Worker     private RemoteAndroidTestRunner getTestRunner(String pkgName, String testClassName,
554*b7c941bbSAndroid Build Coastguard Worker             String testMethodName) {
555*b7c941bbSAndroid Build Coastguard Worker         if (testClassName != null && testClassName.startsWith(".")) {
556*b7c941bbSAndroid Build Coastguard Worker             testClassName = pkgName + testClassName;
557*b7c941bbSAndroid Build Coastguard Worker         }
558*b7c941bbSAndroid Build Coastguard Worker         RemoteAndroidTestRunner testRunner =
559*b7c941bbSAndroid Build Coastguard Worker                 new RemoteAndroidTestRunner(pkgName, RUNNER, getDevice().getIDevice());
560*b7c941bbSAndroid Build Coastguard Worker         testRunner.setMaxTimeToOutputResponse(DEFAULT_SHELL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
561*b7c941bbSAndroid Build Coastguard Worker         testRunner.addInstrumentationArg(TEST_TIMEOUT_INST_ARGS_KEY,
562*b7c941bbSAndroid Build Coastguard Worker                 Long.toString(DEFAULT_TEST_TIMEOUT_MILLIS));
563*b7c941bbSAndroid Build Coastguard Worker         testRunner.addInstrumentationArg(TEST_CONFIG_INST_ARGS_KEY, mJsonName);
564*b7c941bbSAndroid Build Coastguard Worker         if (testClassName != null && testMethodName != null) {
565*b7c941bbSAndroid Build Coastguard Worker             testRunner.setMethodName(testClassName, testMethodName);
566*b7c941bbSAndroid Build Coastguard Worker         } else if (testClassName != null) {
567*b7c941bbSAndroid Build Coastguard Worker             testRunner.setClassName(testClassName);
568*b7c941bbSAndroid Build Coastguard Worker         }
569*b7c941bbSAndroid Build Coastguard Worker         return testRunner;
570*b7c941bbSAndroid Build Coastguard Worker     }
571*b7c941bbSAndroid Build Coastguard Worker 
assertTestsPassed(TestRunResult testRunResult)572*b7c941bbSAndroid Build Coastguard Worker     private void assertTestsPassed(TestRunResult testRunResult) {
573*b7c941bbSAndroid Build Coastguard Worker         if (testRunResult.isRunFailure()) {
574*b7c941bbSAndroid Build Coastguard Worker             throw new AssertionError("Failed to successfully run device tests for "
575*b7c941bbSAndroid Build Coastguard Worker                     + testRunResult.getName() + ": " + testRunResult.getRunFailureMessage());
576*b7c941bbSAndroid Build Coastguard Worker         }
577*b7c941bbSAndroid Build Coastguard Worker         if (testRunResult.getNumTests() != testRunResult.getPassedTests().size()) {
578*b7c941bbSAndroid Build Coastguard Worker             for (Map.Entry<TestDescription, TestResult> resultEntry :
579*b7c941bbSAndroid Build Coastguard Worker                     testRunResult.getTestResults().entrySet()) {
580*b7c941bbSAndroid Build Coastguard Worker                 if (resultEntry.getValue().getStatus().equals(TestStatus.FAILURE)) {
581*b7c941bbSAndroid Build Coastguard Worker                     StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
582*b7c941bbSAndroid Build Coastguard Worker                     errorBuilder.append(resultEntry.getKey().toString());
583*b7c941bbSAndroid Build Coastguard Worker                     errorBuilder.append(":\n");
584*b7c941bbSAndroid Build Coastguard Worker                     errorBuilder.append(resultEntry.getValue().getStackTrace());
585*b7c941bbSAndroid Build Coastguard Worker                     throw new AssertionError(errorBuilder.toString());
586*b7c941bbSAndroid Build Coastguard Worker                 }
587*b7c941bbSAndroid Build Coastguard Worker                 if (resultEntry.getValue().getStatus().equals(TestStatus.ASSUMPTION_FAILURE)) {
588*b7c941bbSAndroid Build Coastguard Worker                     StringBuilder errorBuilder =
589*b7c941bbSAndroid Build Coastguard Worker                             new StringBuilder("On-device tests assumption failed:\n");
590*b7c941bbSAndroid Build Coastguard Worker                     errorBuilder.append(resultEntry.getKey().toString());
591*b7c941bbSAndroid Build Coastguard Worker                     errorBuilder.append(":\n");
592*b7c941bbSAndroid Build Coastguard Worker                     errorBuilder.append(resultEntry.getValue().getStackTrace());
593*b7c941bbSAndroid Build Coastguard Worker                     Assume.assumeTrue(errorBuilder.toString(), false);
594*b7c941bbSAndroid Build Coastguard Worker                 }
595*b7c941bbSAndroid Build Coastguard Worker             }
596*b7c941bbSAndroid Build Coastguard Worker         }
597*b7c941bbSAndroid Build Coastguard Worker     }
598*b7c941bbSAndroid Build Coastguard Worker }
599