TranscodingAPI: Expose MediaTranscodeManager as System API.

Bug: 160260102
Test: Unit test
Change-Id: I38e9efc46e23fe1aafe65100bbc7f72eb200720f
This commit is contained in:
hkuang
2020-08-24 11:38:31 -07:00
committed by Hangyu Kuang
parent a6ae5034cf
commit 3eb57dc580
10 changed files with 338 additions and 122 deletions

View File

@@ -333,6 +333,7 @@ filegroup {
":installd_aidl",
":keystore_aidl",
":libaudioclient_aidl",
":mediatranscoding_aidl_interface-java-source",
":libbinder_aidl",
":libbluetooth-binder-aidl",
":libcamera_client_aidl",
@@ -576,7 +577,6 @@ java_library {
// 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 @@ java_library {
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",

View File

@@ -1741,6 +1741,7 @@ package android.content {
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 @@ package android.media {
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);

View File

@@ -1819,6 +1819,60 @@ package android.media {
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);

View File

@@ -103,6 +103,7 @@ import android.location.LocationManager;
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 @@ public final class SystemServiceRegistry {
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

View File

@@ -4194,6 +4194,17 @@ public abstract class Context {
*/
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.
*

View File

@@ -18,8 +18,11 @@ package android.media;
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 @@ import java.util.concurrent.Executors;
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 final class MediaTranscodeManager implements AutoCloseable {
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 @@ public final class MediaTranscodeManager implements AutoCloseable {
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 @@ public final class MediaTranscodeManager implements AutoCloseable {
/** 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
*/
// 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* @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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* @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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* @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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* @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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
/** The job is paused. */
public static final int STATUS_PAUSED = 4;
/** @hide */
@IntDef(prefix = { "STATUS_" }, value = {
STATUS_PENDING,
STATUS_RUNNING,
@@ -861,6 +876,7 @@ public final class MediaTranscodeManager implements AutoCloseable {
/** 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* @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 @@ public final class MediaTranscodeManager implements AutoCloseable {
*
* @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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
* 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 @@ public final class MediaTranscodeManager implements AutoCloseable {
@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 @@ public final class MediaTranscodeManager implements AutoCloseable {
// 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

View File

@@ -69,6 +69,13 @@ 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 = 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 @@ public class MediaTranscodeManagerTest
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());

View File

@@ -259,7 +259,7 @@ public class MediaTranscodeManagerWithMockServiceTest
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.

View File

@@ -89,7 +89,7 @@ public class MediaTranscodingBenchmark
Log.d(TAG, "setUp");
super.setUp();
mContext = getInstrumentation().getContext();
mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
mMediaTranscodeManager = mContext.getSystemService(MediaTranscodeManager.class);
}
@Override

View File

@@ -1681,6 +1681,7 @@ package android.content {
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 @@ package android.media {
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);