2

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
    }

}

4

0 回答 0