I am new to android programming and I am trying to collect the video as a mp4 file and the sensor data and write the same into a dat file. I have managed to start recording but stop recording freezes the application. There are 2 phases.
Phase I: A SurfaceView is used to overlay the sensor readings as text onto the video input. The SurfaceView runs on the UI thread while the sensor logging and MediaRecorder run on separate threads. The camera preview is generated as desired.
Phase II: Now i try to start recording and also write the relevant sensor data with timestamps into a file. I am using Media Recorder to achieve this. When i try to stop recording the whole program hangs. When in debug mode, the code doesnt freeze at mediaRecorder.stop() but the mp4 file that is generated is corrupt and I am unable to open the same using a Video Player (VLC).
The sensor readings are logged as required. Also, the video recording doesnt work even if I dont log the sensor values.
Can someone help me out with this ?
The Main Activity:
package priyaman.com.glassdatavisualizer;
import com.google.android.glass.media.Sounds;
import com.google.android.glass.touchpad.Gesture;
import com.google.android.glass.touchpad.GestureDetector;
import com.google.android.glass.widget.CardBuilder;
import com.google.android.glass.widget.CardScrollAdapter;
import com.google.android.glass.widget.CardScrollView;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.hardware.Camera;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import priyaman.com.glassdatavisualizer.controllers.CameraController;
import priyaman.com.glassdatavisualizer.controllers.DrawViewController;
import priyaman.com.glassdatavisualizer.controllers.SensorController;
import priyaman.com.glassdatavisualizer.controllers.SensorThread;
import priyaman.com.glassdatavisualizer.service.MotionSensorService;
import priyaman.com.glassdatavisualizer.utilities.CameraUtils;
public class GlassVisualizerActivity extends Activity {
private Camera camera;
private CameraController cameraController;
private GestureDetector mGestureDetector;
private SensorController sensorController;
private SensorThread sensorThread = null;
private DrawViewController drawView;
FrameLayout alParent;
public GestureDetector createGestureDetector(final Context context)
{
GestureDetector gestureDetector = new GestureDetector(context);
//Create a base listener for generic gestures
gestureDetector.setBaseListener( new GestureDetector.BaseListener() {
@Override
public boolean onGesture(Gesture gesture) {
if(priyaman.com.glassdatavisualizer.utilities.Log.D) priyaman.com.glassdatavisualizer.utilities.Log.d("gesture = " + gesture);
if (gesture == Gesture.TAP) {
cameraController.releaseCamera();
if(cameraController.isRecording){
cameraController.stopRecordingThread.run();//recordThread.run();
cameraController = new CameraController(context,camera);
}else{
cameraController.recordThread.run();//startRecording();
}
return true;
} else if (gesture == Gesture.TWO_TAP) {
//handleGestureTwoTap();
//doStopService();
return true;
}
return false;
}
});
return gestureDetector;
}
@Override
protected void onCreate(Bundle bundle) {
Log.d("Debug:", "onCreate: GlassVisualizerActivity");
super.onCreate(bundle);
camera = CameraUtils.getCameraInstance();
Camera.Parameters params = camera.getParameters();
params.setRecordingHint(true);
camera.setParameters(params);
cameraController = new CameraController(this, camera);
//this.setContentView(cameraController);
mGestureDetector = this.createGestureDetector(this);
drawView = new DrawViewController(this,cameraController);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (cameraController != null){
alParent = new FrameLayout(this);
alParent.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.FILL_PARENT,
FrameLayout.LayoutParams.FILL_PARENT));
//doStartService();
alParent.addView(cameraController);
alParent.addView(drawView);
this.setContentView(alParent);
//sensorController = new SensorController(this,drawView);
sensorThread = new SensorThread(this,drawView);
sensorThread.run();
}
}
@Override
protected void onResume() {
super.onResume();
if (cameraController != null) {
cameraController.releaseCamera();
}
//doStartService();
// Set the view
//alParent.addView(drawView);
//alParent.addView(cameraController);
this.setContentView(alParent);
}
@Override
public boolean onGenericMotionEvent(MotionEvent event)
{
if (mGestureDetector != null) {
return mGestureDetector.onMotionEvent(event);
}
return false;
}
@Override
protected void onPause() {
if (cameraController != null) {
cameraController.releaseMediaRecorder(); // if you are using MediaRecorder, release it first
cameraController.releaseCamera(); // release the camera immediately on pause event
}
super.onPause();
}
}
Drawing SurfaceView:
package priyaman.com.glassdatavisualizer.controllers;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import priyaman.com.glassdatavisualizer.utilities.Log;
import priyaman.com.glassdatavisualizer.utilities.Utilities;
/**
* Created by priya_000 on 5/20/2015.
*/
public class DrawViewController extends SurfaceView implements SurfaceHolder.Callback{
private Paint textPaint = new Paint();
public SurfaceHolder surfaceHolder = null;
private Context context = null;
private CameraController cameraController = null;
public DrawViewController(Context context, CameraController cameraController) {
super(context);
this.context = context;
textPaint = new Paint();
textPaint.setARGB(255, 200, 0, 0);
textPaint.setTextSize(20);
setWillNotDraw(false);
surfaceHolder = getHolder();
this.cameraController = cameraController;
}
@Override
protected void onDraw(Canvas canvas){
String content = "";
int counter = 50;
if(Utilities.logSensorValues){
canvas.drawText("Recording:",50,20,textPaint);
}
if(Utilities.lastSensorValuesAccelerometer != null) {
content += "Accelerometer:\n" + Utilities.lastSensorValuesAccelerometer.toString() + "\n";
canvas.drawText("Accelerometer:\n" + Utilities.lastSensorValuesAccelerometer.toString(), 50, counter+=25, textPaint);
}
if(Utilities.lastSensorValuesGravity != null) {
content += "Gravity:\n" + Utilities.lastSensorValuesGravity.toString() + "\n";
canvas.drawText("Gravity:\n" + Utilities.lastSensorValuesGravity.toString() , 50, counter+=25, textPaint);
}
if(Utilities.lastSensorValuesLinearAcceleration != null) {
content += "Linear Acceleration:\n" + Utilities.lastSensorValuesLinearAcceleration.toString() + "\n";
canvas.drawText( "Linear Acceleration:\n" + Utilities.lastSensorValuesLinearAcceleration.toString() , 50, counter+=25, textPaint);
}
if(Utilities.lastSensorValuesGyroscope != null) {
content += "Gyroscope:\n" + Utilities.lastSensorValuesGyroscope.toString() + "\n";
canvas.drawText( "Gyroscope:\n" + Utilities.lastSensorValuesGyroscope.toString() , 50, counter+=25, textPaint);
}
if(Utilities.lastSensorValuesRotationVector != null) {
content += "Rotation Vector:\n" + Utilities.lastSensorValuesRotationVector.toString() + "\n";
canvas.drawText( "Rotation Vector:\n" + Utilities.lastSensorValuesRotationVector.toString() , 50, counter+=25, textPaint);
}
Log.d("Values Published:" + content);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas c = holder.lockCanvas();
if(c!=null)
draw(c);
if(c!=null)
holder.unlockCanvasAndPost(c);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Canvas c = holder.lockCanvas();
if(c!=null)
draw(c);
if(c!=null)
holder.unlockCanvasAndPost(c);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraController.releaseCamera();
}
}
Camera Related SurfaceView:
package priyaman.com.glassdatavisualizer.controllers;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import priyaman.com.glassdatavisualizer.utilities.Log;
import android.os.Handler;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import priyaman.com.glassdatavisualizer.R;
import priyaman.com.glassdatavisualizer.utilities.CameraUtils;
import priyaman.com.glassdatavisualizer.utilities.Utilities;
/**
* Created by priya_000 on 5/19/2015.
*/
public class CameraController extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder finalView;
private Camera camera;
private MediaRecorder mediaRecorder;
public boolean isRecording = false;
public RecThread recordThread = null;
public StopRecordingThread stopRecordingThread = null;
public CameraController(Context context, Camera camera){
super(context);
Log.d("In Camera Controller Constructor");
this.camera = camera;
finalView = getHolder();
finalView.addCallback(this);
finalView.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mediaRecorder = new MediaRecorder();
recordThread = new RecThread(mediaRecorder);
stopRecordingThread = new StopRecordingThread((mediaRecorder));
}
public void surfaceCreated(SurfaceHolder holder) {
Log.v("In create surface");
try {
if(camera==null){
camera = CameraUtils.getCameraInstance();
if(camera == null)
return;
//setMeteringParams();
}
camera.setPreviewDisplay(holder);
} catch (IOException e1) {
Log.e("Error in surface creation",e1);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
//Log that camera controller destroyed
Log.d("In surface destroyed:Camera Controller Destroyed");
if(camera!= null)
camera.stopPreview();
releaseCamera();
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h){
Log.d("In Surface Changed");
if (holder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e){
}
try {
//setMeteringParams();
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e){
}
}
public class RecThread implements Runnable{
MediaRecorder mediaRecorder = null;
public RecThread(MediaRecorder mediaRecorder){
this.mediaRecorder = mediaRecorder;
}
public void run(){
startRecording();
}
public boolean prepareVideoRecorder(){
Log.d("Preparing Video Recorder");
try {
if (camera == null) {
camera = CameraUtils.getCameraInstance();
}
camera.unlock();
//mediaRecorder = new MediaRecorder();
mediaRecorder.setCamera(camera);
//mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
try {
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
}catch(Exception e){
Log.e("Exception caught in setting profile");
e.printStackTrace();
}
//mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
//mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mediaRecorder.setPreviewDisplay(finalView.getSurface());
File outputFile = CameraUtils.getOutputMediaFile(CameraUtils.MEDIA_TYPE_VIDEO);
Utilities.sensorFileName = outputFile.getName();
Utilities.logSensorValues = true;
mediaRecorder.setOutputFile(outputFile.toString());
try {
mediaRecorder.prepare();
} catch (IllegalStateException e) {
Log.d("IllegalStateException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
} catch (IOException e) {
Log.d("IOException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
}
//return true;
}catch(Exception e){
Log.e("Error in prepare Video Recorder",e);
e.printStackTrace();
return false;
}finally{
//this.releaseCamera();
//Log.d("Camera released");
//return false;
Log.d("In finally block");
}
return true;
}
public void startRecording(){
Log.d("In Start Recording");
Log.d("Camera is :" + String.valueOf(camera==null));
if(isRecording){
Log.i("Stopping Recording");
Utilities.logSensorValues = false;
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
Log.i("Sleeping for 1 sec after stopping recording");
}
}, 1000);
mediaRecorder.stop();
Log.i("Video Stored@");
releaseMediaRecorder();
try {
//camera.reconnect();
//camera.lock();
if(camera == null){
camera = CameraUtils.getCameraInstance();
}
camera.setPreviewDisplay(getHolder());
camera.lock();
camera.startPreview();
}catch(Exception e){
Log.e("Preview display on setpreview after release media recotrdr",e);
}
isRecording = false;
}else{
if(prepareVideoRecorder()){
mediaRecorder.start();
Log.i("Started Recording");
isRecording = true;
}
}
}
}
public void releaseMediaRecorder(){
if (mediaRecorder != null) {
mediaRecorder.reset(); // clear recorder configuration
mediaRecorder.release(); // release the recorder object
//mediaRecorder = null; //Not setting it to null as it is required for reuse
/* try {
camera.lock(); // lock camera for later use
}catch(Exception e){
e.printStackTrace();
}*/
}
}
public void releaseCamera(){
if (camera != null){
camera.release(); // release the camera for other applications
camera = null;
}
}
public class StopRecordingThread implements Runnable{
MediaRecorder mediaRecorder = null;
public StopRecordingThread(MediaRecorder mediaRecorder){
this.mediaRecorder = mediaRecorder;
}
public void run(){
Log.i("Stopping Recording");
Utilities.isStopping = true;
mediaRecorder.stop();
Log.i("Video Stored@" );
releaseMediaRecorder();
Utilities.isStopping = false;
try {
//camera.reconnect();
//camera.lock();
if(camera == null){
camera = CameraUtils.getCameraInstance();
}
camera.setPreviewDisplay(getHolder());
camera.lock();
camera.startPreview();
}catch(Exception e){
Log.e("Preview display on setpreview after release media recotrdr",e);
}
Utilities.logSensorValues = false;
isRecording = false;
}
}
}
Sensor Thread:
package priyaman.com.glassdatavisualizer.controllers;
import android.content.Context;
/**
* Created by priya_000 on 5/22/2015.
*/
public class SensorThread extends Thread {
SensorController sensorController;
Context context = null;
DrawViewController drawView = null;
public SensorThread(Context context, DrawViewController drawView){
this.context = context;
this.drawView = drawView;
}
public void run(){
sensorController = new SensorController(context, drawView);
}
}
package priyaman.com.glassdatavisualizer.controllers;
import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Environment;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import priyaman.com.glassdatavisualizer.beans.SensorValueStruct;
import priyaman.com.glassdatavisualizer.utilities.CameraUtils;
import priyaman.com.glassdatavisualizer.utilities.Log;
import priyaman.com.glassdatavisualizer.utilities.Utilities;
/**
* Created by priya_000 on 5/21/2015.
*/
public class SensorController implements SensorEventListener {
private Context context;
private DrawViewController drawView;
// Sensor manager
private SensorManager mSensorManager = null;
// Motion sensors
private Sensor mSensorAccelerometer = null;
private Sensor mSensorGravity = null;
private Sensor mSensorLinearAcceleration = null;
private Sensor mSensorGyroscope = null;
private Sensor mSensorRotationVector = null;
String mediaStorageDir = null;
private void initializeSensorManager()
{
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mSensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
mSensorLinearAcceleration = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
mSensorGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensorRotationVector = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
}
public SensorController(Context context, DrawViewController drawView){
Log.d("onServiceStart() called.");
this.context = context;
this.drawView = drawView;
initializeSensorManager();
mSensorManager.registerListener(this, mSensorAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(this, mSensorGravity, SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(this, mSensorLinearAcceleration, SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(this, mSensorGyroscope, SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(this, mSensorRotationVector, SensorManager.SENSOR_DELAY_NORMAL);
mediaStorageDir = CameraUtils.getOutputMediaFile(3).toString();
}
@Override
public void onSensorChanged(SensorEvent event) {
Log.d("onSensorChanged() called.");
processMotionSensorData(event);
if(Utilities.logSensorValues){
StringBuffer strBuf = new StringBuffer();
if(Utilities.lastSensorValuesAccelerometer != null) {
strBuf.append("Accelerometer:\n" + Utilities.lastSensorValuesAccelerometer.toString() + "\n");
}
if(Utilities.lastSensorValuesGravity != null) {
strBuf.append("Gravity:\n" + Utilities.lastSensorValuesGravity.toString() + "\n");
}
if(Utilities.lastSensorValuesLinearAcceleration != null) {
strBuf.append("Linear Acceleration:\n" + Utilities.lastSensorValuesLinearAcceleration.toString() + "\n");
}
if(Utilities.lastSensorValuesGyroscope != null) {
strBuf.append("Gyroscope:\n" + Utilities.lastSensorValuesGyroscope.toString() + "\n");
}
if(Utilities.lastSensorValuesRotationVector != null) {
strBuf.append("Rotation Vector:\n" + Utilities.lastSensorValuesRotationVector.toString() + "\n");
}
if(Utilities.lastSensorValuesRotationVector != null) {
strBuf.append("Timestamp:\n" + Utilities.lastSensorValuesRotationVector.getTimestamp() + "\n");
}
writeToFile(strBuf.toString());
}
if(!Utilities.isStopping)
drawView.invalidate();
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
Log.d("onAccuracyChanged() called.");
}
private void processMotionSensorData(SensorEvent event)
{
long now = System.currentTimeMillis();
Sensor sensor = event.sensor;
int type = sensor.getType();
long timestamp = event.timestamp;
float[] values = event.values;
int accuracy = event.accuracy;
SensorValueStruct data = new SensorValueStruct(type, timestamp, values, accuracy);
switch(type) {
case Sensor.TYPE_ACCELEROMETER:
Utilities.lastSensorValuesAccelerometer = data;
break;
case Sensor.TYPE_GRAVITY:
Utilities.lastSensorValuesGravity = data;
break;
case Sensor.TYPE_LINEAR_ACCELERATION:
Utilities.lastSensorValuesLinearAcceleration = data;
break;
case Sensor.TYPE_GYROSCOPE:
Utilities.lastSensorValuesGyroscope = data;
break;
case Sensor.TYPE_ROTATION_VECTOR:
Utilities.lastSensorValuesRotationVector = data;
break;
default:
Log.w("Unknown type: " + type);
}
}
private void writeToFile(String data) {
try {
//OutputStreamWriter outputStreamWriter = new FileOutputStream(new File(mediaStorageDir),true);//new OutputStreamWriter(context.openFileOutput(mediaStorageDir, Context.MODE_PRIVATE));
FileOutputStream outputStreamWriter = new FileOutputStream(new File(mediaStorageDir),true);//new OutputStreamWriter(context.openFileOutput(mediaStorageDir, Context.MODE_PRIVATE));
outputStreamWriter.write(data.getBytes());
outputStreamWriter.close();
}
catch (IOException e) {
Log.e("Exception" + "File write failed: " + e.toString());
}
}
}