i am trying to make a video play option in recyclerview adapter in my app i can successfully play video in textureview now i am trying to add mediacontroller and thumbnail in to textureview video . i have tried by googling but i couldnt do.please need help.
this is my textureview interface method implementation:
#Override
public void onSurfaceTextureAvailable(final SurfaceTexture surfaceTexture, int i, int i1) {
try {
if(mediaPlayer!=null) {
final Surface surface = new Surface(surfaceTexture);
mediaPlayer.setDataSource(context, Uri.parse("https://www.w3schools.com/html/mov_bbb.mp4"));
mediaPlayer.setSurface(surface);
mediaPlayer.prepareAsync();
final MediaController mediaController = new MediaController(context);
mediaController.setMediaPlayer(this);
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mediaPlayer) {
// mediaPlayer.start();
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
mediaPlayer.reset();
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
Related
I use MediaPlayer for displaying video. I need to loop it. I use native method setLooping(), but it doesn't work. Video finished and that's all. Here is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_full_screen);
TextureView videoViewFullScreen = (TextureView) findViewById(R.id.video_screen);
path = getIntent().getExtras().getString(SettingsActivity.FILE_PATH);
cycle = getIntent().getExtras().getBoolean(SettingsActivity.CYCLE);
position = getIntent().getIntExtra(FullScreenActivity.POSITION, -1);
videoViewFullScreen.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
final Surface surf = new Surface(surface);
try {
mediaPlayerFullScreen = new MediaPlayer();
mediaPlayerFullScreen.setDataSource(path);
mediaPlayerFullScreen.setSurface(surf);
if (cycle) {
mediaPlayerFullScreen.setLooping(true);
}
mediaPlayerFullScreen.prepareAsync();
mediaPlayerFullScreen.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
if (position != -1) {
Log.d("Position", "Position Full " + position);
mediaPlayerFullScreen.seekTo(position);
}
mediaPlayer.start();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mediaPlayerFullScreen.stop();
mediaPlayerFullScreen.release();
mediaPlayerFullScreen = null;
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayerFullScreen != null) {
mediaPlayerFullScreen.stop();
mediaPlayerFullScreen.release();
mediaPlayerFullScreen = null;
}
}
I looked all similar questions and tried all answers that find here, but none of them helped me.
I have Android version 4.4.2, Firmware version 4.5, Kernel version 3.3.0
Could someone help me? Is there another way to loop video?
I find replacement for setLooping(true), this works for me:
mediaPlayerFullScreen.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mediaPlayerFullScreen.reset();
try {
mediaPlayerFullScreen.setDataSource(path);
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayerFullScreen.setSurface(surf);
mediaPlayerFullScreen.prepareAsync();
mediaPlayerFullScreen.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
}
});
I've implemented a recycler view and each item will play video. My problem is that I need to pause/play the video when it scrolls on/off the screen.
I've implemented that when the user scrolls the video keeps playing.
I have had an issue trying to keep track of the media player when the view scrolls off screen.
I have come up with a solution and I'm hoping I can get some advice to see if may be any issues with the solution.
So I'm using a texture view to display the media player in the recycler view as videoviews aren't compatible when looking to use a recycler or customise the controller.
So in the surface texture destroyed (signalling that the view has left the screen) I pause the media player and when the surfacetexture is available (signalling the view is on screen) I check if the media player is null (as it will be when the screen first loads) if not I start the media player. Below is my video player class in where you can see my implementation.
public class CustomVideoPlayer implements TextureView.SurfaceTextureListener, VideoControllerView.MediaPlayerControl, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnVideoSizeChangedListener {
private Context mContext;
private MediaPlayer mMediaPlayer;
private SurfaceTexture mSurface;
private VideoControllerView mControllerView;
private TextureView mTextureView;
private ProgressBar mProgress;
private FrameLayout mView;
public CustomVideoPlayer(Context ctx, TextureView view, ProgressBar progressDialog, FrameLayout holderView){
this.mContext = ctx;
mTextureView = view;
mTextureView.setSurfaceTextureListener(this);
mProgress = progressDialog;
mControllerView = new VideoControllerView(ctx);
mView = holderView;
mTextureView.setOnTouchListener(new ControlTouchListener());
}
#Override
public boolean canPause() {
return true;
}
#Override
public boolean canSeekBackward() {
return true;
}
#Override
public boolean canSeekForward() {
return true;
}
#Override
public int getBufferPercentage() {
return 0;
}
#Override
public int getCurrentPosition() {
return mMediaPlayer.getCurrentPosition();
}
#Override
public int getDuration() {
return mMediaPlayer.getDuration();
}
#Override
public boolean isPlaying() {
return mMediaPlayer.isPlaying();
}
#Override
public void pause() {
mMediaPlayer.pause();
}
#Override
public void seekTo(int i) {
mMediaPlayer.seekTo(i);
}
#Override
public void start() {
mMediaPlayer.start();
}
#Override
public boolean isFullScreen() {
return false;
}
#Override
public void toggleFullScreen() {
}
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
}
#Override
public void onCompletion(MediaPlayer mp) {
}
#Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurface = surface;
if(mMediaPlayer!=null) {
mMediaPlayer.setSurface(new Surface(mSurface));
mMediaPlayer.start();
}
Log.i(VersysVideoPlayer.class.getSimpleName(), String.valueOf(surface)+"available");
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
//mSurface = surface;
if(mMediaPlayer==null){
Log.i(VersysVideoPlayer.class.getSimpleName(), "MEDIA PLAYER IS NULL");
}else{
Log.i(CustomVideoPlayer.class.getSimpleName(), "MEDIA PLAYER IS NOT NULL");
mMediaPlayer.pause();
}
Log.i(CustomVideoPlayer.class.getSimpleName(), String.valueOf(surface)+"destroyed");
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
public void changePlayState(){
if(mMediaPlayer!=null) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
} else {
mMediaPlayer.start();
}
}else{
Log.i(CustomVideoPlayer.class.getSimpleName(), "MEDIA PLAYER IS NULL");
}
}
public void startVideo(String url){
if(mMediaPlayer!=null){
mMediaPlayer.reset();
mMediaPlayer.release();
//mMediaPlayer = new MediaPlayer();
}else{
mMediaPlayer = new MediaPlayer();
}
if(!mMediaPlayer.isPlaying()){
try {
mMediaPlayer.setSurface(new Surface(mSurface));
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(url);
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
mMediaPlayer.setOnPreparedListener(this);
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onPrepared(MediaPlayer mp) {
Log.i(CustomVideoPlayer.class.getSimpleName(), "ON PREPARED CALLED");
mControllerView.setMediaPlayer(this);
mControllerView.setAnchorView(mView);
mControllerView.show();
mProgress.setVisibility(View.GONE);
mMediaPlayer.start();
// mMediaPlayer.setVolume(0,0);
}
//Touch listener to display video controls
class ControlTouchListener implements View.OnTouchListener{
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
mControllerView.show();
}
return false;
}
}
}
Any comments or opinions are welcomed.
I have a MediaPlayer object that uses a SurfaceHolder object as a surface. There is a button on top of the video that takes me out of the video to a website. When that happens, I pause the player with player.pause(). When I return from the website, I resume the player with player.start(). I know that the surface gets destroyed when the activity is not displayed anymore, and it gets recreated as soon as the activity is restarted. In my surfaceCreated(), I set the surface for the player again (since it no longer has a surface at that point), and then resume. However, the player simply restarts the video from the beginning.
I've tried commenting out the line that takes me to the website, just to see if pause/start works properly and resumes from last spot. It does. I'm not sure why this behaviour doesn't happen when I leave and re-enter the video activity though.
I also tried using the player.seekTo() call. There was no difference. In fact, when I disabled the button taking me to a site to just pausing the video, with the seekTo() call the video ALSO started from the beginning despite position being not 0.
The player object is the same all the way throughout.
Just because the surface is a new one on restart, it doesn't know or care of its contents, does it? The player should be managing that, right?
I'm out of ideas at this point. Can anyone please offer any tips?
UPDATE: So I threw together a quick app just to eliminate any other external factors. Here's the full code for the video class (other class is just an activity with a play button):
public class VideoPlayer extends Activity implements MediaPlayer.OnCompletionListener,
MediaPlayer.OnErrorListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnVideoSizeChangedListener,
SurfaceHolder.Callback {
private MediaPlayer player;
private SurfaceHolder mSurfaceHolder;
private SurfaceView mSurfaceView;
private Button leaveVideoButton;
private boolean isPaused = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.video_layout);
leaveVideoButton = (Button) findViewById(R.id.go_to_web);
leaveVideoButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Uri uri = Uri.parse("http://www.google.com");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
pauseSteps();
startActivity(intent);
}
});
createPlayer();
createSurface();
}
private void createSurface() {
mSurfaceView = (SurfaceView) findViewById(R.id.surface);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
private void createPlayer() {
player = new MediaPlayer();
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
player.setOnPreparedListener(this);
player.setOnVideoSizeChangedListener(this);
player.setOnSeekCompleteListener(this);
}
private void pauseSteps() {
if(player.isPlaying()) {
player.pause();
isPaused = true;
}
}
private void playSteps() {
if(isPaused) {
isPaused = false;
player.start();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (player.isPlaying()) {
player.stop();
}
player.reset();
player.release();
player = null;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
if (!isPaused) {
try {
// player.setDataSource(path);
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.video);
if (afd == null) return;
player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
player.prepare();
} catch (IOException e) {
e.printStackTrace();
}
} else {
playSteps();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void onCompletion(MediaPlayer mp) {
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
player.start();
}
#Override
public void onSeekComplete(MediaPlayer mp) {
}
#Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
}
}
UPDATE 2: So I tried a different video and it resumed just fine from the same spot. This must be some encoding issue.
I using MediaPlayerto play video from http stream is any solution to prepare second media player with another http url while first one still playing?
I want to play two videos without black screen between them, when first one end second start immediately.
Here is my code:
public class VideoActivity extends Activity implements SurfaceHolder.Callback {
private MediaPlayer mediaPlayer;
private SurfaceHolder surfaceHolder;
private SurfaceView playerSurfaceView;
private String videoSrc = "http://192.168.1.101:8090/api/video/";
private String android_id;
private MediaPlayer next;
private static boolean firstCall = true;
private String secondVideoSrc = "http://192.168.1.101:8090/api/secondvideo/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
playerSurfaceView = (SurfaceView)findViewById(R.id.playersurface);
surfaceHolder = playerSurfaceView.getHolder();
surfaceHolder.addCallback(this);
GetDeviceId();
videoSrc += android_id;
secondVideoSrc += android_id;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
prepareVideoPlayer();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
private void GetDeviceId() {
android_id = Settings.Secure.getString(getBaseContext().getContentResolver(),
Settings.Secure.ANDROID_ID);
}
private void prepareVideoPlayer() {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.setDataSource(this, Uri.parse(videoSrc));
mediaPlayer.setScreenOnWhilePlaying(true);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if(firstCall) {
mediaPlayer.start();
firstCall = false;
}
new AsyncPrepareNext().execute();
}
});
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
next.start();
}
});
}
catch(Exception ex) {
}
}
private void prepareNext() {
try {
next = new MediaPlayer();
next.setDisplay(surfaceHolder);
next.setDataSource(this, Uri.parse(secondVideoSrc));
next.setScreenOnWhilePlaying(true);
next.prepareAsync();
next.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
}
});
next.setAudioStreamType(AudioManager.STREAM_MUSIC);
next.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
}
});
}
catch(Exception ex) {
}
}
private class AsyncPrepareNext extends AsyncTask<Void,Void,Void> {
#Override
protected Void doInBackground(Void... params) {
prepareNext();
return null;
}
}
}
When i run this first video starts for 2 or 3 seconds and then stop with some errors like media server died, audio flinger died, error (100,0), error (38,0)
You'll probably need to implement the OnPreparedListener() and OnCompletedListener() to accomplish this and you might need 2 media players for it as well.
#Override
public void onPrepared(MediaPlayer mp){
// Set Video Source for second video
mp.setDataSource(path);
// Prepare second video
mp.prepareAsync();
}
#Override
public void onCompletion(MediaPlayer mp){
// Start second video here
mp.start();
}
and set them like this:
mediaPlayer.setOnPreparedListener(new OnPreparedListener());
mediaPlayer.setOnCompletedListener(new OnCompletionListener());
I'm building an app that records and plays back video. I would like to do so without affecting background music playback, i.e. if I begin playing a video, I do not want to pause other apps' audio. However, on Lollipop, Android's VideoView class automatically requests audio focus when the private method VideoView.openVideo() is called:
AudioManager am = (AudioManager) super.getSystemService(name);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
Any suggestions on how to get around this?
Starting with Android SDK 26 you may want to use VideoView and
if(Build.VERSION.SDK_INT >= Build.Version_CODES.O){
//set this BEFORE start playback
videoView.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE)
}
For older version, there's a workaround described here: https://stackoverflow.com/a/31569930/993439
Basically, copy source code of VideoView and uncomment following lines
AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
I got around this with a stupid solution by copying whole source code of android.widget.VideoView of Lollipop and removing line you mentioned.
Make your own VideoView class. don't use extends VideoView since you can't override openVideo() method.
I don't recommend this as I thinking it's a temporary solution. VideoView Changed a lot between 4.1-5.0 so this can make RuntimeException on Android version other than Lollipop
Edit
I made approach MediaPlayer + SurfaceView as pinxue told us;
It respects aspect ratio within viewWidth and viewHeight.
final String finalFilePath = filePath;
final SurfaceHolder surfaceHolder = sv.getHolder();
final MediaPlayer mediaPlayer = new MediaPlayer();
final LinearLayout.LayoutParams svLayoutParams = new LinearLayout.LayoutParams(viewWidth,viewHeight);
surfaceHolder.addCallback(new SurfaceHolder.Callback(){
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
if(isDebug) {
System.out.println("setting VideoPath to VideoView: "+finalFilePath);
}
mediaPlayer.setDataSource(finalFilePath);
}catch (IOException ioe){
if(isDebug){
ioe.printStackTrace();
}
//mediaPlayer = null;
}
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if(isDebug){
System.out.println("Video is starting...");
}
// for compatibility, we adjust size based on aspect ratio
if ( mp.getVideoWidth() * svLayoutParams.height < svLayoutParams.width * mp.getVideoHeight() ) {
//Log.i("###", "image too wide, correcting");
svLayoutParams.width = svLayoutParams.height * mp.getVideoWidth() / mp.getVideoHeight();
} else if ( mp.getVideoWidth() * svLayoutParams.height > svLayoutParams.width * mp.getVideoHeight() ) {
//Log.i("###", "image too tall, correcting");
svLayoutParams.height = svLayoutParams.width * mp.getVideoHeight() / mp.getVideoWidth();
}
sv.post(new Runnable(){
#Override
public void run() {
sv.setLayoutParams(svLayoutParams);
}
});
mp.start();
}
});
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if(isDebug){
System.out.println("surfaceChanged(holder, "+format+", "+width+", "+height+")");
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
mediaPlayer.setDataSource("");
}catch (IOException ioe){
if(isDebug){
ioe.printStackTrace();
}
}
}
});
if(sv.post(new Runnable() {
#Override
public void run() {
sv.setLayoutParams(svLayoutParams);///
sv.setVisibility(View.VISIBLE);
}})){
if(isDebug) {
System.out.println("post Succeded");
}
}else{
if(isDebug) {
System.out.println("post Failed");
}
}
The accepted solution does not guarantee compatibility across all Android versions and is a dirty hack more than a true solution. I've tried all forms of hacks to get this working, yet none have worked to my satisfaction.
I have come up with a much better solution though - switch from a VideoView to a TextureView and load it with a MediaPlayer. There is no difference from the user's perspective, just no more audio stoppage.
Here's my use case for playing an MP4 looping:
private TextureView _introVideoTextureView;
private MediaPlayer _introMediaPlayer;
...
#Override
public void onCreate(...) {
_introVideoTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
try {
destoryIntroVideo();
_introMediaPlayer = MediaPlayer.create(SignInActivity.this, R.raw.intro_video);
_introMediaPlayer.setSurface(new Surface(surfaceTexture));
_introMediaPlayer.setLooping(true);
_introMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
_introMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
} catch (Exception e) {
System.err.println("Error playing intro video: " + e.getMessage());
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}
});
}
#Override
public void onDestroy() {
super.onDestroy();
destoryIntroVideo();
}
private void destoryIntroVideo() {
if (_introMediaPlayer != null) {
_introMediaPlayer.stop();
_introMediaPlayer.release();
_introMediaPlayer = null;
}
}
You may use MediaPlayer + SurfaceView instead.
use audioManager.abandonAudioFocus(null)
If you look at the VideoView code you will notice it calls the method audioManager.requestAudioFocus with null for the OnAudioFocusChangeListener. When you register a listener with the AudioManager it uses this method to make an ID for the listener
private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
if (l == null) {
return new String(this.toString());
} else {
return new String(this.toString() + l.toString());
}
}
which generates the same ID every time you use null. So if you call abandonAudioFocus with null it will remove any listener that was added with null as the parameter for the OnAudioFocusChangeListener