android 動画圧縮ライブラリについて調べたこと

Androidで動画の圧縮をしたくてライブラリを探してみたが思ったより少なかった。iOSだと標準のライブラリであっさりできた記憶があったのに...。試したのはassetsにmp4ファイルを同梱してそれを圧縮してかかった時間と圧縮後のサイズを検証した。どれくらい圧縮するのかの指定がそれぞれあるのかないのかまでまだドキュメントやソースを読み込めておらず、検証方法としてもしかしたら正しくないのかもしれないがとりあえず現時点の結果をメモしておく

検証したライブラリ

android-transcoder(star : 488)
https://github.com/ypresto/android-transcoder

SiliCompressor(star : 471)
https://github.com/Tourenathan-G5organisation/SiliCompressor

VideoCompressor(star : 206)
https://github.com/fishwjy/VideoCompressor

検証端末(シュミレータ含む)と動画ファイル

端末1 : F05-J、CPU 1.2GHz、メモリ 16GB、Android7
端末2 : SO-01G、CPU 2.5GHz、メモリ 32GB、Android6
端末3 : Nexus7、CPU 1.5GHz、メモリ 2GB
端末4 : シュミレータ Android9
端末5 : シュミレータ Android8

動画時間 : 30秒
動画サイズ : 66949244バイト

検証結果

圧縮後のファイルサイズと処理時間は下記のようになった。検証方法として正しいか自信ないけどこの結果だけみると、SiliCompressorがいいのかな。

android-transcoder

圧縮後のサイズ : 約46%
端末1 サイズ : 30962714, 時間 : 33秒
端末2 サイズ : 31064902, 時間 : 18秒
端末3 サイズ : 30941680, 時間 : 63秒
端末4 サイズ : 31392115, 時間 : 28秒
端末5 サイズ : 31371824, 時間 : 32秒

SiliCompressor

圧縮後のサイズ : 約4%
端末1 サイズ : 2316402, 時間 : 32秒
端末2 サイズ : 2315986, 時間 : 26秒
端末3 サイズ : 2661474, 時間 : 23秒
端末4 サイズ : 2651027, 時間 : 26秒
端末5 サイズ : 2673228, 時間 : 25秒

VideoCompressor

https://github.com/fishwjy/VideoCompressor

圧縮後のサイズ : 約10%
端末1 サイズ : 5534250, 時間 : 41秒
端末2 サイズ : 5536037, 時間 : 29秒
端末3 サイズ : 6523136, 時間 : 24秒
端末4 サイズ : 7160129, 時間 : 28秒
端末5 サイズ : 6760777, 時間 : 28秒

ソース

検証に使ったソース。つっこんでいただきたい

android-transcoder
package xxx.yyy.zzz.android_transcoder_demo;

import android.content.res.AssetManager;
import android.os.ParcelFileDescriptor;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.MediaController;
import android.widget.VideoView;

import net.ypresto.androidtranscoder.MediaTranscoder;
import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final long startTime = System.currentTimeMillis();

        final String inputPath = getCacheDir() + "/sample.mp4";
        final String outputPath = getCacheDir() + "/sample_out.mp4";

        File cacheFile = new File(inputPath);

        AssetManager assetManager = getResources().getAssets();
        InputStream inputStream;
        try {
            inputStream = assetManager.open("sample.mp4");
            int size = inputStream.available();
            byte[] buffer = new byte[size];
            inputStream.read(buffer);
            inputStream.close();

            FileOutputStream fileOutputStream = new FileOutputStream(cacheFile);
            fileOutputStream.write(buffer);
            fileOutputStream.close();

            MediaTranscoder.Listener listener = new MediaTranscoder.Listener() {
                @Override
                public void onTranscodeProgress(double progress) {
                }

                @Override
                public void onTranscodeCompleted() {

                    long fileSize = MainActivity.this.getFileSize(inputPath);
                    Log.d("","##### 圧縮前 " + String.valueOf(fileSize));

                    fileSize = MainActivity.this.getFileSize(outputPath);
                    Log.d("","##### 圧縮後  " + String.valueOf(fileSize));


                    long endTime = System.currentTimeMillis();
                    long time = (endTime - startTime) / 1000;
                    Log.d("","##### 処理時間  " + String.valueOf(time));
                }

                @Override
                public void onTranscodeCanceled() {
                }

                @Override
                public void onTranscodeFailed(Exception exception) {
                    exception.printStackTrace();
                }
            };

            MediaTranscoder.getInstance().transcodeVideo(
                    inputPath,
                    outputPath,
                    MediaFormatStrategyPresets.createAndroid720pStrategy(),
                    listener
            );

        } catch (IOException e){
            e.printStackTrace();
        }
    }

    private long getFileSize(String path) {
        File file = new File(path);
        return file.length();
    }
}
SiliCompressor
package xxx.yyy.zzz.silicompressor_demo;

