Leadership

A surveillance Android app: Developing a continuous video recorder

Android developer Kyle Miller describes the trial-and-error process of creating his surveillance system application.

In my last TechRepublic App Builder post, I presented my ideas for creating a simple home or SMB video surveillance system using an Android device. In this installment in my three-part series, I explain how I created the application and describe the problems I encountered along the way.

Getting started

I broke the problem down into three main tasks:

  • Figure out how to record a video in Android.
  • Adapt the simple video recorder solution to record back-to-back videos.
  • Integrate Dropbox to upload the videos from the Android device as they finish recording.
Recording video in Android First, I started my research on the Android Developers site. Google provides a relatively concise guide on properly recording video in Android in the Camera documentation.

The first sentence of the documentation states that the two main classes you have to deal with are Camera and MediaRecorder. By following Google's documentation and code samples, I was able to successfully capture a sample video in relatively short order. I will spare you the details for now because I learned there was a much simpler approach, which I will cover later in this post.

Recording consecutive videos in Android Next, I had to figure out how to automatically stop and re-start the video recording, as well as decide the length of each video. By testing a couple of video samples, I found that it was best to stick with one-minute videos for now or risk making the files too large and more prone for upload errors. Each video was averaging 0.5 MB to 2 MB depending on the amount of light and motion in the video.

As far as the stop/re-start problem, I initially used a Timer object and found the code worked as long as I called Looper.prepare() in the TimerTask (because it isn't executed on the main UI thread). Later I stumbled across the MediaRecorder.setMaxDuration and MediaRecorder.setOnInfoListener calls, which could replace the Timer object I was using, and I started using those methods instead. By setting a maximum duration on the MediaRecorder, the infoListener object is called when the time limit has been reached, which should be used as the trigger to stop/reset the video.

Hitting a roadblock

I had the first two major chunks of my problem solved, so it was time to start testing. I set up my Android device in a little corner and started recording videos. The first couple of videos recorded without a hitch, and then the app froze. I assumed it was a fluke, so I started the app again. The app froze again a couple of videos into the test. After several frustrating hours of searching, tweaking, and testing, I still hadn't come up with a solution.

During this process, I found a code example online for recording video that was much less complicated. This particular solution didn't involve the Camera class and only dealt with the MediaRecorder class. I assume that the MediaRecorder class takes care of establishing a connection with the Camera class and properly handles it.

I quickly whipped together a prototype with this new code and started testing again. The results were promising (I was able to record about 10 minutes of video), but much to my dismay the application froze, and I was right back to where I was before but with less code.

Finding a solution

The next several days I searched for answers, tested minor tweaks, and pulled my hair out until I finally found the solution (which I swear I tried before). By switching the VideoEncoder to MediaRecorder.VideoEncoder.MPEG_4_SP, the video would no longer randomly freeze, and I was able to record up to two hours of video before I considered it a success.

The Video Recorder Activity has a very basic XML layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center">
<SurfaceView android:id="@+id/surface_camera"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_weight="1">
</SurfaceView>
</LinearLayout>

The Activity used for recording video implements OnClickListener (for starting/stopping the video) and SurfaceHolder.Callback (for determining when the SurfaceView is ready for initiating and preparing the MediaRecorder).

In the onCreate method of the recording Activity, the first thing to do is set a flag that will allow the Activity to keep the screen on:

getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );

Next, create a method-wide instance of MediaRecorder and initialize it (initRecorder will be explained after onCreate):

mRecorder = new MediaRecorder();
initRecorder();
setContentView(R.layout.camera);

Then grab the SurfaceView from the XML layout, store a method-wide reference to its SurfaceHolder, set the activity as the SurfaceHolder callback, and set the holder's type to SURFACE_TYPE_PUSH_BUFFERS:

SurfaceView cameraView = (SurfaceView) findViewById(R.id.surface_camera);
mHolder = cameraView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
Finally, make the SurfaceView clickable:
cameraView.setClickable(true);
cameraView.setOnClickListener(this);

