Loading core/java/android/widget/VideoView.java +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.content.res.Resources; import android.graphics.Canvas; import android.media.AudioManager; import android.media.Cea708CaptionRenderer; import android.media.ClosedCaptionRenderer; import android.media.MediaFormat; import android.media.MediaPlayer; Loading Loading @@ -328,6 +329,7 @@ public class VideoView extends SurfaceView context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer); controller.registerRenderer(new WebVttRenderer(context)); controller.registerRenderer(new TtmlRenderer(context)); controller.registerRenderer(new Cea708CaptionRenderer(context)); controller.registerRenderer(new ClosedCaptionRenderer(context)); mMediaPlayer.setSubtitleAnchor(controller, this); Loading core/java/com/android/internal/widget/SubtitleView.java +4 −3 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.graphics.Paint.Style; import android.graphics.RectF; import android.graphics.Typeface; import android.text.Layout.Alignment; import android.text.SpannableStringBuilder; import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; Loading @@ -54,8 +55,8 @@ public class SubtitleView extends View { /** Temporary rectangle used for computing line bounds. */ private final RectF mLineBounds = new RectF(); /** Reusable string builder used for holding text. */ private final StringBuilder mText = new StringBuilder(); /** Reusable spannable string builder used for holding text. */ private final SpannableStringBuilder mText = new SpannableStringBuilder(); private Alignment mAlignment; private TextPaint mTextPaint; Loading Loading @@ -141,7 +142,7 @@ public class SubtitleView extends View { } public void setText(CharSequence text) { mText.setLength(0); mText.clear(); mText.append(text); mHasMeasurements = false; Loading media/java/android/media/Cea708CaptionRenderer.java 0 → 100644 +2151 −0 File added.Preview size limit exceeded, changes collapsed. Show changes media/java/android/media/ClosedCaptionRenderer.java +217 −165 Original line number Diff line number Diff line Loading @@ -23,12 +23,9 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.CharacterStyle; import android.text.style.StyleSpan; import android.text.style.UnderlineSpan; Loading @@ -52,7 +49,7 @@ import java.util.Vector; /** @hide */ public class ClosedCaptionRenderer extends SubtitleController.Renderer { private final Context mContext; private ClosedCaptionWidget mRenderingWidget; private Cea608CCWidget mCCWidget; public ClosedCaptionRenderer(Context context) { mContext = context; Loading @@ -61,31 +58,35 @@ public class ClosedCaptionRenderer extends SubtitleController.Renderer { @Override public boolean supports(MediaFormat format) { if (format.containsKey(MediaFormat.KEY_MIME)) { return format.getString(MediaFormat.KEY_MIME).equals( MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608); String mimeType = format.getString(MediaFormat.KEY_MIME); return MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType); } return false; } @Override public SubtitleTrack createTrack(MediaFormat format) { if (mRenderingWidget == null) { mRenderingWidget = new ClosedCaptionWidget(mContext); String mimeType = format.getString(MediaFormat.KEY_MIME); if (MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType)) { if (mCCWidget == null) { mCCWidget = new Cea608CCWidget(mContext); } return new ClosedCaptionTrack(mRenderingWidget, format); return new Cea608CaptionTrack(mCCWidget, format); } throw new RuntimeException("No matching format: " + format.toString()); } } /** @hide */ class ClosedCaptionTrack extends SubtitleTrack { private final ClosedCaptionWidget mRenderingWidget; private final CCParser mCCParser; class Cea608CaptionTrack extends SubtitleTrack { private final Cea608CCParser mCCParser; private final Cea608CCWidget mRenderingWidget; ClosedCaptionTrack(ClosedCaptionWidget renderingWidget, MediaFormat format) { Cea608CaptionTrack(Cea608CCWidget renderingWidget, MediaFormat format) { super(format); mRenderingWidget = renderingWidget; mCCParser = new CCParser(renderingWidget); mCCParser = new Cea608CCParser(mRenderingWidget); } @Override Loading @@ -104,6 +105,149 @@ class ClosedCaptionTrack extends SubtitleTrack { } } /** * Abstract widget class to render a closed caption track. * * @hide */ abstract class ClosedCaptionWidget extends ViewGroup implements SubtitleTrack.RenderingWidget { /** @hide */ interface ClosedCaptionLayout { void setCaptionStyle(CaptionStyle captionStyle); void setFontScale(float scale); } private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT; /** Captioning manager, used to obtain and track caption properties. */ private final CaptioningManager mManager; /** Current caption style. */ protected CaptionStyle mCaptionStyle; /** Callback for rendering changes. */ protected OnChangedListener mListener; /** Concrete layout of CC. */ protected ClosedCaptionLayout mClosedCaptionLayout; /** Whether a caption style change listener is registered. */ private boolean mHasChangeListener; public ClosedCaptionWidget(Context context) { this(context, null); } public ClosedCaptionWidget(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); // Cannot render text over video when layer type is hardware. setLayerType(View.LAYER_TYPE_SOFTWARE, null); mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle()); mClosedCaptionLayout = createCaptionLayout(context); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); mClosedCaptionLayout.setFontScale(mManager.getFontScale()); addView((ViewGroup) mClosedCaptionLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); requestLayout(); } public abstract ClosedCaptionLayout createCaptionLayout(Context context); @Override public void setOnChangedListener(OnChangedListener listener) { mListener = listener; } @Override public void setSize(int width, int height) { final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measure(widthSpec, heightSpec); layout(0, 0, width, height); } @Override public void setVisible(boolean visible) { if (visible) { setVisibility(View.VISIBLE); } else { setVisibility(View.GONE); } manageChangeListener(); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); manageChangeListener(); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); manageChangeListener(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ((ViewGroup) mClosedCaptionLayout).measure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { ((ViewGroup) mClosedCaptionLayout).layout(l, t, r, b); } /** * Manages whether this renderer is listening for caption style changes. */ private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() { @Override public void onUserStyleChanged(CaptionStyle userStyle) { mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); } @Override public void onFontScaleChanged(float fontScale) { mClosedCaptionLayout.setFontScale(fontScale); } }; private void manageChangeListener() { final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE; if (mHasChangeListener != needsListener) { mHasChangeListener = needsListener; if (needsListener) { mManager.addCaptioningChangeListener(mCaptioningListener); } else { mManager.removeCaptioningChangeListener(mCaptioningListener); } } } } /** * @hide * Loading @@ -113,11 +257,11 @@ class ClosedCaptionTrack extends SubtitleTrack { * display change with styled text for rendering. * */ class CCParser { class Cea608CCParser { public static final int MAX_ROWS = 15; public static final int MAX_COLS = 32; private static final String TAG = "CCParser"; private static final String TAG = "Cea608CCParser"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int INVALID = -1; Loading Loading @@ -160,11 +304,11 @@ class CCParser { private CCMemory mNonDisplay = new CCMemory(); private CCMemory mTextMem = new CCMemory(); CCParser(DisplayListener listener) { Cea608CCParser(DisplayListener listener) { mListener = listener; } void parse(byte[] data) { public void parse(byte[] data) { CCData[] ccData = CCData.fromByteArray(data); for (int i = 0; i < ccData.length; i++) { Loading @@ -184,8 +328,8 @@ class CCParser { } interface DisplayListener { public void onDisplayChanged(SpannableStringBuilder[] styledTexts); public CaptionStyle getCaptionStyle(); void onDisplayChanged(SpannableStringBuilder[] styledTexts); CaptionStyle getCaptionStyle(); } private CCMemory getMemory() { Loading Loading @@ -480,6 +624,33 @@ class CCParser { } } /** * Mutable version of BackgroundSpan to facilitate text rendering with edge styles. * * @hide */ public static class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance { private int mColor; public MutableBackgroundColorSpan(int color) { mColor = color; } public void setBackgroundColor(int color) { mColor = color; } public int getBackgroundColor() { return mColor; } @Override public void updateDrawState(TextPaint ds) { ds.bgColor = mColor; } } /* CCLineBuilder keeps track of displayable chars, as well as * MidRow styles and PACs, for a single line of CC memory. * Loading Loading @@ -682,8 +853,7 @@ class CCParser { } SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) { ArrayList<SpannableStringBuilder> rows = new ArrayList<SpannableStringBuilder>(MAX_ROWS); ArrayList<SpannableStringBuilder> rows = new ArrayList<>(MAX_ROWS); for (int i = 1; i <= MAX_ROWS; i++) { rows.add(mLines[i] != null ? mLines[i].getStyledText(captionStyle) : null); Loading Loading @@ -1043,128 +1213,40 @@ class CCParser { } } /** * Mutable version of BackgroundSpan to facilitate text rendering with edge * styles. * * @hide */ class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance { private int mColor; public MutableBackgroundColorSpan(int color) { mColor = color; } public void setBackgroundColor(int color) { mColor = color; } public int getBackgroundColor() { return mColor; } @Override public void updateDrawState(TextPaint ds) { ds.bgColor = mColor; } } /** * Widget capable of rendering CEA-608 closed captions. * * @hide */ class ClosedCaptionWidget extends ViewGroup implements SubtitleTrack.RenderingWidget, CCParser.DisplayListener { private static final String TAG = "ClosedCaptionWidget"; class Cea608CCWidget extends ClosedCaptionWidget implements Cea608CCParser.DisplayListener { private static final Rect mTextBounds = new Rect(); private static final String mDummyText = "1234567890123456789012345678901234"; private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT; /** Captioning manager, used to obtain and track caption properties. */ private final CaptioningManager mManager; /** Callback for rendering changes. */ private OnChangedListener mListener; /** Current caption style. */ private CaptionStyle mCaptionStyle; /* Closed caption layout. */ private CCLayout mClosedCaptionLayout; /** Whether a caption style change listener is registered. */ private boolean mHasChangeListener; public ClosedCaptionWidget(Context context) { public Cea608CCWidget(Context context) { this(context, null); } public ClosedCaptionWidget(Context context, AttributeSet attrs) { this(context, null, 0); } public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // Cannot render text over video when layer type is hardware. setLayerType(View.LAYER_TYPE_SOFTWARE, null); mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle()); mClosedCaptionLayout = new CCLayout(context); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); addView(mClosedCaptionLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); requestLayout(); } @Override public void setOnChangedListener(OnChangedListener listener) { mListener = listener; } @Override public void setSize(int width, int height) { final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measure(widthSpec, heightSpec); layout(0, 0, width, height); } @Override public void setVisible(boolean visible) { if (visible) { setVisibility(View.VISIBLE); } else { setVisibility(View.GONE); public Cea608CCWidget(Context context, AttributeSet attrs) { this(context, attrs, 0); } manageChangeListener(); public Cea608CCWidget(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); manageChangeListener(); public Cea608CCWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); manageChangeListener(); public ClosedCaptionLayout createCaptionLayout(Context context) { return new CCLayout(context); } @Override public void onDisplayChanged(SpannableStringBuilder[] styledTexts) { mClosedCaptionLayout.update(styledTexts); ((CCLayout) mClosedCaptionLayout).update(styledTexts); if (mListener != null) { mListener.onChanged(this); Loading @@ -1176,41 +1258,6 @@ class ClosedCaptionWidget extends ViewGroup implements return mCaptionStyle; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mClosedCaptionLayout.measure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mClosedCaptionLayout.layout(l, t, r, b); } /** * Manages whether this renderer is listening for caption style changes. */ private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() { @Override public void onUserStyleChanged(CaptionStyle userStyle) { mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); } }; private void manageChangeListener() { final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE; if (mHasChangeListener != needsListener) { mHasChangeListener = needsListener; if (needsListener) { mManager.addCaptioningChangeListener(mCaptioningListener); } else { mManager.removeCaptioningChangeListener(mCaptioningListener); } } } private static class CCLineBox extends TextView { private static final float FONT_PADDING_RATIO = 0.75f; private static final float EDGE_OUTLINE_RATIO = 0.1f; Loading Loading @@ -1260,8 +1307,7 @@ class ClosedCaptionWidget extends ViewGroup implements @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { float fontSize = MeasureSpec.getSize(heightMeasureSpec) * FONT_PADDING_RATIO; float fontSize = MeasureSpec.getSize(heightMeasureSpec) * FONT_PADDING_RATIO; setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); mOutlineWidth = EDGE_OUTLINE_RATIO * fontSize + 1.0f; Loading Loading @@ -1358,8 +1404,8 @@ class ClosedCaptionWidget extends ViewGroup implements CharSequence text = getText(); if (text instanceof Spannable) { Spannable spannable = (Spannable) text; MutableBackgroundColorSpan[] bgSpans = spannable.getSpans( 0, spannable.length(), MutableBackgroundColorSpan.class); Cea608CCParser.MutableBackgroundColorSpan[] bgSpans = spannable.getSpans( 0, spannable.length(), Cea608CCParser.MutableBackgroundColorSpan.class); for (int i = 0; i < bgSpans.length; i++) { bgSpans[i].setBackgroundColor(color); } Loading @@ -1367,8 +1413,8 @@ class ClosedCaptionWidget extends ViewGroup implements } } private static class CCLayout extends LinearLayout { private static final int MAX_ROWS = CCParser.MAX_ROWS; private static class CCLayout extends LinearLayout implements ClosedCaptionLayout { private static final int MAX_ROWS = Cea608CCParser.MAX_ROWS; private static final float SAFE_AREA_RATIO = 0.9f; private final CCLineBox[] mLineBoxes = new CCLineBox[MAX_ROWS]; Loading @@ -1383,12 +1429,18 @@ class ClosedCaptionWidget extends ViewGroup implements } } void setCaptionStyle(CaptionStyle captionStyle) { @Override public void setCaptionStyle(CaptionStyle captionStyle) { for (int i = 0; i < MAX_ROWS; i++) { mLineBoxes[i].setCaptionStyle(captionStyle); } } @Override public void setFontScale(float fontScale) { // Ignores the font scale changes of the system wide CC preference. } void update(SpannableStringBuilder[] textBuffer) { for (int i = 0; i < MAX_ROWS; i++) { if (textBuffer[i] != null) { Loading Loading @@ -1455,4 +1507,4 @@ class ClosedCaptionWidget extends ViewGroup implements } } } }; } media/java/android/media/MediaPlayer.java +6 −0 Original line number Diff line number Diff line Loading @@ -2127,6 +2127,12 @@ public class MediaPlayer implements SubtitleController.Listener */ public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608"; /** * MIME type for CEA-708 closed caption data. * @hide */ public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708"; /* * A helper function to check if the mime type is supported by media framework. */ Loading Loading
core/java/android/widget/VideoView.java +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.content.res.Resources; import android.graphics.Canvas; import android.media.AudioManager; import android.media.Cea708CaptionRenderer; import android.media.ClosedCaptionRenderer; import android.media.MediaFormat; import android.media.MediaPlayer; Loading Loading @@ -328,6 +329,7 @@ public class VideoView extends SurfaceView context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer); controller.registerRenderer(new WebVttRenderer(context)); controller.registerRenderer(new TtmlRenderer(context)); controller.registerRenderer(new Cea708CaptionRenderer(context)); controller.registerRenderer(new ClosedCaptionRenderer(context)); mMediaPlayer.setSubtitleAnchor(controller, this); Loading
core/java/com/android/internal/widget/SubtitleView.java +4 −3 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.graphics.Paint.Style; import android.graphics.RectF; import android.graphics.Typeface; import android.text.Layout.Alignment; import android.text.SpannableStringBuilder; import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; Loading @@ -54,8 +55,8 @@ public class SubtitleView extends View { /** Temporary rectangle used for computing line bounds. */ private final RectF mLineBounds = new RectF(); /** Reusable string builder used for holding text. */ private final StringBuilder mText = new StringBuilder(); /** Reusable spannable string builder used for holding text. */ private final SpannableStringBuilder mText = new SpannableStringBuilder(); private Alignment mAlignment; private TextPaint mTextPaint; Loading Loading @@ -141,7 +142,7 @@ public class SubtitleView extends View { } public void setText(CharSequence text) { mText.setLength(0); mText.clear(); mText.append(text); mHasMeasurements = false; Loading
media/java/android/media/Cea708CaptionRenderer.java 0 → 100644 +2151 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
media/java/android/media/ClosedCaptionRenderer.java +217 −165 Original line number Diff line number Diff line Loading @@ -23,12 +23,9 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.CharacterStyle; import android.text.style.StyleSpan; import android.text.style.UnderlineSpan; Loading @@ -52,7 +49,7 @@ import java.util.Vector; /** @hide */ public class ClosedCaptionRenderer extends SubtitleController.Renderer { private final Context mContext; private ClosedCaptionWidget mRenderingWidget; private Cea608CCWidget mCCWidget; public ClosedCaptionRenderer(Context context) { mContext = context; Loading @@ -61,31 +58,35 @@ public class ClosedCaptionRenderer extends SubtitleController.Renderer { @Override public boolean supports(MediaFormat format) { if (format.containsKey(MediaFormat.KEY_MIME)) { return format.getString(MediaFormat.KEY_MIME).equals( MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608); String mimeType = format.getString(MediaFormat.KEY_MIME); return MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType); } return false; } @Override public SubtitleTrack createTrack(MediaFormat format) { if (mRenderingWidget == null) { mRenderingWidget = new ClosedCaptionWidget(mContext); String mimeType = format.getString(MediaFormat.KEY_MIME); if (MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType)) { if (mCCWidget == null) { mCCWidget = new Cea608CCWidget(mContext); } return new ClosedCaptionTrack(mRenderingWidget, format); return new Cea608CaptionTrack(mCCWidget, format); } throw new RuntimeException("No matching format: " + format.toString()); } } /** @hide */ class ClosedCaptionTrack extends SubtitleTrack { private final ClosedCaptionWidget mRenderingWidget; private final CCParser mCCParser; class Cea608CaptionTrack extends SubtitleTrack { private final Cea608CCParser mCCParser; private final Cea608CCWidget mRenderingWidget; ClosedCaptionTrack(ClosedCaptionWidget renderingWidget, MediaFormat format) { Cea608CaptionTrack(Cea608CCWidget renderingWidget, MediaFormat format) { super(format); mRenderingWidget = renderingWidget; mCCParser = new CCParser(renderingWidget); mCCParser = new Cea608CCParser(mRenderingWidget); } @Override Loading @@ -104,6 +105,149 @@ class ClosedCaptionTrack extends SubtitleTrack { } } /** * Abstract widget class to render a closed caption track. * * @hide */ abstract class ClosedCaptionWidget extends ViewGroup implements SubtitleTrack.RenderingWidget { /** @hide */ interface ClosedCaptionLayout { void setCaptionStyle(CaptionStyle captionStyle); void setFontScale(float scale); } private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT; /** Captioning manager, used to obtain and track caption properties. */ private final CaptioningManager mManager; /** Current caption style. */ protected CaptionStyle mCaptionStyle; /** Callback for rendering changes. */ protected OnChangedListener mListener; /** Concrete layout of CC. */ protected ClosedCaptionLayout mClosedCaptionLayout; /** Whether a caption style change listener is registered. */ private boolean mHasChangeListener; public ClosedCaptionWidget(Context context) { this(context, null); } public ClosedCaptionWidget(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); // Cannot render text over video when layer type is hardware. setLayerType(View.LAYER_TYPE_SOFTWARE, null); mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle()); mClosedCaptionLayout = createCaptionLayout(context); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); mClosedCaptionLayout.setFontScale(mManager.getFontScale()); addView((ViewGroup) mClosedCaptionLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); requestLayout(); } public abstract ClosedCaptionLayout createCaptionLayout(Context context); @Override public void setOnChangedListener(OnChangedListener listener) { mListener = listener; } @Override public void setSize(int width, int height) { final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measure(widthSpec, heightSpec); layout(0, 0, width, height); } @Override public void setVisible(boolean visible) { if (visible) { setVisibility(View.VISIBLE); } else { setVisibility(View.GONE); } manageChangeListener(); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); manageChangeListener(); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); manageChangeListener(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ((ViewGroup) mClosedCaptionLayout).measure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { ((ViewGroup) mClosedCaptionLayout).layout(l, t, r, b); } /** * Manages whether this renderer is listening for caption style changes. */ private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() { @Override public void onUserStyleChanged(CaptionStyle userStyle) { mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); } @Override public void onFontScaleChanged(float fontScale) { mClosedCaptionLayout.setFontScale(fontScale); } }; private void manageChangeListener() { final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE; if (mHasChangeListener != needsListener) { mHasChangeListener = needsListener; if (needsListener) { mManager.addCaptioningChangeListener(mCaptioningListener); } else { mManager.removeCaptioningChangeListener(mCaptioningListener); } } } } /** * @hide * Loading @@ -113,11 +257,11 @@ class ClosedCaptionTrack extends SubtitleTrack { * display change with styled text for rendering. * */ class CCParser { class Cea608CCParser { public static final int MAX_ROWS = 15; public static final int MAX_COLS = 32; private static final String TAG = "CCParser"; private static final String TAG = "Cea608CCParser"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int INVALID = -1; Loading Loading @@ -160,11 +304,11 @@ class CCParser { private CCMemory mNonDisplay = new CCMemory(); private CCMemory mTextMem = new CCMemory(); CCParser(DisplayListener listener) { Cea608CCParser(DisplayListener listener) { mListener = listener; } void parse(byte[] data) { public void parse(byte[] data) { CCData[] ccData = CCData.fromByteArray(data); for (int i = 0; i < ccData.length; i++) { Loading @@ -184,8 +328,8 @@ class CCParser { } interface DisplayListener { public void onDisplayChanged(SpannableStringBuilder[] styledTexts); public CaptionStyle getCaptionStyle(); void onDisplayChanged(SpannableStringBuilder[] styledTexts); CaptionStyle getCaptionStyle(); } private CCMemory getMemory() { Loading Loading @@ -480,6 +624,33 @@ class CCParser { } } /** * Mutable version of BackgroundSpan to facilitate text rendering with edge styles. * * @hide */ public static class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance { private int mColor; public MutableBackgroundColorSpan(int color) { mColor = color; } public void setBackgroundColor(int color) { mColor = color; } public int getBackgroundColor() { return mColor; } @Override public void updateDrawState(TextPaint ds) { ds.bgColor = mColor; } } /* CCLineBuilder keeps track of displayable chars, as well as * MidRow styles and PACs, for a single line of CC memory. * Loading Loading @@ -682,8 +853,7 @@ class CCParser { } SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) { ArrayList<SpannableStringBuilder> rows = new ArrayList<SpannableStringBuilder>(MAX_ROWS); ArrayList<SpannableStringBuilder> rows = new ArrayList<>(MAX_ROWS); for (int i = 1; i <= MAX_ROWS; i++) { rows.add(mLines[i] != null ? mLines[i].getStyledText(captionStyle) : null); Loading Loading @@ -1043,128 +1213,40 @@ class CCParser { } } /** * Mutable version of BackgroundSpan to facilitate text rendering with edge * styles. * * @hide */ class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance { private int mColor; public MutableBackgroundColorSpan(int color) { mColor = color; } public void setBackgroundColor(int color) { mColor = color; } public int getBackgroundColor() { return mColor; } @Override public void updateDrawState(TextPaint ds) { ds.bgColor = mColor; } } /** * Widget capable of rendering CEA-608 closed captions. * * @hide */ class ClosedCaptionWidget extends ViewGroup implements SubtitleTrack.RenderingWidget, CCParser.DisplayListener { private static final String TAG = "ClosedCaptionWidget"; class Cea608CCWidget extends ClosedCaptionWidget implements Cea608CCParser.DisplayListener { private static final Rect mTextBounds = new Rect(); private static final String mDummyText = "1234567890123456789012345678901234"; private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT; /** Captioning manager, used to obtain and track caption properties. */ private final CaptioningManager mManager; /** Callback for rendering changes. */ private OnChangedListener mListener; /** Current caption style. */ private CaptionStyle mCaptionStyle; /* Closed caption layout. */ private CCLayout mClosedCaptionLayout; /** Whether a caption style change listener is registered. */ private boolean mHasChangeListener; public ClosedCaptionWidget(Context context) { public Cea608CCWidget(Context context) { this(context, null); } public ClosedCaptionWidget(Context context, AttributeSet attrs) { this(context, null, 0); } public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // Cannot render text over video when layer type is hardware. setLayerType(View.LAYER_TYPE_SOFTWARE, null); mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle()); mClosedCaptionLayout = new CCLayout(context); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); addView(mClosedCaptionLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); requestLayout(); } @Override public void setOnChangedListener(OnChangedListener listener) { mListener = listener; } @Override public void setSize(int width, int height) { final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measure(widthSpec, heightSpec); layout(0, 0, width, height); } @Override public void setVisible(boolean visible) { if (visible) { setVisibility(View.VISIBLE); } else { setVisibility(View.GONE); public Cea608CCWidget(Context context, AttributeSet attrs) { this(context, attrs, 0); } manageChangeListener(); public Cea608CCWidget(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); manageChangeListener(); public Cea608CCWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); manageChangeListener(); public ClosedCaptionLayout createCaptionLayout(Context context) { return new CCLayout(context); } @Override public void onDisplayChanged(SpannableStringBuilder[] styledTexts) { mClosedCaptionLayout.update(styledTexts); ((CCLayout) mClosedCaptionLayout).update(styledTexts); if (mListener != null) { mListener.onChanged(this); Loading @@ -1176,41 +1258,6 @@ class ClosedCaptionWidget extends ViewGroup implements return mCaptionStyle; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mClosedCaptionLayout.measure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mClosedCaptionLayout.layout(l, t, r, b); } /** * Manages whether this renderer is listening for caption style changes. */ private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() { @Override public void onUserStyleChanged(CaptionStyle userStyle) { mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); } }; private void manageChangeListener() { final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE; if (mHasChangeListener != needsListener) { mHasChangeListener = needsListener; if (needsListener) { mManager.addCaptioningChangeListener(mCaptioningListener); } else { mManager.removeCaptioningChangeListener(mCaptioningListener); } } } private static class CCLineBox extends TextView { private static final float FONT_PADDING_RATIO = 0.75f; private static final float EDGE_OUTLINE_RATIO = 0.1f; Loading Loading @@ -1260,8 +1307,7 @@ class ClosedCaptionWidget extends ViewGroup implements @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { float fontSize = MeasureSpec.getSize(heightMeasureSpec) * FONT_PADDING_RATIO; float fontSize = MeasureSpec.getSize(heightMeasureSpec) * FONT_PADDING_RATIO; setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); mOutlineWidth = EDGE_OUTLINE_RATIO * fontSize + 1.0f; Loading Loading @@ -1358,8 +1404,8 @@ class ClosedCaptionWidget extends ViewGroup implements CharSequence text = getText(); if (text instanceof Spannable) { Spannable spannable = (Spannable) text; MutableBackgroundColorSpan[] bgSpans = spannable.getSpans( 0, spannable.length(), MutableBackgroundColorSpan.class); Cea608CCParser.MutableBackgroundColorSpan[] bgSpans = spannable.getSpans( 0, spannable.length(), Cea608CCParser.MutableBackgroundColorSpan.class); for (int i = 0; i < bgSpans.length; i++) { bgSpans[i].setBackgroundColor(color); } Loading @@ -1367,8 +1413,8 @@ class ClosedCaptionWidget extends ViewGroup implements } } private static class CCLayout extends LinearLayout { private static final int MAX_ROWS = CCParser.MAX_ROWS; private static class CCLayout extends LinearLayout implements ClosedCaptionLayout { private static final int MAX_ROWS = Cea608CCParser.MAX_ROWS; private static final float SAFE_AREA_RATIO = 0.9f; private final CCLineBox[] mLineBoxes = new CCLineBox[MAX_ROWS]; Loading @@ -1383,12 +1429,18 @@ class ClosedCaptionWidget extends ViewGroup implements } } void setCaptionStyle(CaptionStyle captionStyle) { @Override public void setCaptionStyle(CaptionStyle captionStyle) { for (int i = 0; i < MAX_ROWS; i++) { mLineBoxes[i].setCaptionStyle(captionStyle); } } @Override public void setFontScale(float fontScale) { // Ignores the font scale changes of the system wide CC preference. } void update(SpannableStringBuilder[] textBuffer) { for (int i = 0; i < MAX_ROWS; i++) { if (textBuffer[i] != null) { Loading Loading @@ -1455,4 +1507,4 @@ class ClosedCaptionWidget extends ViewGroup implements } } } }; }
media/java/android/media/MediaPlayer.java +6 −0 Original line number Diff line number Diff line Loading @@ -2127,6 +2127,12 @@ public class MediaPlayer implements SubtitleController.Listener */ public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608"; /** * MIME type for CEA-708 closed caption data. * @hide */ public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708"; /* * A helper function to check if the mime type is supported by media framework. */ Loading