com.aimfire.gallery.service.MovieProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.aimfire.gallery.service.MovieProcessor.java

Source

package com.aimfire.gallery.service;

/*
 * Copyright (c) 2016 Aimfire Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import com.aimfire.main.MainConsts;
import com.aimfire.utilities.ZipUtil;
import com.aimfire.v.p;
import com.aimfire.camarada.BuildConfig;
import com.aimfire.gallery.MediaScanner;
import com.google.firebase.analytics.FirebaseAnalytics;
import com.google.firebase.crash.FirebaseCrash;

import android.media.MediaMetadataRetriever;
import android.media.ThumbnailUtils;
import android.os.Process;
import android.os.SystemClock;
import android.app.IntentService;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

@SuppressWarnings("deprecation")
public class MovieProcessor extends IntentService {
    private static final String TAG = "MovieProcessor";

    /*
     * extracting first keyframe around FIRST_KEYFRAME_TIME_US
     * extracting last keyframe around LAST_KEYFRAME_TIME_US
     */
    private static final long FIRST_KEYFRAME_TIME_US = 0l;
    //private static final long LAST_KEYFRAME_TIME_US = (MainConsts.VIDEO_LENGTH_SECONDS-MainConsts.VIDEO_IFRAME_INTERVAL)*1000000;

    private static final int ERROR_EXTRACT_SYNC_FRAME_ERROR = 0;
    private static final int ERROR_EXTRACT_SYNC_FRAME_EXCEPTION = 1;
    private static final int ERROR_EXTRACT_SIMILARITY_MATRIX = 2;

    /*
     * firebase analytics
     */
    private FirebaseAnalytics mFirebaseAnalytics;

    public MovieProcessor() {
        super("MP");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        /*
         * Obtain the FirebaseAnalytics instance.
         */
        mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);

        boolean[] result = new boolean[] { false, false };
        String previewPath = null;
        String thumbPath = null;
        String configPath = null;
        String convertFilePath = null;

        String exportL = MainConsts.MEDIA_3D_RAW_PATH + "L.png";
        String exportR = MainConsts.MEDIA_3D_RAW_PATH + "R.png";

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        Bundle extras = intent.getExtras();
        if (extras == null) {
            if (BuildConfig.DEBUG)
                Log.e(TAG, "onHandleIntent: error, wrong parameter");
            FirebaseCrash.report(new Exception("onHandleIntent: error, wrong parameter"));
            return;
        }

        String filePathL = extras.getString("lname");
        String filePathR = extras.getString("rname");
        String cvrNameNoExt = MediaScanner.getProcessedCvrName((new File(filePathL)).getName());

        if (BuildConfig.DEBUG)
            Log.d(TAG, "onHandleIntent:left file=" + filePathL + ", right file=" + filePathR);

        String creatorName = extras.getString("creator");
        String creatorPhotoUrl = extras.getString("photo");

        float scale = extras.getFloat(MainConsts.EXTRA_SCALE);

        /*
         * if left/right videos were taken using front facing camera,
         * they need to be swapped when generating sbs 
         */
        int facing = extras.getInt(MainConsts.EXTRA_FACING);
        boolean isFrontCamera = (facing == Camera.CameraInfo.CAMERA_FACING_FRONT) ? true : false;

        MediaMetadataRetriever retriever = new MediaMetadataRetriever();

        Bitmap bitmapL = null;
        Bitmap bitmapR = null;

        long frameTime = FIRST_KEYFRAME_TIME_US;
        if (BuildConfig.DEBUG)
            Log.d(TAG, "extract frame from left at " + frameTime / 1000 + "ms");

        try {
            long startUs = SystemClock.elapsedRealtimeNanos() / 1000;

            FileInputStream inputStreamL = new FileInputStream(filePathL);
            retriever.setDataSource(inputStreamL.getFD());
            inputStreamL.close();

            bitmapL = retriever.getFrameAtTime(frameTime, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);

            FileInputStream inputStreamR = new FileInputStream(filePathR);
            retriever.setDataSource(inputStreamR.getFD());
            inputStreamR.close();

            bitmapR = retriever.getFrameAtTime(frameTime, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);

            retriever.release();

            long stopUs = SystemClock.elapsedRealtimeNanos() / 1000;
            if (BuildConfig.DEBUG)
                Log.d(TAG, "retrieving preview frames took " + (stopUs - startUs) / 1000 + "ms");

            if ((bitmapL != null) && (bitmapR != null)) {
                saveFrame(bitmapL, exportL);
                saveFrame(bitmapR, exportR);
            } else {
                reportError(MediaScanner.getProcessedCvrPath((new File(filePathL)).getName()),
                        ERROR_EXTRACT_SYNC_FRAME_ERROR);
                return;
            }

            previewPath = MainConsts.MEDIA_3D_THUMB_PATH + cvrNameNoExt + ".jpeg";

            result = p.getInstance().f1(exportL, exportR, previewPath, scale, isFrontCamera);
        } catch (Exception ex) {
            retriever.release();
            reportError(MediaScanner.getProcessedCvrPath((new File(filePathL)).getName()),
                    ERROR_EXTRACT_SYNC_FRAME_EXCEPTION);
            return;
        }

        if (!result[0]) {
            reportError(MediaScanner.getProcessedCvrPath((new File(filePathL)).getName()),
                    ERROR_EXTRACT_SIMILARITY_MATRIX);

            File leftFrom = (new File(filePathL));
            File rightFrom = (new File(filePathR));
            File leftExportFrom = (new File(exportL));
            File rightExportFrom = (new File(exportR));

            if (!BuildConfig.DEBUG) {
                leftFrom.delete();
                rightFrom.delete();

                leftExportFrom.delete();
                rightExportFrom.delete();
            } else {
                File leftTo = new File(MainConsts.MEDIA_3D_DEBUG_PATH + leftFrom.getName());
                File rightTo = new File(MainConsts.MEDIA_3D_DEBUG_PATH + rightFrom.getName());

                leftFrom.renameTo(leftTo);
                rightFrom.renameTo(rightTo);

                File leftExportTo = new File(MainConsts.MEDIA_3D_DEBUG_PATH + leftExportFrom.getName());
                File rightExportTo = new File(MainConsts.MEDIA_3D_DEBUG_PATH + rightExportFrom.getName());

                leftExportFrom.renameTo(leftExportTo);
                rightExportFrom.renameTo(rightExportTo);
            }
        } else {
            double[] similarityMat = p.getInstance().g();

            String configData = similarityMat[0] + " " + similarityMat[1] + " " + similarityMat[2] + " "
                    + similarityMat[3] + " " + similarityMat[4] + " " + similarityMat[5];

            if (result[1]) {
                convertFilePath = filePathR;
            } else {
                convertFilePath = filePathL;
            }

            configPath = createConfigFile(convertFilePath, configData);

            /*
             * save the thumbnail
             */
            if (bitmapL != null) {
                thumbPath = MainConsts.MEDIA_3D_THUMB_PATH + cvrNameNoExt + ".jpg";
                saveThumbnail(bitmapL, thumbPath);

                MediaScanner.insertExifInfo(thumbPath, "name=" + creatorName + "photourl=" + creatorPhotoUrl);
            }

            createZipFile(filePathL, filePathR, configPath, thumbPath, previewPath);

            /*
             * let CamcorderActivity know we are done.
             */
            reportResult(MediaScanner.getProcessedCvrPath((new File(filePathL)).getName()));
        }

        /*
         * paranoia
         */
        if (bitmapL != null) {
            bitmapL.recycle();
        }
        if (bitmapR != null) {
            bitmapR.recycle();
        }

        (new File(exportL)).delete();
        (new File(exportR)).delete();
    }

    private void saveThumbnail(Bitmap bitmap, String path) {
        FileOutputStream fos;

        Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, MainConsts.THUMBNAIL_SIZE,
                MainConsts.THUMBNAIL_SIZE);
        try {
            fos = new FileOutputStream(path);
            thumbnail.compress(CompressFormat.JPEG, 100, fos);
            fos.close();
        } catch (Exception e) {
            FirebaseCrash.report(e);
        }
    }

    private void saveFrame(Bitmap bitmap, String path) {
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(path);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
        } catch (Exception e) {
            FirebaseCrash.report(e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                FirebaseCrash.report(e);
            }
        }
    }

    /**
     * filePath1 is the file corrected. filePath2 is the other file
     */
    private void reportResult(String path) {
        mFirebaseAnalytics.logEvent(MainConsts.FIREBASE_CUSTOM_EVENT_SYNC_MOVIE_CAPTURE_COMPLETE, null);

        Intent messageIntent = new Intent(MainConsts.MOVIE_PROCESSOR_MESSAGE);
        messageIntent.putExtra(MainConsts.EXTRA_WHAT, MainConsts.MSG_MOVIE_PROCESSOR_RESULT);
        messageIntent.putExtra(MainConsts.EXTRA_PATH, path);
        messageIntent.putExtra(MainConsts.EXTRA_MSG, true/*isMyMedia*/);
        LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
    }

    private void reportError(String path, int errorCode) {
        Bundle params = new Bundle();
        params.putString("captureErrorCode", Integer.toString(errorCode));
        mFirebaseAnalytics.logEvent(MainConsts.FIREBASE_CUSTOM_EVENT_SYNC_MOVIE_CAPTURE_ERROR, params);

        Intent messageIntent = new Intent(MainConsts.MOVIE_PROCESSOR_MESSAGE);
        messageIntent.putExtra(MainConsts.EXTRA_WHAT, MainConsts.MSG_MOVIE_PROCESSOR_ERROR);
        messageIntent.putExtra(MainConsts.EXTRA_PATH, path);
        messageIntent.putExtra(MainConsts.EXTRA_MSG, true/*isMyMedia*/);
        LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);

        /*
         * delete the placeholder file
         */
        MediaScanner.removeItemMediaList(path);
        (new File(path)).delete();
    }

    private String createConfigFile(String filePath, String configData) {
        /*
         * now we write the result into a configuration file, to be used
         * by movie player later on
         */
        File file = new File(filePath);
        String filename = file.getName();
        String[] tmp = filename.split("\\.");
        String configFilename = tmp[0] + ".config";
        String configPath = MainConsts.MEDIA_3D_RAW_PATH + configFilename;
        File configFile = new File(MainConsts.MEDIA_3D_RAW_PATH, configFilename);

        try {
            FileOutputStream stream = new FileOutputStream(configFile);
            stream.write(configData.getBytes());
            stream.close();
        } catch (IOException e) {
            if (BuildConfig.DEBUG)
                Log.e(TAG, "createConfigFile: error writing config" + e);
            FirebaseCrash.report(e);
        }

        return configPath;
    }

    /**
     * create a zip file left/right mp4 files and config file
     */
    private void createZipFile(String filePathL, String filePathR, String configPath, String thumbPath,
            String previewPath) {
        /*
         * zip the two mp4 files, config file, and thumbnails
         */
        final String input[] = new String[] { filePathL, filePathR, configPath, thumbPath, previewPath };

        final String output = MediaScanner.getProcessedCvrPath((new File(filePathL)).getName());

        try {
            ZipUtil.zip(input, output, MainConsts.MEDIA_3D_ROOT_PATH);
        } catch (IOException e) {
            if (BuildConfig.DEBUG)
                Log.e(TAG, "createZipFile: error creating zip " + e);
            FirebaseCrash.report(e);
        }
    }
}