MediaPlayer plays audio but not video in programmatically created `Surface` - android

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.

Related

android app with multiple media players in same activity - how to use efficiently

I am creating an android activity that will have many mediaplayers inside. The activity will have many mediaplayer objects and if i could describe it its like a
4 X 1 grid of media players. I created the 4X1 grid by using TextureView class's in android.
so the xml layout for the activity looks like this more or less:
<TextureView
android:id="#+id/surface_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextureView
android:id="#+id/surface_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextureView
android:id="#+id/surface_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextureView
android:id="#+id/surface_four"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
So each textureView will be the surface where the MediaPlayers will play there video.
programatically i've set up SurfaceTextureListener's so that i know when the surface is available like this for each one:
TextureView tv1 = (TextureView)findViewById(R.id.surface_one);
tv1.setSurfaceTextureListener(new SurfaceListener_1());
and likewise for the rest. here is how i would do the second texture view setup:
TextureView tv2 = (TextureView)findViewById(R.id.surface_two);
tv2.setSurfaceTextureListener(new SurfaceListener_2());
Then in code i am creating 4 MediaPlayer Objects and setting there surface like this for mediaplayer #1:
private class SurfaceListener_1 implements TextureView.SurfaceTextureListener {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Surface sfc = new Surface(surface);
try {
m_mp1 = new MediaPlayer();
m_mp1.setSurface(sfc); //critical , here i am adding the surface so the media player can play on it
m_mp1.setDataSource(m_context, m_videoUri_one);
m_mp1.prepareAsync();
m_mp1.setLooping(true);
m_mp1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
video1Prepared = true;
}
});
m_mp1.setOnErrorListener(new MediaPlayer.OnErrorListener(){
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return true;
}
});
} catch (Exception e) {
e.printStackTrace();
}
finally {
//release the surface as its now set in the media player
if(sfc!=null)
sfc.release();
}
}
//...... the rest of the call backs not important...
and for the other media players i repeat the same thing so that they all have surfaces to play on. here is how i would do mediaplayer # 2:
private class SurfaceListener_2 implements TextureView.SurfaceTextureListener {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Surface sfc = new Surface(surface);
try {
m_mp2 = new MediaPlayer();
m_mp2.setSurface(sfc); //critical , here i am adding the surface so the media player can play on it
m_mp2.setDataSource(m_context, m_videoUri_two);
m_mp2.prepareAsync();
m_mp2.setLooping(true);
m_mp2.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
video2Prepared = true;
}
});
m_mp2.setOnErrorListener(new MediaPlayer.OnErrorListener(){
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return true;
}
});
} catch (Exception e) {
e.printStackTrace();
}
finally {
//release the surface as its now set in the media player
if(sfc!=null)
sfc.release();
}
}
//...... the rest of the call backs not important...
so in the end what i have created is 4 surfaces where i can play a video. All 4 media players play different video files.
Once all the media players are prepared, i call their start() method on all of them and they play at the same time.
The issue i am facing is that this can be cpu intensive. On some devices i can get a ANR or the app just gets slow to respond.
The media file i am playing is a MP4 and the size of the media files are all about 9 MB.
Is there anything you can recommend so that i can be more cpu efficient in this already heavy task ? For example, can i get the GPU to help ?
Or if i change media type will that make it more efficient ? the video's do not have sound there only visual if that helps.

How to display a video with a transparent/masked background?

The wording on this is most likely wrong, but I want to play this video without having the background black. If you look at this webpage you will see that the video is on a white background which leads me to believe that it is cropped or masked. Which view would I use to do this? I have tried both TextureView and VideoView but both have a black background by default.
I solve it with this:
#Override
public void onPrepared(MediaPlayer mp) {
mp.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.d(TAG, "onInfo, what = " + what);
if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
// video started; hide the placeholder.
placeholder.setVisibility(View.GONE);
return true;
}
return false;
}
});
I think onPrepared just means the video is ready to play, but not means video started playing. If hide placeholder in onPrepared, the screen still show a black screen.
On my Note4 and Nexus, this solution works well.

Android: VideoView setOnErrorListener called only once

