Skeletal implementation of a MediaTranscodeManager API with corresponding JNI layer.
This change is the first iteration of a new high-level media transcode API. At this point the API is not hooked up to the underlying native media transcoding service. More transcoding options will be added in following CLs. Test: Ran MediaTranscodeManagerTest, verified call sequence using logcat. Flashed build on blueline and verified basic media functionality. Change-Id: I190e41b468265bd339d0fbca243db2a196e5a04e
This commit is contained in:
403
media/java/android/media/MediaTranscodeManager.java
Normal file
403
media/java/android/media/MediaTranscodeManager.java
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 android.media;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* MediaTranscodeManager provides an interface to the system's media transcode service.
|
||||
* Transcode requests are put in a queue and processed in order. When a transcode operation is
|
||||
* completed the caller is notified via its OnTranscodingFinishedListener. In the meantime the
|
||||
* caller may use the returned TranscodingJob object to cancel or check the status of a specific
|
||||
* transcode operation.
|
||||
* The currently supported media types are video and still images.
|
||||
*
|
||||
* TODO(lnilsson): Add sample code when API is settled.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class MediaTranscodeManager {
|
||||
private static final String TAG = "MediaTranscodeManager";
|
||||
|
||||
// Invalid ID passed from native means the request was never enqueued.
|
||||
private static final long ID_INVALID = -1;
|
||||
|
||||
// Events passed from native.
|
||||
private static final int EVENT_JOB_STARTED = 1;
|
||||
private static final int EVENT_JOB_PROGRESSED = 2;
|
||||
private static final int EVENT_JOB_FINISHED = 3;
|
||||
|
||||
@IntDef(prefix = { "EVENT_" }, value = {
|
||||
EVENT_JOB_STARTED,
|
||||
EVENT_JOB_PROGRESSED,
|
||||
EVENT_JOB_FINISHED,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Event {}
|
||||
|
||||
private static MediaTranscodeManager sMediaTranscodeManager;
|
||||
private final ConcurrentMap<Long, TranscodingJob> mPendingTranscodingJobs =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Context mContext;
|
||||
|
||||
/**
|
||||
* Listener that gets notified when a transcoding operation has finished.
|
||||
* This listener gets notified regardless of how the operation finished. It is up to the
|
||||
* listener implementation to check the result and take appropriate action.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface OnTranscodingFinishedListener {
|
||||
/**
|
||||
* Called when the transcoding operation has finished. The receiver may use the
|
||||
* TranscodingJob to check the result, i.e. whether the operation succeeded, was canceled or
|
||||
* if an error occurred.
|
||||
* @param transcodingJob The TranscodingJob instance for the finished transcoding operation.
|
||||
*/
|
||||
void onTranscodingFinished(@NonNull TranscodingJob transcodingJob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class describing a transcode operation to be performed. The caller uses this class to
|
||||
* configure a transcoding operation that can then be enqueued using MediaTranscodeManager.
|
||||
*/
|
||||
public static final class TranscodingRequest {
|
||||
private Uri mSrcUri;
|
||||
private Uri mDstUri;
|
||||
private MediaFormat mDstFormat;
|
||||
|
||||
private TranscodingRequest(Builder b) {
|
||||
mSrcUri = b.mSrcUri;
|
||||
mDstUri = b.mDstUri;
|
||||
mDstFormat = b.mDstFormat;
|
||||
}
|
||||
|
||||
/** TranscodingRequest builder class. */
|
||||
public static class Builder {
|
||||
private Uri mSrcUri;
|
||||
private Uri mDstUri;
|
||||
private MediaFormat mDstFormat;
|
||||
|
||||
/**
|
||||
* Specifies the source media file.
|
||||
* @param uri Content uri for the source media file.
|
||||
* @return The builder instance.
|
||||
*/
|
||||
public Builder setSourceUri(Uri uri) {
|
||||
mSrcUri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the destination media file.
|
||||
* @param uri Content uri for the destination media file.
|
||||
* @return The builder instance.
|
||||
*/
|
||||
public Builder setDestinationUri(Uri uri) {
|
||||
mDstUri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the media format of the transcoded media file.
|
||||
* @param dstFormat MediaFormat containing the desired destination format.
|
||||
* @return The builder instance.
|
||||
*/
|
||||
public Builder setDestinationFormat(MediaFormat dstFormat) {
|
||||
mDstFormat = dstFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new TranscodingRequest with the configuration set on this builder.
|
||||
* @return A new TranscodingRequest.
|
||||
*/
|
||||
public TranscodingRequest build() {
|
||||
return new TranscodingRequest(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle to an enqueued transcoding operation. An instance of this class represents a single
|
||||
* enqueued transcoding operation. The caller can use that instance to query the status or
|
||||
* progress, and to get the result once the operation has completed.
|
||||
*/
|
||||
public static final class TranscodingJob {
|
||||
/** The job is enqueued but not yet running. */
|
||||
public static final int STATUS_PENDING = 1;
|
||||
/** The job is currently running. */
|
||||
public static final int STATUS_RUNNING = 2;
|
||||
/** The job is finished. */
|
||||
public static final int STATUS_FINISHED = 3;
|
||||
|
||||
@IntDef(prefix = { "STATUS_" }, value = {
|
||||
STATUS_PENDING,
|
||||
STATUS_RUNNING,
|
||||
STATUS_FINISHED,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Status {}
|
||||
|
||||
/** The job does not have a result yet. */
|
||||
public static final int RESULT_NONE = 1;
|
||||
/** The job completed successfully. */
|
||||
public static final int RESULT_SUCCESS = 2;
|
||||
/** The job encountered an error while running. */
|
||||
public static final int RESULT_ERROR = 3;
|
||||
/** The job was canceled by the caller. */
|
||||
public static final int RESULT_CANCELED = 4;
|
||||
|
||||
@IntDef(prefix = { "RESULT_" }, value = {
|
||||
RESULT_NONE,
|
||||
RESULT_SUCCESS,
|
||||
RESULT_ERROR,
|
||||
RESULT_CANCELED,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Result {}
|
||||
|
||||
/** Listener that gets notified when the progress changes. */
|
||||
@FunctionalInterface
|
||||
public interface OnProgressChangedListener {
|
||||
|
||||
/**
|
||||
* Called when the progress changes. The progress is between 0 and 1, where 0 means
|
||||
* that the job has not yet started and 1 means that it has finished.
|
||||
* @param progress The new progress.
|
||||
*/
|
||||
void onProgressChanged(float progress);
|
||||
}
|
||||
|
||||
private final Executor mExecutor;
|
||||
private final OnTranscodingFinishedListener mListener;
|
||||
private final ReentrantLock mStatusChangeLock = new ReentrantLock();
|
||||
private Executor mProgressChangedExecutor;
|
||||
private OnProgressChangedListener mProgressChangedListener;
|
||||
private long mID;
|
||||
private float mProgress = 0.0f;
|
||||
private @Status int mStatus = STATUS_PENDING;
|
||||
private @Result int mResult = RESULT_NONE;
|
||||
|
||||
private TranscodingJob(long id, @NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OnTranscodingFinishedListener listener) {
|
||||
mID = id;
|
||||
mExecutor = executor;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a progress listener.
|
||||
* @param listener The progress listener.
|
||||
*/
|
||||
public void setOnProgressChangedListener(@NonNull @CallbackExecutor Executor executor,
|
||||
@Nullable OnProgressChangedListener listener) {
|
||||
mProgressChangedExecutor = executor;
|
||||
mProgressChangedListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the transcoding job and notify the listener. If the job happened to finish before
|
||||
* being canceled this call is effectively a no-op and will not update the result in that
|
||||
* case.
|
||||
*/
|
||||
public void cancel() {
|
||||
setJobFinished(RESULT_CANCELED);
|
||||
sMediaTranscodeManager.native_cancelTranscodingRequest(mID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the progress of the transcoding job. The progress is between 0 and 1, where 0 means
|
||||
* that the job has not yet started and 1 means that it is finished.
|
||||
* @return The progress.
|
||||
*/
|
||||
public float getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of the transcoding job.
|
||||
* @return The status.
|
||||
*/
|
||||
public @Status int getStatus() {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result of the transcoding job.
|
||||
* @return The result.
|
||||
*/
|
||||
public @Result int getResult() {
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private void setJobStarted() {
|
||||
mStatus = STATUS_RUNNING;
|
||||
}
|
||||
|
||||
private void setJobProgress(float newProgress) {
|
||||
mProgress = newProgress;
|
||||
|
||||
// Notify listener.
|
||||
OnProgressChangedListener onProgressChangedListener = mProgressChangedListener;
|
||||
if (onProgressChangedListener != null) {
|
||||
mProgressChangedExecutor.execute(
|
||||
() -> onProgressChangedListener.onProgressChanged(mProgress));
|
||||
}
|
||||
}
|
||||
|
||||
private void setJobFinished(int result) {
|
||||
boolean doNotifyListener = false;
|
||||
|
||||
// Prevent conflicting simultaneous status updates from native (finished) and from the
|
||||
// caller (cancel).
|
||||
try {
|
||||
mStatusChangeLock.lock();
|
||||
if (mStatus != STATUS_FINISHED) {
|
||||
mStatus = STATUS_FINISHED;
|
||||
mResult = result;
|
||||
doNotifyListener = true;
|
||||
}
|
||||
} finally {
|
||||
mStatusChangeLock.unlock();
|
||||
}
|
||||
|
||||
if (doNotifyListener) {
|
||||
mExecutor.execute(() -> mListener.onTranscodingFinished(this));
|
||||
}
|
||||
}
|
||||
|
||||
private void processJobEvent(@Event int event, int arg) {
|
||||
switch (event) {
|
||||
case EVENT_JOB_STARTED:
|
||||
setJobStarted();
|
||||
break;
|
||||
case EVENT_JOB_PROGRESSED:
|
||||
setJobProgress((float) arg / 100);
|
||||
break;
|
||||
case EVENT_JOB_FINISHED:
|
||||
setJobFinished(arg);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unsupported event: " + event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the native library.
|
||||
private static native void native_init();
|
||||
// Requests a new job ID from the native service.
|
||||
private native long native_requestUniqueJobID();
|
||||
// Enqueues a transcoding request to the native service.
|
||||
private native boolean native_enqueueTranscodingRequest(
|
||||
long id, @NonNull TranscodingRequest transcodingRequest, @NonNull Context context);
|
||||
// Cancels an enqueued transcoding request.
|
||||
private native void native_cancelTranscodingRequest(long id);
|
||||
|
||||
// Private constructor.
|
||||
private MediaTranscodeManager(@NonNull Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
// Events posted from the native service.
|
||||
@SuppressWarnings("unused")
|
||||
private void postEventFromNative(@Event int event, long id, int arg) {
|
||||
Log.d(TAG, String.format("postEventFromNative. Event %d, ID %d, arg %d", event, id, arg));
|
||||
|
||||
TranscodingJob transcodingJob = mPendingTranscodingJobs.get(id);
|
||||
|
||||
// Job IDs are added to the tracking set before the job is enqueued so it should never
|
||||
// be null unless the service misbehaves.
|
||||
if (transcodingJob == null) {
|
||||
Log.e(TAG, "No matching transcode job found for id " + id);
|
||||
return;
|
||||
}
|
||||
|
||||
transcodingJob.processJobEvent(event, arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MediaTranscodeManager singleton instance.
|
||||
* @param context The application context.
|
||||
* @return the {@link MediaTranscodeManager} singleton instance.
|
||||
*/
|
||||
public static MediaTranscodeManager getInstance(@NonNull Context context) {
|
||||
Preconditions.checkNotNull(context);
|
||||
synchronized (MediaTranscodeManager.class) {
|
||||
if (sMediaTranscodeManager == null) {
|
||||
sMediaTranscodeManager = new MediaTranscodeManager(context.getApplicationContext());
|
||||
}
|
||||
return sMediaTranscodeManager;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a TranscodingRequest for execution.
|
||||
* @param transcodingRequest The TranscodingRequest to enqueue.
|
||||
* @param listenerExecutor Executor on which the listener is notified.
|
||||
* @param listener Listener to get notified when the transcoding job is finished.
|
||||
* @return A TranscodingJob for this operation.
|
||||
*/
|
||||
public @Nullable TranscodingJob enqueueTranscodingRequest(
|
||||
@NonNull TranscodingRequest transcodingRequest,
|
||||
@NonNull @CallbackExecutor Executor listenerExecutor,
|
||||
@NonNull OnTranscodingFinishedListener listener) {
|
||||
Log.i(TAG, "enqueueTranscodingRequest called.");
|
||||
Preconditions.checkNotNull(transcodingRequest);
|
||||
Preconditions.checkNotNull(listenerExecutor);
|
||||
Preconditions.checkNotNull(listener);
|
||||
|
||||
// Reserve a job ID.
|
||||
long jobID = native_requestUniqueJobID();
|
||||
if (jobID == ID_INVALID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add the job to the tracking set.
|
||||
TranscodingJob transcodingJob = new TranscodingJob(jobID, listenerExecutor, listener);
|
||||
mPendingTranscodingJobs.put(jobID, transcodingJob);
|
||||
|
||||
// Enqueue the request with the native service.
|
||||
boolean enqueued = native_enqueueTranscodingRequest(jobID, transcodingRequest, mContext);
|
||||
if (!enqueued) {
|
||||
mPendingTranscodingJobs.remove(jobID);
|
||||
return null;
|
||||
}
|
||||
|
||||
return transcodingJob;
|
||||
}
|
||||
|
||||
static {
|
||||
System.loadLibrary("media_jni");
|
||||
native_init();
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ cc_library_shared {
|
||||
"android_media_MediaProfiles.cpp",
|
||||
"android_media_MediaRecorder.cpp",
|
||||
"android_media_MediaSync.cpp",
|
||||
"android_media_MediaTranscodeManager.cpp",
|
||||
"android_media_ResampleInputStream.cpp",
|
||||
"android_media_Streams.cpp",
|
||||
"android_media_SyncParams.cpp",
|
||||
|
||||
@@ -1453,6 +1453,7 @@ extern int register_android_media_MediaProfiles(JNIEnv *env);
|
||||
extern int register_android_mtp_MtpDatabase(JNIEnv *env);
|
||||
extern int register_android_mtp_MtpDevice(JNIEnv *env);
|
||||
extern int register_android_mtp_MtpServer(JNIEnv *env);
|
||||
extern int register_android_media_MediaTranscodeManager(JNIEnv *env);
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
|
||||
{
|
||||
@@ -1565,6 +1566,11 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (register_android_media_MediaTranscodeManager(env) < 0) {
|
||||
ALOGE("ERROR: MediaTranscodeManager native registration failed");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* success -- return valid version number */
|
||||
result = JNI_VERSION_1_4;
|
||||
|
||||
|
||||
102
media/jni/android_media_MediaTranscodeManager.cpp
Normal file
102
media/jni/android_media_MediaTranscodeManager.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2019, 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.
|
||||
*/
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "MediaTranscodeManager_JNI"
|
||||
|
||||
#include "android_runtime/AndroidRuntime.h"
|
||||
#include "jni.h"
|
||||
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace {
|
||||
|
||||
// NOTE: Keep these enums in sync with their equivalents in MediaTranscodeManager.java.
|
||||
enum {
|
||||
ID_INVALID = -1
|
||||
};
|
||||
|
||||
enum {
|
||||
EVENT_JOB_STARTED = 1,
|
||||
EVENT_JOB_PROGRESSED = 2,
|
||||
EVENT_JOB_FINISHED = 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
RESULT_NONE = 1,
|
||||
RESULT_SUCCESS = 2,
|
||||
RESULT_ERROR = 3,
|
||||
RESULT_CANCELED = 4,
|
||||
};
|
||||
|
||||
struct {
|
||||
jmethodID postEventFromNative;
|
||||
} gMediaTranscodeManagerClassInfo;
|
||||
|
||||
using namespace android;
|
||||
|
||||
void android_media_MediaTranscodeManager_native_init(JNIEnv *env, jclass clazz) {
|
||||
ALOGV("android_media_MediaTranscodeManager_native_init");
|
||||
|
||||
gMediaTranscodeManagerClassInfo.postEventFromNative = env->GetMethodID(
|
||||
clazz, "postEventFromNative", "(IJI)V");
|
||||
LOG_ALWAYS_FATAL_IF(gMediaTranscodeManagerClassInfo.postEventFromNative == NULL,
|
||||
"can't find android/media/MediaTranscodeManager.postEventFromNative");
|
||||
}
|
||||
|
||||
jlong android_media_MediaTranscodeManager_requestUniqueJobID(
|
||||
JNIEnv *env __unused, jobject thiz __unused) {
|
||||
ALOGV("android_media_MediaTranscodeManager_reserveUniqueJobID");
|
||||
static std::atomic_int32_t sJobIDCounter{0};
|
||||
jlong id = (jlong)++sJobIDCounter;
|
||||
return id;
|
||||
}
|
||||
|
||||
jboolean android_media_MediaTranscodeManager_enqueueTranscodingRequest(
|
||||
JNIEnv *env, jobject thiz, jlong id, jobject request, jobject context __unused) {
|
||||
ALOGV("android_media_MediaTranscodeManager_enqueueTranscodingRequest");
|
||||
if (!request) {
|
||||
return ID_INVALID;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(thiz, gMediaTranscodeManagerClassInfo.postEventFromNative,
|
||||
EVENT_JOB_FINISHED, id, RESULT_ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
void android_media_MediaTranscodeManager_cancelTranscodingRequest(
|
||||
JNIEnv *env __unused, jobject thiz __unused, jlong jobID __unused) {
|
||||
ALOGV("android_media_MediaTranscodeManager_cancelTranscodingRequest");
|
||||
}
|
||||
|
||||
const JNINativeMethod gMethods[] = {
|
||||
{ "native_init", "()V",
|
||||
(void *)android_media_MediaTranscodeManager_native_init },
|
||||
{ "native_requestUniqueJobID", "()J",
|
||||
(void *)android_media_MediaTranscodeManager_requestUniqueJobID },
|
||||
{ "native_enqueueTranscodingRequest",
|
||||
"(JLandroid/media/MediaTranscodeManager$TranscodingRequest;Landroid/content/Context;)Z",
|
||||
(void *)android_media_MediaTranscodeManager_enqueueTranscodingRequest },
|
||||
{ "native_cancelTranscodingRequest", "(J)V",
|
||||
(void *)android_media_MediaTranscodeManager_cancelTranscodingRequest },
|
||||
};
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
int register_android_media_MediaTranscodeManager(JNIEnv *env) {
|
||||
return AndroidRuntime::registerNativeMethods(env,
|
||||
"android/media/MediaTranscodeManager", gMethods, NELEM(gMethods));
|
||||
}
|
||||
@@ -7,6 +7,7 @@ android_test {
|
||||
],
|
||||
static_libs: [
|
||||
"mockito-target-minus-junit4",
|
||||
"androidx.test.ext.junit",
|
||||
"androidx.test.rules",
|
||||
"android-ex-camera2",
|
||||
],
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.mediaframeworktest.functional.mediatranscodemanager;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.media.MediaTranscodeManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MediaTranscodeManagerTest {
|
||||
private static final String TAG = "MediaTranscodeManagerTest";
|
||||
|
||||
/** The time to wait for the transcode operation to complete before failing the test. */
|
||||
private static final int TRANSCODE_TIMEOUT_SECONDS = 2;
|
||||
|
||||
@Test
|
||||
public void testMediaTranscodeManager() throws InterruptedException {
|
||||
Log.d(TAG, "Starting: testMediaTranscodeManager");
|
||||
|
||||
Semaphore transcodeCompleteSemaphore = new Semaphore(0);
|
||||
MediaTranscodeManager.TranscodingRequest request =
|
||||
new MediaTranscodeManager.TranscodingRequest.Builder().build();
|
||||
Executor listenerExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
MediaTranscodeManager mediaTranscodeManager =
|
||||
MediaTranscodeManager.getInstance(ApplicationProvider.getApplicationContext());
|
||||
assertNotNull(mediaTranscodeManager);
|
||||
|
||||
MediaTranscodeManager.TranscodingJob job;
|
||||
job = mediaTranscodeManager.enqueueTranscodingRequest(request, listenerExecutor,
|
||||
transcodingJob -> {
|
||||
Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
|
||||
transcodeCompleteSemaphore.release();
|
||||
});
|
||||
assertNotNull(job);
|
||||
|
||||
job.setOnProgressChangedListener(
|
||||
listenerExecutor, progress -> Log.d(TAG, "Progress: " + progress));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user