Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78c2acc0 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "transcoding: Rework MediaTranscodeManager test."

parents 8423c029 48be9935
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ do
adb push --sync $file /data/user/0/com.android.mediatranscodingtest/cache/
done

echo "[==========] running real transcoding tests"
echo "[==========] running MediaTranscodeManagerTest"
adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner

echo "[==========] running MediaTranscodeManagerDiedTest"
adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerDiedTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+255 −0
Original line number Diff line number Diff line
/*
 * 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(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);
    }
}
+224 −68
Original line number Diff line number Diff line
@@ -16,12 +16,15 @@

package com.android.mediatranscodingtest;

import static org.testng.Assert.assertThrows;

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.media.TranscodingTestConfig;
import android.net.Uri;
import android.os.Bundle;
import android.os.FileUtils;
@@ -180,6 +183,227 @@ public class MediaTranscodeManagerTest
        super.tearDown();
    }


    /**
     * Verify that setting null destination uri will throw exception.
     */
    @Test
    public void testCreateTranscodingRequestWithNullDestinationUri() throws Exception {
        assertThrows(IllegalArgumentException.class, () -> {
            TranscodingRequest request =
                    new TranscodingRequest.Builder()
                            .setSourceUri(mSourceHEVCVideoUri)
                            .setDestinationUri(null)
                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
                            .setVideoTrackFormat(createMediaFormat())
                            .build();
        });
    }

    /**
     * Verify that setting null source uri will throw exception.
     */
    @Test
    public void testCreateTranscodingRequestWithNullSourceUri() throws Exception {
        assertThrows(IllegalArgumentException.class, () -> {
            TranscodingRequest request =
                    new TranscodingRequest.Builder()
                            .setSourceUri(null)
                            .setDestinationUri(mDestinationUri)
                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
                            .build();
        });
    }

    /**
     * Verify that not setting source uri will throw exception.
     */
    @Test
    public void testCreateTranscodingRequestWithoutSourceUri() throws Exception {
        assertThrows(UnsupportedOperationException.class, () -> {
            TranscodingRequest request =
                    new TranscodingRequest.Builder()
                            .setDestinationUri(mDestinationUri)
                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
                            .setVideoTrackFormat(createMediaFormat())
                            .build();
        });
    }

    /**
     * Verify that not setting destination uri will throw exception.
     */
    @Test
    public void testCreateTranscodingRequestWithoutDestinationUri() throws Exception {
        assertThrows(UnsupportedOperationException.class, () -> {
            TranscodingRequest request =
                    new TranscodingRequest.Builder()
                            .setSourceUri(mSourceHEVCVideoUri)
                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
                            .setVideoTrackFormat(createMediaFormat())
                            .build();
        });
    }

    /**
     * Verify that setting image transcoding mode will throw exception.
     */
    @Test
    public void testCreateTranscodingRequestWithUnsupportedMode() throws Exception {
        assertThrows(UnsupportedOperationException.class, () -> {
            TranscodingRequest request =
                    new TranscodingRequest.Builder()
                            .setSourceUri(mSourceHEVCVideoUri)
                            .setDestinationUri(mDestinationUri)
                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_IMAGE)
                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
                            .setVideoTrackFormat(createMediaFormat())
                            .build();
        });
    }

    /**
     * Verify that setting video transcoding without setting video format will throw exception.
     */
    @Test
    public void testCreateTranscodingRequestWithoutVideoFormat() throws Exception {
        assertThrows(UnsupportedOperationException.class, () -> {
            TranscodingRequest request =
                    new TranscodingRequest.Builder()
                            .setSourceUri(mSourceHEVCVideoUri)
                            .setDestinationUri(mDestinationUri)
                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
                            .build();
        });
    }

    void testTranscodingWithExpectResult(Uri srcUri, Uri dstUri, int expectedResult)
            throws Exception {
        Semaphore transcodeCompleteSemaphore = new Semaphore(0);
        TranscodingTestConfig testConfig = new TranscodingTestConfig();
        testConfig.passThroughMode = true;
        testConfig.processingTotalTimeMs = 300; // minimum time spent on transcoding.

        TranscodingRequest request =
                new TranscodingRequest.Builder()
                        .setSourceUri(srcUri)
                        .setDestinationUri(dstUri)
                        .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
                        .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
                        .setVideoTrackFormat(createMediaFormat())
                        .setTestConfig(testConfig)
                        .build();
        Executor listenerExecutor = Executors.newSingleThreadExecutor();

        TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
                transcodingJob -> {
                    Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
                    assertTrue("Transcoding should failed.",
                            transcodingJob.getResult() == expectedResult);
                    transcodeCompleteSemaphore.release();
                });
        assertNotNull(job);

        if (job != null) {
            Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
            boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
                    TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
            assertTrue("Transcode failed to complete in time.", finishedOnTime);
        }

        if (expectedResult == TranscodingJob.RESULT_SUCCESS) {
            // Checks the destination file get generated.
            File file = new File(dstUri.getPath());
            assertTrue("Failed to create destination file", file.exists());

            // Removes the file.
            file.delete();
        }
    }

    // Tests transcoding from invalid a invalid  and expects failure.
    @Test
    public void testTranscodingInvalidSrcUri() throws Exception {
        Log.d(TAG, "Starting: testMediaTranscodeManager");

        Uri invalidSrcUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
                + mContext.getPackageName() + "/source.mp4");
        Log.d(TAG, "Transcoding from source: " + invalidSrcUri);

        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
        // The full path of this file is:
        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
                + mContext.getPackageName() + "/temp.mp4");
        Log.d(TAG, "Transcoding to destination: " + destinationUri);

        testTranscodingWithExpectResult(invalidSrcUri, destinationUri, TranscodingJob.RESULT_ERROR);
    }

    // Tests transcoding to a uri in res folder and expects failure as we could not write to res
    // folder.
    @Test
    public void testTranscodingToResFolder() throws Exception {
        Log.d(TAG, "Starting: testMediaTranscodeManager");

        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
        // The full path of this file is:
        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
                + mContext.getPackageName() + "/temp.mp4");
        Log.d(TAG, "Transcoding to destination: " + destinationUri);

        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
                TranscodingJob.RESULT_ERROR);
    }

    // Tests transcoding to a uri in internal storage folder and expects success.
    @Test
    public void testTranscodingToCacheDir() throws Exception {
        Log.d(TAG, "Starting: testMediaTranscodeManager");

        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
        // The full path of this file is:
        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
                + mContext.getCacheDir().getAbsolutePath() + "/temp.mp4");

        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
                TranscodingJob.RESULT_SUCCESS);
    }

    // Tests transcoding to a uri in internal files directory and expects success.
    @Test
    public void testTranscodingToInternalFilesDir() throws Exception {
        Log.d(TAG, "Starting: testMediaTranscodeManager");

        // Create a file Uri:
        // file:///storage/emulated/0/Android/data/com.android.mediatranscodingtest/files/temp.mp4
        Uri destinationUri = Uri.fromFile(new File(mContext.getFilesDir(), "temp.mp4"));
        Log.i(TAG, "Transcoding to files dir: " + destinationUri);

        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
                TranscodingJob.RESULT_SUCCESS);
    }

    // Tests transcoding to a uri in external files directory and expects success.
    @Test
    public void testTranscodingToExternalFilesDir() throws Exception {
        Log.d(TAG, "Starting: testMediaTranscodeManager");

        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/files/temp.mp4
        Uri destinationUri = Uri.fromFile(new File(mContext.getExternalFilesDir(null), "temp.mp4"));
        Log.i(TAG, "Transcoding to files dir: " + destinationUri);

        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
                TranscodingJob.RESULT_SUCCESS);
    }

    @Test
    public void testTranscodingFromHevcToAvc() throws Exception {
        Semaphore transcodeCompleteSemaphore = new Semaphore(0);
@@ -354,73 +578,5 @@ public class MediaTranscodeManagerTest
        return UiDevice.getInstance(
                InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
    }

    @Test
    public void testHandleTranscoderServiceDied() throws Exception {
        try {
            if (!checkIfRoot()) {
                throw new AssertionError("must be root to run this test; try adb root?");
            } else {
                Log.i(TAG, "Device is root");
            }
        } catch (IOException e) {
            throw new AssertionError(e);
        }

        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());
                    assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_ERROR);
                    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(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);
    }
}
+0 −503

File deleted.

Preview size limit exceeded, changes collapsed.

+1 −1

File changed.

Preview size limit exceeded, changes collapsed.