안드로이드 개발 질문/답변
(글 수 45,052)
제가 mediarecorder를 통해서 동영상을 녹화하고 있습니다.
그런데 몇십초 단위로 영상을 저장하게 되는데 동영상이 끝날때 record stop()을 호출하여 해제를 하고 있습니다. 그런데 이 부분에서 해제하는데 10초 가까이 걸리면서 연속적으로 동영상이 저장되지 않고 시간간격이 생기게 됩니다. 제가 구글과 관련 커뮤니티 사이트에서 자료를 찾아보았지만 이와 관련한 내용에 대해서는 찾지를 못했습니다.
그래서 제가 생각해 낸것이 안드로이드 캠코더 어플을 보면 초기시작할때만 시간이 좀 걸리고 녹화,중지시 preview는 계속 살아있으면서 파일이 저장되는것을 확인했는데요.
그와같이 preview를 계속살려두고 정해놓은 시간단위로 연속으로 파일을 만들려고 하고 있습니다.
혹시 방법이나 아시는 자료 있으시면 꼭 좀 부탁드리겠습니다.
camera 객체를 surface를 이용해서 preview를 하게되면 jpg파일로 저장하게 되는데 그 방법을 써야되는것인지, 현재는 mediarecorder를 이용해서 setDisplayPreview를 이용해서 미리보기 하다가 시작이벤트를 발생시켜서 start(),stop()하도록 해놓았는데 이걸 응용할 수 있는지 머리가 터질것 같습니다..
고수님들의 도움 꼭 좀 부탁드리겠습니다 ㅠ.ㅠ
그런데 몇십초 단위로 영상을 저장하게 되는데 동영상이 끝날때 record stop()을 호출하여 해제를 하고 있습니다. 그런데 이 부분에서 해제하는데 10초 가까이 걸리면서 연속적으로 동영상이 저장되지 않고 시간간격이 생기게 됩니다. 제가 구글과 관련 커뮤니티 사이트에서 자료를 찾아보았지만 이와 관련한 내용에 대해서는 찾지를 못했습니다.
그래서 제가 생각해 낸것이 안드로이드 캠코더 어플을 보면 초기시작할때만 시간이 좀 걸리고 녹화,중지시 preview는 계속 살아있으면서 파일이 저장되는것을 확인했는데요.
그와같이 preview를 계속살려두고 정해놓은 시간단위로 연속으로 파일을 만들려고 하고 있습니다.
혹시 방법이나 아시는 자료 있으시면 꼭 좀 부탁드리겠습니다.
camera 객체를 surface를 이용해서 preview를 하게되면 jpg파일로 저장하게 되는데 그 방법을 써야되는것인지, 현재는 mediarecorder를 이용해서 setDisplayPreview를 이용해서 미리보기 하다가 시작이벤트를 발생시켜서 start(),stop()하도록 해놓았는데 이걸 응용할 수 있는지 머리가 터질것 같습니다..
고수님들의 도움 꼭 좀 부탁드리겠습니다 ㅠ.ㅠ
2010.06.11 11:31:22
구글 카메라 소스 코드 살펴봐도, MediaRecorder 사용하고 그냥 stop()을 호출하고 있더군요.
Release()는 하지 않고요.
Stop()이 10초나 걸리는 이유가 뭔지 모르겠네요. Stop()이 아닌 다른 곳에서 오래 걸리는 것은 아닌지 확인해보시는 게 좋을 것 같습니다.
2010.06.11 11:35:44
우선 답변해 주셔서 감사합니다.
코드는 정확하게 stop()에서 오래걸리는게 맞습니다.
그래서 surface view를 두 개 생성해서 해보려고 하는데 잘 안되네요
2010.06.11 13:09:56
stop()에서 오래 걸리면 어떻게 할 방법이 없을 것 같으네요.
MediaRecorder.Stop()은 PVMediaRecorder.Stop()을 호출하게 되어 있습니다.
이때 PV 쪽에서는 File에다 Write Flush하고, Encoder Relase하고, Video랑 마이크 연결 끊고 이런 짓들을 합니다.
제법 해야될 일들이 많아서 시간이 좀 걸립니다. (그래도 10초는 이해못할 시간입니다. PVAuthorEngine에 과도하게 command가 들어가 있어 이 command들이 다 처리될 때까지 stop이 처리안 되어서 늦게 될 수도 있을 것 같기도 한데. PVAuthor는 command가 많지 않아 이럴 일도 없을 것 같으네요)
Stop()이 오래 걸리면 MediaRecorder는 사용하실 수가 없을 것 같네요.
그렇다고 직접 PV를 제어한다고 해도 여전히 동일하게 지연이 발생할 것 같습니다.
코드 한번 올려주시면 좀 더 파악하기가 좋을 것 같으네요.
2010.06.11 14:55:21
onCreate함수에서 아래와 같이 초기화를 합니다. recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); if(config_resolution == 640) recorder.setVideoSize(640, 480); else if(config_resolution == 720) recorder.setVideoSize(720, 480); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); private void startRecording() { try { recorder.start(); setpoint_enable = true; new CountDownTimer(20000, 1000) { public void onTick(long millisUntilFinished) { video_count = millisUntilFinished / 1000; SetPoint(); } public void onFinish() { if(bModeChange == false){ recorder.stop(); <== 이부분에서 10초 이상 걸립니다. recorder.release(); recorder = null; if(setpoint_enable == true){ setpoint_enable = false; SaveTrack(mapLocations, DriveRecorder.this, "/sdcard/drive_rec/track/"+track_file_name + ".trk"); } Intent intent = getIntent(); intent.putExtra("modechange", 0); setResult(RESULT_OK, intent); } else bModeChange = false; } }.start();
위의 함수에서 스타트를 하고 쓰레드가 끝날때 멈추게 되어있습니다.
이과정을 activity전환하면서 반복하게 됩니다.
아래는 preview suface입니다
소스는 대략 이와 같습니다.
class Preview extends SurfaceView implements SurfaceHolder.Callback { SurfaceHolder mHolder; MediaRecorder tempRecorder; String file_name; Preview(Context context, MediaRecorder recorder, String filename) { super(context); tempRecorder = recorder; file_name = filename; mHolder = getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public Surface getSurface() { return mHolder.getSurface(); } public void start() throws IOException { tempRecorder.setOutputFile(file_name); tempRecorder.setPreviewDisplay(mHolder.getSurface()); try{ tempRecorder.prepare(); } catch (Exception e) { String message = e.getMessage(); tempRecorder.release(); tempRecorder = null; } } public void surfaceCreated(SurfaceHolder holder) { try { start(); } catch (IOException e) { tempRecorder.release(); tempRecorder = null; } } public void surfaceDestroyed(SurfaceHolder holder) { if(tempRecorder!=null) { tempRecorder.stop(); tempRecorder.release(); tempRecorder = null; } } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { } }
mediarecorder는 한번 셋팅하고 계속적으로 preview를 하다가 녹화 시작,끝에 대해서만 파일로 처리할 수 있는 방법은 없을까요?
2010.06.14 11:46:37
안드로원에서 아래와 같이 테스트 코드 만들어서 해봤는데, stop()에서는 1초 가량 걸리더군요.
안드로원이 720P를 지원하지 않아 해상도와 프레임 수는 줄이고 했습니다.
Stop()에서 여전히 문제가 된다면 해상도랑 프레임수 줄이고 한번 해보시죠
package won.ki; import android.app.Activity; import android.os.Bundle; import android.media.*; import android.view.*; import android.util.*; import android.os.*; import java.io.*; public class CamTest extends Activity implements SurfaceHolder.Callback, Handler.Callback { final private String TAG = "CamTest"; //handler command final private int START_RECORDING = 1; final private int STOP_RECORDING = 2; final private int INIT_RECORDER = 3; final private int RELEASE_RECORDER = 4; final private int START_INTERVAL_RECORD = 5; private SurfaceHolder mSurfaceHolder = null; private MediaRecorder mMediaRecorder = null; private Handler mHandler; private CountDownTimer mTimer = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //preview surface SurfaceView surView = (SurfaceView)findViewById(R.id.preview); // surView.setAspectRatio(320.0f / 240.0f); SurfaceHolder holder = surView.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //handler mHandler = new Handler(this); } @Override public void onResume() { super.onResume(); } @Override public void onStop() { super.onStop(); if ( mTimer!=null ) { mTimer.cancel(); mTimer = null; } stopMediaRecorder(); releaseMediaRecorder(); } int mIth = 0; protected void startIntervalRecording() { mTimer = new CountDownTimer(20000, 1000) { boolean recordStart = false; public void onTick(long millisUntilFinished) { if ( !recordStart) { recordStart = true; mHandler.sendEmptyMessage(START_RECORDING); } } public void onFinish() { mHandler.sendEmptyMessage(STOP_RECORDING); mHandler.sendEmptyMessage(RELEASE_RECORDER); mHandler.sendEmptyMessage(INIT_RECORDER); mHandler.sendEmptyMessage(START_INTERVAL_RECORD); } }; mTimer.start(); } protected void initMediaRecorder() { if ( mSurfaceHolder==null ) { Log.e(TAG, "No Surface Holder"); return; } mMediaRecorder = new MediaRecorder(); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); // mMediaRecorder.setMaxDuration(200000); mMediaRecorder.setOutputFile("/sdcard/" + "Record" + mIth + ".mp4"); mIth++; mMediaRecorder.setVideoFrameRate(16); mMediaRecorder.setVideoSize(320,240); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); try { mMediaRecorder.prepare(); } catch (IOException exception) { releaseMediaRecorder(); return; } } protected void releaseMediaRecorder() { if ( mMediaRecorder==null ) return; mMediaRecorder.reset(); mMediaRecorder.release(); mMediaRecorder = null; } protected void startMediaRecorder() { if ( mMediaRecorder!=null ) { Log.v(TAG, "Before Record Start"); mMediaRecorder.start(); Log.v(TAG, "Record Started"); } } protected void stopMediaRecorder() { if ( mMediaRecorder!=null ) { Log.v(TAG, "Before Record Stop"); mMediaRecorder.stop(); Log.v(TAG, "Record Stopped"); } } //-------------------------------------------------------------------- // SurfaceHolder.Callback Implementation public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { //do something } public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; //init video initMediaRecorder(); startIntervalRecording(); } public void surfaceDestroyed(SurfaceHolder holder) { mSurfaceHolder = null; } //-------------------------------------------------------------------- // Handler.Callback Implementation public boolean handleMessage(Message msg) { switch (msg.what ) { case START_RECORDING: startMediaRecorder(); return true; case STOP_RECORDING: stopMediaRecorder(); return true; case INIT_RECORDER: initMediaRecorder(); return true; case RELEASE_RECORDER: releaseMediaRecorder(); return true; case START_INTERVAL_RECORD: startIntervalRecording(); return true; } return false; } }