import android.content.Context;
import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.os.Trace;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.MediaController;
import android.widget.VideoView;

import com.iceteck.silicompressorr.SiliCompressor;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final String inputPath = getCacheDir() + "/sample.mp4";
        final String outputDir = getCacheDir() + "";

        File cacheFile = new File(inputPath);

        AssetManager assetManager = getResources().getAssets();
        InputStream inputStream;

        try {
            inputStream = assetManager.open("sample.mp4");
            int size = inputStream.available();
            byte[] buffer = new byte[size];
            inputStream.read(buffer);
            inputStream.close();

            FileOutputStream fileOutputStream = new FileOutputStream(cacheFile);
            fileOutputStream.write(buffer);
            fileOutputStream.close();

            VideoCompressTask task = new VideoCompressTask(getApplicationContext(), inputPath, outputDir);
            task.execute();

        } catch(IOException e) {
            e.printStackTrace();
        }

    }

    class VideoCompressTask extends AsyncTask<Void, String, String> {

        Context context;
        String inputPath;
        String outputDir;
        final long startTime = System.currentTimeMillis();

        public VideoCompressTask(Context context, String inputPath, String outputDir) {
            this.context = context;
            this.inputPath = inputPath;
            this.outputDir = outputDir;
        }

        @Override
        protected String doInBackground(Void... params) {
            String filePath = null;
            try {
                filePath = SiliCompressor.with(context).compressVideo(this.inputPath, this.outputDir);
            } catch (Exception e) {
                cancel(true);
                e.printStackTrace();
            }
            return filePath;
        }

        @Override
        protected void onPostExecute(String filePath) {
            long fileSize = MainActivity.this.getFileSize(inputPath);
            Log.d("","##### 圧縮前 " + String.valueOf(fileSize));

            fileSize = MainActivity.this.getFileSize(filePath);
            Log.d("","##### 圧縮後  " + String.valueOf(fileSize));

            long endTime = System.currentTimeMillis();
            long time = (endTime - startTime) / 1000;
            Log.d("","##### 処理時間  " + String.valueOf(time));
        }

        @Override
        protected void onCancelled() {
        }
    }

    private long getFileSize(String path) {
        File file = new File(path);
        return file.length();
    }
}

**

package xxx.yyy.zzz.video_compressor_demo;

import android.content.res.AssetManager;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.MediaController;
import android.widget.VideoView;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import xxx.yyy.zzz.video_compressor_demo.videocompressor.VideoCompress;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final long startTime = System.currentTimeMillis();

        final String inputPath = getCacheDir() + "/sample.mp4";
        final String outputPath = getCacheDir() + "/sample_out.mp4";

        File cacheFile = new File(inputPath);

        AssetManager assetManager = getResources().getAssets();
        InputStream inputStream;

        try {
            inputStream = assetManager.open("sample.mp4");
            int size = inputStream.available();
            byte[] buffer = new byte[size];
            inputStream.read(buffer);
            inputStream.close();

            FileOutputStream fileOutputStream = new FileOutputStream(cacheFile);
            fileOutputStream.write(buffer);
            fileOutputStream.close();

            VideoCompress.compressVideoLow(inputPath, outputPath, new VideoCompress.CompressListener() {
                @Override
                public void onStart() {
                }
                @Override
                public void onSuccess() {
                    long fileSize = MainActivity.this.getFileSize(inputPath);
                    Log.d("","##### 圧縮前 " + String.valueOf(fileSize));

                    fileSize = MainActivity.this.getFileSize(outputPath);
                    Log.d("","##### 圧縮後  " + String.valueOf(fileSize));


                    long endTime = System.currentTimeMillis();
                    long time = (endTime - startTime) / 1000;
                    Log.d("","##### 処理時間  " + String.valueOf(time));
                }
                @Override
                public void onFail() {
                }
                @Override
                public void onProgress(float percent) {
                }
            });
        } catch (IOException e){
            e.printStackTrace();
        }
    }

    private long getFileSize(String path) {
        File file = new File(path);
        return file.length();
    }
}

以上です