MediaSourcepublic class MediaSource extends android.filterfw.core.Filter
Fields Summary |
---|
private String | mSourceUrlThe source URL for the media source. Can be an http: link to a remote
resource, or a file: link to a local media file | private android.content.res.AssetFileDescriptor | mSourceAssetAn open asset file descriptor to a local media source. Default is null | private android.content.Context | mContextThe context for the MediaPlayer to resolve the sourceUrl.
Make sure this is set before the sourceUrl to avoid unexpected result.
If the sourceUrl is not a content URI, it is OK to keep this as null. | private boolean | mSelectedIsUrlWhether the media source is a URL or an asset file descriptor. Defaults
to false. | private boolean | mWaitForNewFrameWhether the filter will always wait for a new video frame, or whether it
will output an old frame again if a new frame isn't available. Defaults
to true. | private boolean | mLoopingWhether the media source should loop automatically or not. Defaults to
true. | private float | mVolumeVolume control. Currently sound is piped directly to the speakers, so
this defaults to mute. | private int | mOrientationOrientation. This controls the output orientation of the video. Valid
values are 0, 90, 180, 270 | private android.media.MediaPlayer | mMediaPlayer | private android.filterfw.core.GLFrame | mMediaFrame | private android.graphics.SurfaceTexture | mSurfaceTexture | private android.filterfw.core.ShaderProgram | mFrameExtractor | private android.filterfw.core.MutableFrameFormat | mOutputFormat | private int | mWidth | private int | mHeight | private static final int | PREP_TIMEOUT | private static final int | PREP_TIMEOUT_REPEAT | private static final int | NEWFRAME_TIMEOUT | private static final int | NEWFRAME_TIMEOUT_REPEAT | private final String | mFrameShader | private static final float[] | mSourceCoords_0 | private static final float[] | mSourceCoords_270 | private static final float[] | mSourceCoords_180 | private static final float[] | mSourceCoords_90 | private boolean | mGotSize | private boolean | mPrepared | private boolean | mPlaying | private boolean | mNewFrameAvailable | private boolean | mOrientationUpdated | private boolean | mPaused | private boolean | mCompleted | private final boolean | mLogVerbose | private static final String | TAG | private MediaPlayer.OnVideoSizeChangedListener | onVideoSizeChangedListener | private MediaPlayer.OnPreparedListener | onPreparedListener | private MediaPlayer.OnCompletionListener | onCompletionListener | private SurfaceTexture.OnFrameAvailableListener | onMediaFrameAvailableListener |
Constructors Summary |
---|
public MediaSource(String name)
super(name);
mNewFrameAvailable = false;
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
|
Methods Summary |
---|
public void | close(android.filterfw.core.FilterContext context)
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
mPrepared = false;
mGotSize = false;
mPlaying = false;
mPaused = false;
mCompleted = false;
mNewFrameAvailable = false;
mMediaPlayer.release();
mMediaPlayer = null;
mSurfaceTexture.release();
mSurfaceTexture = null;
if (mLogVerbose) Log.v(TAG, "MediaSource closed");
| private void | createFormats()
mOutputFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
FrameFormat.TARGET_GPU);
| public void | fieldPortValueUpdated(java.lang.String name, android.filterfw.core.FilterContext context)
if (mLogVerbose) Log.v(TAG, "Parameter update");
if (name.equals("sourceUrl")) {
if (isOpen()) {
if (mLogVerbose) Log.v(TAG, "Opening new source URL");
if (mSelectedIsUrl) {
setupMediaPlayer(mSelectedIsUrl);
}
}
} else if (name.equals("sourceAsset") ) {
if (isOpen()) {
if (mLogVerbose) Log.v(TAG, "Opening new source FD");
if (!mSelectedIsUrl) {
setupMediaPlayer(mSelectedIsUrl);
}
}
} else if (name.equals("loop")) {
if (isOpen()) {
mMediaPlayer.setLooping(mLooping);
}
} else if (name.equals("sourceIsUrl")) {
if (isOpen()){
if (mSelectedIsUrl){
if (mLogVerbose) Log.v(TAG, "Opening new source URL");
} else {
if (mLogVerbose) Log.v(TAG, "Opening new source Asset");
}
setupMediaPlayer(mSelectedIsUrl);
}
} else if (name.equals("volume")) {
if (isOpen()) {
mMediaPlayer.setVolume(mVolume, mVolume);
}
} else if (name.equals("orientation") && mGotSize) {
if (mOrientation == 0 || mOrientation == 180) {
mOutputFormat.setDimensions(mWidth, mHeight);
} else {
mOutputFormat.setDimensions(mHeight, mWidth);
}
mOrientationUpdated = true;
}
| public void | open(android.filterfw.core.FilterContext context)
if (mLogVerbose) {
Log.v(TAG, "Opening MediaSource");
if (mSelectedIsUrl) {
Log.v(TAG, "Current URL is " + mSourceUrl);
} else {
Log.v(TAG, "Current source is Asset!");
}
}
mMediaFrame = (GLFrame)context.getFrameManager().newBoundFrame(
mOutputFormat,
GLFrame.EXTERNAL_TEXTURE,
0);
mSurfaceTexture = new SurfaceTexture(mMediaFrame.getTextureId());
if (!setupMediaPlayer(mSelectedIsUrl)) {
throw new RuntimeException("Error setting up MediaPlayer!");
}
| public synchronized void | pauseVideo(boolean pauseState)
if (isOpen()) {
if (pauseState && !mPaused) {
mMediaPlayer.pause();
} else if (!pauseState && mPaused) {
mMediaPlayer.start();
}
}
mPaused = pauseState;
| protected void | prepare(android.filterfw.core.FilterContext context)
if (mLogVerbose) Log.v(TAG, "Preparing MediaSource");
mFrameExtractor = new ShaderProgram(context, mFrameShader);
// SurfaceTexture defines (0,0) to be bottom-left. The filter framework
// defines (0,0) as top-left, so do the flip here.
mFrameExtractor.setSourceRect(0, 1, 1, -1);
createFormats();
| public void | process(android.filterfw.core.FilterContext context)
// Note: process is synchronized by its caller in the Filter base class
if (mLogVerbose) Log.v(TAG, "Processing new frame");
if (mMediaPlayer == null) {
// Something went wrong in initialization or parameter updates
throw new NullPointerException("Unexpected null media player!");
}
if (mCompleted) {
// Video playback is done, so close us down
closeOutputPort("video");
return;
}
if (!mPlaying) {
int waitCount = 0;
if (mLogVerbose) Log.v(TAG, "Waiting for preparation to complete");
while (!mGotSize || !mPrepared) {
try {
this.wait(PREP_TIMEOUT);
} catch (InterruptedException e) {
// ignoring
}
if (mCompleted) {
// Video playback is done, so close us down
closeOutputPort("video");
return;
}
waitCount++;
if (waitCount == PREP_TIMEOUT_REPEAT) {
mMediaPlayer.release();
throw new RuntimeException("MediaPlayer timed out while preparing!");
}
}
if (mLogVerbose) Log.v(TAG, "Starting playback");
mMediaPlayer.start();
}
// Use last frame if paused, unless just starting playback, in which case
// we want at least one valid frame before pausing
if (!mPaused || !mPlaying) {
if (mWaitForNewFrame) {
if (mLogVerbose) Log.v(TAG, "Waiting for new frame");
int waitCount = 0;
while (!mNewFrameAvailable) {
if (waitCount == NEWFRAME_TIMEOUT_REPEAT) {
if (mCompleted) {
// Video playback is done, so close us down
closeOutputPort("video");
return;
} else {
throw new RuntimeException("Timeout waiting for new frame!");
}
}
try {
this.wait(NEWFRAME_TIMEOUT);
} catch (InterruptedException e) {
if (mLogVerbose) Log.v(TAG, "interrupted");
// ignoring
}
waitCount++;
}
mNewFrameAvailable = false;
if (mLogVerbose) Log.v(TAG, "Got new frame");
}
mSurfaceTexture.updateTexImage();
mOrientationUpdated = true;
}
if (mOrientationUpdated) {
float[] surfaceTransform = new float[16];
mSurfaceTexture.getTransformMatrix(surfaceTransform);
float[] sourceCoords = new float[16];
switch (mOrientation) {
default:
case 0:
Matrix.multiplyMM(sourceCoords, 0,
surfaceTransform, 0,
mSourceCoords_0, 0);
break;
case 90:
Matrix.multiplyMM(sourceCoords, 0,
surfaceTransform, 0,
mSourceCoords_90, 0);
break;
case 180:
Matrix.multiplyMM(sourceCoords, 0,
surfaceTransform, 0,
mSourceCoords_180, 0);
break;
case 270:
Matrix.multiplyMM(sourceCoords, 0,
surfaceTransform, 0,
mSourceCoords_270, 0);
break;
}
if (mLogVerbose) {
Log.v(TAG, "OrientationHint = " + mOrientation);
String temp = String.format("SetSourceRegion: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f",
sourceCoords[4], sourceCoords[5],sourceCoords[0], sourceCoords[1],
sourceCoords[12], sourceCoords[13],sourceCoords[8], sourceCoords[9]);
Log.v(TAG, temp);
}
mFrameExtractor.setSourceRegion(sourceCoords[4], sourceCoords[5],
sourceCoords[0], sourceCoords[1],
sourceCoords[12], sourceCoords[13],
sourceCoords[8], sourceCoords[9]);
mOrientationUpdated = false;
}
Frame output = context.getFrameManager().newFrame(mOutputFormat);
mFrameExtractor.process(mMediaFrame, output);
long timestamp = mSurfaceTexture.getTimestamp();
if (mLogVerbose) Log.v(TAG, "Timestamp: " + (timestamp / 1000000000.0) + " s");
output.setTimestamp(timestamp);
pushOutput("video", output);
output.release();
mPlaying = true;
| private synchronized boolean | setupMediaPlayer(boolean useUrl)Creates a media player, sets it up, and calls prepare
mPrepared = false;
mGotSize = false;
mPlaying = false;
mPaused = false;
mCompleted = false;
mNewFrameAvailable = false;
if (mLogVerbose) Log.v(TAG, "Setting up playback.");
if (mMediaPlayer != null) {
// Clean up existing media players
if (mLogVerbose) Log.v(TAG, "Resetting existing MediaPlayer.");
mMediaPlayer.reset();
} else {
// Create new media player
if (mLogVerbose) Log.v(TAG, "Creating new MediaPlayer.");
mMediaPlayer = new MediaPlayer();
}
if (mMediaPlayer == null) {
throw new RuntimeException("Unable to create a MediaPlayer!");
}
// Set up data sources, etc
try {
if (useUrl) {
if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to URI " + mSourceUrl);
if (mContext == null) {
mMediaPlayer.setDataSource(mSourceUrl);
} else {
mMediaPlayer.setDataSource(mContext, Uri.parse(mSourceUrl.toString()));
}
} else {
if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to asset " + mSourceAsset);
mMediaPlayer.setDataSource(mSourceAsset.getFileDescriptor(), mSourceAsset.getStartOffset(), mSourceAsset.getLength());
}
} catch(IOException e) {
mMediaPlayer.release();
mMediaPlayer = null;
if (useUrl) {
throw new RuntimeException(String.format("Unable to set MediaPlayer to URL %s!", mSourceUrl), e);
} else {
throw new RuntimeException(String.format("Unable to set MediaPlayer to asset %s!", mSourceAsset), e);
}
} catch(IllegalArgumentException e) {
mMediaPlayer.release();
mMediaPlayer = null;
if (useUrl) {
throw new RuntimeException(String.format("Unable to set MediaPlayer to URL %s!", mSourceUrl), e);
} else {
throw new RuntimeException(String.format("Unable to set MediaPlayer to asset %s!", mSourceAsset), e);
}
}
mMediaPlayer.setLooping(mLooping);
mMediaPlayer.setVolume(mVolume, mVolume);
// Bind it to our media frame
Surface surface = new Surface(mSurfaceTexture);
mMediaPlayer.setSurface(surface);
surface.release();
// Connect Media Player to callbacks
mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
mMediaPlayer.setOnPreparedListener(onPreparedListener);
mMediaPlayer.setOnCompletionListener(onCompletionListener);
// Connect SurfaceTexture to callback
mSurfaceTexture.setOnFrameAvailableListener(onMediaFrameAvailableListener);
if (mLogVerbose) Log.v(TAG, "Preparing MediaPlayer.");
mMediaPlayer.prepareAsync();
return true;
| public void | setupPorts()
// Add input port
addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
FrameFormat.TARGET_GPU));
| public void | tearDown(android.filterfw.core.FilterContext context)
if (mMediaFrame != null) {
mMediaFrame.release();
}
|
|