TranscodingAPI: Expose MediaTranscodeManager as System API.
Bug: 160260102
Test: Unit test
Change-Id: I38e9efc46e23fe1aafe65100bbc7f72eb200720f
diff --git a/Android.bp b/Android.bp
index e756b34..8475fbd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -333,6 +333,7 @@
":installd_aidl",
":keystore_aidl",
":libaudioclient_aidl",
+ ":mediatranscoding_aidl_interface-java-source",
":libbinder_aidl",
":libbluetooth-binder-aidl",
":libcamera_client_aidl",
@@ -576,7 +577,6 @@
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
// in favor of an API stubs dependency in java_library "framework" below.
"mimemap",
- "mediatranscoding_aidl_interface-java",
],
// For backwards compatibility.
stem: "framework",
@@ -623,7 +623,6 @@
static_libs: [
"exoplayer2-extractor",
"android.hardware.wifi-V1.0-java-constants",
- "mediatranscoding_aidl_interface-java",
// Additional dependencies needed to build the ike API classes.
"ike-internals",
diff --git a/api/system-current.txt b/api/system-current.txt
index 00a59bf..dbe2e31 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1741,6 +1741,7 @@
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
+ field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
field public static final String NETD_SERVICE = "netd";
field public static final String NETWORK_SCORE_SERVICE = "network_score";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
@@ -4333,6 +4334,60 @@
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
}
+ public final class MediaTranscodeManager implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+ method protected void finalize();
+ field public static final int PRIORITY_OFFLINE = 2; // 0x2
+ field public static final int PRIORITY_REALTIME = 1; // 0x1
+ field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
+ }
+
+ @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
+ method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
+ }
+
+ public static final class MediaTranscodeManager.TranscodingJob {
+ method public void cancel();
+ method public int getJobId();
+ method @IntRange(from=0, to=100) public int getProgress();
+ method public int getResult();
+ method public int getStatus();
+ method public boolean retry();
+ method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+ method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+ field public static final int RESULT_CANCELED = 4; // 0x4
+ field public static final int RESULT_ERROR = 3; // 0x3
+ field public static final int RESULT_NONE = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 2; // 0x2
+ field public static final int STATUS_FINISHED = 3; // 0x3
+ field public static final int STATUS_PAUSED = 4; // 0x4
+ field public static final int STATUS_PENDING = 1; // 0x1
+ field public static final int STATUS_RUNNING = 2; // 0x2
+ }
+
+ @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
+ method public void onProgressUpdate(@IntRange(from=0, to=100) int);
+ }
+
+ public static final class MediaTranscodeManager.TranscodingRequest {
+ method @NonNull public android.net.Uri getDestinationUri();
+ method public int getPriority();
+ method @NonNull public android.net.Uri getSourceUri();
+ method public int getType();
+ method @Nullable public android.media.MediaFormat getVideoTrackFormat();
+ }
+
+ public static final class MediaTranscodeManager.TranscodingRequest.Builder {
+ ctor public MediaTranscodeManager.TranscodingRequest.Builder();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);
diff --git a/api/test-current.txt b/api/test-current.txt
index 5741fe7..2dd7409 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1819,6 +1819,60 @@
method @NonNull public String getOriginalId();
}
+ public final class MediaTranscodeManager implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+ method protected void finalize();
+ field public static final int PRIORITY_OFFLINE = 2; // 0x2
+ field public static final int PRIORITY_REALTIME = 1; // 0x1
+ field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
+ }
+
+ @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
+ method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
+ }
+
+ public static final class MediaTranscodeManager.TranscodingJob {
+ method public void cancel();
+ method public int getJobId();
+ method @IntRange(from=0, to=100) public int getProgress();
+ method public int getResult();
+ method public int getStatus();
+ method public boolean retry();
+ method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+ method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+ field public static final int RESULT_CANCELED = 4; // 0x4
+ field public static final int RESULT_ERROR = 3; // 0x3
+ field public static final int RESULT_NONE = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 2; // 0x2
+ field public static final int STATUS_FINISHED = 3; // 0x3
+ field public static final int STATUS_PAUSED = 4; // 0x4
+ field public static final int STATUS_PENDING = 1; // 0x1
+ field public static final int STATUS_RUNNING = 2; // 0x2
+ }
+
+ @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
+ method public void onProgressUpdate(@IntRange(from=0, to=100) int);
+ }
+
+ public static final class MediaTranscodeManager.TranscodingRequest {
+ method @NonNull public android.net.Uri getDestinationUri();
+ method public int getPriority();
+ method @NonNull public android.net.Uri getSourceUri();
+ method public int getType();
+ method @Nullable public android.media.MediaFormat getVideoTrackFormat();
+ }
+
+ public static final class MediaTranscodeManager.TranscodingRequest.Builder {
+ ctor public MediaTranscodeManager.TranscodingRequest.Builder();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
+ }
+
public final class PlaybackParams implements android.os.Parcelable {
method public int getAudioStretchMode();
method public android.media.PlaybackParams setAudioStretchMode(int);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 59997cc..3b11b0d 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -103,6 +103,7 @@
import android.media.AudioManager;
import android.media.MediaFrameworkInitializer;
import android.media.MediaRouter;
+import android.media.MediaTranscodeManager;
import android.media.midi.IMidiManager;
import android.media.midi.MidiManager;
import android.media.projection.MediaProjectionManager;
@@ -305,6 +306,15 @@
return new AudioManager(ctx);
}});
+ registerService(Context.MEDIA_TRANSCODING_SERVICE, MediaTranscodeManager.class,
+ new CachedServiceFetcher<MediaTranscodeManager>() {
+ @Override
+ public MediaTranscodeManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new MediaTranscodeManager(ctx);
+ }
+ });
+
registerService(Context.MEDIA_ROUTER_SERVICE, MediaRouter.class,
new CachedServiceFetcher<MediaRouter>() {
@Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 52b0467..98f7887 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4195,6 +4195,17 @@
public static final String AUDIO_SERVICE = "audio";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
+ * android.media.MediaTranscodeManager} for transcoding media.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ * @see android.media.MediaTranscodeManager
+ */
+ @SystemApi
+ public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
+
+ /**
* AuthService orchestrates biometric and PIN/pattern/password authentication.
*
* BiometricService was split into two services, AuthService and BiometricService, where
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 4e2ae5c..cf61152 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -18,8 +18,11 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -97,6 +100,8 @@
TODO(hkuang): Clarify whether supports framerate conversion.
@hide
*/
+@TestApi
+@SystemApi
public final class MediaTranscodeManager implements AutoCloseable {
private static final String TAG = "MediaTranscodeManager";
@@ -127,20 +132,23 @@
public static final int TRANSCODING_TYPE_IMAGE = 2;
@Override
- public void close() throws Exception {
+ public void close() {
release();
}
/**
* Releases the MediaTranscodeManager.
*/
- //TODO(hkuang): add test for it.
- private void release() throws Exception {
+ private void release() {
synchronized (mLock) {
- if (mTranscodingClient != null) {
- mTranscodingClient.unregister();
- } else {
- throw new UnsupportedOperationException("Failed to release");
+ try {
+ if (mTranscodingClient != null) {
+ mTranscodingClient.unregister();
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to unregister the client");
+ } finally {
+ mTranscodingClient = null;
}
}
}
@@ -482,24 +490,22 @@
throw new UnsupportedOperationException("Failed to register new client");
}
- /* Private constructor. */
- private MediaTranscodeManager(@NonNull Context context,
- IMediaTranscodingService transcodingService) {
+ /**
+ * @hide
+ */
+ public MediaTranscodeManager(@NonNull Context context) {
mContext = context;
mContentResolver = mContext.getContentResolver();
mPackageName = mContext.getPackageName();
mPid = Os.getuid();
mUid = Os.getpid();
- mTranscodingClient = registerClient(transcodingService);
+ IMediaTranscodingService service = getService(false /*retry*/);
+ mTranscodingClient = registerClient(service);
}
@Override
protected void finalize() {
- try {
- release();
- } catch (Exception ex) {
- Log.e(TAG, "Failed to release");
- }
+ release();
}
public static final class TranscodingRequest {
@@ -555,25 +561,25 @@
/** Return the type of the transcoding. */
@TranscodingType
- int getType() {
+ public int getType() {
return mType;
}
/** Return source uri of the transcoding. */
@NonNull
- Uri getSourceUri() {
+ public Uri getSourceUri() {
return mSourceUri;
}
/** Return destination uri of the transcoding. */
@NonNull
- Uri getDestinationUri() {
+ public Uri getDestinationUri() {
return mDestinationUri;
}
/** Return priority of the transcoding. */
@TranscodingPriority
- int getPriority() {
+ public int getPriority() {
return mPriority;
}
@@ -581,10 +587,20 @@
* Return the video track format of the transcoding.
* This will be null is the transcoding is not for video transcoding.
*/
- MediaFormat getVideoTrackFormat() {
+ @Nullable
+ public MediaFormat getVideoTrackFormat() {
return mVideoTrackFormat;
}
+ /**
+ * Return TestConfig of the transcoding.
+ * @hide
+ */
+ @Nullable
+ public TranscodingTestConfig getTestConfig() {
+ return mTestConfig;
+ }
+
/* Writes the TranscodingRequest to a parcel. */
private TranscodingRequestParcel writeToParcel() {
TranscodingRequestParcel parcel = new TranscodingRequestParcel();
@@ -665,7 +681,7 @@
* Builder class for {@link TranscodingRequest} objects.
* Use this class to configure and create a <code>TranscodingRequest</code> instance.
*/
- public static class Builder {
+ public static final class Builder {
private @NonNull Uri mSourceUri;
private @NonNull Uri mDestinationUri;
private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
@@ -684,7 +700,7 @@
*/
// TODO(hkuang): Add documentation on how the app could generate the correct Uri.
@NonNull
- public Builder setSourceUri(@NonNull Uri sourceUri) throws IllegalArgumentException {
+ public Builder setSourceUri(@NonNull Uri sourceUri) {
if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) {
throw new IllegalArgumentException(
"You must specify a non-empty source Uri.");
@@ -701,8 +717,7 @@
* @throws IllegalArgumentException if Uri is null or empty.
*/
@NonNull
- public Builder setDestinationUri(@NonNull Uri destinationUri)
- throws IllegalArgumentException {
+ public Builder setDestinationUri(@NonNull Uri destinationUri) {
if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) {
throw new IllegalArgumentException(
"You must specify a non-empty destination Uri.");
@@ -719,8 +734,7 @@
* @throws IllegalArgumentException if flags is invalid.
*/
@NonNull
- public Builder setPriority(@TranscodingPriority int priority)
- throws IllegalArgumentException {
+ public Builder setPriority(@TranscodingPriority int priority) {
if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) {
throw new IllegalArgumentException("Invalid priority: " + priority);
}
@@ -738,8 +752,7 @@
* @throws IllegalArgumentException if flags is invalid.
*/
@NonNull
- public Builder setType(@TranscodingType int type)
- throws IllegalArgumentException {
+ public Builder setType(@TranscodingType int type) {
if (type != TRANSCODING_TYPE_VIDEO && type != TRANSCODING_TYPE_IMAGE) {
throw new IllegalArgumentException("Invalid transcoding type");
}
@@ -763,8 +776,7 @@
* @throws IllegalArgumentException if videoFormat is invalid.
*/
@NonNull
- public Builder setVideoTrackFormat(@NonNull MediaFormat videoFormat)
- throws IllegalArgumentException {
+ public Builder setVideoTrackFormat(@NonNull MediaFormat videoFormat) {
if (videoFormat == null) {
throw new IllegalArgumentException("videoFormat must not be null");
}
@@ -784,9 +796,11 @@
* Sets the delay in processing this request.
* @param config test config.
* @return The same builder instance.
+ * @hide
*/
@VisibleForTesting
- public Builder setTestConfig(TranscodingTestConfig config) {
+ @NonNull
+ public Builder setTestConfig(@NonNull TranscodingTestConfig config) {
mTestConfig = config;
return this;
}
@@ -799,7 +813,7 @@
* device.
*/
@NonNull
- public TranscodingRequest build() throws UnsupportedOperationException {
+ public TranscodingRequest build() {
if (mSourceUri == null) {
throw new UnsupportedOperationException("Source URI must not be null");
}
@@ -843,6 +857,7 @@
/** The job is paused. */
public static final int STATUS_PAUSED = 4;
+ /** @hide */
@IntDef(prefix = { "STATUS_" }, value = {
STATUS_PENDING,
STATUS_RUNNING,
@@ -861,6 +876,7 @@
/** The job was canceled by the caller. */
public static final int RESULT_CANCELED = 4;
+ /** @hide */
@IntDef(prefix = { "RESULT_" }, value = {
RESULT_NONE,
RESULT_SUCCESS,
@@ -878,24 +894,26 @@
* where 0 means that the job has not yet started and 100 means that it has finished.
* @param progress The new progress ranging from 0 ~ 100 inclusive.
*/
- void onProgressUpdate(int progress);
+ void onProgressUpdate(@IntRange(from = 0, to = 100) int progress);
}
private final ITranscodingClient mJobOwner;
private final Executor mListenerExecutor;
private final OnTranscodingFinishedListener mListener;
private int mJobId = -1;
- @GuardedBy("this")
+ // Lock for internal state.
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private Executor mProgressUpdateExecutor = null;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private OnProgressUpdateListener mProgressUpdateListener = null;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private int mProgress = 0;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private int mProgressUpdateInterval = 0;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private @Status int mStatus = STATUS_PENDING;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private @Result int mResult = RESULT_NONE;
private TranscodingJob(
@@ -932,20 +950,24 @@
* @param executor The executor on which listener will be invoked.
* @param listener The progress listener.
*/
- public synchronized void setOnProgressUpdateListener(
+ public void setOnProgressUpdateListener(
int minProgressUpdateInterval,
@NonNull @CallbackExecutor Executor executor,
@Nullable OnProgressUpdateListener listener) {
- Objects.requireNonNull(executor, "listenerExecutor must not be null");
- Objects.requireNonNull(listener, "listener must not be null");
- mProgressUpdateExecutor = executor;
- mProgressUpdateListener = listener;
+ synchronized (mLock) {
+ Objects.requireNonNull(executor, "listenerExecutor must not be null");
+ Objects.requireNonNull(listener, "listener must not be null");
+ mProgressUpdateExecutor = executor;
+ mProgressUpdateListener = listener;
+ }
}
- private synchronized void updateStatusAndResult(@Status int jobStatus,
+ private void updateStatusAndResult(@Status int jobStatus,
@Result int jobResult) {
- mStatus = jobStatus;
- mResult = jobResult;
+ synchronized (mLock) {
+ mStatus = jobStatus;
+ mResult = jobResult;
+ }
}
/**
@@ -953,8 +975,10 @@
*
* @return true if successfully resubmit the job to the service. False otherwise.
*/
- public synchronized boolean retry() {
- // TODO(hkuang): Implement this.
+ public boolean retry() {
+ synchronized (mLock) {
+ // TODO(hkuang): Implement this.
+ }
return true;
}
@@ -963,38 +987,46 @@
* 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 synchronized void cancel() {
- // Check if the job is finished already.
- if (mStatus != STATUS_FINISHED) {
- try {
- mJobOwner.cancelJob(mJobId);
- } catch (RemoteException re) {
- //TODO(hkuang): Find out what to do if failing to cancel the job.
- Log.e(TAG, "Failed to cancel the job due to exception: " + re);
- }
- mStatus = STATUS_FINISHED;
- mResult = RESULT_CANCELED;
+ public void cancel() {
+ synchronized (mLock) {
+ // Check if the job is finished already.
+ if (mStatus != STATUS_FINISHED) {
+ try {
+ mJobOwner.cancelJob(mJobId);
+ } catch (RemoteException re) {
+ //TODO(hkuang): Find out what to do if failing to cancel the job.
+ Log.e(TAG, "Failed to cancel the job due to exception: " + re);
+ }
+ mStatus = STATUS_FINISHED;
+ mResult = RESULT_CANCELED;
- // Notifies client the job is canceled.
- mListenerExecutor.execute(() -> mListener.onTranscodingFinished(this));
+ // Notifies client the job is canceled.
+ mListenerExecutor.execute(() -> mListener.onTranscodingFinished(this));
+ }
}
}
/**
- * 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.
+ * Gets the progress of the transcoding job. The progress is between 0 and 100, where 0
+ * means that the job has not yet started and 100 means that it is finished. For the
+ * cancelled job, the progress will be the last updated progress before it is cancelled.
* @return The progress.
*/
- public synchronized int getProgress() {
- return mProgress;
+ @IntRange(from = 0, to = 100)
+ public int getProgress() {
+ synchronized (mLock) {
+ return mProgress;
+ }
}
/**
* Gets the status of the transcoding job.
* @return The status.
*/
- public synchronized @Status int getStatus() {
- return mStatus;
+ public @Status int getStatus() {
+ synchronized (mLock) {
+ return mStatus;
+ }
}
/**
@@ -1009,53 +1041,22 @@
* Gets the result of the transcoding job.
* @return The result.
*/
- public synchronized @Result int getResult() {
- return mResult;
- }
-
- private synchronized void updateProgress(int newProgress) {
- mProgress = newProgress;
- }
-
- private synchronized void updateStatus(int newStatus) {
- mStatus = newStatus;
- }
- }
-
- /**
- * Gets the MediaTranscodeManager singleton instance.
- *
- * @param context The application context.
- * @return the {@link MediaTranscodeManager} singleton instance.
- * @throws UnsupportedOperationException if failing to acquire the MediaTranscodeManager.
- */
- public static MediaTranscodeManager getInstance(@NonNull Context context) {
- // Acquires the MediaTranscoding service.
- IMediaTranscodingService service = getService(false /*retry*/);
- return getInstance(context, service);
- }
-
- /** Similar as above, but wait till the service is ready. */
- @VisibleForTesting
- public static MediaTranscodeManager getInstance(@NonNull Context context, boolean retry) {
- // Acquires the MediaTranscoding service.
- IMediaTranscodingService service = getService(retry);
- return getInstance(context, service);
- }
-
- /** Similar as above, but allow injecting transcodingService for testing. */
- @VisibleForTesting
- public static MediaTranscodeManager getInstance(@NonNull Context context,
- IMediaTranscodingService transcodingService) {
- Objects.requireNonNull(context, "context must not be null");
-
- synchronized (MediaTranscodeManager.class) {
- if (sMediaTranscodeManager == null) {
- sMediaTranscodeManager = new MediaTranscodeManager(context.getApplicationContext(),
- transcodingService);
+ public @Result int getResult() {
+ synchronized (mLock) {
+ return mResult;
}
+ }
- return sMediaTranscodeManager;
+ private void updateProgress(int newProgress) {
+ synchronized (mLock) {
+ mProgress = newProgress;
+ }
+ }
+
+ private void updateStatus(int newStatus) {
+ synchronized (mLock) {
+ mStatus = newStatus;
+ }
}
}
@@ -1077,7 +1078,7 @@
@NonNull TranscodingRequest transcodingRequest,
@NonNull @CallbackExecutor Executor listenerExecutor,
@NonNull OnTranscodingFinishedListener listener)
- throws UnsupportedOperationException, FileNotFoundException {
+ throws FileNotFoundException {
Log.i(TAG, "enqueueRequest called.");
Objects.requireNonNull(transcodingRequest, "transcodingRequest must not be null");
Objects.requireNonNull(listenerExecutor, "listenerExecutor must not be null");
@@ -1092,8 +1093,14 @@
// Synchronizes the access to mPendingTranscodingJobs to make sure the job Id is
// inserted in the mPendingTranscodingJobs in the callback handler.
synchronized (mPendingTranscodingJobs) {
- if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
- throw new UnsupportedOperationException("Failed to enqueue request");
+ synchronized (mLock) {
+ if (mTranscodingClient == null) {
+ // TODO(hkuang): Handle the case if client is temporarily unavailable.
+ }
+
+ if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
+ throw new UnsupportedOperationException("Failed to enqueue request");
+ }
}
// Wraps the TranscodingJobParcel into a TranscodingJob and returns it to client for
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 009a41e..a707c7a 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -69,6 +69,13 @@
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 = 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;
@@ -129,13 +136,31 @@
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 = MediaTranscodeManager.getInstance(mContext, true /*retry*/);
+ mMediaTranscodeManager = getManager();
assertNotNull(mMediaTranscodeManager);
androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java
index 748c21a..167e474 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java
@@ -259,7 +259,7 @@
super.setUp();
mTranscodingService = new MockTranscodingService();
mContext = getInstrumentation().getContext();
- mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext, mTranscodingService);
+ mMediaTranscodeManager = mContext.getSystemService(MediaTranscodeManager.class);
assertNotNull(mMediaTranscodeManager);
// Setup source HEVC file uri.
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
index 5c87d30..04909ef 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -89,7 +89,7 @@
Log.d(TAG, "setUp");
super.setUp();
mContext = getInstrumentation().getContext();
- mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
+ mMediaTranscodeManager = mContext.getSystemService(MediaTranscodeManager.class);
}
@Override
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 7cce0f2..f64be2b 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -1681,6 +1681,7 @@
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
+ field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
field public static final String NETD_SERVICE = "netd";
field public static final String NETWORK_SCORE_SERVICE = "network_score";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
@@ -4273,6 +4274,60 @@
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
}
+ public final class MediaTranscodeManager implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+ method protected void finalize();
+ field public static final int PRIORITY_OFFLINE = 2; // 0x2
+ field public static final int PRIORITY_REALTIME = 1; // 0x1
+ field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
+ }
+
+ @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
+ method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
+ }
+
+ public static final class MediaTranscodeManager.TranscodingJob {
+ method public void cancel();
+ method public int getJobId();
+ method @IntRange(from=0, to=100) public int getProgress();
+ method public int getResult();
+ method public int getStatus();
+ method public boolean retry();
+ method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+ method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+ field public static final int RESULT_CANCELED = 4; // 0x4
+ field public static final int RESULT_ERROR = 3; // 0x3
+ field public static final int RESULT_NONE = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 2; // 0x2
+ field public static final int STATUS_FINISHED = 3; // 0x3
+ field public static final int STATUS_PAUSED = 4; // 0x4
+ field public static final int STATUS_PENDING = 1; // 0x1
+ field public static final int STATUS_RUNNING = 2; // 0x2
+ }
+
+ @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
+ method public void onProgressUpdate(@IntRange(from=0, to=100) int);
+ }
+
+ public static final class MediaTranscodeManager.TranscodingRequest {
+ method @NonNull public android.net.Uri getDestinationUri();
+ method public int getPriority();
+ method @NonNull public android.net.Uri getSourceUri();
+ method public int getType();
+ method @Nullable public android.media.MediaFormat getVideoTrackFormat();
+ }
+
+ public static final class MediaTranscodeManager.TranscodingRequest.Builder {
+ ctor public MediaTranscodeManager.TranscodingRequest.Builder();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);