Inside initRecoder all the properties of the MediaRecorder are set:

mRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mRecorder.setVideoSize(320, 240);
mRecorder.setVideoFrameRate(15);
File mediaStorageDir = new File("/sdcard/VideoSurveillance/");
if ( !mediaStorageDir.exists() ) {
if ( !mediaStorageDir.mkdirs() ){
Logger.Debug("failed to create directory");
}
}
String filePath = "/sdcard/VideoSurveillance/" + "VID_"+ new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".mp4";
mRecorder.setOutputFile(filePath);
mRecorder.setPreviewDisplay(mHolder.getSurface());

The SurfaceHolder.Callback methods, surfaceCreated, surfaceChanged, and surfaceDestroyed, must be implemented. surfaceChanged can be ignored, but it must be implemented. surfaceDestroyed will be discussed in a bit. In surfaceCreated, the only thing to do is prepare the MediaRecorder:

mMediaRecorder.prepare();

The last main piece to look at is the onClick handler of the SurfaceView. If the MediaRecorder is not running, then call its start method and schedule a Timer (I gave up on the onInfoListener because of errors I saw in logcat):

mRecorder.start();
mStopTimer = new Timer();
mStopTimer.schedule( new TimerTask() {
@Override
public void run() {
Looper.prepare();
restartRecorder();
}
}, 60 * 1000 );

Alternately, if the MediaRecorder is running, we can treat the click as a trigger to stop the automation process by resetting the MediaRecorder, initializing and preparing it (in case you want to start recording again), and then canceling the Timer that is scheduled:

mRecorder.reset();
initRecorder();
mRecorder.prepare();
mStopTimer.cancel();

Inside the restartRecorder method (from above), the MediaRecorder is reset, initialized, prepared, and started. Then a new stop Timer is set:

mRecorder.reset();
initRecorder();
mRecorder.prepare();
mRecorder.start();
mStopTimer = new Timer();
mStopTimer.schedule( new TimerTask() {
@Override
public void run() {
Looper.prepare();
restartRecorder();
}
}, 60 * 1000 );

Part three

In the final part of this series, I will discuss how I went about Dropbox integration and provide the entire source code for this app.

About

Kyle is a senior software engineer specializing in web/Android development living in Austin TX. He's a self-professed "gadget freak" whose passion for mobile devices drove him to jump into the mobile industry in 2010. He enjoys the fast-paced nature ...

5 comments
BALTHOR
BALTHOR

I suspect that if your wireless computer or cell phone works when you take it into the shop but not at home,it's a hacker in your neighborhood.It could even be that AT&T or the government will shut down the networks during heavy hacking.

BALTHOR
BALTHOR

So my DVD writer's electronics get told what to do by the DVD writer program.Wouldn't it be very upsetting if something didn't work.I learned an interesting one yesterday.I use a registry file to make a software program run.In a downloaded mini version of XP I could not get the reg file to enter.I went to regedit and imported the file and it worked.The registry appears to be BIOS and Firmware adjustments.I now truly see all software cracks as a registry update,The program is written,then uploaded now they need to write a crack to get it to work after the program is wrecked by hackers.TeamAir writes cracks!In the registry you can export a registry file for the program right after it is written then use it in your crack.

BALTHOR
BALTHOR

When you investigate the computer and software on your own it's a voyage of discovery that stretches your problem solving capabilities to the maximum.You have all your amassed tricks on the table and you head in to another project.When you get stuck there's always some websites that can yield results.YouTube gets me unstuck sometimes.However, I've never seen a YouTube download link work."Get Win 7 fully cracked,cleared and free of Bill" didn't work.If you have made a restore file you fears are groundless,the sky's the limit,and you are undaunted.Trial and error is the way that it's seen but this is new territory here.You truly do not know what is going to happen at all.It either works or it doesn't.If you keep going and give it all you got you just might be disappointed.But tomorrow's another day and maybe the gods will all be with you.

Editor's Picks