• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 android.videoencodingmin.app;
18 
19 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
20 import static android.mediav2.common.cts.CodecTestBase.CONTEXT;
21 import static android.mediav2.common.cts.CodecTestBase.MEDIA_CODEC_LIST_REGULAR;
22 import static android.mediav2.common.cts.CodecTestBase.selectCodecs;
23 import static android.mediav2.common.cts.DecodeStreamToYuv.getFormatInStream;
24 import static android.os.Environment.buildPath;
25 
26 import android.Manifest;
27 import android.content.pm.PackageManager;
28 import android.media.MediaFormat;
29 import android.mediav2.common.cts.CodecEncoderSurfaceTestBase;
30 import android.mediav2.common.cts.CodecTestBase;
31 import android.mediav2.common.cts.EncoderConfigParams;
32 import android.mediav2.common.cts.OutputManager;
33 import android.os.Environment;
34 
35 import androidx.test.ext.junit.runners.AndroidJUnit4;
36 import androidx.test.filters.LargeTest;
37 import androidx.test.platform.app.InstrumentationRegistry;
38 
39 import com.android.compatibility.common.util.Preconditions;
40 
41 import org.json.JSONArray;
42 import org.json.JSONException;
43 import org.json.JSONObject;
44 import org.junit.Assert;
45 import org.junit.Assume;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.io.File;
50 import java.io.FileOutputStream;
51 import java.io.IOException;
52 import java.nio.charset.StandardCharsets;
53 import java.nio.file.Files;
54 import java.nio.file.Paths;
55 import java.util.ArrayList;
56 
57 /**
58  * Test transcoding using media codec api.
59  * <p>
60  * The test decodes an input clip to surface. This decoded output is fed as input to encoder.
61  * Assuming no frame drops, the test expects,
62  * <ul>
63  *     <li>The number of encoded frames to be identical to number of frames present in input clip
64  *     .</li>
65  *     <li>The encoder output timestamps list should be identical to decoder input timestamp list
66  *     .</li>
67  * </ul>
68  * <p>
69  * The test has provision to validate the encoded output by computing PSNR against input. This is
70  * however disabled as VMAF is chosen for analysis. This analysis is done on host side.
71  */
72 @RunWith(AndroidJUnit4.class)
73 public class VideoTranscoderTest {
74     private static final String SDCARD_MOUNT_POINT =
75             Environment.getExternalStorageDirectory().getAbsolutePath();
76     private static final String MEDIA_DIR = SDCARD_MOUNT_POINT + "/vqf/input/";
77     public static final String ENC_CONFIG_JSON = "conf-json";
78     private static final String ENC_CONFIG_FILE;
79     private static String PATH_PREFIX;
80     public static final int DEFAULT_TEST_TIMEOUT_MS = 360000;
81 
82     private String mEncoderName;
83     private String mEncMediaType;
84     private String mDecoderName;
85     private String mTestFileMediaType;
86     private String mTestFile;
87     private EncoderConfigParams[] mEncCfgParams;
88     private String[] mOutputFileNames;
89     private int mDecColorFormat;
90 
91     static {
92         android.os.Bundle args = InstrumentationRegistry.getArguments();
93         ENC_CONFIG_FILE = args.getString(ENC_CONFIG_JSON);
94     }
95 
96     static class TestTranscode extends CodecEncoderSurfaceTestBase {
97         private final String mOutputFileName;
98 
TestTranscode(String encoder, String mediaType, String decoder, String testFileMediaType, String testFile, EncoderConfigParams encCfgParams, String outputFileName, int decColorFormat, boolean isOutputToneMapped, boolean usePersistentSurface, String allTestParams)99         TestTranscode(String encoder, String mediaType, String decoder, String testFileMediaType,
100                 String testFile, EncoderConfigParams encCfgParams, String outputFileName,
101                 int decColorFormat, boolean isOutputToneMapped, boolean usePersistentSurface,
102                 String allTestParams) {
103             super(encoder, mediaType, decoder, testFileMediaType, testFile, encCfgParams,
104                     decColorFormat, isOutputToneMapped, usePersistentSurface, allTestParams);
105             mOutputFileName = outputFileName;
106         }
107 
108         @Override
setUpCodecEncoderSurfaceTestBase()109         public void setUpCodecEncoderSurfaceTestBase()
110                 throws IOException, CloneNotSupportedException {
111             super.setUpCodecEncoderSurfaceTestBase();
112             mEncoderFormat = mEncCfgParams.getFormat();
113         }
114 
getTempFilePath(String infix)115         private String getTempFilePath(String infix) throws IOException {
116             String totalPath = PATH_PREFIX + infix + ".mp4";
117             new FileOutputStream(totalPath).close();
118             return totalPath;
119         }
120 
doTranscode()121         public void doTranscode()
122                 throws IOException, InterruptedException, CloneNotSupportedException {
123             try {
124                 setUpCodecEncoderSurfaceTestBase();
125                 encodeToMemory(false, false, false, new OutputManager(), true,
126                         getTempFilePath(mOutputFileName));
127             } finally {
128                 tearDownCodecEncoderSurfaceTestBase();
129             }
130         }
131     }
132 
parseEncoderConfigurationFile(String jsonPath)133     private void parseEncoderConfigurationFile(String jsonPath) throws JSONException, IOException {
134         Preconditions.assertTestFileExists(jsonPath);
135         String jsonString =
136                 new String(Files.readAllBytes(Paths.get(jsonPath)), StandardCharsets.UTF_8);
137         JSONArray jsonArray = new JSONArray(jsonString);
138         JSONObject obj = jsonArray.getJSONObject(0);
139         mTestFile = MEDIA_DIR + "samples/" + obj.getString("RefFileName");
140         mTestFileMediaType = obj.getString("RefMediaType");
141         mEncMediaType = obj.getString("TestMediaType");
142         int width = obj.getInt("Width");
143         int height = obj.getInt("Height");
144         String componentType = obj.getString("EncoderType");
145         CodecTestBase.ComponentClass cType = CodecTestBase.ComponentClass.ALL;
146         if (componentType.equals("hw")) {
147             cType = CodecTestBase.ComponentClass.HARDWARE;
148         } else if (componentType.equals("sw")) {
149             cType = CodecTestBase.ComponentClass.SOFTWARE;
150         }
151         mDecColorFormat = COLOR_FormatSurface;
152         JSONArray codecConfigs = obj.getJSONArray("CodecConfigs");
153         mEncCfgParams = new EncoderConfigParams[codecConfigs.length()];
154         mOutputFileNames = new String[codecConfigs.length()];
155         for (int i = 0; i < codecConfigs.length(); i++) {
156             JSONObject codecConfig = codecConfigs.getJSONObject(i);
157             mEncCfgParams[i] = new EncoderConfigParams.Builder(mEncMediaType)
158                     .setWidth(width)
159                     .setHeight(height)
160                     .setKeyFrameInterval(codecConfig.getInt("KeyFrameInterval"))
161                     .setMaxBFrames(codecConfig.getInt("MaxBFrames"))
162                     .setBitRate(codecConfig.getInt("BitRate"))
163                     .setProfile(codecConfig.getInt("Profile"))
164                     .setLevel(codecConfig.getInt("Level"))
165                     .setColorFormat(COLOR_FormatSurface)
166                     .build();
167             String outFileName = codecConfig.getString("EncodedFileName");
168             mOutputFileNames[i] = outFileName.substring(0, outFileName.lastIndexOf('.'));
169         }
170         MediaFormat format = getFormatInStream(mTestFileMediaType, mTestFile);
171         mDecoderName = MEDIA_CODEC_LIST_REGULAR.findDecoderForFormat(format);
172         ArrayList<MediaFormat> formats = new ArrayList<>();
173         for (EncoderConfigParams param : mEncCfgParams) {
174             formats.add(param.getFormat());
175         }
176         ArrayList<String> codecs = selectCodecs(mEncMediaType, formats, null, true, cType);
177         if (!codecs.isEmpty()) mEncoderName = codecs.get(0);
178     }
179 
180     @LargeTest
181     @Test(timeout = DEFAULT_TEST_TIMEOUT_MS)
testTranscode()182     public void testTranscode() throws IOException, InterruptedException,
183             JSONException, CloneNotSupportedException {
184         Assume.assumeTrue("Test did not receive config file for encoding", ENC_CONFIG_FILE != null);
185         parseEncoderConfigurationFile(MEDIA_DIR + "json/" + ENC_CONFIG_FILE);
186         Assume.assumeTrue("Found no encoder supporting the config file", mEncoderName != null);
187         Assume.assumeTrue("Found no decoder supporting the config file", mDecoderName != null);
188         Assert.assertEquals("Apk does not have permissions to write to external storage",
189                 PackageManager.PERMISSION_GRANTED,
190                 CONTEXT.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE));
191         File pub = new File(SDCARD_MOUNT_POINT + "/vqf/output/");
192         File dir = buildPath(pub,
193                 "output_" + ENC_CONFIG_FILE.substring(0, ENC_CONFIG_FILE.lastIndexOf('.')));
194         if (!dir.exists()) {
195             Assert.assertTrue("Unable to create dir " + dir.getAbsolutePath(), dir.mkdirs());
196         }
197         PATH_PREFIX = dir.getAbsolutePath() + File.separator;
198         for (int i = 0; i < mEncCfgParams.length; i++) {
199             TestTranscode ep = new TestTranscode(mEncoderName, mEncMediaType,
200                     mDecoderName, mTestFileMediaType, mTestFile, mEncCfgParams[i],
201                     mOutputFileNames[i], mDecColorFormat, false, false, "");
202             ep.doTranscode();
203         }
204     }
205 }
206