I am working with some code that creates a Surface
programmatically and uses it to display the camera preview. I am trying to modify it to instead display a video. The changed code (below) plays the video's audio but there is no video--just a black screen.
The camera preview version works fine, so I don't think the issue is with how the SurfaceTexture
or Surface
are created or displayed.
int texid = getTexture(); //native method
mSurfaceTexture = new SurfaceTexture(texid);
Log.e(TAG, "texid is "+texid);
mSurfaceTexture.setOnFrameAvailableListener(new OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
Log.d(TAG, "onFrameAvailable");
}
});
final Surface surface = new Surface(mSurfaceTexture);
mediaPlayer.setSurface(surface);
String mSourceString = "clips/key_frames_movie_small.mp4";
AssetManager assetManager = mContext.getResources().getAssets();
AssetFileDescriptor clipFd = assetManager.openFd(mSourceString);
mediaPlayer.setDataSource(clipFd.getFileDescriptor(),
clipFd.getStartOffset(),
clipFd.getLength());
clipFd.close();
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(TAG, "ERROR: "+what + ", " + extra);
return false;
}
});
mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.e(TAG, "INFO: "+what + ", " + extra);
return false;
}
});
mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
Log.e(TAG, "onVideoSizeChanged: "+width + ", " + height);
}
});
mediaPlayer.prepare();
mediaPlayer.start();
Things I've tried:
- Using the MediaPlayer to play the same video in a
SurfaceView
that's in the xml layout. This works fine, so I don't think there's any problem with the video itself. - Removing background colors from xml, as suggested here. No effect.
- Playing a small video (320x240) (screen dimens are 1920x1080) in case resolution was a problem, as described here
- Used logs to verify that the surface was created before calling
mediaPlayer.setSurface(surface)
as suggested here. - Called
surfaceView.setZOrderOnTop(true)
,surfaceView.setZOrderMediaOverlay(true)
,surfaceView.setVisibility(View.VISIBLE)
to make sure the view isn't partially hidden by anything. No effect. - Called
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
as described here out of desperation, even though I'm running on 6.0.1.
How can I get the video to play in the surface without changing how the surface texture or surface are created?
Edit:
Logs look like this:
E/com.package: texid is 1
D/MediaPlayer: setSubtitleAnchor in MediaPlayer
W/MediaPlayer: info/warning (3, 0)
D/MediaPlayer: setSubtitleAnchor in MediaPlayer
E/com.package: onVideoSizeChanged: 960, 540
E/com.package: onVideoSizeChanged: 960, 540
E/com.package: INFO: 3, 0
D/com.package: onFrameAvailable
According to the docs, the info code 3 indicates that "The player just pushed the very first video frame for rendering."
onFrameAvailable
is called only once. If I do surfaceTexture.updateTexImage();
inside the onFrameAvailable()
callback, there are (perhaps once a second) additional calls to onFrameAvailable()
, but it doesn't affect the display, which stays black.