Skip to content

Commit

Permalink
Merge pull request tidev#7929 from m1ga/android_video
Browse files Browse the repository at this point in the history
[TIMOB-2122] (Android) Added support for Video Recording in Android
  • Loading branch information
ashcoding committed Apr 26, 2016
2 parents 61fcd62 + a11795d commit 226e97a
Show file tree
Hide file tree
Showing 4 changed files with 389 additions and 63 deletions.
Expand Up @@ -15,6 +15,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Arrays;

import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollFunction;
Expand Down Expand Up @@ -49,6 +50,7 @@
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.media.CamcorderProfile;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
Expand All @@ -73,7 +75,6 @@ public class MediaModule extends KrollModule
protected static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture";
protected static final String PROP_AUTOHIDE = "autohide";
protected static final String PROP_AUTOSAVE = "saveToPhotoGallery";
protected static final String PROP_WHICH_CAMERA = "whichCamera";
protected static final String PROP_OVERLAY = "overlay";

@Kroll.constant public static final int UNKNOWN_ERROR = -1;
Expand Down Expand Up @@ -105,6 +106,9 @@ public class MediaModule extends KrollModule
@Kroll.constant public static final int VIDEO_PLAYBACK_STATE_SEEKING_FORWARD = 4;
@Kroll.constant public static final int VIDEO_PLAYBACK_STATE_SEEKING_BACKWARD = 5;

@Kroll.constant public static final int VIDEO_QUALITY_LOW = CamcorderProfile.QUALITY_LOW;
@Kroll.constant public static final int VIDEO_QUALITY_HIGH = CamcorderProfile.QUALITY_HIGH;

@Kroll.constant public static final int VIDEO_FINISH_REASON_PLAYBACK_ENDED = 0;
@Kroll.constant public static final int VIDEO_FINISH_REASON_PLAYBACK_ERROR = 1;
@Kroll.constant public static final int VIDEO_FINISH_REASON_USER_EXITED = 2;
Expand All @@ -116,12 +120,16 @@ public class MediaModule extends KrollModule

@Kroll.constant public static final String MEDIA_TYPE_PHOTO = "public.image";
@Kroll.constant public static final String MEDIA_TYPE_VIDEO = "public.video";


@Kroll.constant public static final int CAMERA_FRONT = 0;
@Kroll.constant public static final int CAMERA_REAR = 1;
@Kroll.constant public static final int CAMERA_FLASH_OFF = 0;
@Kroll.constant public static final int CAMERA_FLASH_ON = 1;
@Kroll.constant public static final int CAMERA_FLASH_AUTO = 2;

private static String mediaType = MEDIA_TYPE_PHOTO;
private static String extension = ".jpg";

public MediaModule()
{
Expand Down Expand Up @@ -170,6 +178,11 @@ private void launchNativeCamera(KrollDict cameraOptions)
KrollFunction cancelCallback = null;
KrollFunction errorCallback = null;
boolean saveToPhotoGallery = false;
String[] mediaTypes = null;
String intentType = MediaStore.ACTION_IMAGE_CAPTURE;
int videoMaximumDuration = 0;
int videoQuality = VIDEO_QUALITY_HIGH;
int cameraType = 0;

if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_SUCCESS)) {
successCallback = (KrollFunction) cameraOptions.get(TiC.PROPERTY_SUCCESS);
Expand All @@ -183,16 +196,36 @@ private void launchNativeCamera(KrollDict cameraOptions)
if (cameraOptions.containsKeyAndNotNull("saveToPhotoGallery")) {
saveToPhotoGallery = cameraOptions.getBoolean("saveToPhotoGallery");
}

if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_VIDEO_MAX_DURATION)) {
videoMaximumDuration = cameraOptions.getInt(TiC.PROPERTY_VIDEO_MAX_DURATION)/1000;
}
if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_WHICH_CAMERA)) {
cameraType = cameraOptions.getInt(TiC.PROPERTY_WHICH_CAMERA);
}
if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_VIDEO_QUALITY)) {
videoQuality = cameraOptions.getInt(TiC.PROPERTY_VIDEO_QUALITY);
}
if (cameraOptions.containsKeyAndNotNull("mediaTypes")) {
mediaTypes = cameraOptions.getStringArray("mediaTypes");
if (Arrays.asList(mediaTypes).contains(MEDIA_TYPE_VIDEO)){
mediaType = MEDIA_TYPE_VIDEO;
intentType = MediaStore.ACTION_VIDEO_CAPTURE;
extension = ".mp4";
} else {
mediaType = MEDIA_TYPE_PHOTO;
intentType = MediaStore.ACTION_IMAGE_CAPTURE;
extension = ".jpg";
}
}

