1 /* 2 * Copyright (C) 2014 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.media.misc.cts; 18 19 import static org.junit.Assert.assertTrue; 20 21 import android.content.res.AssetFileDescriptor; 22 import android.media.MediaExtractor; 23 import android.media.cts.MediaTestBase; 24 import android.media.cts.TestUtils.Monitor; 25 import android.os.Build; 26 import android.os.ParcelFileDescriptor; 27 import android.platform.test.annotations.AppModeFull; 28 import android.platform.test.annotations.Presubmit; 29 import android.platform.test.annotations.RequiresDevice; 30 import android.util.Log; 31 import android.webkit.cts.CtsTestServer; 32 33 import androidx.test.ext.junit.runners.AndroidJUnit4; 34 import androidx.test.filters.SmallTest; 35 36 import com.android.compatibility.common.util.ApiLevelUtil; 37 import com.android.compatibility.common.util.Preconditions; 38 39 import org.apache.http.impl.DefaultHttpServerConnection; 40 import org.apache.http.impl.io.SocketOutputBuffer; 41 import org.apache.http.io.SessionOutputBuffer; 42 import org.apache.http.params.HttpParams; 43 import org.apache.http.util.CharArrayBuffer; 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 49 import java.io.File; 50 import java.io.FileNotFoundException; 51 import java.io.IOException; 52 import java.net.Socket; 53 import java.util.Map; 54 import java.util.Set; 55 import java.util.UUID; 56 import java.util.concurrent.CountDownLatch; 57 import java.util.concurrent.TimeUnit; 58 59 @SmallTest 60 @RequiresDevice 61 @AppModeFull(reason = "TODO: evaluate and port to instant") 62 @RunWith(AndroidJUnit4.class) 63 public class NativeDecoderTest extends MediaTestBase { 64 private static final String TAG = "DecoderTest"; 65 66 private static final boolean sIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S); 67 68 static final String mInpPrefix = WorkDir.getMediaDirString(); 69 short[] mMasterBuffer; 70 71 static { 72 // Load jni on initialization. 73 Log.i("@@@", "before loadlibrary"); 74 System.loadLibrary("ctsmediamisc_jni"); 75 Log.i("@@@", "after loadlibrary"); 76 } 77 78 @Before 79 @Override setUp()80 public void setUp() throws Throwable { 81 super.setUp(); 82 } 83 84 @After 85 @Override tearDown()86 public void tearDown() { 87 super.tearDown(); 88 } 89 getAssetFileDescriptorFor(final String res)90 protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res) 91 throws FileNotFoundException { 92 Preconditions.assertTestFileExists(mInpPrefix + res); 93 File inpFile = new File(mInpPrefix + res); 94 ParcelFileDescriptor parcelFD = 95 ParcelFileDescriptor.open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY); 96 return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize()); 97 } 98 99 @Presubmit 100 @Test testFormat()101 public void testFormat() throws Exception { 102 assertTrue("media format fail, see log for details", testFormatNative()); 103 } 104 testFormatNative()105 private static native boolean testFormatNative(); 106 107 @Presubmit 108 @Test testPssh()109 public void testPssh() throws Exception { 110 testPssh("psshtest.mp4"); 111 } 112 testPssh(final String res)113 private void testPssh(final String res) throws Exception { 114 AssetFileDescriptor fd = getAssetFileDescriptorFor(res); 115 116 MediaExtractor ex = new MediaExtractor(); 117 ex.setDataSource(fd.getParcelFileDescriptor().getFileDescriptor(), 118 fd.getStartOffset(), fd.getLength()); 119 testPssh(ex); 120 ex.release(); 121 122 boolean ret = testPsshNative( 123 fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength()); 124 assertTrue("native pssh error", ret); 125 } 126 testPssh(MediaExtractor ex)127 private static void testPssh(MediaExtractor ex) { 128 Map<UUID, byte[]> map = ex.getPsshInfo(); 129 Set<UUID> keys = map.keySet(); 130 for (UUID uuid: keys) { 131 Log.i("@@@", "uuid: " + uuid + ", data size " + 132 map.get(uuid).length); 133 } 134 } 135 testPsshNative(int fd, long offset, long size)136 private static native boolean testPsshNative(int fd, long offset, long size); 137 138 @Test testCryptoInfo()139 public void testCryptoInfo() throws Exception { 140 assertTrue("native cryptoinfo failed, see log for details", testCryptoInfoNative()); 141 } 142 testCryptoInfoNative()143 private static native boolean testCryptoInfoNative(); 144 145 @Presubmit 146 @Test testMediaFormat()147 public void testMediaFormat() throws Exception { 148 assertTrue("native mediaformat failed, see log for details", testMediaFormatNative()); 149 } 150 testMediaFormatNative()151 private static native boolean testMediaFormatNative(); 152 153 @Presubmit 154 @Test testAMediaDataSourceClose()155 public void testAMediaDataSourceClose() throws Throwable { 156 157 final CtsTestServer slowServer = new SlowCtsTestServer(); 158 final String url = slowServer.getAssetUrl("noiseandchirps.ogg"); 159 final long ds = createAMediaDataSource(url); 160 final long ex = createAMediaExtractor(); 161 162 try { 163 setAMediaExtractorDataSourceAndFailIfAnr(ex, ds); 164 } finally { 165 slowServer.shutdown(); 166 deleteAMediaExtractor(ex); 167 deleteAMediaDataSource(ds); 168 } 169 170 } 171 setAMediaExtractorDataSourceAndFailIfAnr(final long ex, final long ds)172 private void setAMediaExtractorDataSourceAndFailIfAnr(final long ex, final long ds) 173 throws Throwable { 174 final Monitor setAMediaExtractorDataSourceDone = new Monitor(); 175 final int HEAD_START_MILLIS = 1000; 176 final int ANR_TIMEOUT_MILLIS = 2500; 177 final int JOIN_TIMEOUT_MILLIS = 1500; 178 179 Thread setAMediaExtractorDataSourceThread = new Thread() { 180 public void run() { 181 setAMediaExtractorDataSource(ex, ds); 182 setAMediaExtractorDataSourceDone.signal(); 183 } 184 }; 185 186 try { 187 setAMediaExtractorDataSourceThread.start(); 188 Thread.sleep(HEAD_START_MILLIS); 189 closeAMediaDataSource(ds); 190 boolean closed = setAMediaExtractorDataSourceDone.waitForSignal(ANR_TIMEOUT_MILLIS); 191 assertTrue("close took longer than " + ANR_TIMEOUT_MILLIS, closed); 192 } finally { 193 setAMediaExtractorDataSourceThread.join(JOIN_TIMEOUT_MILLIS); 194 } 195 196 } 197 198 private class SlowCtsTestServer extends CtsTestServer { 199 200 private static final int SERVER_DELAY_MILLIS = 5000; 201 private final CountDownLatch mDisconnected = new CountDownLatch(1); 202 SlowCtsTestServer()203 SlowCtsTestServer() throws Exception { 204 super(mContext); 205 } 206 207 @Override createHttpServerConnection()208 protected DefaultHttpServerConnection createHttpServerConnection() { 209 return new SlowHttpServerConnection(mDisconnected, SERVER_DELAY_MILLIS); 210 } 211 212 @Override shutdown()213 public void shutdown() { 214 mDisconnected.countDown(); 215 super.shutdown(); 216 } 217 } 218 219 private static class SlowHttpServerConnection extends DefaultHttpServerConnection { 220 221 private final CountDownLatch mDisconnected; 222 private final int mDelayMillis; 223 SlowHttpServerConnection(CountDownLatch disconnected, int delayMillis)224 public SlowHttpServerConnection(CountDownLatch disconnected, int delayMillis) { 225 mDisconnected = disconnected; 226 mDelayMillis = delayMillis; 227 } 228 229 @Override createHttpDataTransmitter( Socket socket, int buffersize, HttpParams params)230 protected SessionOutputBuffer createHttpDataTransmitter( 231 Socket socket, int buffersize, HttpParams params) throws IOException { 232 return createSessionOutputBuffer(socket, buffersize, params); 233 } 234 createSessionOutputBuffer( Socket socket, int buffersize, HttpParams params)235 SessionOutputBuffer createSessionOutputBuffer( 236 Socket socket, int buffersize, HttpParams params) throws IOException { 237 return new SocketOutputBuffer(socket, buffersize, params) { 238 @Override 239 public void write(byte[] b) throws IOException { 240 write(b, 0, b.length); 241 } 242 243 @Override 244 public void write(byte[] b, int off, int len) throws IOException { 245 while (len-- > 0) { 246 write(b[off++]); 247 } 248 } 249 250 @Override 251 public void writeLine(String s) throws IOException { 252 delay(); 253 super.writeLine(s); 254 } 255 256 @Override 257 public void writeLine(CharArrayBuffer buffer) throws IOException { 258 delay(); 259 super.writeLine(buffer); 260 } 261 262 @Override 263 public void write(int b) throws IOException { 264 delay(); 265 super.write(b); 266 } 267 268 private void delay() throws IOException { 269 try { 270 mDisconnected.await(mDelayMillis, TimeUnit.MILLISECONDS); 271 } catch (InterruptedException e) { 272 // Ignored 273 } 274 } 275 276 }; 277 } 278 } 279 280 private static native long createAMediaExtractor(); 281 private static native long createAMediaDataSource(String url); 282 private static native int setAMediaExtractorDataSource(long ex, long ds); 283 private static native void closeAMediaDataSource(long ds); 284 private static native void deleteAMediaExtractor(long ex); 285 private static native void deleteAMediaDataSource(long ds); 286 287 } 288 289