<|start_filename|>tiny-android-ffmpeg/src/main/java/com/equationl/ffmpeg/CpuArch.java<|end_filename|>
package com.equationl.ffmpeg;

public enum CpuArch {
    ARMv7, ARM64, x86, x86_64, NONE
}


<|start_filename|>tiny-android-ffmpeg/src/main/java/com/equationl/ffmpeg/FFcommandExecuteAsyncTask.java<|end_filename|>
package com.equationl.ffmpeg;

import android.os.AsyncTask;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Map;
import java.util.concurrent.TimeoutException;

class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> implements FFtask {

    private final String[] cmd;
    private Map<String, String> environment;
    private final FFcommandExecuteResponseHandler ffmpegExecuteResponseHandler;
    private final ShellCommand shellCommand;
    private final long timeout;
    private long startTime;
    private Process process;
    private String output = "";
    private boolean quitPending;

    FFcommandExecuteAsyncTask(String[] cmd, Map<String, String> environment, long timeout, FFcommandExecuteResponseHandler ffmpegExecuteResponseHandler) {
        this.cmd = cmd;
        this.timeout = timeout;
        this.environment = environment;
        this.ffmpegExecuteResponseHandler = ffmpegExecuteResponseHandler;
        this.shellCommand = new ShellCommand();
    }

    @Override
    protected void onPreExecute() {
        startTime = System.currentTimeMillis();
        if (ffmpegExecuteResponseHandler != null) {
            ffmpegExecuteResponseHandler.onStart();
        }
    }

    @Override
    protected CommandResult doInBackground(Void... params) {
        try {
            process = shellCommand.run(cmd, environment);
            if (process == null) {
                return CommandResult.getDummyFailureResponse();
            }
            Log.d("Running publishing updates method");
            checkAndUpdateProcess();
            return CommandResult.getOutputFromProcess(process);
        } catch (TimeoutException e) {
            Log.e("FFmpeg binary timed out", e);
            return new CommandResult(false, e.getMessage());
        } catch (Exception e) {
            Log.e("Error running FFmpeg binary", e);
        } finally {
            Util.destroyProcess(process);
        }
        return CommandResult.getDummyFailureResponse();
    }

    @Override
    protected void onProgressUpdate(String... values) {
        if (values != null && values[0] != null && ffmpegExecuteResponseHandler != null) {
            ffmpegExecuteResponseHandler.onProgress(values[0]);
        }
    }

    @Override
    protected void onPostExecute(CommandResult commandResult) {
        if (ffmpegExecuteResponseHandler != null) {
            output += commandResult.output;
            if (commandResult.success) {
                ffmpegExecuteResponseHandler.onSuccess(output);
            } else {
                ffmpegExecuteResponseHandler.onFailure(output);
            }
            ffmpegExecuteResponseHandler.onFinish();
        }
    }

