blob: f00c14d79beceed831425443158fea986abc5f88 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mediatranscodingtest;
import android.content.ContentResolver;
import android.content.Context;
import android.media.MediaFormat;
import android.media.MediaTranscodeManager;
import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
import android.net.Uri;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/*
* Service died tests for MediaTranscodeManager in the media framework.
*
* To run this test suite:
make frameworks/base/media/tests/MediaTranscodingTest
make mediatranscodingtest
adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
adb shell am instrument -e class \
com.android.mediatranscodingtest.MediaTranscodeManagerDiedTest \
-w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
*
*/
public class MediaTranscodeManagerDiedTest
extends ActivityInstrumentationTestCase2<MediaTranscodingTest> {
private static final String TAG = "MediaTranscodeManagerDiedTest";
/** The time to wait for the transcode operation to complete before failing the test. */
private static final int TRANSCODE_TIMEOUT_SECONDS = 10;
/** Maximum number of retry to connect to the service. */
private static final int CONNECT_SERVICE_RETRY_COUNT = 100;
/** Interval between trying to reconnect to the service. */
private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40;
private Context mContext;
private MediaTranscodeManager mMediaTranscodeManager = null;
private Uri mSourceHEVCVideoUri = null;
private Uri mSourceAVCVideoUri = null;
private Uri mDestinationUri = null;
// Setting for transcoding to H.264.
private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
private static final int BIT_RATE = 20000000; // 20Mbps
private static final int WIDTH = 1920;
private static final int HEIGHT = 1080;
public MediaTranscodeManagerDiedTest() {
super("com.android.MediaTranscodeManagerTest", MediaTranscodingTest.class);
}
// Copy the resource to cache.
private Uri resourceToUri(Context context, int resId, String name) throws IOException {
Uri resUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(context.getResources().getResourcePackageName(resId))
.appendPath(context.getResources().getResourceTypeName(resId))
.appendPath(context.getResources().getResourceEntryName(resId))
.build();
Uri cacheUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ mContext.getCacheDir().getAbsolutePath() + "/" + name);
InputStream is = mContext.getResources().openRawResource(resId);
OutputStream os = mContext.getContentResolver().openOutputStream(cacheUri);
FileUtils.copy(is, os);
return cacheUri;
}
private static Uri generateNewUri(Context context, String filename) {
File outFile = new File(context.getExternalCacheDir(), filename);
return Uri.fromFile(outFile);
}
/**
* Creates a MediaFormat with the basic set of values.
*/
private static MediaFormat createMediaFormat() {
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
return format;
}
private MediaTranscodeManager getManager() {
for (int count = 1; count <= CONNECT_SERVICE_RETRY_COUNT; count++) {
Log.d(TAG, "Trying to connect to service. Try count: " + count);
MediaTranscodeManager manager = mContext.getSystemService(MediaTranscodeManager.class);
if (manager != null) {
return manager;
}
try {
// Sleep a bit before retry.
Thread.sleep(INTERVAL_CONNECT_SERVICE_RETRY_MS);
} catch (InterruptedException ie) {
/* ignore */
}
}
throw new UnsupportedOperationException("Failed to acquire MediaTranscodeManager");
}
@Override
public void setUp() throws Exception {
Log.d(TAG, "setUp");
super.setUp();
mContext = getInstrumentation().getContext();
mMediaTranscodeManager = getManager();
assertNotNull(mMediaTranscodeManager);
androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
// Setup source HEVC file uri.
mSourceHEVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyHEVC, "VideoOnlyHEVC.mp4");
// Setup source AVC file uri.
mSourceAVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyAVC,
"VideoOnlyAVC.mp4");
// Setup destination file.
mDestinationUri = generateNewUri(mContext, "transcoded.mp4");
}
@Override
public void tearDown() throws Exception {
super.tearDown();
}
// [[ $(adb shell whoami) == "root" ]]
private boolean checkIfRoot() {
try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation()
.executeShellCommand("whoami");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
new FileInputStream(result.getFileDescriptor())))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
if (line.contains("root")) {
return true;
}
}
} catch (IOException ie) {
return false;
}
return false;
}
private String executeShellCommand(String cmd) throws Exception {
return UiDevice.getInstance(
InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
}
@Test
public void testHandleTranscoderServiceDied() throws Exception {
if (!checkIfRoot()) {
throw new AssertionError("must be root to run this test; try adb root?");
}
Semaphore transcodeCompleteSemaphore = new Semaphore(0);
Semaphore jobStartedSemaphore = new Semaphore(0);
// Transcode a 15 seconds video, so that the transcoding is not finished when we kill the
// service.
Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4");
Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
TranscodingRequest request =
new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(destinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
.setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
.setVideoTrackFormat(createMediaFormat())
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
Log.i(TAG, "transcoding to " + createMediaFormat());
TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
transcodingJob -> {
Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
transcodeCompleteSemaphore.release();
});
assertNotNull(job);
AtomicInteger progressUpdateCount = new AtomicInteger(0);
// Set progress update executor and use the same executor as result listener.
job.setOnProgressUpdateListener(listenerExecutor,
new TranscodingJob.OnProgressUpdateListener() {
@Override
public void onProgressUpdate(TranscodingJob job, int newProgress) {
if (newProgress > 0) {
jobStartedSemaphore.release();
}
}
});
// Wait for progress update so the job is in running state.
jobStartedSemaphore.tryAcquire(TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue("Job is not running", job.getStatus() == TranscodingJob.STATUS_RUNNING);
// Kills the service and expects receiving failure of the job.
executeShellCommand("pkill -f media.transcoding");
Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode result.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
assertTrue("Invalid job result", job.getResult()== TranscodingJob.RESULT_ERROR);
}
}