//Create an output file irrespective of whether saveToGallery
//is true or false. If false, we'll delete it later
File imageFile = null;

if (saveToPhotoGallery) {
imageFile = MediaModule.createGalleryImageFile();
imageFile = MediaModule.createGalleryImageFile(extension);
} else {
imageFile = MediaModule.createExternalStorageFile();
imageFile = MediaModule.createExternalStorageFile(extension);
}

//Sanity Checks
Expand All @@ -218,8 +251,14 @@ private void launchNativeCamera(KrollDict cameraOptions)

//Create Intent
Uri fileUri = Uri.fromFile(imageFile); // create a file to save the image
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Intent intent = new Intent(intentType);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, videoQuality);
intent.putExtra("android.intent.extras.CAMERA_FACING", cameraType);

if (videoMaximumDuration>0){
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, videoMaximumDuration);
}

//Setup CameraResultHandler
Activity activity = TiApplication.getInstance().getCurrentActivity();
Expand All @@ -234,6 +273,7 @@ private void launchNativeCamera(KrollDict cameraOptions)
resultHandler.saveToPhotoGallery = saveToPhotoGallery;
resultHandler.activitySupport = activitySupport;
resultHandler.lastImageId = getLastImageId(activity);
resultHandler.intentType = intentType;
activity.runOnUiThread(resultHandler);


Expand All @@ -246,7 +286,10 @@ private void launchCameraActivity(KrollDict cameraOptions, TiViewProxy overLayPr
KrollFunction errorCallback = null;
boolean saveToPhotoGallery = false;
boolean autohide = true;

int videoMaximumDuration = 0;
int videoQuality = VIDEO_QUALITY_HIGH;
int cameraType = 0;
String[] mediaTypes = null;
int flashMode = CAMERA_FLASH_OFF;
int whichCamera = CAMERA_REAR;

Expand All @@ -268,8 +311,29 @@ private void launchCameraActivity(KrollDict cameraOptions, TiViewProxy overLayPr
if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_CAMERA_FLASH_MODE)) {
flashMode = cameraOptions.getInt(TiC.PROPERTY_CAMERA_FLASH_MODE);
}
if (cameraOptions.containsKeyAndNotNull(PROP_WHICH_CAMERA)) {
whichCamera = cameraOptions.getInt(PROP_WHICH_CAMERA);
if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_WHICH_CAMERA)) {
whichCamera = cameraOptions.getInt(TiC.PROPERTY_WHICH_CAMERA);
}

// VIDEO
if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_VIDEO_MAX_DURATION)) {
videoMaximumDuration = cameraOptions.getInt(TiC.PROPERTY_VIDEO_MAX_DURATION);
}
if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_WHICH_CAMERA)) {
cameraType = cameraOptions.getInt(TiC.PROPERTY_WHICH_CAMERA);
}
if (cameraOptions.containsKeyAndNotNull(TiC.PROPERTY_VIDEO_QUALITY)) {
videoQuality = cameraOptions.getInt(TiC.PROPERTY_VIDEO_QUALITY);
}
if (cameraOptions.containsKeyAndNotNull("mediaTypes")) {
mediaTypes = cameraOptions.getStringArray("mediaTypes");
if (Arrays.asList(mediaTypes).contains(MEDIA_TYPE_VIDEO)){
mediaType = MEDIA_TYPE_VIDEO;
extension = ".mp4";
} else {
mediaType = MEDIA_TYPE_PHOTO;
extension = ".jpg";
}
}

