Configure Subtitle Display Method Media Player Subsystem and Interactive Application
The Subtitle Display method provides the ability to discern between image and text-based subtitles and how to render image or text. This accommodates non-4K legacy media that is not packaged with text-based subtitles in the same Interactive application.
Text-based subtitles are the preferred method on NEXT and future monitors. Text-based subtitles provide more flexibility around rendering subtitles, such as color, size and coordinates, and additionally serve to meet accessibility requirements.
This guide details how to configure subtitle displays for the Media Player subsystem and Interactive application.
Prerequisites
You will need the following:
- An Interactive application that contains media.
Subtitle Display Method Configuration
Set Subtitle_Display_Method to the
following:
0— Image rendered by the Media Player subsystem1— Image rendered by the Interactive application
Example:
Subtitle_Display_Method = 1;Media Player Subsystem
When Subtitle_Display_Method is set to 1,
the Media Player subsystem notifies the client to display a
subtitle image.
The notification includes the starting coordinates and
name of the file containing the image. The end coordinates
are set to -1. If the name of the file is
NULL, nothing displays.
On Android, the MediaPlayer
class is expected to implement
onTimedTextListener and receive a
TimedText object using
onTimedText.
Regardless of the configuration value, if the media is prepared with text-based subtitles, the notification includes text to display. The starting and ending coordinates are set to non-negative numbers.
Interactive Application
For an image-based subtitle to be rendered by the
Interactive application, MediaPlayer is notified via
OnTimedText() of the image file to display and
starting coordinates of the image. The ending coordinates
are set to -1.
When the image-based subtitle is in the
onTimedText callback of MediaPlayer, the image
path is received.
Subtitle Display Sample Code
@Override
public void onTimedText(MediaPlayer mpTimedText, TimedText text) {
if (text == null)
return;
if (text == null) {
return;
}
Rect rect = text.getBounds();
String content = text.getText();
if (content == null)
content = "";
if (rect.bottom >= 0 && rect.left >= 0 && rect.right >= 0 && rect.top >=
0) {
// this is text based subtitle.
Log.d(TAG, "onTimedText text subtitle text = " + content + "
bottom = " + rect.bottom +
" left = " + rect.left + " right = " + rect.right + " top = " +
rect.top);
// add code to display subtitle at the given position
} else {
// this is image based subtitle.
Log.d(TAG, "onTimedText imagePath = " + content + " bottom = " +
rect.bottom +
" left = " + rect.left + " right = " + rect.right + " top = " +
rect.top);
mSubtitleImageView.setVisibility(View.VISIBLE);
String path = content;
int left = text.getBounds().left;
int top = text.getBounds().top;
Log.i(APP, "onTimedText location - " + path + " left - " + left +
" top - " + top + " bottom = " +
text.getBounds().bottom +
" right = " + text.getBounds().right);
displayImageSubtitle(path, left, top);
}
}
private void displayImageSubtitle(String subPath, int left, int top) {
if (subPath != null && fileExists(subPath)) {
Bitmap srcBitmap = BitmapFactory.decodeFile(subPath);
if (srcBitmap == null) {
Log.e(APP, "Unable to decode bitmap " + subPath);
mSubtitleImageView.setImageDrawable(null);
return;
}
Bitmap filteredBitmap = srcBitmap.copy(Bitmap.Config.ARGB_8888,
true);
int subtitleWidth = filteredBitmap.getWidth();
int subtitleHeight = filteredBitmap.getHeight();
int videoHeight = 0;
int videoWidth = 0;
if (mVpaMediaPlayer != null) {
videoHeight = mVpaMediaPlayer.getVideoHeight();
videoWidth = mVpaMediaPlayer.getVideoWidth();
}
if (videoHeight == 0 || subtitleWidth == 0 || videoWidth == 0) {
Log.e(APP, "drawSub invalid videoHeight = " + videoHeight +
" subtitleWidth = " + subtitleWidth);
mSubtitleImageView.setImageDrawable(null);
return;
}
int[] allpixels = new int[subtitleWidth * subtitleHeight];
srcBitmap.getPixels(allpixels, 0, subtitleWidth, 0, 0,
subtitleWidth, subtitleHeight);
int maskColor = Color.rgb(0, 1, 0);
for (int i = 0; i < allpixels.length; i++) {
// convert background color to transparent
if (allpixels[i] == Color.BLACK || allpixels[i] == maskColor) {
allpixels[i] = Color.TRANSPARENT;
}
}
filteredBitmap.setPixels(allpixels, 0, subtitleWidth, 0, 0,
subtitleWidth, subtitleHeight);
double scaleFactor = ((double) mScreenWidth) / subtitleWidth;
int scaledSubtitleHeight = (int)(scaleFactor * subtitleHeight);
int scaledX = (int)((double) left / videoWidth) * mScreenWidth;
int scaledY = (int)(((double) top / videoHeight) * mScreenHeight);
Log.d(APP, "drawSub scaledSubtitleWidth = " + mScreenWidth +
" scaledSubtitleHeight = " + scaledSubtitleHeight +
" subtitleWidth = " + subtitleWidth +
" subtitleHeight = " + subtitleHeight + " screenWidth = " +
mScreenWidth + " screenHeight = " + mScreenHeight +
" scaledX = " + scaledX + " scaledY = " + scaledY +
" orig x = " + left + " orig y = " + top);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(filteredBitmap,
mScreenWidth, scaledSubtitleHeight, true);
RelativeLayout.LayoutParams params = (LayoutParams) mSubtitleImageView
.getLayoutParams();
params.leftMargin = scaledX;
params.topMargin = scaledY;
mSubtitleImageView.setLayoutParams(params);
mSubtitleImageView.setImageBitmap(scaledBitmap);
} else {
Log.d(APP, "subtitle image does not exist");
mSubtitleImageView.setImageDrawable(null);
}
}