/* * AnBox, and an Android Blackbox application for the have-not-so-much-money's * Copyright (C) 2010 Yoonsoo Kim, Heekuk Lee, Heejin Sohn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.ivehicle.AnBox; import java.io.File; import java.io.IOException; import com.ivehicle.AnBox.R; import com.ivehicle.util.Log; import android.app.Activity; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.media.MediaRecorder; import android.net.Uri; import android.os.PowerManager; import android.provider.MediaStore; import android.provider.MediaStore.Video; import android.view.SurfaceHolder; import android.view.SurfaceView; public class VisionRecorder implements SurfaceHolder.Callback, DataStorageManager.DataRecorder, MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener { private static final int ST_NOT_INIT = 0; private static final int ST_RECORDER_INIT = 1; private static final int ST_PREVIEWING = 2; private static final int ST_RECORDING = 3; private static final int ST_WAITING_SF = 4; private static final int ST_SURFACE_INIT = 5; private int state = ST_NOT_INIT; private long dateRestart = 0; private Activity act_ = null; PowerManager.WakeLock wl = null; private String videoFileName = null; private ContentValues videoValues = null; private MediaRecorder recorder = null; private SurfaceHolder surfaceHolder = null; private long recordingStarted = 0; public VisionRecorder(Activity act, boolean faked) { act_ = act; SurfaceView sv = (SurfaceView)act_.findViewById(R.id.surface_view); SurfaceHolder surfaceHolder = sv.getHolder(); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); surfaceHolder.addCallback(this); Log.d(Config.TAG, toString() + "(): after init SurfaceHolder"); } public void onError(MediaRecorder mr, int what, int extra) { Log.d(Config.TAG, "VisionRecorder.onError(): what = " + what + " extra = " + extra); } public void onInfo(MediaRecorder mr, int what, int extra) { Log.d(Config.TAG, "VisionRecorder.onInfo(): what = " + what + " extra = " + extra); } public void surfaceCreated(SurfaceHolder holder) { Log.d(Config.TAG, "Surface created!"); surfaceHolder = holder; handleSurfaceCreatedEvent(); } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d(Config.TAG, "Surface changed!"); } public void surfaceDestroyed(SurfaceHolder holder) { Log.d(Config.TAG, "Surface destroyed!"); handleStopEvent(); surfaceHolder = null; if (state == ST_SURFACE_INIT) state = ST_NOT_INIT; } private void createVideoPath(long dateTaken) { Log.d(Config.TAG, toString() + ".createVideoPath()"); String title = Config.getFileName(dateTaken); String filename = Config.getMovieFileName(dateTaken); ContentValues values = new ContentValues(8); values.put(Video.Media.TITLE, title); values.put(Video.Media.DISPLAY_NAME, title + "." + Config.MOVIE_EXT); values.put(Video.Media.DATE_TAKEN, dateTaken); values.put(Video.Media.MIME_TYPE, Config.MOVIE_MIME_TYPE); values.put(Video.Media.DATA, filename); videoFileName = filename; Log.v(Config.TAG, toString() + ".createVideoPath(): Current camera video filename: " + videoFileName); videoValues = values; } private void initRecording() { createVideoPath(dateRestart); if (wl == null) { PowerManager pm = (PowerManager) act_.getSystemService(Context.POWER_SERVICE); wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, Config.TAG); wl.acquire(); } if (recorder == null) { recorder = new MediaRecorder(); } recorder.reset(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setOutputFile(videoFileName); recorder.setVideoFrameRate(15); recorder.setVideoSize(640, 480); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); recorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); Log.d(Config.TAG, toString() + ".initRecording(): after init MediaRecorder, Returning"); } private void startPreview() throws IllegalStateException, IOException { recorder.setPreviewDisplay(surfaceHolder.getSurface()); recorder.prepare(); Log.d(Config.TAG, toString() + ".startPreview(): after MediaRecorder.prepare()"); } private void startRecording() { recorder.start(); recordingStarted = System.currentTimeMillis(); Log.d(Config.TAG, toString() + ".startRecording(): after MediaRecorder.start()"); } private void stopRecording() { Log.d(Config.TAG, toString() + ".stopRecording(): stop recording..."); recorder.stop(); Log.d(Config.TAG, toString() + ".stopRecording(): Recorded video filename:" + videoFileName); videoValues.put(Video.Media.DURATION, System.currentTimeMillis() - recordingStarted); videoValues.put(Video.Media.SIZE, new File(videoFileName).length()); Uri videoUri = act_.getContentResolver().insert( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoValues); if (videoUri == null) { Log.d(Config.TAG, toString() + ".stopRecording(): Content resolver failed"); return; } Log.d(Config.TAG, toString() + ".stopRecording(): Video URI = " + videoUri.getPath()); videoValues = null; // Force Media scanner to refresh now. Technically, this is // unnecessary, as the media scanner will run periodically but // helpful for testing. act_.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, videoUri)); Log.d(Config.TAG, toString() + ".stopRecording(): Video file published"); } private void deinitRecording() { recorder.reset(); recorder.release(); recorder = null; wl.release(); wl = null; } private void handlePrepareEvent() { try { switch (state) { case ST_NOT_INIT: initRecording(); state = ST_RECORDER_INIT; break; case ST_RECORDING: stopRecording(); initRecording(); startPreview(); startRecording(); state = ST_RECORDING; break; case ST_SURFACE_INIT: initRecording(); startPreview(); state = ST_PREVIEWING; break; default: break; } } catch (Exception e) { handleExceptionEvent(e); } } private void handleSurfaceCreatedEvent() { try { switch (state) { case ST_NOT_INIT: initRecording(); startPreview(); state = ST_PREVIEWING; break; case ST_RECORDER_INIT: startPreview(); state = ST_PREVIEWING; break; case ST_WAITING_SF: startPreview(); startRecording(); state = ST_RECORDING; break; default: break; } } catch (Exception e) { handleExceptionEvent(e); } } private void handleStartEvent() { try { switch (state) { case ST_NOT_INIT: initRecording(); state = ST_WAITING_SF; break; case ST_RECORDER_INIT: state = ST_WAITING_SF; break; case ST_PREVIEWING: startRecording(); state = ST_RECORDING; break; case ST_SURFACE_INIT: initRecording(); startPreview(); startRecording(); state = ST_RECORDING; break; default: break; } } catch (Exception e) { handleExceptionEvent(e); } } private void handleStopEvent() { try { switch (state) { case ST_RECORDER_INIT: case ST_WAITING_SF: deinitRecording(); state = ST_NOT_INIT; break; case ST_PREVIEWING: deinitRecording(); state = ST_SURFACE_INIT; break; case ST_RECORDING: stopRecording(); deinitRecording(); state = ST_SURFACE_INIT; break; default: state = ST_NOT_INIT; break; } } catch (Exception e) { handleExceptionEvent(e); } } private void handleExceptionEvent(Exception e) { Log.e(Config.TAG, "Exception occurred: ", e); deinitRecording(); switch (state) { case ST_NOT_INIT: case ST_RECORDER_INIT: case ST_WAITING_SF: state = ST_NOT_INIT; break; case ST_PREVIEWING: case ST_RECORDING: case ST_SURFACE_INIT: state = ST_SURFACE_INIT; break; } } public synchronized String prepareRecording(long dateRestart) { Log.d(Config.TAG, toString() + ".prepareRecording()"); this.dateRestart = dateRestart; handlePrepareEvent(); Log.d(Config.TAG, toString() + ".prepareRecording(): Returning"); return videoFileName; } public synchronized void start() { Log.d(Config.TAG, toString() + ".start()"); handleStartEvent(); } public synchronized void stop() { handleStopEvent(); } public synchronized boolean isRecording() { return (state == ST_RECORDING); } }