    private void checkAndUpdateProcess() throws TimeoutException, InterruptedException {
        while (!Util.isProcessCompleted(process)) {

            // checking if process is completed
            if (Util.isProcessCompleted(process)) {
                return;
            }

            // Handling timeout
            if (timeout != Long.MAX_VALUE && System.currentTimeMillis() > startTime + timeout) {
                throw new TimeoutException("FFmpeg binary timed out");
            }

            try {
                String line;
                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                while ((line = reader.readLine()) != null) {
                    if (isCancelled()) {
                        process.destroy();
                        process.waitFor();
                        return;
                    }

                    if (quitPending) {
                        sendQ();
                        process = null;
                        return;
                    }

                    output += line + "\n";
                    publishProgress(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isProcessCompleted() {
        return Util.isProcessCompleted(process);
    }

    @Override
    public boolean killRunningProcess() {
        return Util.killAsync(this);
    }

    @Override
    public void sendQuitSignal() {
        quitPending = true;
    }

    private void sendQ() {
        OutputStream outputStream = process.getOutputStream();
        try {
            outputStream.write("q\n".getBytes());
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


<|start_filename|>tiny-android-ffmpeg/src/main/java/com/equationl/ffmpeg/FFcheckFileListener.java<|end_filename|>
package com.equationl.ffmpeg;

import java.io.IOException;

public interface FFcheckFileListener {
    /**
     * Call when need download ffmpeg
     *
     * @param prefix the prefix of ffmpeg file, also ffmpeg's cpu arch type
     * @param fFbinaryInterface FFbinaryInterface
     * */
    //void startDownload(String prefix, FFbinaryInterface fFbinaryInterface);

    /**
     * Call when copy file to application storage fail
     *
     * */
    void onCopyFileFail(IOException e);

    /**
     * call when all work done, include download file, copy file, check file.
    * */
    void onAllFinish();

    /**
     * call when check file fail*/
    void onVerifyFileFail();
}


<|start_filename|>tiny-android-ffmpeg/src/main/java/com/equationl/ffmpeg/FFbinaryContextProvider.java<|end_filename|>
package com.equationl.ffmpeg;

import android.content.Context;

public interface FFbinaryContextProvider {

    Context provide();
}


<|start_filename|>sample/src/main/java/com/equationl/ffmpeg/example/MainActivity.java<|end_filename|>
package com.equationl.ffmpeg.example;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.equationl.ffmpeg.ExecuteBinaryResponseHandler;
import com.equationl.ffmpeg.FFmpeg;
import com.equationl.ffmpeg.FFtask;

import java.io.File;

/*
* Create by equationl
* fork from https://github.com/equationl/video_shot_pro/blob/master/app/src/main/java/com/equationl/videoshotpro/CommandActivity.java
* */
public class MainActivity extends AppCompatActivity {
    Button btn;
    TextView textview;
    EditText edittext;
    ScrollView sv;
    FFmpeg ffmpeg;
    FFtask fftask;

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

        btn = findViewById(R.id.command_button);
        textview = findViewById(R.id.command_text);
        edittext = findViewById(R.id.command_editText);
        sv = findViewById(R.id.command_scroll);

        try {
            //Get ffmpeg
            ffmpeg = getFFmpeg();
            //ffmpeg not support this device
            if (ffmpeg == null) {
                Toast.makeText(this, R.string.toast_ffmpeg_not_support, Toast.LENGTH_LONG).show();
                finish();
            }
        } catch (Exception e) {
            //ffmpeg is not initialized
            //So just init it
            ffmpeg = FFmpeg.getInstance(this);
        }

        btn.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                String text = edittext.getText().toString();
                textview.setText("");
                if (!ffmpeg.isCommandRunning(fftask)) {
                    String[] cmd = text.split(" ");
                    fftask = ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() {
                        @Override
                        public void onStart() {
                            btn.setClickable(false);
                            btn.setEnabled(false);
                        }
                        @Override
                        public void onFailure(String message) {
                            textview.setText(String.format("%s\n执行失败", message));
                            //sv.fullScroll(ScrollView.FOCUS_DOWN);
                            scrollToBottom(sv, textview);
                            btn.setClickable(true);
                        }
                        @Override
                        public void onSuccess(String message) {
                            textview.setText(String.format("%s\n执行成功", message));
                            //sv.fullScroll(ScrollView.FOCUS_DOWN);
                            scrollToBottom(sv, textview);
                            btn.setClickable(true);
                        }
                        @Override
                        public void onProgress(String message) {
                            textview.setText(String.format("%s\n%s\n", textview.getText().toString(), message));
                            //sv.fullScroll(ScrollView.FOCUS_DOWN);
                            scrollToBottom(sv, textview);
                        }
                        @Override
                        public void onFinish() {
                            //btn.setClickable(true);
                            btn.setEnabled(true);
                        }
                    });
                }
            }
        });
    }


    /**
     *  作者：gundumw100
     *  原文：https://blog.csdn.net/gundumw100/article/details/69983948
     * */
    public static void scrollToBottom(final View scroll, final View inner) {
        Handler mHandler = new Handler();
        mHandler.post(new Runnable() {
            public void run() {
                if (scroll == null || inner == null) {
                    return;
                }
                int offset = inner.getMeasuredHeight() - scroll.getHeight();
                if (offset < 0) {
                    offset = 0;
                }
                scroll.scrollTo(0, offset);
            }
        });
    }

    /**
     * Get FFmpeg
     *
     * @return
     * <p>if ffmpeg could use and binary file exist, return ffmmpeg object</p>
     * <p>if ffmpeg can't use, return null</p>
     * <p>if ffmpeg binary file not exist, toast to download then throws exception</p>
     * */
    FFmpeg getFFmpeg() throws Exception{
        //init ffmpeg
        FFmpeg fFmpeg = FFmpeg.getInstance(this);
        //Check if device support
        if (fFmpeg.isSupported()) {
            //Check if ffmpeg binary file exist
            if (fFmpeg.isFFmpegExist()) {
                return fFmpeg;
            }
            else {
                //If not exist, just download it
                String prefix = fFmpeg.getPrefix();
                showNeedDownloadDialog(prefix);
                throw new Exception("ffmpeg not exist");
            }
        }
        else {
            return null;
        }
    }

    void showNeedDownloadDialog(String prefix) {
        new AlertDialog.Builder(this)
                .setTitle(R.string.downloadActivity_dialog_askDownload_title)
                .setMessage(R.string.downloadActivity_dialog_askDownload_message)
                .setCancelable(false)
                .setNegativeButton(R.string.downloadActivity_dialog_askDownload_btn_cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        Toast.makeText(MainActivity.this, R.string.downloadActivity_toast_downloadData_not_download, Toast.LENGTH_LONG).show();
                    }
                })
                .setPositiveButton(R.string.downloadActivity_dialog_askDownload_btn_ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        //Go to DownloadActivity to download file
                        /*Intent intent = new Intent(context, DownloadActivity.class);
                        intent.putExtra("prefix", prefix);
                        context.startActivity(intent); */

                        //Fixme You need to implement download file by yourself
                        //You can find all the binary file at https://github.com/formatBCE/FFmpeg-Android/tree/master/android-ffmpeg/src/main/assets
                        //You need put it on your own server and download by yourself

                        //For this sample, I will use local file instead download file
                        File file = new File("/sdcard/ffmpeg");   //Fixme need permission

                        //Download finish, set it to ffmpeg
                        ffmpeg.setFFmpegFile(file);
                    }
                })
                .show();
    }
}
