I am using MediaPlayer and SurfaceView to stream a video from a server. The video plays fine however if the activity goes in "Paused" state, eg. when the user taps home button or recent button, On resuming the player activity the SurfaceView becomes black. I know when you leave the activity, the surfaceView is destroyed and upon resuming it is created again, so as workaround I saved the player's current position in onPause() of the activity and when the activity resumes, I seek the player to that position. This didn't work either.
So my question is how can I make the surfaceView keep the frame/picture it was displaying when the user goes out of the activity?
public class VideoPlayer extends Activity implements SurfaceHolder.Callback, OnPreparedListener {
private long PREV_PLAYER_POS;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_videoplayer);
mDecorView = getWindow().getDecorView();
mediaController = new VideoControllerView(this, mDecorView);
handler = new Handler();
Intent intent = getIntent();
usedURL = intent.getExtras().getString("vidURL");
vidID = intent.getExtras().getString("id");
mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
mProgressBar.setVisibility(ProgressBar.VISIBLE);
mContext = this;
surfView = (SurfaceView) findViewById(R.id.videoView4);
SurfaceHolder holder = surfView.getHolder();
holder.addCallback(this);
mMediaPlayer = new MediaPlayer();
mMediaPlayerControl = new VideoControllerView.MediaPlayerControl() {
……
}
mMediaPlayer.setOnPreparedListener(this);
mediaController.setOnSeekStartListener(new onSeekStartListener() {
#Override
public void onSeekStarted() {
}
});
mediaController.setMediaPlayer(mMediaPlayerControl);
mediaController.setAnchorView((ViewGroup) findViewById(R.id.container4));
mediaController.setEnabled(true);
mediaController.show();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
mMediaPlayer.setDisplay(holder);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mMediaPlayer.setDisplay(holder);
try {
mMediaPlayer.setDataSource(usedURL);
mMediaPlayer.prepareAsync();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void onPrepared(MediaPlayer mp) {
surfView.requestLayout();
surfView.invalidate();
mMediaPlayer.start();
}
#Override
public void onSurfaceViewSizeChanged(int width, int height) {
}
#Override
protected void onResume() {
super.onResume();
mMediaPlayer.seekTo((int)PREV_PLAYER_POS);
}
#Override
public void onBackPressed() {
super.onBackPressed();
mMediaPlayer.stop();
}
#Override
protected void onPause() {
super.onPause();
PREV_PLAYER_POS = mMediaPlayer.getCurrentPosition();
mMediaPlayer.pause();
}
#Override
protected void onDestroy() {
super.onDestroy();
mMediaPlayer.release();
mMediaPlayer = null;
}
}
Best Regards
Start your video in onResume,
#Override
protected void onResume() {
super.onResume();
mMediaPlayer.start();
mMediaPlayer.seekTo(length);
}
Hope it ll work..
The SurfaceView's surface may or may not be destroyed when you bring something up in front of the activity. See this section in the graphics architecture document for an overview. If it didn't get destroyed, then surfaceCreated() won't be called again, and your app will never call mMediaPlayer.setDisplay(holder) to connect the MediaPlayer to the surface.
I would guess that, if you brought up "recents" and then rotated the device, things would work when you returned (because the device rotation forces the surface to be recreated). You may want to add some logging to the various callbacks to see when they fire.
You will need to have a static variable that tracks whether or not the surface has been destroyed (e.g. static bool haveSurface = false, set to true in surfaceCreated(), false in surfaceDestroyed(). If it's true in onCreate(), call mMediaPlayer.setDisplay(holder) immediately.
Grafika has some examples of working with the odd SurfaceView-vs-Activity lifecycle issues, though I don't think any of them quite fit your use case.
Related
I'm currently working on getting the onStop() and onResume() methods right. The problem that I have is that my surfaceCreated() methode gets called everytime when I reopen the app through the onResume() methode. Because the game gets initialized in the surfaceCreated() method, which obviously causes the game to restart. But my goal is to make that it's just continues where it stopped.
I tried to stop the initializing process with the boolean started, but this causes the app to crash even it got initialized at the start.
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback{
public MainThread thread;
private static MainActivity context;
private boolean started = false;
public GamePanel(MainActivity context){
super(context);
this.context = context;
getHolder().addCallback(this);
thread = new MainThread(this.getHolder(), this);
setFocusable(true);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if(!started){
DisplayMetrics displayMetrics = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
thread.start();
}
thread.setRunning(true);
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
thread.setRunning(false);
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
I removed all the unimportant stuff
public class MainActivity extends AppCompatActivity {
private GamePanel gamePanel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
gamePanel = new GamePanel(this);
setContentView(gamePanel);
}
protected void onResume(){
super.onResume();
gamePanel.thread.setRunning(true);
}
protected void onStop(){
super.onStop();
}
protected void onPause(){
super.onPause();
gamePanel.thread.setRunning(false);
}
}
Im stopping the thread by setting thread.running to false. Which should pause the app logic and only continues as soon the onResume() method is called by setting thread.running to true again.
Do i have to recreate the SurfaceView everytime i reopen the app?
Im new to android and i couldn't find the solution on the web.
Thanks in advance!
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 have a MediaPlayer in a Fragment which retains its instance on configuration changes. The player is playing a video loaded from my assets directory. I have the scenario set up with the goal of reproducing the YouTube app playback where the audio keeps playing during the configuration changes and the display is detached and reattached to the media player.
When I start the playback and rotate the device, the position jumps forward about 6 seconds and (necessarily) the audio cuts out when this happens. Afterwards, the playback continues normally. I have no idea what could be causing this to happen.
As requested, here is the code:
public class MainFragment extends Fragment implements SurfaceHolder.Callback, MediaController.MediaPlayerControl {
private static final String TAG = MainFragment.class.getSimpleName();
AssetFileDescriptor mVideoFd;
SurfaceView mSurfaceView;
MediaPlayer mMediaPlayer;
MediaController mMediaController;
boolean mPrepared;
boolean mShouldResumePlayback;
int mBufferingPercent;
SurfaceHolder mSurfaceHolder;
#Override
public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) {
super.onInflate(activity, attrs, savedInstanceState);
final String assetFileName = "test-video.mp4";
try {
mVideoFd = activity.getAssets().openFd(assetFileName);
} catch (IOException ioe) {
Log.e(TAG, "Can't open file " + assetFileName + "!");
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
// initialize the media player
mMediaPlayer = new MediaPlayer();
try {
mMediaPlayer.setDataSource(mVideoFd.getFileDescriptor(), mVideoFd.getStartOffset(), mVideoFd.getLength());
} catch (IOException ioe) {
Log.e(TAG, "Unable to read video file when setting data source.");
throw new RuntimeException("Can't read assets file!");
}
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mPrepared = true;
}
});
mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
mBufferingPercent = percent;
}
});
mMediaPlayer.prepareAsync();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_main, container, false);
mSurfaceView = (SurfaceView) view.findViewById(R.id.surface);
mSurfaceView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mMediaController.show();
}
});
mSurfaceHolder = mSurfaceView.getHolder();
if (mSurfaceHolder == null) {
throw new RuntimeException("SufraceView's holder is null");
}
mSurfaceHolder.addCallback(this);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mMediaController = new MediaController(getActivity());
mMediaController.setEnabled(false);
mMediaController.setMediaPlayer(this);
mMediaController.setAnchorView(view);
}
#Override
public void onResume() {
super.onResume();
if (mShouldResumePlayback) {
start();
} else {
mSurfaceView.post(new Runnable() {
#Override
public void run() {
mMediaController.show();
}
});
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaController.setEnabled(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// nothing
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mMediaPlayer.setDisplay(null);
}
#Override
public void onPause() {
if (mMediaPlayer.isPlaying() && !getActivity().isChangingConfigurations()) {
pause();
mShouldResumePlayback = true;
}
super.onPause();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
#Override
public void onDestroyView() {
mMediaController.setAnchorView(null);
mMediaController = null;
mMediaPlayer.setDisplay(null);
mSurfaceHolder.removeCallback(this);
mSurfaceHolder = null;
mSurfaceView = null;
super.onDestroyView();
}
#Override
public void onDestroy() {
mMediaPlayer.release();
mMediaPlayer = null;
try {
mVideoFd.close();
} catch (IOException ioe) {
Log.e(TAG, "Can't close asset file..", ioe);
}
mVideoFd = null;
super.onDestroy();
}
// MediaControler methods:
#Override
public void start() {
mMediaPlayer.start();
}
#Override
public void pause() {
mMediaPlayer.pause();
}
#Override
public int getDuration() {
return mMediaPlayer.getDuration();
}
#Override
public int getCurrentPosition() {
return mMediaPlayer.getCurrentPosition();
}
#Override
public void seekTo(int pos) {
mMediaPlayer.seekTo(pos);
}
#Override
public boolean isPlaying() {
return mMediaPlayer.isPlaying();
}
#Override
public int getBufferPercentage() {
return mBufferingPercent;
}
#Override
public boolean canPause() {
return true;
}
#Override
public boolean canSeekBackward() {
return true;
}
#Override
public boolean canSeekForward() {
return true;
}
#Override
public int getAudioSessionId() {
return mMediaPlayer.getAudioSessionId();
}
}
The if block in the onPause method is not being hit.
Update:
After doing a bit more debugging, removing the interaction with the SurfaceHolder causes the problem to go away. In other words, if I don't setDisplay on the MediaPlayer the audio will work fine during the configuration change: no pause, no skip. It would seem there is some timing issue with setting the display on the MediaPlayer that is confusing the player.
Additionally, I have found that you must hide() the MediaController before you remove it during the configuration change. This improves stability but does not fix the skipping issue.
Another update:
If you care, the Android media stack looks like this:
MediaPlayer.java
-> android_media_MediaPlayer.cpp
-> MediaPlayer.cpp
-> IMediaPlayer.cpp
-> MediaPlayerService.cpp
-> BnMediaPlayerService.cpp
-> IMediaPlayerService.cpp
-> *ConcreteMediaPlayer*
-> *BaseMediaPlayer* (Stagefright, NuPlayerDriver, Midi, etc)
-> *real MediaPlayerProxy* (AwesomePlayer, NuPlayer, etc)
-> *RealMediaPlayer* (AwesomePlayerSource, NuPlayerDecoder, etc)
-> Codec
-> HW/SW decoder
Upon examining AwesomePlayer, it appears this awesome player takes the liberty of pausing itself for you when you setSurface():
status_t AwesomePlayer::setNativeWindow_l(const sp<ANativeWindow> &native) {
mNativeWindow = native;
if (mVideoSource == NULL) {
return OK;
}
ALOGV("attempting to reconfigure to use new surface");
bool wasPlaying = (mFlags & PLAYING) != 0;
pause_l();
mVideoRenderer.clear();
shutdownVideoDecoder_l();
status_t err = initVideoDecoder();
if (err != OK) {
ALOGE("failed to reinstantiate video decoder after surface change.");
return err;
}
if (mLastVideoTimeUs >= 0) {
mSeeking = SEEK;
mSeekTimeUs = mLastVideoTimeUs;
modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR);
}
if (wasPlaying) {
play_l();
}
return OK;
}
This reveals that setting the surface will cause the player to destroy whatever surface was previously being used as well as the video decoder along with it. While setting a surface to null should not cause the audio to stop, setting it to a new surface requires the video decoder to be reinitialized and the player to seek to the current location in the video. By convention, seeking will never take you further than you request, that is, if you overshoot a keyframe when seeking, you should land on the frame you overshot (as opposed to the next one).
My hypothesis, then, is that the Android MediaPlayer does not honor this convention and jumps forward to the next keyframe when seeking. This, coupled with a video source that has sparse keyframes, could explain the jumping I am experiencing. I have not looked at AwesomePlayer's implementation of seek, though. It was mentioned to me that jumping to the next keyframe is something that needs to happen if your MediaPlayer is developed with streaming in mind since the stream can be discarded as soon as it has been consumed. Point being, it might not be that far fetch to think the MediaPlayer would choose to jump forward as opposed to backwards.
Final Update:
While I still don't know why the playback skips when attaching a new Surface as the display for a MediaPlayer, thanks to the accepted answer, I have gotten the playback to be seamless during rotation.
Thanks to natez0r's answer, I have managed to get the setup described working. However, I use a slightly different method. I'll detail it here for reference.
I have one Fragment which I flag to be retained on configuration changes. This fragment handles both the media playback (MediaPlayer), and the standard TextureView (which provides the SurfaceTexture where the video buffer gets dumped). I initialize the media playback only once my Activity has finished onResume() and once the SurfaceTexture is available. Instead of subclassing TextureView, I simply call setSurfaceTexture (since it's public) in my fragment once I receive a reference to the SurfaceTexture. The only two things retained when a configuration change happens are the MediaPlayer reference, and the SurfaceTexture reference.
I've uploaded the source of my sample project to Github. Feel free to take a look!
I know this question is a tad old now, but I was able to get this working in my app without the skipping. The issue is the surface getting destroyed (killing whatever buffer it had in it). This may not solve all your issues because it targets API 16, but you can manage your own SurfaceTexture inside your custom TextureView where the video is drawn:
private SurfaceTexture mTexture;
private TextureView.SurfaceTextureListener mSHCallback =
new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
int height) {
mTexture = surface;
mPlayer.setSurface(new Surface(mTexture));
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
int height) {
mTexture = surface;
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mTexture = surface;
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
mTexture = surface;
}
};
the key is returning false in onSurfaceTextureDestroyed and holding onto mTexture. When the view gets re-attached to the window you can set the surfaceTexture:
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mTexture != null) {
setSurfaceTexture(mTexture);
}
}
This allows my view to continue playing video from EXACTLY where it left off.
I'm running into the problem when I my play a video with my app using mediaplayer. Sometimes the video will play fine but other times there will just be a black screen. Its not the file that I'm calling the video from because I've tested on the same file and it works sometimes and sometimes doesn't. I feel this problem most commonly occurs after I have played a couple of videos. I usually get away with playing 3-4 before the problem starts. I was wondering what caused this and how I could fix it. My code is posted below.
public class FullImageActivity extends Activity implements SurfaceHolder.Callback, OnPreparedListener, OnErrorListener{
private static final String TAG = null;
MediaPlayer player;
SurfaceView surfaceview;
SurfaceHolder surfaceHolder;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.full_image);
Intent i = getIntent();
long id = i.getExtras().getLong("id");
String path = i.getExtras().getString("videopath");
surfaceview = (SurfaceView)findViewById(R.id.surfaceview);
surfaceHolder = surfaceview.getHolder();
surfaceHolder.addCallback(this);
player = new MediaPlayer();
try {
player.reset();
player.setOnErrorListener(this);
player.setDataSource(path);
player.setOnPreparedListener(this);
player.prepare();
player.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
#Override
public void onPrepared(MediaPlayer p) {
}
#Override
public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
Log.i(TAG, "THERE WAS AN ERROR");
return false;
}
Thanks.
It's difficult to know exactly what the problem is.
A couple of things to try:
Make sure you are calling .release() in onPause(). This just ensures resources are released appropriately. You will need to reset things in onResume(), etc.
http://developer.android.com/reference/android/media/MediaPlayer.html#release()
Unless you really need to manipulate the surface, consider using a VideoView. It's not as fully featured as the MediaPlayer object but takes away some of the hassle of dealing with a SurfaceView, and manages the state for you.
http://developer.android.com/reference/android/widget/VideoView.html
The problem is, that your implementation relies on the assumption, that the activity gets destroyed, when the media player finished.
Yet it is possible, that your activity already exists, when your intent is fired up. In that case, your application skips onCreate(), so the media player can't be initialised.
You should override the onStart() method to launch the media player.
My SurfaceView is not getting destroyed even if onPause of the activity is called.
I am taking care of the thread in
public void surfaceCreated(SurfaceHolder holder) {
if (mGameThread.getState() == Thread.State.TERMINATED) {
createGameThread(getHolder(), getContext());
}
mGameThread.setRunning(true);
mGameThread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
mGameThread.setRunning(false);
while (retry) {
try {
mGameThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
As an hack I have to check the state of the thread in onResume and if the thread is already terminated, I would finish the activity
protected void onResume() {
Log.d(mLogTag, "onResume()");
super.onResume();
if (mGameThread != null) {
if (mGameThread.getState() == Thread.State.TERMINATED) {
finish();
}
}
}
Unfortunately it is not possible to move the thread handling from surfaceDestroyed and surfaceCreated to onPause() and onResume() of the activity. Is it possible to manually destroy the SurfaceView in the onPause() and recreate it in onResume()?
You can add surface view dynamically on your view.
Example :layout.xml
<FrameLayout
android:id="#+id/fragment_file_videoplayer_surface_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
MainActivity.java
FrameLayout fl_surfaceview_container =
(FrameLayout)findViewById(R.id.fragment_file_videoplayer_surface_container);
// Add surfaceView on Framelayout
SurfaceView videoSurface = new SurfaceView(getActivity());
fl_surfaceview_container.addView(videoSurface);
//if remove or destroy surfaceview
fl_surfaceview_container.removeAllViews();
You can add a layout as a parent of the surfaceview, then set visibility of the layout GONE in onPause(), and set VISIBLE in onResume() of the activity.
Yes possible.
First initialize Size
Size currentSurfaceSize;
cameraSurface.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(QR_Reader_Activity.this,
new String[]{Manifest.permission.CAMERA}, RequestCameraPermission);
permission = true;
return;
}
try {
cameraSource.start(cameraSurface.getHolder());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
currentSurfaceSize = new Size(width, height);
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
onPause();
}
});
where do you want to destroy the surface, use this below code.
if (currentSurfaceSize==null){
cameraSurface = (SurfaceView) cameraSurface.getHolder();
cameraSurface.removeCallbacks((Runnable) cameraSurface);
}