Below is the piece of my code for handling the error of my video player. This error callback listener gets triggered for the first time only. After that, it's not capturing the error.
videoPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.msgPleaseNoConnection),
Toast.LENGTH_SHORT).show();
vVideoBufferLoader.setVisibility(View.INVISIBLE);
return false;
}
});
Note:
I tried returning true from that callback which means I handled the error. But it doesn't solve the problem too.
The goal of the MediaPlayer's OnErrorListener is to signal when an error has occurred, at which point the MediaPlayer object is in an end state.
http://developer.android.com/reference/android/media/MediaPlayer.html
If you are using the MediaPlayer constructor to 'reset' the object elsewhere in the code, you are essentially creating a new MediaPlayer object and saving it over the older one. If this is the case, then you also need to reassign the OnErrorListener.
Here's a short snippet of how I've been using OnErrorListener in my app:
private MediaPlayer.OnErrorListener vidVwErrorListener = new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) { //if there was an error in trying to play the intro video
if (tryLrgClip) { // If the larger-resolution clip failed to play, try playing the backup (lower-resolution) clip.
tryLrgClip = false;
trySmClip = true;
vidVwSplashView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + SPLASH_VIDEOS));
vidVwSplashView.start();
} else { // If that didn't work either, give up on playing a video, and do something else
tryLrgClip = trySmClip = false;
vidVwSplashView.setVisibility(View.GONE);
//Something else
}
return true;
}
};
I hope that helps!

Bizarre Invalidate behaviour

I have a custom viewgroup (Custom2) inside a custom viewgroup (Custom1).
Custom1 has an imageview displaying a RectShape Shapedrawable and two textviews.
I set an onClickListener in Custom2 which removes the imageview using this.removeViewAt(0) and creates a new TextureView, then calling this.addView (mTextureView, 0) and finally invalidate. I then get the textureview to play a video using mediaplayer.
Here is the code:
public void setToTextureView() {
//TextureView when not debugging
TextureView View;
View = (TextureView) new TextureView(mContext);
View.setLayoutParams(new LayoutParams(600, 500));
this.removeViewAt(0);
this.addView(View, 0);
View.invalidate();
Log.d("Debug", "Invalidate Called");
View.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Surface mSurface = new Surface(surface);
MediaPlayer mMediaPlayer = new MediaPlayer(); //Debug MediaPlayer issues by setting ItemToDebug = "MediaPlayer"
try {
mMediaPlayer.setDataSource(mContext, Uri.parse("android.resource://" + mContext.getPackageName() + "/" + R.raw.t));
if (ItemToDebug.equals("MediaPlayer")) {
Log.d("MediaPlayer", "Data source set");
}
mMediaPlayer.setSurface(mSurface);
if (ItemToDebug.equals("MediaPlayer")) {
Log.d("MediaPLayer", "Surface set");
}
mMediaPlayer.prepare();
if (ItemToDebug.equals("MediaPlayer")) {
Log.d("MediaPLayer", "Prepared");
}
} catch (IOException e) {
Log.d("IOException", e.getMessage());
}
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if (ItemToDebug.equals("MediaPlayer")) {
Log.d("Debug", "Media Player Prepared");
}
mp.start();
}
});
}
Now, I notice bizarrely that the ImageView disappears and an empty space appears (of the correct size) when I click the view, but no video plays, although the code appears to execute fine upto invalidate();
However, I can then get the TextureView to get a surfaceTexture and play the video when I interrupt the program and go back in (i.e by pressing the button that takes you to the screen with all the open programs, and selecting my activity again.)
I don't have a good understanding of exactly what is happening but I am pretty sure that no SurfaceTexture is being created because onAttached() isn't being called.
Does anybody know what might be going on?
Cheers,
J
The view disappears, I need to call requestLayout() in order to draw something else there.

MediaPlayer: buffer status for internet radio stream?

I am playing internet radio streams using MediaPlayer. However buffering seems to take about 10 seconds for me each time I start playing a stream. Is there any way I can show:
The progress when starting to play? E.g. a progress bar in the GUI
The buffer status once playing? (e.g. to give a warning when buffer is running dry for some reason)
I tried using setOnBufferingUpdateListener() but as far as I can tell, it only works for streams where the duration is known. (So not applicable to internet radio, unless I'm wrong - it was not being fired)
Implements MediaPlayer.OnInfoListener, overriding the method onInfo you have action to control the status of the MediaPlayer as follow:
#Override
public boolean onInfo(MediaPlayer arg0, int what, int extra) {
Log.i(LOG_TAG, "MediaPlayer INFO code = " + what + " extra=" + extra);
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
Log.d(LOG_TAG, "Start buffering...");
} else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
Log.d(LOG_TAG, "End buffering...");
Log.d(LOG_TAG, "Start playing...");
}
return false;
}
Take a look at http://developer.android.com/reference/android/media/MediaPlayer.html for other actions;)

Categories

Resources