diff options
author | Santiago Seifert <aquilescanta@google.com> | 2019-11-19 20:38:56 +0000 |
---|---|---|
committer | Santiago Seifert <aquilescanta@google.com> | 2019-12-05 11:47:17 +0000 |
commit | a4be4662653ea02964640f8f2331e46a244ed723 (patch) | |
tree | 804c180dd61784f36e19b36867d1140c889a670c | |
parent | 0b975928115ca80f4dfb33c9f0418b593d1472c0 (diff) |
Add initial implementation of MediaParser
Bug: 132153067
Bug: 134057371
Test: Pending.
Change-Id: I2d2881df34a6f4da13bfefffb58194eaaab6b4e3
-rw-r--r-- | api/current.txt | 44 | ||||
-rw-r--r-- | api/test-lint-baseline.txt | 400 | ||||
-rw-r--r-- | media/Android.bp | 5 | ||||
-rw-r--r-- | media/apex/java/android/media/MediaParser.java | 743 |
4 files changed, 878 insertions, 314 deletions
diff --git a/api/current.txt b/api/current.txt index b4be9e19da10..34b1235fc32f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -25591,6 +25591,50 @@ package android.media { field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1 } + public final class MediaParser { + method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException, java.lang.InterruptedException; + method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...); + method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer); + method @Nullable public String getExtractorName(); + method @NonNull public static java.util.List<java.lang.String> getExtractorNames(@NonNull android.media.MediaFormat); + method public void release(); + method public void seek(@NonNull android.media.MediaParser.SeekPoint); + } + + public static interface MediaParser.InputReader { + method public long getLength(); + method public long getPosition(); + method public int read(@NonNull byte[], int, int) throws java.io.IOException, java.lang.InterruptedException; + } + + public static interface MediaParser.OutputConsumer { + method public void onFormat(int, @NonNull android.media.MediaFormat); + method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo); + method public void onSampleData(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException, java.lang.InterruptedException; + method public void onSeekMap(@NonNull android.media.MediaParser.SeekMap); + method public void onTracksFound(int); + } + + public static interface MediaParser.SeekMap { + method public long getDurationUs(); + method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long); + method public boolean isSeekable(); + field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000 + } + + public static final class MediaParser.SeekPoint { + field @NonNull public static final android.media.MediaParser.SeekPoint START; + field public final long position; + field public final long timeUs; + } + + public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader { + method public void seekToPosition(long); + } + + public static final class MediaParser.UnrecognizedInputFormatException extends java.io.IOException { + } + public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { ctor public MediaPlayer(); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt index ba85ae66cd1a..c8fdf506c793 100644 --- a/api/test-lint-baseline.txt +++ b/api/test-lint-baseline.txt @@ -8,35 +8,35 @@ AcronymName: android.app.NotificationChannel#setImportanceLockedByOEM(boolean): ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION: ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_ADDITIONAL_CALL_INFO: - Inconsistent extra value; expected `android.telephony.ims.extra.ADDITIONAL_CALL_INFO`, was `AdditionalCallInfo` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CALL_RAT_TYPE: - Inconsistent extra value; expected `android.telephony.ims.extra.CALL_RAT_TYPE`, was `CallRadioTech` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CHILD_NUMBER: - Inconsistent extra value; expected `android.telephony.ims.extra.CHILD_NUMBER`, was `ChildNum` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNA: - Inconsistent extra value; expected `android.telephony.ims.extra.CNA`, was `cna` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNAP: - Inconsistent extra value; expected `android.telephony.ims.extra.CNAP`, was `cnap` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CODEC: - Inconsistent extra value; expected `android.telephony.ims.extra.CODEC`, was `Codec` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DIALSTRING: - Inconsistent extra value; expected `android.telephony.ims.extra.DIALSTRING`, was `dialstring` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DISPLAY_TEXT: - Inconsistent extra value; expected `android.telephony.ims.extra.DISPLAY_TEXT`, was `DisplayText` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_EMERGENCY_CALL: - Inconsistent extra value; expected `android.telephony.ims.extra.EMERGENCY_CALL`, was `e_call` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_IS_CALL_PULL: - Inconsistent extra value; expected `android.telephony.ims.extra.IS_CALL_PULL`, was `CallPull` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OI: - Inconsistent extra value; expected `android.telephony.ims.extra.OI`, was `oi` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OIR: - Inconsistent extra value; expected `android.telephony.ims.extra.OIR`, was `oir` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_REMOTE_URI: - Inconsistent extra value; expected `android.telephony.ims.extra.REMOTE_URI`, was `remote_uri` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_USSD: - Inconsistent extra value; expected `android.telephony.ims.extra.USSD`, was `ussd` + ActionValue: android.telephony.ims.ImsReasonInfo#EXTRA_MSG_SERVICE_NOT_AUTHORIZED: - Inconsistent extra value; expected `android.telephony.ims.extra.MSG_SERVICE_NOT_AUTHORIZED`, was `Forbidden. Not Authorized for Service` + ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_CLEANUP: ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_DOWNLOAD_RESULT_INTERNAL: @@ -100,13 +100,13 @@ ArrayReturn: android.os.NativeHandle#getFileDescriptors(): ArrayReturn: android.security.keystore.AttestationUtils#attestDeviceIds(android.content.Context, int[], byte[]): ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - Method parameter should be Collection<ImsSsInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsSsInfo[]` + ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1: - Method parameter should be Collection<ImsCallForwardInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsCallForwardInfo[]` + ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - Method parameter should be Collection<ImsSsInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsSsInfo[]` + ArrayReturn: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0: - Method parameter should be Collection<Uri> (or subclass) instead of raw array; was `android.net.Uri[]` + ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0: ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): @@ -268,7 +268,7 @@ ConcreteCollection: android.service.autofill.InternalTransformation#batchApply(a ConcreteCollection: android.service.autofill.UserData#getFieldClassificationAlgorithms(): ConcreteCollection: android.telephony.ims.ImsConferenceState#mParticipants: - Field type is concrete collection (`java.util.HashMap`); must be higher-level interface + ContextFirst: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1: @@ -338,9 +338,9 @@ ExecutorRegistration: android.os.RemoteCallback#RemoteCallback(android.os.Remote ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler): ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener): - Registration methods should have overload that accepts delivery Executor: `setListener` + ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener): - Registration methods should have overload that accepts delivery Executor: `setListener` + ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener): ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener): @@ -458,11 +458,13 @@ InterfaceConstant: android.telecom.PhoneAccountSuggestionService#SERVICE_INTERFA InternalField: android.telephony.ims.ImsConferenceState#mParticipants: - Internal field mParticipants must not be exposed + KotlinOperator: android.os.WorkSource#get(int): +KotlinOperator: android.util.SparseArrayMap#get(int, String): + Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object) ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener: @@ -472,9 +474,9 @@ ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListen ListenerInterface: android.os.IncidentManager.AuthListener: ListenerInterface: android.telephony.ims.ImsCallSessionListener: - Listeners should be an interface, or otherwise renamed Callback: ImsCallSessionListener + ListenerInterface: android.telephony.ims.ImsUtListener: - Listeners should be an interface, or otherwise renamed Callback: ImsUtListener + ListenerLast: android.hardware.camera2.CameraDevice#createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) parameter #4: @@ -494,17 +496,17 @@ ManagerConstructor: android.content.pm.ShortcutManager#ShortcutManager(android.c ManagerLookup: android.telephony.ims.ImsMmTelManager#createForSubscriptionId(int): - Managers must always be obtained from Context (`createForSubscriptionId`) + ManagerLookup: android.telephony.ims.ProvisioningManager#createForSubscriptionId(int): - Managers must always be obtained from Context (`createForSubscriptionId`) + MethodNameTense: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable(): - Unexpected tense; probably meant `enabled`, was `getCapabilitiesToEnable` + MethodNameUnits: android.telephony.ims.ImsCallForwardInfo#getTimeSeconds(): - Returned time values must be in milliseconds, was `getTimeSeconds` + MinMaxConstant: android.os.UserHandle#MIN_SECONDARY_USER_ID: @@ -1458,7 +1460,7 @@ MissingNullability: android.telecom.PhoneAccountSuggestionService#onBind(android MissingNullability: android.telecom.PhoneAccountSuggestionService#onBind(android.content.Intent) parameter #0: MissingNullability: android.telephony.CallQuality#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `dest` in method `writeToParcel` + MissingNullability: android.telephony.DataSpecificRegistrationInfo#writeToParcel(android.os.Parcel, int) parameter #0: MissingNullability: android.telephony.LteVopsSupportInfo#writeToParcel(android.os.Parcel, int) parameter #0: @@ -1476,9 +1478,9 @@ MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(St MissingNullability: android.telephony.TelephonyManager#checkCarrierPrivilegesForPackage(String) parameter #0: MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent): - Missing nullability on method `getCarrierPackageNamesForIntent` return + MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent) parameter #0: - Missing nullability on parameter `intent` in method `getCarrierPackageNamesForIntent` + MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag(): MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion(): @@ -1516,315 +1518,315 @@ MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(St MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #8: MissingNullability: android.telephony.ims.ImsCallForwardInfo#getNumber(): - Missing nullability on method `getNumber` return + MissingNullability: android.telephony.ims.ImsCallForwardInfo#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #2: - Missing nullability on parameter `callExtras` in method `ImsCallProfile` + MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #3: - Missing nullability on parameter `mediaProfile` in method `ImsCallProfile` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String): - Missing nullability on method `getCallExtra` return + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String) parameter #0: - Missing nullability on parameter `name` in method `getCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String): - Missing nullability on method `getCallExtra` return + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #0: - Missing nullability on parameter `name` in method `getCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #1: - Missing nullability on parameter `defaultValue` in method `getCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String) parameter #0: - Missing nullability on parameter `name` in method `getCallExtraBoolean` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String, boolean) parameter #0: - Missing nullability on parameter `name` in method `getCallExtraBoolean` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String) parameter #0: - Missing nullability on parameter `name` in method `getCallExtraInt` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String, int) parameter #0: - Missing nullability on parameter `name` in method `getCallExtraInt` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtras(): - Missing nullability on method `getCallExtras` return + MissingNullability: android.telephony.ims.ImsCallProfile#getMediaProfile(): - Missing nullability on method `getMediaProfile` return + MissingNullability: android.telephony.ims.ImsCallProfile#getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `callProfile` in method `getVideoStateFromImsCallProfile` + MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #0: - Missing nullability on parameter `name` in method `setCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #1: - Missing nullability on parameter `value` in method `setCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraBoolean(String, boolean) parameter #0: - Missing nullability on parameter `name` in method `setCallExtraBoolean` + MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraInt(String, int) parameter #0: - Missing nullability on parameter `name` in method `setCallExtraInt` + MissingNullability: android.telephony.ims.ImsCallProfile#updateCallExtras(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `updateCallExtras` + MissingNullability: android.telephony.ims.ImsCallProfile#updateCallType(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `updateCallType` + MissingNullability: android.telephony.ims.ImsCallProfile#updateMediaProfile(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `updateMediaProfile` + MissingNullability: android.telephony.ims.ImsCallProfile#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionConferenceExtendFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `newSession` in method `callSessionConferenceExtendReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `callSessionConferenceExtendReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `newSession` in method `callSessionConferenceExtended` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `callSessionConferenceExtended` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState) parameter #0: - Missing nullability on parameter `state` in method `callSessionConferenceStateUpdated` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo) parameter #2: - Missing nullability on parameter `reasonInfo` in method `callSessionHandover` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo) parameter #2: - Missing nullability on parameter `reasonInfo` in method `callSessionHandoverFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHeld(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionHeld` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionHoldFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldReceived(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionHoldReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiated(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionInitiated` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionInitiatedFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionInviteParticipantsRequestFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase) parameter #0: - Missing nullability on parameter `newSession` in method `callSessionMergeComplete` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionMergeFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `newSession` in method `callSessionMergeStarted` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `callSessionMergeStarted` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionProgressing` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionRemoveParticipantsRequestFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionResumeFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeReceived(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionResumeReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumed(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionResumed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttMessageReceived(String) parameter #0: - Missing nullability on parameter `rttMessage` in method `callSessionRttMessageReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `callProfile` in method `callSessionRttModifyRequestReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification) parameter #0: - Missing nullability on parameter `suppSrvNotification` in method `callSessionSuppServiceReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionTerminated(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionTerminated` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionUpdateFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateReceived(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionUpdateReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdated(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionUpdated` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUssdMessageReceived(int, String) parameter #1: - Missing nullability on parameter `ussdMessage` in method `callSessionUssdMessageReceived` + MissingNullability: android.telephony.ims.ImsConferenceState#getConnectionStateForStatus(String) parameter #0: - Missing nullability on parameter `status` in method `getConnectionStateForStatus` + MissingNullability: android.telephony.ims.ImsConferenceState#mParticipants: - Missing nullability on field `mParticipants` in class `class android.telephony.ims.ImsConferenceState` + MissingNullability: android.telephony.ims.ImsConferenceState#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsExternalCallState#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsReasonInfo#ImsReasonInfo(int, int, String) parameter #2: - Missing nullability on parameter `extraMessage` in method `ImsReasonInfo` + MissingNullability: android.telephony.ims.ImsReasonInfo#getExtraMessage(): - Missing nullability on method `getExtraMessage` return + MissingNullability: android.telephony.ims.ImsReasonInfo#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsService#createMmTelFeature(int): - Missing nullability on method `createMmTelFeature` return + MissingNullability: android.telephony.ims.ImsService#createRcsFeature(int): - Missing nullability on method `createRcsFeature` return + MissingNullability: android.telephony.ims.ImsService#getConfig(int): - Missing nullability on method `getConfig` return + MissingNullability: android.telephony.ims.ImsService#getRegistration(int): - Missing nullability on method `getRegistration` return + MissingNullability: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) parameter #0: - Missing nullability on parameter `c` in method `onUpdateSupportedImsFeatures` + MissingNullability: android.telephony.ims.ImsService#querySupportedImsFeatures(): - Missing nullability on method `querySupportedImsFeatures` return + MissingNullability: android.telephony.ims.ImsSsData#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsSsInfo#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsStreamMediaProfile#copyFrom(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - Missing nullability on parameter `profile` in method `copyFrom` + MissingNullability: android.telephony.ims.ImsStreamMediaProfile#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #4: - Missing nullability on parameter `number` in method `ImsSuppServiceNotification` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #5: - Missing nullability on parameter `history` in method `ImsSuppServiceNotification` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#history: - Missing nullability on field `history` in class `class android.telephony.ims.ImsSuppServiceNotification` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#number: - Missing nullability on field `number` in class `class android.telephony.ims.ImsSuppServiceNotification` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsUtListener#onSupplementaryServiceIndication(android.telephony.ims.ImsSsData) parameter #0: - Missing nullability on parameter `ssData` in method `onSupplementaryServiceIndication` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - Missing nullability on parameter `cbInfo` in method `onUtConfigurationCallBarringQueried` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1: - Missing nullability on parameter `cfInfo` in method `onUtConfigurationCallForwardQueried` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - Missing nullability on parameter `cwInfo` in method `onUtConfigurationCallWaitingQueried` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueried(int, android.os.Bundle) parameter #1: - Missing nullability on parameter `configuration` in method `onUtConfigurationQueried` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - Missing nullability on parameter `error` in method `onUtConfigurationQueryFailed` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - Missing nullability on parameter `error` in method `onUtConfigurationUpdateFailed` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities) parameter #0: - Missing nullability on parameter `CameraCapabilities` in method `changeCameraCapabilities` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #0: - Missing nullability on parameter `fromProfile` in method `onSendSessionModifyRequest` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1: - Missing nullability on parameter `toProfile` in method `onSendSessionModifyRequest` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyResponse(android.telecom.VideoProfile) parameter #0: - Missing nullability on parameter `responseProfile` in method `onSendSessionModifyResponse` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String) parameter #0: - Missing nullability on parameter `cameraId` in method `onSetCamera` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String, int) parameter #0: - Missing nullability on parameter `cameraId` in method `onSetCamera` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetDisplaySurface(android.view.Surface) parameter #0: - Missing nullability on parameter `surface` in method `onSetDisplaySurface` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPauseImage(android.net.Uri) parameter #0: - Missing nullability on parameter `uri` in method `onSetPauseImage` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPreviewSurface(android.view.Surface) parameter #0: - Missing nullability on parameter `surface` in method `onSetPreviewSurface` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyRequest(android.telecom.VideoProfile) parameter #0: - Missing nullability on parameter `VideoProfile` in method `receiveSessionModifyRequest` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1: - Missing nullability on parameter `requestedProfile` in method `receiveSessionModifyResponse` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #2: - Missing nullability on parameter `responseProfile` in method `receiveSessionModifyResponse` + MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToDisable(): - Missing nullability on method `getCapabilitiesToDisable` return + MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable(): - Missing nullability on method `getCapabilitiesToEnable` return + MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `dest` in method `writeToParcel` + MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #0: - Missing nullability on parameter `request` in method `changeEnabledCapabilities` + MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #1: - Missing nullability on parameter `c` in method `changeEnabledCapabilities` + MissingNullability: android.telephony.ims.feature.MmTelFeature#queryCapabilityStatus(): - Missing nullability on method `queryCapabilityStatus` return + MissingNullability: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities) parameter #0: - Missing nullability on parameter `c` in method `MmTelCapabilities` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#accept(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1: - Missing nullability on parameter `profile` in method `accept` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#deflect(String) parameter #0: - Missing nullability on parameter `deflectNumber` in method `deflect` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#extendToConference(String[]) parameter #0: - Missing nullability on parameter `participants` in method `extendToConference` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallId(): - Missing nullability on method `getCallId` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallProfile(): - Missing nullability on method `getCallProfile` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getImsVideoCallProvider(): - Missing nullability on method `getImsVideoCallProvider` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getLocalCallProfile(): - Missing nullability on method `getLocalCallProfile` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String): - Missing nullability on method `getProperty` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String) parameter #0: - Missing nullability on parameter `name` in method `getProperty` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getRemoteCallProfile(): - Missing nullability on method `getRemoteCallProfile` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#hold(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - Missing nullability on parameter `profile` in method `hold` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#inviteParticipants(String[]) parameter #0: - Missing nullability on parameter `participants` in method `inviteParticipants` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#removeParticipants(String[]) parameter #0: - Missing nullability on parameter `participants` in method `removeParticipants` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#resume(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - Missing nullability on parameter `profile` in method `resume` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendDtmf(char, android.os.Message) parameter #1: - Missing nullability on parameter `result` in method `sendDtmf` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttMessage(String) parameter #0: - Missing nullability on parameter `rttMessage` in method `sendRttMessage` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttModifyRequest(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `toProfile` in method `sendRttModifyRequest` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendUssd(String) parameter #0: - Missing nullability on parameter `ussdMessage` in method `sendUssd` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener) parameter #0: - Missing nullability on parameter `listener` in method `setListener` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `callee` in method `start` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `start` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `participants` in method `startConference` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `startConference` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#update(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1: - Missing nullability on parameter `profile` in method `update` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase.State#toString(int): - Missing nullability on method `toString` return + MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#getConfigString(int): - Missing nullability on method `getConfigString` return + MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#notifyProvisionedValueChanged(int, String) parameter #1: - Missing nullability on parameter `value` in method `notifyProvisionedValueChanged` + MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#setConfig(int, String) parameter #1: - Missing nullability on parameter `value` in method `setConfig` + MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#getServiceFeatures(): - Missing nullability on method `getServiceFeatures` return + MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `dest` in method `writeToParcel` + MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int): - Missing nullability on method `addFeature` return + MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#build(): - Missing nullability on method `build` return + MissingNullability: android.telephony.ims.stub.ImsMultiEndpointImplBase#onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>) parameter #0: - Missing nullability on parameter `externalCallDialogs` in method `onImsExternalCallStateUpdate` + MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `info` in method `onDeregistered` + MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0: - Missing nullability on parameter `uris` in method `onSubscriberAssociatedUriChanged` + MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - Missing nullability on parameter `info` in method `onTechnologyChangeFailed` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#getSmsFormat(): - Missing nullability on method `getSmsFormat` return + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #1: - Missing nullability on parameter `format` in method `onSmsReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #2: - Missing nullability on parameter `pdu` in method `onSmsReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #1: - Missing nullability on parameter `format` in method `onSmsStatusReportReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #2: - Missing nullability on parameter `pdu` in method `onSmsStatusReportReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #2: - Missing nullability on parameter `format` in method `onSmsStatusReportReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #3: - Missing nullability on parameter `pdu` in method `onSmsStatusReportReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #2: - Missing nullability on parameter `format` in method `sendSms` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #3: - Missing nullability on parameter `smsc` in method `sendSms` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #5: - Missing nullability on parameter `pdu` in method `sendSms` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#queryCallForward(int, String) parameter #1: - Missing nullability on parameter `number` in method `queryCallForward` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener) parameter #0: - Missing nullability on parameter `listener` in method `setListener` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#transact(android.os.Bundle) parameter #0: - Missing nullability on parameter `ssInfo` in method `transact` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarring(int, int, String[]) parameter #2: - Missing nullability on parameter `barrList` in method `updateCallBarring` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarringForServiceClass(int, int, String[], int) parameter #2: - Missing nullability on parameter `barrList` in method `updateCallBarringForServiceClass` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallForward(int, int, String, int, int) parameter #2: - Missing nullability on parameter `number` in method `updateCallForward` + MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0: @@ -2276,7 +2278,7 @@ NotCloseable: android.app.prediction.AppPredictor: NotCloseable: android.os.HwParcel: NotCloseable: android.telephony.ims.stub.ImsUtImplBase: - Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.telephony.ims.stub.ImsUtImplBase + OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]): @@ -2290,21 +2292,21 @@ OnNameExpected: android.service.notification.NotificationAssistantService#attach OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported(): OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#disableIms(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#enableIms(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#getConfig(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#getRegistration(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#querySupportedImsFeatures(): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#readyForFeatureCreation(): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#dispose(int): OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int): @@ -2444,7 +2446,7 @@ RethrowRemoteException: android.os.HwBinder#transact(int, android.os.HwParcel, a RethrowRemoteException: android.os.IHwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int): RethrowRemoteException: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) + RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener): RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener): @@ -2524,7 +2526,7 @@ SamShouldBeLast: android.service.autofill.ImageTransformation#apply(android.serv SamShouldBeLast: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>): SamShouldBeLast: android.telephony.ims.ImsMmTelManager#getFeatureState(java.util.function.Consumer<java.lang.Integer>, java.util.concurrent.Executor): - SAM-compatible parameters (such as parameter 1, "callback", in android.telephony.ims.ImsMmTelManager.getFeatureState) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions + SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object): SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long): @@ -2597,6 +2599,8 @@ UserHandle: android.app.role.RoleManager#removeOnRoleHoldersChangedListenerAsUse UserHandle: android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>): +UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle): + When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle): UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle): diff --git a/media/Android.bp b/media/Android.bp index 022fa9b7bb9e..1912930f2081 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -45,8 +45,8 @@ java_library { filegroup { name: "updatable-media-srcs", srcs: [ - ":mediasession2-srcs", ":mediaparser-srcs", + ":mediasession2-srcs", ], } @@ -73,7 +73,8 @@ filegroup { name: "mediaparser-srcs", srcs: [ "apex/java/android/media/MediaParser.java" - ] + ], + path: "apex/java" } metalava_updatable_media_args = " --error UnhiddenSystemApi " + diff --git a/media/apex/java/android/media/MediaParser.java b/media/apex/java/android/media/MediaParser.java index c06e2837bcdc..8824269ea0c0 100644 --- a/media/apex/java/android/media/MediaParser.java +++ b/media/apex/java/android/media/MediaParser.java @@ -15,10 +15,47 @@ */ package android.media; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.text.TextUtils; import android.util.Pair; - +import android.util.SparseArray; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.DefaultExtractorInput; +import com.google.android.exoplayer2.extractor.Extractor; +import com.google.android.exoplayer2.extractor.ExtractorInput; +import com.google.android.exoplayer2.extractor.ExtractorOutput; +import com.google.android.exoplayer2.extractor.PositionHolder; +import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints; +import com.google.android.exoplayer2.extractor.TrackOutput; +import com.google.android.exoplayer2.extractor.amr.AmrExtractor; +import com.google.android.exoplayer2.extractor.flv.FlvExtractor; +import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; +import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; +import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; +import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor; +import com.google.android.exoplayer2.extractor.ogg.OggExtractor; +import com.google.android.exoplayer2.extractor.ts.Ac3Extractor; +import com.google.android.exoplayer2.extractor.ts.Ac4Extractor; +import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; +import com.google.android.exoplayer2.extractor.ts.PsExtractor; +import com.google.android.exoplayer2.extractor.ts.TsExtractor; +import com.google.android.exoplayer2.extractor.wav.WavExtractor; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer2.util.ParsableByteArray; + +import java.io.EOFException; import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * Parses media container formats and extracts contained media samples and metadata. @@ -32,16 +69,93 @@ import java.util.List; * <p>Users must implement the following to use this class. * * <ul> - * <li>{@link Input}: Provides the media containers bytes to parse. - * <li>{@link OutputCallback}: Provides a sink for all extracted data and metadata. + * <li>{@link InputReader}: Provides the media container's bytes to parse. + * <li>{@link OutputConsumer}: Provides a sink for all extracted data and metadata. * </ul> * - * TODO: Add usage example here. + * <p>The following code snippet includes a usage example: + * + * <pre> + * MyOutputConsumer myOutputConsumer = new MyOutputConsumer(); + * MyInputReader myInputReader = new MyInputReader("www.example.com"); + * MediaParser mediaParser = MediaParser.create(myOutputConsumer); + * + * while (mediaParser.advance(myInputReader)) {} + * + * mediaParser.release(); + * mediaParser = null; + * </pre> + * + * <p>The following code snippet provides a rudimentary {@link OutputConsumer} sample implementation + * which extracts and publishes all video samples: + * + * <pre> + * + * class VideoOutputConsumer implements MediaParser.OutputConsumer { + * + * private static final int MAXIMUM_SAMPLE_SIZE = ...; + * private byte[] sampleDataBuffer = new byte[MAXIMUM_SAMPLE_SIZE]; + * private int videoTrackIndex = -1; + * private int bytesWrittenCount = 0; + * + * \@Override + * public void onSeekMap(int i, @NonNull MediaFormat mediaFormat) { \/* Do nothing. *\/ } + * + * \@Override + * public void onFormat(int i, @NonNull MediaFormat mediaFormat) { + * if (videoTrackIndex == -1 && mediaFormat + * .getString(MediaFormat.KEY_MIME, \/* defaultValue= *\/ "").startsWith("video/")) { + * videoTrackIndex = i; + * } + * } + * + * \@Override + * public void onSampleData(int trackIndex, @NonNull InputReader inputReader) + * throws IOException, InterruptedException { + * int numberOfBytesToRead = (int) inputReader.getLength(); + * if (videoTrackIndex != trackIndex) { + * // Discard contents. + * inputReader.read(\/* bytes= *\/ null, \/* offset= *\/ 0, numberOfBytesToRead); + * } + * int bytesRead = inputReader.read(sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead); + * bytesWrittenCount += bytesRead; + * } + * + * \@Override + * public void onSampleCompleted( + * int trackIndex, + * long timeUs, + * int flags, + * int size, + * int offset, + * \@Nullable CryptoInfo cryptoData) { + * if (videoTrackIndex != trackIndex) { + * return; // It's not the video track. Ignore. + * } + * byte[] sampleData = new byte[size]; + * System.arraycopy(sampleDataBuffer, bytesWrittenCount - size - offset, sampleData, \/* + * destPos= *\/ 0, size); + * // Place trailing bytes at the start of the buffer. + * System.arraycopy( + * sampleDataBuffer, + * bytesWrittenCount - offset, + * sampleDataBuffer, + * \/* destPos= *\/ 0, + * \/* size= *\/ offset); + * publishSample(sampleData, timeUs, flags); + * } + * } + * + * </pre> */ -// @HiddenApi public final class MediaParser { - /** Maps seek positions to corresponding positions in the stream. */ + /** + * Maps seek positions to {@link SeekPoint SeekPoints} in the stream. + * + * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start + * playing media samples. + */ public interface SeekMap { /** Returned by {@link #getDurationUs()} when the duration is unknown. */ @@ -62,13 +176,14 @@ public final class MediaParser { * <p>{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with * timestamp equal to or smaller than {@code timeUs}. * - * <p>{@code getSeekPoints(timeUs).second} contains the earlies seek point for samples with + * <p>{@code getSeekPoints(timeUs).second} contains the earliest seek point for samples with * timestamp equal to or greater than {@code timeUs}. If a seek point exists for {@code * timeUs}, the returned pair will contain the same {@link SeekPoint} twice. * * @param timeUs A seek time in microseconds. * @return The corresponding {@link SeekPoint SeekPoints}. */ + @NonNull Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs); } @@ -76,30 +191,30 @@ public final class MediaParser { public static final class SeekPoint { /** A {@link SeekPoint} whose time and byte offset are both set to 0. */ - public static final SeekPoint START = new SeekPoint(0, 0); + public static final @NonNull SeekPoint START = new SeekPoint(0, 0); /** The time of the seek point, in microseconds. */ - public final long mTimeUs; + public final long timeUs; /** The byte offset of the seek point. */ - public final long mPosition; + public final long position; /** * @param timeUs The time of the seek point, in microseconds. * @param position The byte offset of the seek point. */ - public SeekPoint(long timeUs, long position) { - this.mTimeUs = timeUs; - this.mPosition = position; + private SeekPoint(long timeUs, long position) { + this.timeUs = timeUs; + this.position = position; } @Override - public String toString() { - return "[timeUs=" + mTimeUs + ", position=" + mPosition + "]"; + public @NonNull String toString() { + return "[timeUs=" + timeUs + ", position=" + position + "]"; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -107,25 +222,26 @@ public final class MediaParser { return false; } SeekPoint other = (SeekPoint) obj; - return mTimeUs == other.mTimeUs && mPosition == other.mPosition; + return timeUs == other.timeUs && position == other.position; } @Override public int hashCode() { - int result = (int) mTimeUs; - result = 31 * result + (int) mPosition; + int result = (int) timeUs; + result = 31 * result + (int) position; return result; } } /** Provides input data to {@link MediaParser}. */ - public interface Input { + public interface InputReader { /** * Reads up to {@code readLength} bytes of data and stores them into {@code buffer}, * starting at index {@code offset}. * - * <p>The call will block until at least one byte of data has been read. + * <p>This method blocks until at least one byte is read, the end of input is detected, or + * an exception is thrown. The read position advances to the first unread byte. * * @param buffer The buffer into which the read data should be stored. * @param offset The start offset into {@code buffer} at which data should be written. @@ -134,7 +250,7 @@ public final class MediaParser { * of the input has been reached. * @throws java.io.IOException If an error occurs reading from the source. */ - int read(byte[] buffer, int offset, int readLength) + int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException, InterruptedException; /** Returns the current read position (byte offset) in the stream. */ @@ -144,22 +260,39 @@ public final class MediaParser { long getLength(); } - /** Receives extracted media sample data and metadata from {@link MediaParser}. */ - public interface OutputCallback { + /** {@link InputReader} that allows setting the read position. */ + public interface SeekableInputReader extends InputReader { /** - * Called when the number of tracks is defined. + * Sets the read position at the given {@code position}. * - * @param numberOfTracks The number of tracks in the stream. + * <p>{@link #advance} will immediately return after calling this method. + * + * @param position The position to seek to, in bytes. */ - void onTracksFound(int numberOfTracks); + void seekToPosition(long position); + } + + /** Receives extracted media sample data and metadata from {@link MediaParser}. */ + public interface OutputConsumer { /** * Called when a {@link SeekMap} has been extracted from the stream. * + * <p>This method is called at least once before any samples are {@link #onSampleCompleted + * complete}. May be called multiple times after that in order to add {@link SeekPoint + * SeekPoints}. + * * @param seekMap The extracted {@link SeekMap}. */ - void onSeekMap(SeekMap seekMap); + void onSeekMap(@NonNull SeekMap seekMap); + + /** + * Called when the number of tracks is found. + * + * @param numberOfTracks The number of tracks in the stream. + */ + void onTracksFound(int numberOfTracks); /** * Called when the {@link MediaFormat} of the track is extracted from the stream. @@ -167,7 +300,7 @@ public final class MediaParser { * @param trackIndex The index of the track for which the {@link MediaFormat} was found. * @param format The extracted {@link MediaFormat}. */ - void onFormat(int trackIndex, MediaFormat format); + void onFormat(int trackIndex, @NonNull MediaFormat format); /** * Called to write sample data to the output. @@ -176,16 +309,15 @@ public final class MediaParser { * thrown {@link IOException} caused by reading from {@code input}. * * @param trackIndex The index of the track to which the sample data corresponds. - * @param input The {@link Input} from which to read the data. - * @return + * @param inputReader The {@link InputReader} from which to read the data. */ - int onSampleData(int trackIndex, Input input) throws IOException, InterruptedException; + void onSampleData(int trackIndex, @NonNull InputReader inputReader) + throws IOException, InterruptedException; /** - * Defines the boundaries and metadata of an extracted sample. + * Called once all the data of a sample has been passed to {@link #onSampleData}. * - * <p>The corresponding sample data will have already been passed to the output via calls to - * {@link #onSampleData}. + * <p>Also includes sample metadata, like presentation timestamp and flags. * * @param trackIndex The index of the track to which the sample corresponds. * @param timeUs The media timestamp associated with the sample, in microseconds. @@ -203,57 +335,22 @@ public final class MediaParser { int flags, int size, int offset, - MediaCodec.CryptoInfo cryptoData); - } - - /** - * Controls the behavior of extractors' implementations. - * - * <p>DESIGN NOTE: For setting flags like workarounds and special behaviors for adaptive - * streaming. - */ - public static final class Parameters { - - // TODO: Implement. - - } - - /** Holds the result of an {@link #advance} invocation. */ - public static final class ResultHolder { - - /** Creates a new instance with {@link #result} holding {@link #ADVANCE_RESULT_CONTINUE}. */ - public ResultHolder() { - result = ADVANCE_RESULT_CONTINUE; - } - - /** - * May hold {@link #ADVANCE_RESULT_END_OF_INPUT}, {@link #ADVANCE_RESULT_CONTINUE}, {@link - * #ADVANCE_RESULT_SEEK}. - */ - public int result; - - /** - * If {@link #result} holds {@link #ADVANCE_RESULT_SEEK}, holds the stream position required - * from the passed {@link Input} to the next {@link #advance} call. If {@link #result} does - * not hold {@link #ADVANCE_RESULT_SEEK}, the value of this variable is undefined and should - * be ignored. - */ - public long seekPosition; + @Nullable MediaCodec.CryptoInfo cryptoData); } /** * Thrown if all extractors implementations provided to {@link #create} failed to sniff the * input content. */ - // @HiddenApi public static final class UnrecognizedInputFormatException extends IOException { /** * Creates a new instance which signals that the extractors with the given names failed to * parse the input. */ - public static UnrecognizedInputFormatException createForExtractors( - String... extractorNames) { + @NonNull + private static UnrecognizedInputFormatException createForExtractors( + @NonNull String... extractorNames) { StringBuilder builder = new StringBuilder(); builder.append("None of the available extractors ( "); builder.append(extractorNames[0]); @@ -270,21 +367,9 @@ public final class MediaParser { } } - // Public constants. + // Private constants. - /** - * Returned by {@link #advance} if the {@link Input} passed to the next {@link #advance} is - * required to provide data continuing from the position in the stream reached by the returning - * call. - */ - public static final int ADVANCE_RESULT_CONTINUE = -1; - /** Returned by {@link #advance} if the end of the {@link Input} was reached. */ - public static final int ADVANCE_RESULT_END_OF_INPUT = -2; - /** - * Returned by {@link #advance} when its next call expects a specific stream position, which - * will be held by {@link ResultHolder#seekPosition}. - */ - public static final int ADVANCE_RESULT_SEEK = -3; + private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME; // Instance creation methods. @@ -293,13 +378,15 @@ public final class MediaParser { * instance will attempt extraction without sniffing the content. * * @param name The name of the extractor that will be associated with the created instance. - * @param outputCallback The {@link OutputCallback} to which track data and samples are pushed. - * @param parameters Parameters that control specific aspects of the behavior of the extractors. + * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed. * @return A new instance. + * @throws IllegalArgumentException If an invalid name is provided. */ - public static MediaParser createByName( - String name, OutputCallback outputCallback, Parameters parameters) { - throw new UnsupportedOperationException(); + public static @NonNull MediaParser createByName( + @NonNull String name, @NonNull OutputConsumer outputConsumer) { + String[] nameAsArray = new String[] {name}; + assertValidNames(nameAsArray); + return new MediaParser(outputConsumer, /* sniff= */ false, name); } /** @@ -307,30 +394,46 @@ public final class MediaParser { * the first {@link #advance} call. Extractor implementations will sniff the content in order of * appearance in {@code extractorNames}. * - * @param outputCallback The {@link OutputCallback} to track data and samples are obtained. - * @param parameters Parameters that control specific aspects of the behavior of the extractors. + * @param outputConsumer The {@link OutputConsumer} to which extracted data is output. * @param extractorNames The names of the extractors to sniff the content with. If empty, a * default array of names is used. * @return A new instance. */ - public static MediaParser create( - OutputCallback outputCallback, Parameters parameters, String... extractorNames) { - throw new UnsupportedOperationException(); + public static @NonNull MediaParser create( + @NonNull OutputConsumer outputConsumer, @NonNull String... extractorNames) { + assertValidNames(extractorNames); + if (extractorNames.length == 0) { + extractorNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]); + } + return new MediaParser(outputConsumer, /* sniff= */ true, extractorNames); } // Misc static methods. /** * Returns an immutable list with the names of the extractors that are suitable for container - * formats with the given {@code mimeTypes}. If an empty string is passed, all available - * extractors' names are returned. + * formats with the given {@link MediaFormat}. * - * <p>TODO: Replace string with media type object. + * <p>TODO: List which properties are taken into account. E.g. MimeType. */ - public static List<String> getExtractorNames(String mimeTypes) { + public static @NonNull List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) { throw new UnsupportedOperationException(); } + // Private fields. + + private final OutputConsumer mOutputConsumer; + private final String[] mExtractorNamesPool; + private final PositionHolder mPositionHolder; + private final InputReadingDataSource mDataSource; + private final ExtractorInputAdapter mScratchExtractorInputAdapter; + private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter; + private String mExtractorName; + private Extractor mExtractor; + private ExtractorInput mExtractorInput; + private long mPendingSeekPosition; + private long mPendingSeekTimeUs; + // Public methods. /** @@ -344,8 +447,8 @@ public final class MediaParser { * @return The name of the backing extractor implementation, or null if the backing extractor * implementation has not yet been selected. */ - public String getExtractorName() { - throw new UnsupportedOperationException(); + public @Nullable String getExtractorName() { + return mExtractorName; } /** @@ -357,26 +460,85 @@ public final class MediaParser { * <p>If this instance was created using {@link #create}. the first call to this method will * sniff the content with the extractors with the provided names. * - * @param input The {@link Input} from which to obtain the media container data. - * @param resultHolder The {@link ResultHolder} into which the result of the operation will be - * written. + * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media + * container data. + * @return Whether there is any data left to extract. Returns false if the end of input has been + * reached. * @throws UnrecognizedInputFormatException */ - public void advance(Input input, ResultHolder resultHolder) + public boolean advance(@NonNull SeekableInputReader seekableInputReader) throws IOException, InterruptedException { - throw new UnsupportedOperationException(); + if (mExtractorInput == null) { + // TODO: For efficiency, the same implementation should be used, by providing a + // clearBuffers() method, or similar. + mExtractorInput = + new DefaultExtractorInput( + mDataSource, + seekableInputReader.getPosition(), + seekableInputReader.getLength()); + } + mDataSource.mInputReader = seekableInputReader; + + if (mExtractor == null) { + for (String extractorName : mExtractorNamesPool) { + Extractor extractor = + EXTRACTOR_FACTORIES_BY_NAME.get(extractorName).createInstance(); + try { + if (extractor.sniff(mExtractorInput)) { + mExtractorName = extractorName; + mExtractor = extractor; + mExtractor.init(new ExtractorOutputAdapter()); + break; + } + } catch (EOFException e) { + // Do nothing. + } catch (IOException | InterruptedException e) { + throw new IllegalStateException(e); + } finally { + mExtractorInput.resetPeekPosition(); + } + } + if (mExtractor == null) { + UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool); + } + return true; + } + + if (isPendingSeek()) { + mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeUs); + removePendingSeek(); + } + + mPositionHolder.position = seekableInputReader.getPosition(); + int result = mExtractor.read(mExtractorInput, mPositionHolder); + if (result == Extractor.RESULT_END_OF_INPUT) { + return false; + } + if (result == Extractor.RESULT_SEEK) { + mExtractorInput = null; + seekableInputReader.seekToPosition(mPositionHolder.position); + } + return true; } /** * Seeks within the media container being extracted. * - * <p>Following a call to this method, the {@link Input} passed to the next invocation of {@link - * #advance} must provide data starting from {@link SeekPoint#mPosition} in the stream. + * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link + * OutputConsumer#onSeekMap(SeekMap)}. + * + * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of + * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream. * * @param seekPoint The {@link SeekPoint} to seek to. */ - public void seek(SeekPoint seekPoint) { - throw new UnsupportedOperationException(); + public void seek(@NonNull SeekPoint seekPoint) { + if (mExtractor == null) { + mPendingSeekPosition = seekPoint.position; + mPendingSeekTimeUs = seekPoint.timeUs; + } else { + mExtractor.seek(seekPoint.position, seekPoint.timeUs); + } } /** @@ -386,6 +548,359 @@ public final class MediaParser { * invoked. DESIGN NOTE: Should be removed. There shouldn't be any resource for releasing. */ public void release() { - throw new UnsupportedOperationException(); + mExtractorInput = null; + mExtractor = null; + } + + // Private methods. + + private MediaParser( + OutputConsumer outputConsumer, boolean sniff, String... extractorNamesPool) { + mOutputConsumer = outputConsumer; + mExtractorNamesPool = extractorNamesPool; + if (!sniff) { + mExtractorName = extractorNamesPool[0]; + mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance(); + } + mPositionHolder = new PositionHolder(); + mDataSource = new InputReadingDataSource(); + removePendingSeek(); + mScratchExtractorInputAdapter = new ExtractorInputAdapter(); + mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter(); + } + + private boolean isPendingSeek() { + return mPendingSeekPosition >= 0; + } + + private void removePendingSeek() { + mPendingSeekPosition = -1; + mPendingSeekTimeUs = -1; + } + + // Private classes. + + private static final class InputReadingDataSource implements DataSource { + + public InputReader mInputReader; + + @Override + public void addTransferListener(TransferListener transferListener) { + // Do nothing. + } + + @Override + public long open(DataSpec dataSpec) { + throw new UnsupportedOperationException(); + } + + @Override + public int read(byte[] buffer, int offset, int readLength) throws IOException { + // TODO: Reevaluate interruption in Input. + try { + return mInputReader.read(buffer, offset, readLength); + } catch (InterruptedException e) { + // TODO: Remove. + throw new RuntimeException(); + } + } + + @Override + public Uri getUri() { + return null; + } + + @Override + public Map<String, List<String>> getResponseHeaders() { + return null; + } + + @Override + public void close() { + throw new UnsupportedOperationException(); + } + } + + private final class ExtractorOutputAdapter implements ExtractorOutput { + + private final SparseArray<TrackOutput> mTrackOutputAdapters; + private boolean mTracksEnded; + + private ExtractorOutputAdapter() { + mTrackOutputAdapters = new SparseArray<>(); + } + + @Override + public TrackOutput track(int id, int type) { + TrackOutput trackOutput = mTrackOutputAdapters.get(id); + if (trackOutput == null) { + trackOutput = new TrackOutputAdapter(mTrackOutputAdapters.size()); + mTrackOutputAdapters.put(id, trackOutput); + } + return trackOutput; + } + + @Override + public void endTracks() { + mOutputConsumer.onTracksFound(mTrackOutputAdapters.size()); + } + + @Override + public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { + mOutputConsumer.onSeekMap(new ExoToMediaParserSeekMapAdapter(exoplayerSeekMap)); + } + } + + private class TrackOutputAdapter implements TrackOutput { + + private final int mTrackIndex; + + private TrackOutputAdapter(int trackIndex) { + mTrackIndex = trackIndex; + } + + @Override + public void format(Format format) { + mOutputConsumer.onFormat(mTrackIndex, toMediaFormat(format)); + } + + @Override + public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) + throws IOException, InterruptedException { + mScratchExtractorInputAdapter.setExtractorInput(input, length); + long positionBeforeReading = mScratchExtractorInputAdapter.getPosition(); + mOutputConsumer.onSampleData(mTrackIndex, mScratchExtractorInputAdapter); + return (int) (mScratchExtractorInputAdapter.getPosition() - positionBeforeReading); + } + + @Override + public void sampleData(ParsableByteArray data, int length) { + mScratchParsableByteArrayAdapter.resetWithByteArray(data, length); + try { + mOutputConsumer.onSampleData(mTrackIndex, mScratchParsableByteArrayAdapter); + } catch (IOException | InterruptedException e) { + // Unexpected. + throw new RuntimeException(e); + } + } + + @Override + public void sampleMetadata( + long timeUs, int flags, int size, int offset, CryptoData encryptionData) { + mOutputConsumer.onSampleCompleted( + mTrackIndex, timeUs, flags, size, offset, toCryptoInfo(encryptionData)); + } + } + + private static final class ExtractorInputAdapter implements InputReader { + + private ExtractorInput mExtractorInput; + private int mCurrentPosition; + private long mLength; + + public void setExtractorInput(ExtractorInput extractorInput, long length) { + mExtractorInput = extractorInput; + mCurrentPosition = 0; + mLength = length; + } + + // Input implementation. + + @Override + public int read(byte[] buffer, int offset, int readLength) + throws IOException, InterruptedException { + int readBytes = mExtractorInput.read(buffer, offset, readLength); + mCurrentPosition += readBytes; + return readBytes; + } + + @Override + public long getPosition() { + return mCurrentPosition; + } + + @Override + public long getLength() { + return mLength - mCurrentPosition; + } + } + + private static final class ParsableByteArrayAdapter implements InputReader { + + private ParsableByteArray mByteArray; + private long mLength; + private int mCurrentPosition; + + public void resetWithByteArray(ParsableByteArray byteArray, long length) { + mByteArray = byteArray; + mCurrentPosition = 0; + mLength = length; + } + + // Input implementation. + + @Override + public int read(byte[] buffer, int offset, int readLength) { + mByteArray.readBytes(buffer, offset, readLength); + mCurrentPosition += readLength; + return readLength; + } + + @Override + public long getPosition() { + return mCurrentPosition; + } + + @Override + public long getLength() { + return mLength - mCurrentPosition; + } + } + + /** Creates extractor instances. */ + private interface ExtractorFactory { + + /** Returns a new extractor instance. */ + Extractor createInstance(); + } + + private static class ExoToMediaParserSeekMapAdapter implements SeekMap { + + private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap; + + private ExoToMediaParserSeekMapAdapter( + com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { + mExoPlayerSeekMap = exoplayerSeekMap; + } + + @Override + public boolean isSeekable() { + return mExoPlayerSeekMap.isSeekable(); + } + + @Override + public long getDurationUs() { + return mExoPlayerSeekMap.getDurationUs(); + } + + @Override + public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) { + SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs); + return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second)); + } + } + + // Private static methods. + + private static MediaFormat toMediaFormat(Format format) { + + // TODO: Add if (value != Format.NO_VALUE); + + MediaFormat result = new MediaFormat(); + result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate); + result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount); + if (format.colorInfo != null) { + result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer); + result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange); + result.setInteger(MediaFormat.KEY_COLOR_STANDARD, format.colorInfo.colorSpace); + if (format.colorInfo.hdrStaticInfo != null) { + result.setByteBuffer( + MediaFormat.KEY_HDR_STATIC_INFO, + ByteBuffer.wrap(format.colorInfo.hdrStaticInfo)); + } + } + result.setString(MediaFormat.KEY_MIME, format.sampleMimeType); + result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate); + result.setInteger(MediaFormat.KEY_WIDTH, format.width); + result.setInteger(MediaFormat.KEY_HEIGHT, format.height); + List<byte[]> initData = format.initializationData; + if (initData != null) { + for (int i = 0; i < initData.size(); i++) { + result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i))); + } + } + result.setString(MediaFormat.KEY_LANGUAGE, format.language); + result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); + result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding); + result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees); + result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate); + + int selectionFlags = format.selectionFlags; + // We avoid setting selection flags in the MediaFormat, unless explicitly signaled by the + // extractor. + if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) { + result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1); + } + if ((selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0) { + result.setInteger(MediaFormat.KEY_IS_DEFAULT, 1); + } + if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) { + result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1); + } + + // LACK OF SUPPORT FOR: + // format.accessibilityChannel; + // format.codecs; + // format.containerMimeType; + // format.drmInitData; + // format.encoderDelay; + // format.encoderPadding; + // format.id; + // format.metadata; + // format.pixelWidthHeightRatio; + // format.roleFlags; + // format.stereoMode; + // format.subsampleOffsetUs; + return result; + } + + private static int toFrameworkFlags(int flags) { + // TODO: Implement. + return 0; + } + + private static MediaCodec.CryptoInfo toCryptoInfo(TrackOutput.CryptoData encryptionData) { + // TODO: Implement. + return null; + } + + /** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */ + private static SeekPoint toSeekPoint( + com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) { + return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position); + } + + private static void assertValidNames(@NonNull String[] names) { + for (String name : names) { + if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) { + throw new IllegalArgumentException( + "Invalid extractor name: " + + name + + ". Supported extractors are: " + + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet()) + + "."); + } + } + } + + // Static initialization. + + static { + // Using a LinkedHashMap to keep the insertion order when iterating over the keys. + LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>(); + extractorFactoriesByName.put("exo.Ac3Extractor", Ac3Extractor::new); + extractorFactoriesByName.put("exo.Ac4Extractor", Ac4Extractor::new); + extractorFactoriesByName.put("exo.AdtsExtractor", AdtsExtractor::new); + extractorFactoriesByName.put("exo.AmrExtractor", AmrExtractor::new); + extractorFactoriesByName.put("exo.FlvExtractor", FlvExtractor::new); + extractorFactoriesByName.put("exo.FragmentedMp4Extractor", FragmentedMp4Extractor::new); + extractorFactoriesByName.put("exo.MatroskaExtractor", MatroskaExtractor::new); + extractorFactoriesByName.put("exo.Mp3Extractor", Mp3Extractor::new); + extractorFactoriesByName.put("exo.Mp4Extractor", Mp4Extractor::new); + extractorFactoriesByName.put("exo.OggExtractor", OggExtractor::new); + extractorFactoriesByName.put("exo.PsExtractor", PsExtractor::new); + extractorFactoriesByName.put("exo.TsExtractor", TsExtractor::new); + extractorFactoriesByName.put("exo.WavExtractor", WavExtractor::new); + EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName); } } |