In 2009,
I ran a qualifying time for the NYC marathon. SO,
I decided to run it, AND
make a live streaming camera hat that sent photos every 5 seconds to my web site
However then I got really excited and ran my first ~4 miles at sub 6min pace — ie I crashed and burned and ran the latter half a whole hour slower the first half…
(and then while switching servers I accidentally deleted the photos)
Maybe someeeeday I will run a marathon again.
In meantime, you can look at the code. It’s five years out of date AND really awful.
package com.android.hat;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.graphics.PixelFormat;
public class Preview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback, Camera.ShutterCallback, Camera.PictureCallback{
private static final String TAG = "Hat";
SurfaceHolder holder;
Camera camera;
Preview(Context context) {
super(context);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
holder = getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
Log.d(TAG, "Created");
}
// Called once the holder is ready
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
camera = Camera.open();
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
// Because the CameraDevice object is not a shared resource, it's very
// important to release it when the activity is paused.
camera.release();
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters params = camera.getParameters();
List sizes = params.getSupportedPictureSizes();
Size optimalSize = getOptimalPreviewSize(sizes, w, h);
params.setPreviewSize(optimalSize.width, optimalSize.height);
params.setPictureSize(optimalSize.width, optimalSize.height);
params.setPictureFormat(PixelFormat.JPEG);
params.setJpegQuality(50);
params.setSceneMode(Camera.Parameters.SCENE_MODE_SPORTS);
params.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
params.setRotation(0);
camera.setParameters(params);
camera.startPreview();
Log.d(TAG, "1st Preview called using: " + params.flatten());
}
public void takePhoto() {
camera.startPreview();
camera.setOneShotPreviewCallback(this);
}
public void onPreviewFrame(byte[] _data, Camera _camera) {
camera.takePicture(this, null, this);
Log.d(TAG, "Photo taken at: " + System.currentTimeMillis());
}
/** Handles data for jpeg picture */
public void onPictureTaken(byte[] data, Camera camera) {
String fileName = String.format("/sdcard/sd/hat/%d.jpg", System.currentTimeMillis());
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(fileName);
outStream.write(data);
outStream.close();
Log.d(TAG, "picture saved - wrote bytes: " + data.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
new Uploader(String.format("%d.jpg", System.currentTimeMillis()), fileName);
Log.v(TAG, "Took Picture: " + fileName + "is like this big: " + data.length);
}
@Override
public void onShutter() {
Log.d(TAG, "Shutter'd");
}
private Size getOptimalPreviewSize(List sizes, int w, int h) {
if (sizes == null) return null;
Size optimalSize = null;
int targetWidth = 800;
// Try to find an size match
for (Size size : sizes) {
if(size.width > targetWidth){
optimalSize = size;
}
}
Log.v(TAG, "this is the size: " + optimalSize.width);
return optimalSize;
}
}
package com.android.hat;
import java.io.File;
import java.io.IOException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import android.util.Log;
public class Uploader extends Thread {
private static final String TAG = "Preview";
private String localFilePath;
private String fileNameOnServer;
public Uploader(String _fileNameOnServer, String _localFilePath) {
localFilePath = _localFilePath;
fileNameOnServer = _fileNameOnServer;
start();
Log.d(TAG, "Created");
}
public void run() {
String urlString = "http://spencermccormick.com/_php/marathon_jpg.php";
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(urlString);
Log.d(TAG, "Uploading " + localFilePath + " to " + urlString);
try {
MultipartEntity entity = new MultipartEntity();
entity.addPart("uploadedfilename", new StringBody(fileNameOnServer));
entity.addPart("uploadedfile", new FileBody(new File(localFilePath)));
httppost.setEntity(entity);
httpclient.execute(httppost);
} catch (ClientProtocolException e) {
} catch (IOException e) {
}
}
}