diff --git a/api/system-current.txt b/api/system-current.txt index 9d0f6785fca35..3ec3467137928 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4394,6 +4394,14 @@ package android.media { method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat); } + public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver { + ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver(); + method @Nullable public android.media.MediaFormat resolveVideoFormat(); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat); + method public boolean shouldTranscode(); + field public static final String CAPS_SUPPORTS_HEVC = "support-hevc"; + } + 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 1aa3db6963b78..3de1d93785c19 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1870,6 +1870,14 @@ package android.media { method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat); } + public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver { + ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver(); + method @Nullable public android.media.MediaFormat resolveVideoFormat(); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat); + method public boolean shouldTranscode(); + field public static final String CAPS_SUPPORTS_HEVC = "support-hevc"; + } + public final class PlaybackParams implements android.os.Parcelable { method public int getAudioStretchMode(); method public android.media.PlaybackParams setAudioStretchMode(int); diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java index 1c5288b046853..451677f6a8bcb 100644 --- a/media/java/android/media/MediaTranscodeManager.java +++ b/media/java/android/media/MediaTranscodeManager.java @@ -27,6 +27,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.net.Uri; +import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; @@ -814,6 +815,141 @@ public final class MediaTranscodeManager { return new TranscodingRequest(this); } } + + /** + * Helper class for deciding if transcoding is needed, and if so, the track + * formats to use. + */ + public static class MediaFormatResolver { + private static final int BIT_RATE = 20000000; // 20Mbps + + private MediaFormat mSrcVideoFormatHint; + private MediaFormat mSrcAudioFormatHint; + private Bundle mClientCaps; + + /** + * A key describing whether the client supports HEVC-encoded video. + * + * The value associated with this key is a boolean. If unspecified, it's up to + * the MediaFormatResolver to determine the default. + * + * @see #setClientCapabilities(Bundle) + */ + public static final String CAPS_SUPPORTS_HEVC = "support-hevc"; + + /** + * Sets the abilities of the client consuming the media. Must be called + * before {@link #shouldTranscode()} or {@link #resolveVideoFormat()}. + * + * @param clientCaps A Bundle object containing the client's capabilities, such as + * {@link #CAPS_SUPPORTS_HEVC}. + * @return the same VideoFormatResolver instance. + * @hide + */ + @NonNull + public MediaFormatResolver setClientCapabilities(@NonNull Bundle clientCaps) { + mClientCaps = clientCaps; + return this; + } + + /** + * Sets the video format hint about the source. Must be called before + * {@link #shouldTranscode()} or {@link #resolveVideoFormat()}. + * + * @param format A MediaFormat object containing information about the source's + * video track format that could affect the transcoding decision. + * Such information could include video codec types, color spaces, + * whether special format info (eg. slow-motion markers) are present, + * etc.. If a particular information is not present, it will not be + * used to make the decision. + * @return the same MediaFormatResolver instance. + */ + @NonNull + public MediaFormatResolver setSourceVideoFormatHint(@NonNull MediaFormat format) { + mSrcVideoFormatHint = format; + return this; + } + + /** + * Sets the audio format hint about the source. + * + * @param format A MediaFormat object containing information about the source's + * audio track format that could affect the transcoding decision. + * @return the same MediaFormatResolver instance. + * @hide + */ + @NonNull + public MediaFormatResolver setSourceAudioFormatHint(@NonNull MediaFormat format) { + mSrcAudioFormatHint = format; + return this; + } + + /** + * Returns whether the source content should be transcoded. + * + * @return true if the source should be transcoded. + * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)} + * or {@link #setSourceVideoFormatHint(MediaFormat)} was not called. + */ + public boolean shouldTranscode() { + if (mClientCaps == null) { + throw new UnsupportedOperationException( + "Client caps must be set!"); + } + // Video src hint must be provided, audio src hint is not used right now. + if (mSrcVideoFormatHint == null) { + throw new UnsupportedOperationException( + "Source video format hint must be set!"); + } + boolean supportHevc = mClientCaps.getBoolean(CAPS_SUPPORTS_HEVC, false); + if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals( + mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) { + return true; + } + // TODO: add more checks as needed below. + return false; + } + + /** + * Retrieves the video track format to be used on + * {@link Builder#setVideoTrackFormat(MediaFormat)} for this configuration. + * + * @return the video track format to be used if transcoding should be performed, + * and null otherwise. + * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)} + * or {@link #setSourceVideoFormatHint(MediaFormat)} was not called. + */ + @Nullable + public MediaFormat resolveVideoFormat() { + if (!shouldTranscode()) { + return null; + } + // TODO(hkuang): Only modified the video codec type, and use fixed bitrate for now. + // May switch to transcoding profile when it's available. + MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint); + videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC); + videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); + return videoTrackFormat; + } + + /** + * Retrieves the audio track format to be used for transcoding. + * + * @return the audio track format to be used if transcoding should be performed, and + * null otherwise. + * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)} + * or {@link #setSourceVideoFormatHint(MediaFormat)} was not called. + * @hide + */ + @Nullable + public MediaFormat resolveAudioFormat() { + if (!shouldTranscode()) { + return null; + } + // Audio transcoding is not supported yet, always return null. + return null; + } + } } /** diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java index 1a3e3608f37fb..33d6d64c7f371 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java @@ -24,6 +24,7 @@ import android.media.MediaFormat; import android.media.MediaTranscodeManager; import android.media.MediaTranscodeManager.TranscodingJob; import android.media.MediaTranscodeManager.TranscodingRequest; +import android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver; import android.media.TranscodingTestConfig; import android.net.Uri; import android.os.Bundle; @@ -414,17 +415,27 @@ public class MediaTranscodeManagerTest Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4"); + Bundle clientCaps = new Bundle(); + clientCaps.putBoolean(MediaFormatResolver.CAPS_SUPPORTS_HEVC, false); + MediaFormatResolver resolver = new MediaFormatResolver() + .setSourceVideoFormatHint(MediaFormat.createVideoFormat( + MediaFormat.MIMETYPE_VIDEO_HEVC, WIDTH, HEIGHT)) + .setClientCapabilities(clientCaps); + assertTrue(resolver.shouldTranscode()); + MediaFormat videoTrackFormat = resolver.resolveVideoFormat(); + assertNotNull(videoTrackFormat); + TranscodingRequest request = new TranscodingRequest.Builder() .setSourceUri(mSourceHEVCVideoUri) .setDestinationUri(destinationUri) .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .setVideoTrackFormat(createMediaFormat()) + .setVideoTrackFormat(videoTrackFormat) .build(); Executor listenerExecutor = Executors.newSingleThreadExecutor(); - Log.i(TAG, "transcoding to " + createMediaFormat()); + Log.i(TAG, "transcoding to " + videoTrackFormat); TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor, transcodingJob -> { diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 71bbe79a925a1..ff3999f090e04 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -4334,6 +4334,14 @@ package android.media { method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat); } + public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver { + ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver(); + method @Nullable public android.media.MediaFormat resolveVideoFormat(); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat); + method public boolean shouldTranscode(); + field public static final String CAPS_SUPPORTS_HEVC = "support-hevc"; + } + public class PlayerProxy { method public void pause(); method public void setPan(float);