In my application I need to have live streaming (music), buffer it and when it is finished, play it and capture video (video together with music which is playing, something like Karaoke). the problem is When I run the application, Log cat shows me following messages. what is the reason of application lost the surface?
05-05 15:43:48.929: D/MediaRecorder(4745): doCleanUp
05-05 15:43:49.519: E/MediaRecorderJNI(4745): Application lost the surface
05-05 15:43:49.542: D/***VideoRecording***(4745): IOException preparing MediaRecorder: invalid preview surface
05-05 15:43:49.542: D/***VideoRecording***(4745): java.io.IOException: invalid preview surface
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.media.MediaRecorder._prepare(Native Method)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.media.MediaRecorder.prepare(MediaRecorder.java:591)
05-05 15:43:49.542: D/***VideoRecording***(4745): at com.astro.mania.activities.VideoRecording.prepareVideoRecorder(VideoRecording.java:340)
05-05 15:43:49.542: D/***VideoRecording***(4745): at com.astro.mania.activities.VideoRecording.onCreate(VideoRecording.java:161)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1615)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.os.Handler.dispatchMessage(Handler.java:99)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.os.Looper.loop(Looper.java:123)
05-05 15:43:49.542: D/***VideoRecording***(4745): at android.app.ActivityThread.main(ActivityThread.java:3687)
05-05 15:43:49.542: D/***VideoRecording***(4745): at java.lang.reflect.Method.invokeNative(Native Method)
05-05 15:43:49.542: D/***VideoRecording***(4745): at java.lang.reflect.Method.invoke(Method.java:507)
05-05 15:43:49.542: D/***VideoRecording***(4745): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
05-05 15:43:49.542: D/***VideoRecording***(4745): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
05-05 15:43:49.542: D/***VideoRecording***(4745): at dalvik.system.NativeStart.main(Native Method)
05-05 15:43:49.562: D/MediaRecorder(4745): reset:E
05-05 15:43:49.562: D/MediaRecorder(4745): doCleanUp
05-05 15:43:49.562: D/MediaRecorder(4745): doReset:E
05-05 15:43:49.578: D/MediaRecorder(4745): doReset:X
05-05 15:43:49.578: D/MediaRecorder(4745): close
05-05 15:43:49.582: D/MediaRecorder(4745): reset:X
05-05 15:43:49.582: D/MediaRecorder(4745): release:E
My CamcorderView calss is:
public class CamcorderView extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder mHolder;
private Camera mCamera;
//Create constructor of Preview Class. In this, get an object of
//surfaceHolder class by calling getHolder() method. After that add
//callback to the surfaceHolder. The callback will inform when surface is
//created/changed/destroyed. Also set surface not to have its own buffers.
public CamcorderView(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
// Implement the methods of SurfaceHolder.Callback interface
// SurfaceCreated : This method gets called when surface is created.
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.e("Error in CamcorderView", "Error setting camera preview: " + e.getMessage(), e);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.e("Error>>>>>", "Error starting camera preview: " + e.getMessage(), e);
}
}
}
and Video recording class:
public class VideoRecording extends Activity implements OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener {
private static final String TAG = "***VideoRecording***";
private static final int MEDIA_TYPE_VIDEO = 1;
private static final String MEDIA_NAME = "Mania-Karaoke";
private static final String VIDEO_FORMAT = ".mp4";
private static final String AUDIO_SOURCE = "http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3";
// private static String AUDIO_SOURCE;
private boolean mIsMusicReadyToBePlayed = false;
private boolean mIsMusicPrepared = false;
private String fileUri; //Holds the address of current video which is capturing
private Camera mCamera;
private CamcorderView preview;
private MediaRecorder mMediaRecorder = null;
private boolean isRecording = false;
private boolean isAudioPlayerReady = false;
private boolean isVideoRecorderReady = false;
private MediaPlayer mediaPlayer = null;
private ImageView stopButton;
private ImageView captureButton;
private ImageView imHide;
private RelativeLayout rlMenu;
private TextView tvInfo;
private Handler mHandler;
private Runnable mUpdateUITimerTask;
private int elapsedTime = 0;
private DecimalFormat df;
private Animation animation;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camcorder_preview);
Log.i(TAG, "VideoRecording activity started...");
Bundle extras = getIntent().getExtras();
if (extras == null) {
return;
}
// AUDIO_SOURCE = extras.getString("SONG_URL");
Log.i(TAG, "Music comes from: " + AUDIO_SOURCE);
// Close activity if there is no camera
if(!checkCameraHardware(VideoRecording.this)){
Toast.makeText(VideoRecording.this, "Sorry! Your device doesn't have camera.", Toast.LENGTH_LONG).show();
VideoRecording.this.finish();
}
// Create an instance of Camera
mCamera = getCameraInstance();
if(mCamera == null){
Toast.makeText(VideoRecording.this, "Oops! Your device doesn't have camera or it's in use.", Toast.LENGTH_LONG).show();
releaseCamera();
VideoRecording.this.finish();
}
// Initialize Animation, setting alpha of button view 0 and 1 in order to blink the button
recordAnimation();
// Set volume button of mobile device to increase/decrease sound instead of ringtone
setVolumeControlStream(AudioManager.STREAM_MUSIC);
tvInfo = (TextView) findViewById(R.id.tv_info);
df = new DecimalFormat("#.##");
mHandler = new Handler();
mUpdateUITimerTask = new Runnable() {
public void run() {
elapsedTime++;
int hours = elapsedTime / 3600;
int remainder = elapsedTime - hours * 3600;
int mins = remainder / 60;
remainder = remainder - mins * 60;
int secs = remainder;
File file = new File(getOutputMediaFileUri());
float fileSize = file.length();
if(fileSize <= 1024)
tvInfo.setText("E.T: " + hours + ":" + mins + ":" + secs + " / " + df.format(fileSize));
else if((fileSize > 1024) && (fileSize <= 1024*1024)){
fileSize /= 1024;
tvInfo.setText("E.T: " + hours + ":" + mins + ":" + secs + " / " + df.format(fileSize) + " K");
} else if((fileSize > 1024*1024) && (fileSize <= 1024*1024*1024)){
fileSize /= 1024*1024;
tvInfo.setText("E.T: " + hours + ":" + mins + ":" + secs + " / " + df.format(fileSize) + " M");
} else{
fileSize /= 1024*1024*1024;
tvInfo.setText("E.T: " + hours + ":" + mins + ":" + secs + " / " + df.format(fileSize) + " G");
}
// Log.i(TAG, " " + elapsedTime + " / " + fileSize + " : " + fileUri.toString());
mHandler.postDelayed(mUpdateUITimerTask, 1000);
}
};
// Create our Preview view and set it as the content of our activity.
preview = new CamcorderView(this, mCamera);
FrameLayout flPreview = (FrameLayout) findViewById(R.id.camera_preview);
flPreview.addView(preview);
// initialize media player
isAudioPlayerReady = prepareAudioPlayer();
isVideoRecorderReady = prepareVideoRecorder();
// Add a listener to the Capture button
captureButton = (ImageView) findViewById(R.id.btn_record);
captureButton.setVisibility(View.INVISIBLE);
captureButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
captureButton.setEnabled(false);
stopButton.setEnabled(true);
if (!isRecording) {
// initialize video camera
if (isAudioPlayerReady && isVideoRecorderReady) {
// Play background music
startMusicPlayback();
// Camera is available and unlocked, MediaRecorder is prepared,
// now you can start recording
mMediaRecorder.start();
// inform the user that recording has started
Toast.makeText(VideoRecording.this, "Video recording started", Toast.LENGTH_LONG).show();
isRecording = true;
captureButton.startAnimation(animation);
// Try to call handller each one second to update elapse time on screen
mHandler.postDelayed(mUpdateUITimerTask, 1000);
}
}
}
});
stopButton = (ImageView) findViewById(R.id.btn_stop);
stopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
captureButton.setEnabled(true);
stopButton.setEnabled(false);
if (isRecording) {
// stop recording and release camera
pauseMusicPlayback();
// mCamera.lock(); // take camera access back from MediaRecorder
// inform the user that recording has stopped
Toast.makeText(VideoRecording.this, "Video recording stopped", Toast.LENGTH_LONG).show();
isRecording = false;
captureButton.clearAnimation();
// Try to stop handller
elapsedTime = 0;
mHandler.removeCallbacks(mUpdateUITimerTask);
}
}
});
Button btnDone = (Button) findViewById(R.id.btn_done);
btnDone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
VideoRecording.this.finish();
}
});
rlMenu = (RelativeLayout) findViewById(R.id.rlMenu);
imHide = (ImageView) findViewById(R.id.btn_hide);
imHide.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
rlMenu.setVisibility(View.INVISIBLE);
}
});
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause called");
// Reset Audio manager to stream ring when user pushes volume button
setVolumeControlStream(AudioManager.STREAM_RING);
releaseMediaRecorder(); // if you are using MediaRecorder, release it first
releaseCamera(); // release the camera immediately on pause event
releaseMediaPlayer();
elapsedTime = 0;
mHandler.removeCallbacks(mUpdateUITimerTask);
captureButton.clearAnimation();
// Closing the activity
VideoRecording.this.finish();
Log.i(TAG, "All resources are released from memory in onPause() method.");
}
public void onBufferingUpdate(MediaPlayer arg0, int percent) {
if(percent <= 99)
Log.i(TAG, "onBufferingUpdate percent:" + percent);
else {
captureButton.setVisibility(View.VISIBLE);
mIsMusicReadyToBePlayed = true;
}
}
public void onCompletion(MediaPlayer arg0) {
Log.i(TAG, "onCompletion called");
}
public void onPrepared(MediaPlayer mediaplayer) {
Log.i(TAG, "onPrepared called");
mIsMusicPrepared = true;
}
private void startMusicPlayback() {
Log.i(TAG, "startMusicPlayback called");
if(mIsMusicReadyToBePlayed && mIsMusicPrepared) {
if(!mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(0);
mediaPlayer.start();
}
}
}
private void pauseMusicPlayback() {
Log.i(TAG, "pauseMusicPlayback called");
if(mediaPlayer.isPlaying())
mediaPlayer.pause();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
rlMenu.setVisibility(View.VISIBLE);
return super.onTouchEvent(event);
}
private boolean prepareVideoRecorder(){
mMediaRecorder = new MediaRecorder();
// Step 1: Unlock and set camera to MediaRecorder
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
// Step 2: Set sources
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// // Step *: Optional settings
// mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
// Step 4: Set output file
mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());
// Step 5: Set the preview output
mMediaRecorder.setPreviewDisplay(preview.getHolder().getSurface());
// Step 7: Prepare configured MediaRecorder
try {
mMediaRecorder.prepare();
Log.i(TAG, "Media Recorder prepared successfully.");
} catch (IllegalStateException e) {
Log.e(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage(), e);
releaseMediaRecorder();
return false;
} catch (IOException e) {
Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage(), e);
releaseMediaRecorder();
return false;
}
// Optional settings
// mMediaRecorder.setVideoEncodingBitRate();
// mMediaRecorder.setVideoSize();
// mMediaRecorder.setVideoFrameRate();
// mMediaRecorder.setAudioEncodingBitRate();
// mMediaRecorder.setAudioChannels();
// mMediaRecorder.setAudioSamplingRate();
return true;
}
private boolean prepareAudioPlayer() {
boolean flag = false;
// Create a new media player and set the listeners
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(AUDIO_SOURCE);
mediaPlayer.prepare();
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
flag = true;
Log.i(TAG, "Media Player prepared successfully.");
} catch (IllegalArgumentException e) {
Log.e(TAG, "Media Player did not prepare successfully.");
Log.e(TAG, "IllegalArgumentException > " + e.getMessage(), e);
} catch (IllegalStateException e) {
Log.e(TAG, "Media Player did not prepare successfully.");
Log.e(TAG, "IllegalStateException > " + e.getMessage(), e);
} catch (IOException e) {
Log.e(TAG, "Media Player did not prepare successfully.");
Log.e(TAG, "IOException > " + e.getMessage(), e);
}
return flag;
}
/** Create a file Uri for saving an image or video */
private String getOutputMediaFileUri(){
return fileUri;
}
/** Create a File for saving an image or video */
private File getOutputMediaFile(int type) {
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), MEDIA_NAME);
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.e(MEDIA_NAME, "failed to create directory");
Toast.makeText(VideoRecording.this, "SD Card not found!", Toast.LENGTH_LONG).show();
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_VIDEO){
mediaFile = new File(mediaStorageDir.getPath() + File.separator + MEDIA_NAME + "_"+ timeStamp + VIDEO_FORMAT); // or .mp4
fileUri = mediaFile.toString();
} else {
return null;
}
return mediaFile;
}
private void releaseMediaRecorder() {
if (mMediaRecorder != null) {
mMediaRecorder.reset(); // clear recorder configuration
mMediaRecorder.release(); // release the recorder object
mMediaRecorder = null;
mCamera.lock(); // lock camera for later use
Log.i(TAG, "Media Recorder released successfully.");
}
}
private void releaseCamera() {
if (mCamera != null){
mCamera.release(); // release the camera for other applications
mCamera = null;
Log.i(TAG, "Camera released successfully.");
}
}
/** A safe way to get an instance of the Camera object. */
public Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
} catch (Exception e){
// Camera is not available (in use or does not exist)
Toast.makeText(VideoRecording.this, "Sorry! Your device doesn't have camera or it's in use.", Toast.LENGTH_LONG).show();
}
return c; // returns null if camera is unavailable
}
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
private void releaseMediaPlayer() {
if(mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
Log.i(TAG, "Media Player released successfully.");
}
}
private void recordAnimation(){
animation = new AlphaAnimation(1, 0); // Change alpha from fully visible to invisible
animation.setDuration(500); // duration - half a second
animation.setInterpolator(new LinearInterpolator()); // do not alter animation rate
animation.setRepeatCount(Animation.INFINITE); // Repeat animation infinitely
animation.setRepeatMode(Animation.REVERSE); // Reverse animation at the end so the button will fade back in
}
}