私のアプリケーションでは、ライブ ストリーミング (音楽) を行い、それをバッファリングし、終了したら再生し、ビデオ (再生中の音楽と一緒にビデオ、カラオケのようなもの) をキャプチャする必要があります。問題は、アプリケーションを実行すると、Log cat に次のメッセージが表示されることです。アプリケーションが表面を失った理由は何ですか?
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
私の CamcorderView カルスは次のとおりです。
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);
}
}
}
およびビデオ録画クラス:
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
}
}