Merge "Add Audio Presentation APIs to TIF"
This commit is contained in:
@@ -21369,7 +21369,8 @@ package android.media {
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
|
||||
}
|
||||
|
||||
public final class AudioPresentation {
|
||||
public final class AudioPresentation implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public java.util.Map<java.util.Locale,java.lang.String> getLabels();
|
||||
method public java.util.Locale getLocale();
|
||||
method public int getMasteringIndication();
|
||||
@@ -21378,6 +21379,7 @@ package android.media {
|
||||
method public boolean hasAudioDescription();
|
||||
method public boolean hasDialogueEnhancement();
|
||||
method public boolean hasSpokenSubtitles();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field public static final int CONTENT_COMMENTARY = 5; // 0x5
|
||||
field public static final int CONTENT_DIALOG = 4; // 0x4
|
||||
field public static final int CONTENT_EMERGENCY = 6; // 0x6
|
||||
@@ -21387,11 +21389,14 @@ package android.media {
|
||||
field public static final int CONTENT_UNKNOWN = -1; // 0xffffffff
|
||||
field public static final int CONTENT_VISUALLY_IMPAIRED = 2; // 0x2
|
||||
field public static final int CONTENT_VOICEOVER = 7; // 0x7
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPresentation> CREATOR;
|
||||
field public static final int MASTERED_FOR_3D = 3; // 0x3
|
||||
field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4
|
||||
field public static final int MASTERED_FOR_STEREO = 1; // 0x1
|
||||
field public static final int MASTERED_FOR_SURROUND = 2; // 0x2
|
||||
field public static final int MASTERING_NOT_INDICATED = 0; // 0x0
|
||||
field public static final int PRESENTATION_ID_UNKNOWN = -1; // 0xffffffff
|
||||
field public static final int PROGRAM_ID_UNKNOWN = -1; // 0xffffffff
|
||||
}
|
||||
|
||||
public static final class AudioPresentation.Builder {
|
||||
@@ -27063,6 +27068,8 @@ package android.media.tv {
|
||||
method public void layoutSurface(int, int, int, int);
|
||||
method public void notifyAdResponse(@NonNull android.media.tv.AdResponse);
|
||||
method public void notifyAitInfoUpdated(@NonNull android.media.tv.AitInfo);
|
||||
method public void notifyAudioPresentationChanged(@NonNull java.util.List<android.media.AudioPresentation>);
|
||||
method public void notifyAudioPresentationSelected(int, int);
|
||||
method public void notifyBroadcastInfoResponse(@NonNull android.media.tv.BroadcastInfoResponse);
|
||||
method public void notifyChannelRetuned(android.net.Uri);
|
||||
method public void notifyContentAllowed();
|
||||
@@ -27086,6 +27093,7 @@ package android.media.tv {
|
||||
method public void onRemoveBroadcastInfo(int);
|
||||
method public void onRequestAd(@NonNull android.media.tv.AdRequest);
|
||||
method public void onRequestBroadcastInfo(@NonNull android.media.tv.BroadcastInfoRequest);
|
||||
method public boolean onSelectAudioPresentation(int, int);
|
||||
method public boolean onSelectTrack(int, @Nullable String);
|
||||
method public abstract void onSetCaptionEnabled(boolean);
|
||||
method public void onSetInteractiveAppNotificationEnabled(boolean);
|
||||
@@ -27215,11 +27223,13 @@ package android.media.tv {
|
||||
ctor public TvView(android.content.Context, android.util.AttributeSet);
|
||||
ctor public TvView(android.content.Context, android.util.AttributeSet, int);
|
||||
method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
|
||||
method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
|
||||
method public String getSelectedTrack(int);
|
||||
method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
|
||||
method public boolean onUnhandledInputEvent(android.view.InputEvent);
|
||||
method public void overrideTvAppAttributionSource(@NonNull android.content.AttributionSource);
|
||||
method public void reset();
|
||||
method public void selectAudioPresentation(int, int);
|
||||
method public void selectTrack(int, String);
|
||||
method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
|
||||
method public void setCallback(@Nullable android.media.tv.TvView.TvInputCallback);
|
||||
@@ -27252,6 +27262,8 @@ package android.media.tv {
|
||||
public abstract static class TvView.TvInputCallback {
|
||||
ctor public TvView.TvInputCallback();
|
||||
method public void onAitInfoUpdated(@NonNull String, @NonNull android.media.tv.AitInfo);
|
||||
method public void onAudioPresentationSelected(@NonNull String, int, int);
|
||||
method public void onAudioPresentationsChanged(@NonNull String, @NonNull java.util.List<android.media.AudioPresentation>);
|
||||
method public void onChannelRetuned(String, android.net.Uri);
|
||||
method public void onConnectionFailed(String);
|
||||
method public void onContentAllowed(String);
|
||||
|
||||
19
media/java/android/media/AudioPresentation.aidl
Normal file
19
media/java/android/media/AudioPresentation.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
parcelable AudioPresentation;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,9 @@ package android.media;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.icu.util.ULocale;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -49,7 +52,7 @@ import java.util.Objects;
|
||||
* Applications that parse media streams and extract presentation information on their own
|
||||
* can create instances of AudioPresentation by using {@link AudioPresentation.Builder} class.
|
||||
*/
|
||||
public final class AudioPresentation {
|
||||
public final class AudioPresentation implements Parcelable {
|
||||
private final int mPresentationId;
|
||||
private final int mProgramId;
|
||||
private final ULocale mLanguage;
|
||||
@@ -126,7 +129,7 @@ public final class AudioPresentation {
|
||||
private final boolean mAudioDescriptionAvailable;
|
||||
private final boolean mSpokenSubtitlesAvailable;
|
||||
private final boolean mDialogueEnhancementAvailable;
|
||||
private final Map<ULocale, CharSequence> mLabels;
|
||||
private final HashMap<ULocale, String> mLabels;
|
||||
|
||||
/**
|
||||
* No preferred reproduction channel layout.
|
||||
@@ -160,9 +163,14 @@ public final class AudioPresentation {
|
||||
public static final int MASTERED_FOR_HEADPHONE = 4;
|
||||
|
||||
/**
|
||||
* This ID is reserved. No items can be explicitly assigned this ID.
|
||||
* Unknown audio presentation ID, this indicates audio presentation ID is not selected.
|
||||
*/
|
||||
private static final int UNKNOWN_ID = -1;
|
||||
public static final int PRESENTATION_ID_UNKNOWN = -1;
|
||||
|
||||
/**
|
||||
* Unknown audio program ID, this indicates audio program ID is not selected.
|
||||
*/
|
||||
public static final int PROGRAM_ID_UNKNOWN = -1;
|
||||
|
||||
/**
|
||||
* This allows an application developer to construct an AudioPresentation object with all the
|
||||
@@ -191,7 +199,7 @@ public final class AudioPresentation {
|
||||
boolean audioDescriptionAvailable,
|
||||
boolean spokenSubtitlesAvailable,
|
||||
boolean dialogueEnhancementAvailable,
|
||||
@NonNull Map<ULocale, CharSequence> labels) {
|
||||
@NonNull Map<ULocale, String> labels) {
|
||||
mPresentationId = presentationId;
|
||||
mProgramId = programId;
|
||||
mLanguage = language;
|
||||
@@ -199,7 +207,18 @@ public final class AudioPresentation {
|
||||
mAudioDescriptionAvailable = audioDescriptionAvailable;
|
||||
mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
|
||||
mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
|
||||
mLabels = new HashMap<ULocale, CharSequence>(labels);
|
||||
mLabels = new HashMap<ULocale, String>(labels);
|
||||
}
|
||||
|
||||
private AudioPresentation(@NonNull Parcel in) {
|
||||
mPresentationId = in.readInt();
|
||||
mProgramId = in.readInt();
|
||||
mLanguage = in.readSerializable(ULocale.class.getClassLoader(), ULocale.class);
|
||||
mMasteringIndication = in.readInt();
|
||||
mAudioDescriptionAvailable = in.readBoolean();
|
||||
mSpokenSubtitlesAvailable = in.readBoolean();
|
||||
mDialogueEnhancementAvailable = in.readBoolean();
|
||||
mLabels = in.readSerializable(HashMap.class.getClassLoader(), HashMap.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,13 +244,13 @@ public final class AudioPresentation {
|
||||
*/
|
||||
public Map<Locale, String> getLabels() {
|
||||
Map<Locale, String> localeLabels = new HashMap<Locale, String>(mLabels.size());
|
||||
for (Map.Entry<ULocale, CharSequence> entry : mLabels.entrySet()) {
|
||||
localeLabels.put(entry.getKey().toLocale(), entry.getValue().toString());
|
||||
for (Map.Entry<ULocale, String> entry : mLabels.entrySet()) {
|
||||
localeLabels.put(entry.getKey().toLocale(), entry.getValue());
|
||||
}
|
||||
return localeLabels;
|
||||
}
|
||||
|
||||
private Map<ULocale, CharSequence> getULabels() {
|
||||
private Map<ULocale, String> getULabels() {
|
||||
return mLabels;
|
||||
}
|
||||
|
||||
@@ -335,13 +354,13 @@ public final class AudioPresentation {
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final int mPresentationId;
|
||||
private int mProgramId = UNKNOWN_ID;
|
||||
private int mProgramId = PROGRAM_ID_UNKNOWN;
|
||||
private ULocale mLanguage = new ULocale("");
|
||||
private int mMasteringIndication = MASTERING_NOT_INDICATED;
|
||||
private boolean mAudioDescriptionAvailable = false;
|
||||
private boolean mSpokenSubtitlesAvailable = false;
|
||||
private boolean mDialogueEnhancementAvailable = false;
|
||||
private Map<ULocale, CharSequence> mLabels = new HashMap<ULocale, CharSequence>();
|
||||
private HashMap<ULocale, String> mLabels = new HashMap<ULocale, String>();
|
||||
|
||||
/**
|
||||
* Create a {@link Builder}. Any field that should be included in the
|
||||
@@ -402,7 +421,10 @@ public final class AudioPresentation {
|
||||
* @param labels Text label indexed by its locale corresponding to the language code.
|
||||
*/
|
||||
public @NonNull Builder setLabels(@NonNull Map<ULocale, CharSequence> labels) {
|
||||
mLabels = new HashMap<ULocale, CharSequence>(labels);
|
||||
mLabels.clear();
|
||||
for (Map.Entry<ULocale, CharSequence> entry : labels.entrySet()) {
|
||||
mLabels.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -448,4 +470,35 @@ public final class AudioPresentation {
|
||||
mDialogueEnhancementAvailable, mLabels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(getPresentationId());
|
||||
dest.writeInt(getProgramId());
|
||||
dest.writeSerializable(getULocale());
|
||||
dest.writeInt(getMasteringIndication());
|
||||
dest.writeBoolean(hasAudioDescription());
|
||||
dest.writeBoolean(hasSpokenSubtitles());
|
||||
dest.writeBoolean(hasDialogueEnhancement());
|
||||
dest.writeSerializable(mLabels);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Parcelable.Creator<AudioPresentation> CREATOR =
|
||||
new Parcelable.Creator<AudioPresentation>() {
|
||||
@Override
|
||||
public AudioPresentation createFromParcel(@NonNull Parcel in) {
|
||||
return new AudioPresentation(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioPresentation[] newArray(int size) {
|
||||
return new AudioPresentation[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media.tv;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.media.AudioPresentation;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.AitInfo;
|
||||
@@ -37,6 +38,8 @@ oneway interface ITvInputClient {
|
||||
void onSessionReleased(int seq);
|
||||
void onSessionEvent(in String name, in Bundle args, int seq);
|
||||
void onChannelRetuned(in Uri channelUri, int seq);
|
||||
void onAudioPresentationsChanged(in List<AudioPresentation> AudioPresentations, int seq);
|
||||
void onAudioPresentationSelected(int presentationId, int programId, int seq);
|
||||
void onTracksChanged(in List<TvTrackInfo> tracks, int seq);
|
||||
void onTrackSelected(int type, in String trackId, int seq);
|
||||
void onVideoAvailable(int seq);
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.content.AttributionSource;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.media.AudioPresentation;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
@@ -78,6 +79,8 @@ interface ITvInputManager {
|
||||
void tune(in IBinder sessionToken, in Uri channelUri, in Bundle params, int userId);
|
||||
void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
|
||||
void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);
|
||||
void selectAudioPresentation(in IBinder sessionToken, int presentationId, int programId,
|
||||
int userId);
|
||||
|
||||
void setInteractiveAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media.tv;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.media.AudioPresentation;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
@@ -41,6 +42,7 @@ oneway interface ITvInputSession {
|
||||
void setVolume(float volume);
|
||||
void tune(in Uri channelUri, in Bundle params);
|
||||
void setCaptionEnabled(boolean enabled);
|
||||
void selectAudioPresentation(int presentationId, int programId);
|
||||
void selectTrack(int type, in String trackId);
|
||||
|
||||
void setInteractiveAppNotificationEnabled(boolean enable);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.media.tv;
|
||||
|
||||
import android.media.AudioPresentation;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.AitInfo;
|
||||
@@ -34,6 +35,8 @@ oneway interface ITvInputSessionCallback {
|
||||
void onSessionCreated(ITvInputSession session, in IBinder hardwareSessionToken);
|
||||
void onSessionEvent(in String name, in Bundle args);
|
||||
void onChannelRetuned(in Uri channelUri);
|
||||
void onAudioPresentationsChanged(in List<AudioPresentation> tvAudioPresentations);
|
||||
void onAudioPresentationSelected(int presentationId, int programId);
|
||||
void onTracksChanged(in List<TvTrackInfo> tracks);
|
||||
void onTrackSelected(int type, in String trackId);
|
||||
void onVideoAvailable();
|
||||
|
||||
@@ -74,6 +74,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 26;
|
||||
private static final int DO_REQUEST_AD = 27;
|
||||
private static final int DO_NOTIFY_AD_BUFFER = 28;
|
||||
private static final int DO_SELECT_AUDIO_PRESENTATION = 29;
|
||||
|
||||
private final boolean mIsRecordingSession;
|
||||
private final HandlerCaller mCaller;
|
||||
@@ -239,6 +240,12 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
mTvInputRecordingSessionImpl.resumeRecording((Bundle) msg.obj);
|
||||
break;
|
||||
}
|
||||
case DO_SELECT_AUDIO_PRESENTATION: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
mTvInputSessionImpl.selectAudioPresentation(args.argi1, args.argi2);
|
||||
args.recycle();
|
||||
break;
|
||||
}
|
||||
case DO_REQUEST_BROADCAST_INFO: {
|
||||
mTvInputSessionImpl.requestBroadcastInfo((BroadcastInfoRequest) msg.obj);
|
||||
break;
|
||||
@@ -274,8 +281,8 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
+ "Consider handling the tune request in a separate thread.");
|
||||
}
|
||||
if (durationMs > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
|
||||
throw new RuntimeException("Too much time to handle a request. (type=" + msg.what +
|
||||
", " + durationMs + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
|
||||
throw new RuntimeException("Too much time to handle a request. (type=" + msg.what
|
||||
+ ", " + durationMs + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,6 +328,12 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_CAPTION_ENABLED, enabled));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectAudioPresentation(int presentationId, int programId) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_SELECT_AUDIO_PRESENTATION,
|
||||
presentationId, programId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectTrack(int type, String trackId) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_SELECT_TRACK, type, trackId));
|
||||
@@ -435,7 +448,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
}
|
||||
|
||||
private final class TvInputEventReceiver extends InputEventReceiver {
|
||||
public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
|
||||
TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
|
||||
super(inputChannel, looper);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioFormat.Encoding;
|
||||
import android.media.AudioPresentation;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.tv.interactive.TvInteractiveAppManager;
|
||||
import android.net.Uri;
|
||||
@@ -548,6 +549,27 @@ public final class TvInputManager {
|
||||
public void onChannelRetuned(Session session, Uri channelUri) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the audio presentation information of the session has been changed.
|
||||
*
|
||||
* @param session A {@link TvInputManager.Session} associated with this callback.
|
||||
* @param audioPresentations An updated list of selectable audio presentations.
|
||||
*/
|
||||
public void onAudioPresentationsChanged(Session session,
|
||||
List<AudioPresentation> audioPresentations) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when an audio presentation is selected.
|
||||
*
|
||||
* @param session A {@link TvInputManager.Session} associated with this callback.
|
||||
* @param presentationId The ID of the selected audio presentation.
|
||||
* @param programId The ID of the program providing the selected audio presentation.
|
||||
*/
|
||||
public void onAudioPresentationSelected(Session session, int presentationId,
|
||||
int programId) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the track information of the session has been changed.
|
||||
*
|
||||
@@ -776,6 +798,25 @@ public final class TvInputManager {
|
||||
});
|
||||
}
|
||||
|
||||
void postAudioPresentationsChanged(final List<AudioPresentation> audioPresentations) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSessionCallback.onAudioPresentationsChanged(mSession, audioPresentations);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void postAudioPresentationSelected(final int presentationId, final int programId) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSessionCallback.onAudioPresentationSelected(mSession, presentationId,
|
||||
programId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void postTracksChanged(final List<TvTrackInfo> tracks) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
@@ -1233,6 +1274,36 @@ public final class TvInputManager {
|
||||
record.postChannelRetuned(channelUri);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations,
|
||||
int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
if (record == null) {
|
||||
Log.e(TAG, "Callback not found for seq " + seq);
|
||||
return;
|
||||
}
|
||||
if (record.mSession.updateAudioPresentations(audioPresentations)) {
|
||||
record.postAudioPresentationsChanged(audioPresentations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioPresentationSelected(int presentationId, int programId, int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
if (record == null) {
|
||||
Log.e(TAG, "Callback not found for seq " + seq);
|
||||
return;
|
||||
}
|
||||
if (record.mSession.updateAudioPresentationSelection(presentationId,
|
||||
programId)) {
|
||||
record.postAudioPresentationSelected(presentationId, programId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(List<TvTrackInfo> tracks, int seq) {
|
||||
@@ -2401,12 +2472,18 @@ public final class TvInputManager {
|
||||
|
||||
private final Object mMetadataLock = new Object();
|
||||
// @GuardedBy("mMetadataLock")
|
||||
private final List<AudioPresentation> mAudioPresentations = new ArrayList<>();
|
||||
// @GuardedBy("mMetadataLock")
|
||||
private final List<TvTrackInfo> mAudioTracks = new ArrayList<>();
|
||||
// @GuardedBy("mMetadataLock")
|
||||
private final List<TvTrackInfo> mVideoTracks = new ArrayList<>();
|
||||
// @GuardedBy("mMetadataLock")
|
||||
private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<>();
|
||||
// @GuardedBy("mMetadataLock")
|
||||
private int mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN;
|
||||
// @GuardedBy("mMetadataLock")
|
||||
private int mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN;
|
||||
// @GuardedBy("mMetadataLock")
|
||||
private String mSelectedAudioTrackId;
|
||||
// @GuardedBy("mMetadataLock")
|
||||
private String mSelectedVideoTrackId;
|
||||
@@ -2554,9 +2631,12 @@ public final class TvInputManager {
|
||||
return;
|
||||
}
|
||||
synchronized (mMetadataLock) {
|
||||
mAudioPresentations.clear();
|
||||
mAudioTracks.clear();
|
||||
mVideoTracks.clear();
|
||||
mSubtitleTracks.clear();
|
||||
mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN;
|
||||
mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN;
|
||||
mSelectedAudioTrackId = null;
|
||||
mSelectedVideoTrackId = null;
|
||||
mSelectedSubtitleTrackId = null;
|
||||
@@ -2587,6 +2667,119 @@ public final class TvInputManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an audio presentation
|
||||
*
|
||||
* @param presentationId The ID of the audio presentation to select.
|
||||
* @param programId The ID of the program offering the selected audio presentation.
|
||||
* @see #getAudioPresentations
|
||||
*/
|
||||
public void selectAudioPresentation(int presentationId, int programId) {
|
||||
synchronized (mMetadataLock) {
|
||||
if (presentationId != AudioPresentation.PRESENTATION_ID_UNKNOWN
|
||||
&& !containsAudioPresentation(mAudioPresentations, presentationId)) {
|
||||
Log.w(TAG, "Invalid audio presentation id: " + presentationId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mToken == null) {
|
||||
Log.w(TAG, "The session has been already released");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mService.selectAudioPresentation(mToken, presentationId, programId, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsAudioPresentation(List<AudioPresentation> audioPresentations,
|
||||
int presentationId) {
|
||||
synchronized (mMetadataLock) {
|
||||
for (AudioPresentation audioPresentation : audioPresentations) {
|
||||
if (audioPresentation.getPresentationId() == presentationId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of audio presentations.
|
||||
*
|
||||
* @return the list of audio presentations.
|
||||
* Returns empty AudioPresentation list if no presentations are available.
|
||||
*/
|
||||
public List<AudioPresentation> getAudioPresentations() {
|
||||
synchronized (mMetadataLock) {
|
||||
if (mAudioPresentations == null) {
|
||||
return new ArrayList<AudioPresentation>();
|
||||
}
|
||||
return new ArrayList<AudioPresentation>(mAudioPresentations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the program ID of the selected audio presentation.
|
||||
*
|
||||
* @return The ID of the program providing the selected audio presentation.
|
||||
* Returns {@value AudioPresentation.PROGRAM_ID_UNKNOWN} if no audio presentation has
|
||||
* been selected from a program.
|
||||
* @see #selectAudioPresentation
|
||||
*/
|
||||
public int getSelectedProgramId() {
|
||||
synchronized (mMetadataLock) {
|
||||
return mSelectedAudioProgramId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presentation ID of the selected audio presentation.
|
||||
*
|
||||
* @return The ID of the selected audio presentation.
|
||||
* Returns {@value AudioPresentation.PRESENTATION_ID_UNKNOWN} if no audio presentation
|
||||
* has been selected.
|
||||
* @see #selectAudioPresentation
|
||||
*/
|
||||
public int getSelectedAudioPresentationId() {
|
||||
synchronized (mMetadataLock) {
|
||||
return mSelectedAudioPresentationId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to onAudioPresentationsChanged() and updates the internal audio presentation
|
||||
* information.
|
||||
* @return true if there is an update.
|
||||
*/
|
||||
boolean updateAudioPresentations(List<AudioPresentation> audioPresentations) {
|
||||
synchronized (mMetadataLock) {
|
||||
mAudioPresentations.clear();
|
||||
for (AudioPresentation presentation : audioPresentations) {
|
||||
mAudioPresentations.add(presentation);
|
||||
}
|
||||
return !mAudioPresentations.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to onAudioPresentationSelected() and updates the internal audio presentation
|
||||
* selection information.
|
||||
* @return true if there is an update.
|
||||
*/
|
||||
boolean updateAudioPresentationSelection(int presentationId, int programId) {
|
||||
synchronized (mMetadataLock) {
|
||||
if ((programId != mSelectedAudioProgramId)
|
||||
|| (presentationId != mSelectedAudioPresentationId)) {
|
||||
mSelectedAudioPresentationId = presentationId;
|
||||
mSelectedAudioProgramId = programId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a track.
|
||||
*
|
||||
|
||||
93
media/java/android/media/tv/TvInputService.java
Executable file → Normal file
93
media/java/android/media/tv/TvInputService.java
Executable file → Normal file
@@ -32,6 +32,7 @@ import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.media.AudioPresentation;
|
||||
import android.media.PlaybackParams;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
@@ -756,6 +757,74 @@ public abstract class TvInputService extends Service {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an updated list of all audio presentations available from a Next Generation Audio
|
||||
* service. This is used by the framework to maintain the audio presentation information for
|
||||
* a given track of {@link TvTrackInfo#TYPE_AUDIO}, which in turn is used by
|
||||
* {@link TvView#getAudioPresentations} for the application to retrieve metadata for the
|
||||
* current audio track. The TV input service must call this method as soon as the audio
|
||||
* track presentation information becomes available or is updated. Note that in a case
|
||||
* where a part of the information for the current track is updated, it is not necessary
|
||||
* to create a new {@link TvTrackInfo} object with a different track ID.
|
||||
*
|
||||
* @param audioPresentations A list of audio presentation information pertaining to the
|
||||
* selected track.
|
||||
*/
|
||||
public void notifyAudioPresentationChanged(@NonNull final List<AudioPresentation>
|
||||
audioPresentations) {
|
||||
final List<AudioPresentation> ap = new ArrayList<>(audioPresentations);
|
||||
executeOrPostRunnableOnMainThread(new Runnable() {
|
||||
@MainThread
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "notifyAudioPresentationsChanged");
|
||||
}
|
||||
if (mSessionCallback != null) {
|
||||
mSessionCallback.onAudioPresentationsChanged(ap);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "error in notifyAudioPresentationsChanged", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the presentation and program IDs of the selected audio presentation. This is used
|
||||
* to inform the application that a specific audio presentation is selected. The TV input
|
||||
* service must call this method as soon as an audio presentation is selected either by
|
||||
* default or in response to a call to {@link #onSelectTrack}. The selected audio
|
||||
* presentation ID for a currently selected audio track is maintained in the framework until
|
||||
* the next call to this method even after the entire audio presentation list for the track
|
||||
* is updated (but is reset when the session is tuned to a new channel), so care must be
|
||||
* taken not to result in an obsolete track audio presentation ID.
|
||||
*
|
||||
* @param presentationId The ID of the selected audio presentation for the current track.
|
||||
* @param programId The ID of the program providing the selected audio presentation.
|
||||
* @see #onSelectAudioPresentation
|
||||
*/
|
||||
public void notifyAudioPresentationSelected(final int presentationId, final int programId) {
|
||||
executeOrPostRunnableOnMainThread(new Runnable() {
|
||||
@MainThread
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "notifyAudioPresentationSelected");
|
||||
}
|
||||
if (mSessionCallback != null) {
|
||||
mSessionCallback.onAudioPresentationSelected(presentationId, programId);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "error in notifyAudioPresentationSelected", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Informs the application that the user is allowed to watch the current program content.
|
||||
*
|
||||
@@ -1263,6 +1332,23 @@ public abstract class TvInputService extends Service {
|
||||
public void onSetInteractiveAppNotificationEnabled(boolean enabled) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an audio presentation.
|
||||
*
|
||||
* <p>On successfully selecting the audio presentation,
|
||||
* {@link #notifyAudioPresentationSelected} is invoked to provide updated information about
|
||||
* the selected audio presentation to applications.
|
||||
*
|
||||
* @param presentationId The ID of the audio presentation to select.
|
||||
* @param programId The ID of the program providing the selected audio presentation.
|
||||
* @return {@code true} if the audio presentation selection was successful,
|
||||
* {@code false} otherwise.
|
||||
* @see #notifyAudioPresentationSelected
|
||||
*/
|
||||
public boolean onSelectAudioPresentation(int presentationId, int programId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a private command sent from the application to the TV input. This can be used
|
||||
* to provide domain-specific features that are only known between certain TV inputs and
|
||||
@@ -1596,6 +1682,13 @@ public abstract class TvInputService extends Service {
|
||||
onSetCaptionEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #onSelectAudioPresentation}.
|
||||
*/
|
||||
void selectAudioPresentation(int presentationId, int programId) {
|
||||
onSelectAudioPresentation(presentationId, programId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #onSelectTrack}.
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.media.AudioPresentation;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.tv.TvInputManager.Session;
|
||||
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
|
||||
@@ -54,6 +55,7 @@ import android.view.ViewGroup;
|
||||
import android.view.ViewRootImpl;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
@@ -452,6 +454,35 @@ public class TvView extends ViewGroup {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an audio presentation.
|
||||
*
|
||||
* @param presentationId The ID of the audio presentation to select.
|
||||
* @param programId The ID of the program providing the selected audio presentation.
|
||||
* @see #getAudioPresentations
|
||||
*/
|
||||
public void selectAudioPresentation(int presentationId, int programId) {
|
||||
if (mSession != null) {
|
||||
mSession.selectAudioPresentation(presentationId, programId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of audio presentations from the selected track of type
|
||||
* {@link TvTrackInfo#TYPE_AUDIO}.
|
||||
*
|
||||
* @return the list of audio presentations from the selected audio track, or an empty list if no
|
||||
* audio presentations are available.
|
||||
* @see #selectAudioPresentation
|
||||
*/
|
||||
@NonNull
|
||||
public List<AudioPresentation> getAudioPresentations() {
|
||||
if (mSession == null) {
|
||||
return new ArrayList<AudioPresentation>();
|
||||
}
|
||||
return mSession.getAudioPresentations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a track.
|
||||
*
|
||||
@@ -996,6 +1027,27 @@ public class TvView extends ViewGroup {
|
||||
public void onChannelRetuned(String inputId, Uri channelUri) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the audio presentation information has been changed.
|
||||
*
|
||||
* @param inputId The ID of the TV input bound to this view.
|
||||
* @param audioPresentations A list of updated audio presentation information.
|
||||
*/
|
||||
public void onAudioPresentationsChanged(@NonNull String inputId,
|
||||
@NonNull List<AudioPresentation> audioPresentations) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when audio presentation selection has changed.
|
||||
*
|
||||
* @param inputId The ID of the TV input bound to this view.
|
||||
* @param presentationId The ID of the audio presentation selected.
|
||||
* @param programId The ID of the program providing the selected audio presentation.
|
||||
*/
|
||||
public void onAudioPresentationSelected(@NonNull String inputId, int presentationId,
|
||||
int programId) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the track information has been changed.
|
||||
*
|
||||
@@ -1261,6 +1313,37 @@ public class TvView extends ViewGroup {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioPresentationsChanged(Session session,
|
||||
List<AudioPresentation> audioPresentations) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onAudioPresentationsChanged(" + audioPresentations + ")");
|
||||
}
|
||||
if (this != mSessionCallback) {
|
||||
Log.w(TAG, "onAudioPresentationsChanged - session not created");
|
||||
return;
|
||||
}
|
||||
if (mCallback != null) {
|
||||
mCallback.onAudioPresentationsChanged(mInputId, audioPresentations);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioPresentationSelected(Session session, int presentationId,
|
||||
int programId) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onAudioPresentationSelected(presentationId=" + presentationId
|
||||
+ ", programId=" + programId + ")");
|
||||
}
|
||||
if (this != mSessionCallback) {
|
||||
Log.w(TAG, "onAudioPresentationSelected - session not created");
|
||||
return;
|
||||
}
|
||||
if (mCallback != null) {
|
||||
mCallback.onAudioPresentationSelected(mInputId, presentationId, programId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(Session session, List<TvTrackInfo> tracks) {
|
||||
if (DEBUG) {
|
||||
|
||||
60
services/core/java/com/android/server/tv/TvInputManagerService.java
Executable file → Normal file
60
services/core/java/com/android/server/tv/TvInputManagerService.java
Executable file → Normal file
@@ -44,6 +44,7 @@ import android.content.pm.UserInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.hdmi.HdmiControlManager;
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.media.AudioPresentation;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
@@ -1835,6 +1836,28 @@ public final class TvInputManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectAudioPresentation(IBinder sessionToken, int presentationId,
|
||||
int programId, int userId) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
|
||||
userId, "selectAudioPresentation");
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
getSessionLocked(sessionToken, callingUid,
|
||||
resolvedUserId).selectAudioPresentation(
|
||||
presentationId, programId);
|
||||
} catch (RemoteException | SessionNotFoundException e) {
|
||||
Slog.e(TAG, "error in selectAudioPresentation", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
|
||||
@@ -3359,6 +3382,43 @@ public final class TvInputManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onAudioPresentationChanged(" + audioPresentations + ")");
|
||||
}
|
||||
if (mSessionState.session == null || mSessionState.client == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mSessionState.client.onAudioPresentationsChanged(audioPresentations,
|
||||
mSessionState.seq);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "error in onAudioPresentationsChanged", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioPresentationSelected(int presentationId, int programId) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onAudioPresentationSelected(presentationId=" + presentationId
|
||||
+ ", programId=" + programId + ")");
|
||||
}
|
||||
if (mSessionState.session == null || mSessionState.client == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mSessionState.client.onAudioPresentationSelected(presentationId, programId,
|
||||
mSessionState.seq);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "error in onAudioPresentationSelected", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(List<TvTrackInfo> tracks) {
|
||||
synchronized (mLock) {
|
||||
|
||||
Reference in New Issue
Block a user