TiCameraActivity.callbackContext = getKrollObject();
Expand All @@ -280,6 +344,9 @@ private void launchCameraActivity(KrollDict cameraOptions, TiViewProxy overLayPr
TiCameraActivity.autohide = autohide;
TiCameraActivity.overlayProxy = overLayProxy;
TiCameraActivity.whichCamera = whichCamera;
TiCameraActivity.videoQuality = videoQuality;
TiCameraActivity.videoMaximumDuration = videoMaximumDuration;
TiCameraActivity.mediaType = mediaType;
TiCameraActivity.setFlashMode(flashMode);

//Create Intent and Launch
Expand Down Expand Up @@ -425,7 +492,7 @@ public void saveToPhotoGallery(Object arg, @Kroll.argument(optional=true)HashMap

BufferedInputStream bis = null;
BufferedOutputStream bos = null;
String extension = null;

if (theBlob.getType() == TiBlob.TYPE_IMAGE) {
Bitmap image = theBlob.getImage();
if (image.hasAlpha()) {
Expand Down Expand Up @@ -506,12 +573,12 @@ public boolean handleMessage(Message message)
{
return super.handleMessage(message);
}

protected static File createExternalStorageFile() {
return createExternalStorageFile(null);
}
protected static File createGalleryImageFile() {
return createGalleryImageFile(null);
return createGalleryImageFile(extension);
}

private static File createExternalStorageFile(String extension) {
Expand All @@ -524,6 +591,7 @@ private static File createExternalStorageFile(String extension) {
}
}
String ext = (extension == null) ? ".jpg" : extension;

File imageFile;
try {
imageFile = TiFileHelper.getInstance().getTempFile(appPictureDir, ext, false);
Expand Down Expand Up @@ -554,7 +622,6 @@ private static File createGalleryImageFile(String extension) {
Log.e(TAG, "Failed to create gallery image file: " + e.getMessage());
return null;
}

return imageFile;
}

Expand All @@ -568,17 +635,23 @@ protected class CameraResultHandler implements TiActivityResultHandler, Runnable
protected Intent cameraIntent;
protected int lastImageId;
private boolean validFileCreated;
protected String intentType;

//Validates if the file is a valid bitmap
private void validateFile() throws Throwable
{
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;

BitmapFactory.decodeStream(new FileInputStream(imageFile), null, opts);
if (opts.outWidth == -1 || opts.outHeight == -1) {
throw new Exception("Could not decode the bitmap from imageFile");
if (intentType == MediaStore.ACTION_VIDEO_CAPTURE){
// video
} else {
// bitmap
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;

BitmapFactory.decodeStream(new FileInputStream(imageFile), null, opts);
if (opts.outWidth == -1 || opts.outHeight == -1) {
throw new Exception("Could not decode the bitmap from imageFile");
}
}
} catch (Throwable t) {
Log.e(TAG, t.getMessage());
Expand Down Expand Up @@ -608,10 +681,8 @@ private void checkAndDeleteDuplicate(Activity activity)
} catch (Throwable t) {
//Ignore error
}

imageFile = saveToPhotoGallery? MediaModule.createGalleryImageFile() : MediaModule.createExternalStorageFile();
imageFile = saveToPhotoGallery? MediaModule.createGalleryImageFile(extension) : MediaModule.createExternalStorageFile(extension);
}

long compareLength = (validFileCreated) ? imageFile.length() : 0;

while(imageCursor.moveToNext()){
Expand Down Expand Up @@ -720,7 +791,7 @@ public void onResult(Activity activity, int requestCode, int resultCode, Intent
if (!saveToPhotoGallery) {
//Create a file in the internal data directory and delete the original file
try {
File dataFile = TiFileFactory.createDataFile("tia", ".jpg");
File dataFile = TiFileFactory.createDataFile("tia", extension);
copyFile(imageFile, dataFile);
imageFile.delete();
imageFile = dataFile;
Expand Down Expand Up @@ -964,7 +1035,7 @@ protected static KrollDict createDictForImage(TiBlob imageData, String mimeType)
cropRect.put("height", height);
d.put("cropRect", cropRect);

d.put("mediaType", MEDIA_TYPE_PHOTO);
d.put("mediaType", mediaType);
d.put("media", imageData);

return d;
Expand All @@ -984,7 +1055,7 @@ KrollDict createDictForImage(int width, int height, byte[] data) {
cropRect.put("width", width);
cropRect.put("height", height);
d.put("cropRect", cropRect);
d.put("mediaType", MEDIA_TYPE_PHOTO);
d.put("mediaType", mediaType);
d.put("media", TiBlob.blobFromData(data, "image/png"));

return d;
Expand Down Expand Up @@ -1108,6 +1179,28 @@ public void takePicture()
Log.e(TAG, "Camera preview is not open, unable to take photo");
}
}

@Kroll.method
public void startVideoCapture()
{
// make sure the preview / camera are open before trying to take photo
if (TiCameraActivity.cameraActivity != null) {
TiCameraActivity.startVideoCapture();
} else {
Log.e(TAG, "Camera preview is not open, unable to take photo");
}
}

@Kroll.method
public void stopVideoCapture()
{
// make sure the preview / camera are open before trying to take photo
if (TiCameraActivity.cameraActivity != null) {
TiCameraActivity.stopVideoCapture();
} else {
Log.e(TAG, "Camera preview is not open, unable to take photo");
}
}

@Kroll.method
public void switchCamera(int whichCamera)
Expand Down Expand Up @@ -1168,4 +1261,3 @@ public String getApiName()
return "Ti.Media";
}
}

0 comments on commit 226e97a

Please sign in to comment.