투케이2K

166. (TWOK/UTIL) [Android/Java] C_FFmpeg_Module - FFmpeg 비디오 동영상 트랜스 코딩 관련 클래스 파일 본문

투케이2K 유틸파일

166. (TWOK/UTIL) [Android/Java] C_FFmpeg_Module - FFmpeg 비디오 동영상 트랜스 코딩 관련 클래스 파일

투케이2K 2025. 9. 13. 09:43
728x90

[설 명]

프로그램 : Android / Java

설 명 : [Android/Java] C_FFmpeg_Module - FFmpeg 비디오 동영상 트랜스 코딩 관련 클래스 파일

 

[소스 코드]

 

-----------------------------------------------------------------------------------------
[사전 설명 및 설정 사항]
-----------------------------------------------------------------------------------------

- 개발 환경 : Mobile / Android


- 개발 기술 : FFmpeg / Video / Codec


- 사전) FFmpeg 간단 설명 : 

  >> FFmpeg 이란 디지털 음성 스트림과 영상 스트림에 대해서 다양한 종류의 형태로 기록하고 변환하는 컴퓨터 프로그램입니다 (미디어 포맷 변환 도구)

  >> FFmpeg 은 명령어를 직접 입력하는 방식으로 동작하며 여러 가지 자유 소프트웨어와 오픈 소스 라이브러리로 구성되어 있습니다

-----------------------------------------------------------------------------------------





-----------------------------------------------------------------------------------------
[소스 코드]
-----------------------------------------------------------------------------------------

package com.example.javaproject.C_Module;

import android.content.Context;
import android.os.Environment;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Region;
import com.amazonaws.services.iot.AWSIot;
import com.amazonaws.services.iot.AWSIotClient;
import com.amazonaws.services.iot.model.ListThingsRequest;
import com.amazonaws.services.iot.model.ListThingsResult;
import com.amazonaws.services.iot.model.ThingAttribute;
import com.arthenica.ffmpegkit.FFmpegKit;
import com.arthenica.ffmpegkit.ReturnCode;
import com.example.javaproject.C_Format;
import com.example.javaproject.C_Util;
import com.example.javaproject.S_FinalData;
import com.example.javaproject.S_Log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import io.reactivex.rxjava3.core.Observable;

public class C_FFmpeg_Module {


    /**
     * // --------------------------------------------------------------------------------------
     * TODO [클래스 설명]
     * // --------------------------------------------------------------------------------------
     * 1. [설명] : FFmpeg 비디오 동영상 트랜스 코딩 관련 클래스 파일
     * // --------------------------------------------------------------------------------------
     * 2. 필요 퍼미션 :
     *
     * // TODO [기본 권한]
     * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     * <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     *
     * // TODO [외부 및 공용 저장소 사용 관련]
     * <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
     * // --------------------------------------------------------------------------------------
     * 3. 참고 사이트 :
     *
     * [ffmpeg aar 파일 다운로드 사이트]
     *
     * https://artifactory.appodeal.com/appodeal-public/com/arthenica/ffmpeg-kit-full-gpl/6.0-2.LTS/
     *
     *
     * [라이브러리 Git 사이트]
     *
     * https://github.com/arthenica/ffmpeg-kit/wiki/Android
     *
     *
     * [ffmpeg 라이브러리 빌드 관련 이슈]
     *
     * https://stackoverflow.com/questions/79600161/could-not-find-com-arthenicaffmpeg-kit-full6-0-2
     *
     *
     * [안드로이드 프로젝트 libs 폴더 생성 방법]
     *
     * https://blog.naver.com/kkh0977/222355981962?trackingCode=blog_bloghome_searchlist
     * // --------------------------------------------------------------------------------------
     * 4. import 정리 :
     *
     * import com.arthenica.ffmpegkit.FFmpegKit;
     * import com.arthenica.ffmpegkit.ReturnCode;
     * // --------------------------------------------------------------------------------------
     * */





    /**
     * // --------------------------------------------------------------------------------------
     * TODO [빠른 로직 찾기 : 주석 로직 찾기]
     * // --------------------------------------------------------------------------------------
     * [SEARCH FAST] : [RETURN] : getDownloadFolderPath : 공용 다운로드 저장소 경로 확인
     * // --------------------------------------------------------------------------------------
     * [SEARCH FAST] : [RETURN] : getFileExistsList : 특정 파일 폴더 경로에 포함 된 특정 파일 확장자 리스트 확인
     * // --------------------------------------------------------------------------------------
     * [SEARCH FAST] : [Observable] : getMp4FileTransferMkvFile : MP4 파일을 MKV 파일로 동영상 파일 변환 수행
     * // --------------------------------------------------------------------------------------
     * [SEARCH FAST] : [Observable] : getFileInfo : 특정 비디오 파일 정보 (codec , bitrate) 확인 수행
     * // --------------------------------------------------------------------------------------
     *
     * // --------------------------------------------------------------------------------------
     *
     * // --------------------------------------------------------------------------------------
     */





    // ------------------------------------------------------------------------------------------
    // TODO [전역 변수 선언]

    // ------------------------------------------------------------------------------------------
    private static String ACTIVITY_NAME = "C_FFmpeg_Module";






    // -----------------------------------------------------------------------------------------
    // TODO [SEARCH FAST] : [RETURN] : getDownloadFolderPath : 공용 다운로드 저장소 경로 확인
    // -----------------------------------------------------------------------------------------
    // [호출 소스 코드]
    // -----------------------------------------------------------------------------------------
    /*
    try {

        // [다운로드 폴더 저장소 경로 확인]
        String FILE_PATH = C_FFmpeg_Module.getDownloadFolderPath();

    }
    catch (Exception e) {
        S_Log._printStackTrace_(null, S_FinalData.LOG_BUG_STATE, null, e);
    }
    */
    // -----------------------------------------------------------------------------------------
    public static String getDownloadFolderPath() {

        /**
         * // -----------------------------------------
         * 1. 추가로 외부 저장소에 파일 읽기 및 쓰기를 위해서 Manifest 권한 필요 및 인텐트 이동 수행
         *
         * 권한 : <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
         *
         * 인텐트 이동 : Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
         * // -----------------------------------------
         * 2. 참고 설명 :
         *
         * 해당 권한을 추가 시 마켓에 특정 용도로 사용하는 앱 목적을 밝혀야합니다 (앱 등록 및 업데이트 리젝 될 수 있습니다)
         *
         * 다운로드 폴더 저장소 경로 : /storage/emulated/0/Download
         * // -----------------------------------------
         * */


        // [리턴 값 선언]
        String returnData = "";


        // [로직 처리 실시]
        try {

            // [외부 저장소 경로 확인]
            String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();

            //filePath += "/"; // [Test Add]

            S_Log.d("KWON_TWOK", "\n>>>>>>>>>>>> [Origin] : [filePath] : " + filePath + " >>>>>>>>>>>>\n");

            if (C_Util.stringNotNull(filePath) == true){

                if (filePath.endsWith("/") == true){ // [마지막 경로에 특수 문자 포함 확인]
                    filePath = filePath.substring(0, filePath.length()-1);

                    S_Log.w("KWON_TWOK", "\n>>>>>>>>>>>> [Replace] : [filePath] : " + filePath + " >>>>>>>>>>>>\n");
                }

            }

            returnData = filePath; // [Return Add Value]

        } catch (Exception e) {
            S_Log._printStackTrace_(null, S_FinalData.LOG_BUG_STATE, null, e);
        }


        // ===============================================================
        S_Log._D_(ACTIVITY_NAME + " :: getDownloadFolderPath :: 공용 다운로드 폴더 저장소 경로 확인", new String[]{ "RETURN :: " + String.valueOf(returnData) });
        // ===============================================================


        // [리턴 반환 실시]
        return returnData;
    }






    // -----------------------------------------------------------------------------------------
    // TODO [SEARCH FAST] : [RETURN] : getFileExistsList : 특정 파일 폴더 경로에 포함 된 특정 파일 확장자 리스트 확인
    // -----------------------------------------------------------------------------------------
    // [호출 소스 코드]
    // -----------------------------------------------------------------------------------------
    /*
    try {

        // [파일 경로 작성]
        String filePath = "/storage/emulated/0/Download";
        String fileMimeType = "mp4";

        // [특정 폴더에 저장 된 파일 리스트 확인]
        ArrayList<String> FILE_LIST = C_FFmpeg_Module.getFileExistsList(filePath, fileMimeType);

    }
    catch (Exception e) {
        S_Log._printStackTrace_(null, S_FinalData.LOG_BUG_STATE, null, e);
    }
    */
    // -----------------------------------------------------------------------------------------
    public static ArrayList<String> getFileExistsList(String filePath, String fileMimeType) {

        // [리턴 값 선언]
        ArrayList<String> returnData = new ArrayList<>();
        String M_LOG = "";


        // [로직 처리 실시]
        try {

            if (C_Util.stringNotNull(filePath) == true && C_Util.stringNotNull(fileMimeType) == true){

                File file = new File(filePath);

                if (file.exists() == true){

                    File list[] = file.listFiles();

                    if (list != null && list.length > 0){

                        for (int i=0; i<list.length; i++){
                            File item = list[i];

                            String itemPath = item.getAbsolutePath().trim();

                            if (C_Util.stringNotNull(itemPath) == true){
                                S_Log.w("KWON_TWOK", "\n>>>>>>>>>>>> [List] : [filePath] : " + itemPath + " >>>>>>>>>>>>\n");

                                if (itemPath.endsWith(fileMimeType) == true){ // file mime type check

                                    // ex path : /storage/emulated/0/Download/testAudioRecord.mp4
                                    returnData.add(itemPath);
                                }
                            }
                        }


                        if (returnData != null && returnData.size()>0){
                            M_LOG = "[Success] :: File mimeType Found";
                        }
                        else{
                            M_LOG = "[Error] :: returnData is null (file mimeType Not Found)";
                        }
                    }
                    else {
                        M_LOG = "[Error] :: file.listFiles is null";
                    }
                }
                else {
                    M_LOG = "[Error] :: file exists false";
                }
            }
            else {
                M_LOG = "[Error] : Input Data Is Null";
            }

        } catch (Exception e) {
            S_Log._printStackTrace_(null, S_FinalData.LOG_BUG_STATE, null, e);
        }


        // ===============================================================
        S_Log._D_(ACTIVITY_NAME + " :: getFileExistsList :: 특정 파일 폴더 경로에 포함 된 특정 파일 확장자 리스트 확인", new String[]{ "M_LOG :: " + M_LOG, "RETURN :: " + String.valueOf(returnData) });
        // ===============================================================


        // [리턴 반환 실시]
        return returnData;
    }






    // ------------------------------------------------------------------------------------------
    // TODO [SEARCH FAST] : [Observable] : getMp4FileTransferMkvFile : MP4 파일을 MKV 파일로 동영상 파일 변환 수행
    // -----------------------------------------------------------------------------------------
    // [호출 소스 코드]
    // -----------------------------------------------------------------------------------------
    /*
    try {

        // [변수 선언]
        String INPUT_PATH = "/storage/emulated/0/Download/video.mp4"; // [mp4 파일이 저장 된 경로 : 휴대폰 다운로드 폴더]
        String OUT_PUT_PATH = "/storage/emulated/0/Download/output.mkv"; // [변환 된 mkv 파일이 저장 될 경로 : 휴대폰 다운로드 폴더]

        C_FFmpeg_Module.getMp4FileTransferMkvFile(A_Intro.this, INPUT_PATH, OUT_PUT_PATH)
                .subscribeOn(AndroidSchedulers.mainThread()) // [Observable (생성자) 로직을 IO 스레드에서 실행 : 백그라운드]
                .observeOn(Schedulers.io()) // [Observer (관찰자) 로직을 메인 스레드에서 실행]
                .subscribe(new Observer<Boolean>() { // [Observable.create 타입 지정]
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                    }

                    @Override
                    public void onNext(@NonNull Boolean value) {
                        S_Log._W_("MP4 파일을 MKV 파일로 동영상 파일 변환 수행 :: onNext", new String[]{String.valueOf(value)});
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        S_Log._E_("MP4 파일을 MKV 파일로 동영상 파일 변환 수행 :: onError", new String[]{String.valueOf(e.getMessage())});
                    }

                    @Override
                    public void onComplete() {
                    }
                });

    }
    catch (Exception e) {
        S_Log._printStackTrace_(null, S_FinalData.LOG_BUG_STATE, null, e);
    }
    */
    // -----------------------------------------------------------------------------------------
    public static String getMp4FileTransferMkvFile_Message = "";
    public static Observable<Boolean> getMp4FileTransferMkvFile(Context mContext, String INPUT_PATH, String OUT_PUT_PATH) {
        S_Log._D_(ACTIVITY_NAME + " :: getMp4FileTransferMkvFile :: MP4 파일을 MKV 파일로 동영상 파일 변환 수행", new String[]{
                "INPUT_PATH :: " + String.valueOf(INPUT_PATH),
                "OUT_PUT_PATH :: " + String.valueOf(OUT_PUT_PATH)
        });

        // [로직 처리 실시]
        return Observable.create(subscriber -> {

            // [리턴 변수 선언]
            boolean returnData[] = {false};


            // [에러 메시지 초기화]
            getMp4FileTransferMkvFile_Message = "";


            // [인풋 데이터 널 체크 수행]
            if (mContext != null
                    && C_Util.stringNotNull(INPUT_PATH) == true
                    && C_Util.stringNotNull(OUT_PUT_PATH) == true){

                // [파일 확장자 포함 확인]
                if (INPUT_PATH.contains(".") && INPUT_PATH.toLowerCase().contains("mp4")
                        && OUT_PUT_PATH.contains(".") && OUT_PUT_PATH.toLowerCase().contains("mkv")){

                    // --------------------------------------------
                    // TODO [백그라운드 Thread 처리 수행]
                    // --------------------------------------------
                    new Thread(){
                        @Override
                        public void run(){

                            try {

                                File inputFile = new File(INPUT_PATH);

                                if (inputFile.exists() == true){ // [파일 경로에 파일이 존재하는 경우]

                                    // --------------------------------------------
                                    // TODO [command : 파일 변환 커맨드 명령어 작성]
                                    // --------------------------------------------
                                    String command = String.format("-i %s -c copy %s", INPUT_PATH, OUT_PUT_PATH);

                                    S_Log._W_(ACTIVITY_NAME + " :: getMp4FileTransferMkvFile :: 저장 된 MP4 파일을 MKV 형식으로 변환 커맨드 확인", new String[]{
                                            "command :: " + command,
                                    });


                                    // --------------------------------------------
                                    // TODO [FFmpegKit.executeAsync 사용해 mp4 to mkv 파일 변환 수행]
                                    // --------------------------------------------
                                    // TODO implementation 'com.arthenica:smart-exception-java:0.2.1' 라이브러리가 없는 경우 에러가 발생하니 필수 추가 필요
                                    // ---------------------------------------------
                                    FFmpegKit.executeAsync(command, session -> {
                                        if (ReturnCode.isSuccess(session.getReturnCode())) {
                                            S_Log._W_(ACTIVITY_NAME + " :: getMp4FileTransferMkvFile :: 저장 된 MP4 파일을 MKV 형식으로 변환 성공", new String[]{
                                                    "getSessionId :: " + String.valueOf(session.getSessionId()),
                                                    "getCommand :: " + String.valueOf(session.getCommand()),
                                                    "getReturnCode :: " + String.valueOf(session.getReturnCode()),
                                                    "getAllLogsAsString :: " + String.valueOf(session.getAllLogsAsString()),
                                            });

                                            // [리턴 반환 수행]
                                            returnData[0] = true;

                                            if (subscriber != null && subscriber.isDisposed() == false){
                                                subscriber.onNext(returnData[0]);
                                                subscriber.onComplete();
                                            }

                                        } else {
                                            S_Log._E_(ACTIVITY_NAME + " :: getMp4FileTransferMkvFile :: 저장 된 MP4 파일을 MKV 형식으로 변환 실패", new String[]{
                                                    "getSessionId :: " + String.valueOf(session.getSessionId()),
                                                    "getCommand :: " + String.valueOf(session.getCommand()),
                                                    "getReturnCode :: " + String.valueOf(session.getReturnCode()),
                                                    "getAllLogsAsString :: " + String.valueOf(session.getAllLogsAsString()),
                                                    "getFailStackTrace :: " + String.valueOf(session.getFailStackTrace()),
                                            });

                                            // [에러 메시지 삽입]

                                            getMp4FileTransferMkvFile_Message = C_Format.form_message(new String[]{
                                                    "CODE :: " + C_Util.getSourceCodeLine(),
                                                    "EXPLANATION :: " + "저장 된 MP4 파일을 MKV 형식으로 변환 수행에 실패했습니다.",
                                                    "ERROR :: " + String.valueOf(session.getFailStackTrace())
                                            });

                                            if (subscriber != null && subscriber.isDisposed() == false){
                                                subscriber.onError(new Throwable(getMp4FileTransferMkvFile_Message));
                                                subscriber.onComplete();
                                            }

                                        }
                                    });

                                }
                                else {
                                    S_Log._E_(ACTIVITY_NAME + " :: getMp4FileTransferMkvFile :: MP4 파일을 MKV 파일로 동영상 파일 변환 에러", new String[]{"FILE EXISTS FALSE)"});

                                    // [에러 메시지 삽입]
                                    getMp4FileTransferMkvFile_Message = C_Format.form_message(new String[]{
                                            "CODE :: " + C_Util.getSourceCodeLine(),
                                            "EXPLANATION :: " + "MP4 파일 존재 여부 확인이 필요합니다.",
                                            "ERROR :: FILE EXISTS FALSE"
                                    });

                                    if (subscriber != null && subscriber.isDisposed() == false){
                                        subscriber.onError(new Throwable(getMp4FileTransferMkvFile_Message));
                                        subscriber.onComplete();
                                    }
                                }

                            } catch (Exception es) {
                                es.printStackTrace();

                                S_Log._E_(ACTIVITY_NAME + " :: getMp4FileTransferMkvFile :: MP4 파일을 MKV 파일로 동영상 파일 변환 실패", new String[]{String.valueOf(es.getMessage())});

                                // [에러 메시지 삽입]
                                getMp4FileTransferMkvFile_Message = C_Format.form_message(new String[]{
                                        "CODE :: " + C_Util.getSourceCodeLine(),
                                        "EXPLANATION :: " + "MP4 파일을 MKV 파일로 동영상 파일 변환 중 예외 상황이 발생했습니다.",
                                        "EXCEPTION [1] :: " + String.valueOf(es.getMessage())
                                });

                                if (subscriber != null && subscriber.isDisposed() == false){
                                    subscriber.onError(new Throwable(getMp4FileTransferMkvFile_Message));
                                    subscriber.onComplete();
                                }

                            }

                        }
                    }.start();

                }
                else {
                    S_Log._E_(ACTIVITY_NAME + " :: getMp4FileTransferMkvFile :: MP4 파일을 MKV 파일로 동영상 파일 변환 에러", new String[]{"FILE TYPE ERROR)"});

                    // [에러 메시지 삽입]
                    getMp4FileTransferMkvFile_Message = C_Format.form_message(new String[]{
                            "CODE :: " + C_Util.getSourceCodeLine(),
                            "EXPLANATION :: " + "MP4 파일을 MKV 파일로 동영상 파일 변환에 필요한 파일 형식 타입을 확인해주세요.",
                            "ERROR :: FILE TYPE ERROR"
                    });

                    if (subscriber != null && subscriber.isDisposed() == false){
                        subscriber.onError(new Throwable(getMp4FileTransferMkvFile_Message));
                        subscriber.onComplete();
                    }
                }

            }
            else {
                S_Log._E_(ACTIVITY_NAME + " :: getMp4FileTransferMkvFile :: MP4 파일을 MKV 파일로 동영상 파일 변환 에러", new String[]{"Input Data Is Null (INPUT_PATH, OUT_PUT_PATH)"});

                // [에러 메시지 삽입]
                getMp4FileTransferMkvFile_Message = C_Format.form_message(new String[]{
                        "CODE :: " + C_Util.getSourceCodeLine(),
                        "EXPLANATION :: " + "MP4 파일을 MKV 파일로 동영상 파일 변환에 필요한 필수 값을 확인해주세요.",
                        "ERROR :: Input Data Is Null (INPUT_PATH, OUT_PUT_PATH)"
                });

                if (subscriber != null && subscriber.isDisposed() == false){
                    subscriber.onError(new Throwable(getMp4FileTransferMkvFile_Message));
                    subscriber.onComplete();
                }

            }

        });
    }






    // -----------------------------------------------------------------------------------------
    // TODO [SEARCH FAST] : [Observable] : getFileInfo : 특정 비디오 파일 정보 (codec , bitrate) 확인 수행
    // -----------------------------------------------------------------------------------------
    // [호출 소스 코드]
    // -----------------------------------------------------------------------------------------
    /*
    try {

        // [변수 선언]
        String FILE_PATH = "/storage/emulated/0/Download/video.mp4"; // [mp4 파일이 저장 된 경로 : 휴대폰 다운로드 폴더]
        //String FILE_PATH = "https://service-notice.s3.ap-northeast-2.amazonaws.com/wcpcontrol/access_cloud_join.mp4"; // [mp4 파일이 저장 된 경로 : Http 경로]

        C_FFmpeg_Module.getFileInfo(A_Intro.this, FILE_PATH)
                .subscribeOn(AndroidSchedulers.mainThread()) // [Observable (생성자) 로직을 IO 스레드에서 실행 : 백그라운드]
                .observeOn(Schedulers.io()) // [Observer (관찰자) 로직을 메인 스레드에서 실행]
                .subscribe(new Observer<HashMap<String, Object>>() { // [Observable.create 타입 지정]
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                    }

                    @Override
                    public void onNext(@NonNull HashMap<String, Object> value) {
                        S_Log._W_("특정 비디오 파일 정보 (codec , bitrate) 확인 수행 :: onNext", new String[]{String.valueOf(value)});
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        S_Log._E_("특정 비디오 파일 정보 (codec , bitrate) 확인 수행 :: onError", new String[]{String.valueOf(e.getMessage())});
                    }

                    @Override
                    public void onComplete() {
                    }
                });

    }
    catch (Exception e) {
        S_Log._printStackTrace_(null, S_FinalData.LOG_BUG_STATE, null, e);
    }
    */
    // -----------------------------------------------------------------------------------------
    public static String getFileInfo_Message = "";
    public static Observable<HashMap<String, Object>> getFileInfo(Context mContext, String FILE_PATH) {
        S_Log._D_(ACTIVITY_NAME + " :: getFileInfo :: 특정 비디오 파일 정보 (codec , bitrate) 확인 수행", new String[]{
                "FILE_PATH :: " + String.valueOf(FILE_PATH)
        });

        // [로직 처리 실시]
        return Observable.create(subscriber -> {

            // [리턴 변수 선언]
            HashMap<String, Object> returnData = new HashMap<>();


            // [에러 메시지 초기화]
            getFileInfo_Message = "";


            // [인풋 데이터 널 체크 수행]
            if (mContext != null
                    && C_Util.stringNotNull(FILE_PATH) == true){

                new Thread(){
                    @Override
                    public void run(){

                        try {

                            boolean searchFlag = false;

                            File file = new File(FILE_PATH);

                            if (file.exists() == true){ // [파일 경로에 파일이 존재하는 경우]
                                searchFlag = true; // Local Path

                                S_Log.w("KWON_TWOK", ">>>>>>>>>>>>>>>>>>>>>>>>>> [Local Path] : Exists >>>>>>>>>>>>>>>>>>>>>>>>>>");
                            }

                            if (FILE_PATH.startsWith("http") == true){
                                searchFlag = true; // Http Path

                                S_Log.w("KWON_TWOK", ">>>>>>>>>>>>>>>>>>>>>>>>>> [Http Path] : Exists >>>>>>>>>>>>>>>>>>>>>>>>>>");
                            }

                            if (searchFlag == true){

                                // --------------------------------------------
                                // TODO [command : 파일 변환 커맨드 명령어 작성]
                                // --------------------------------------------
                                String command = String.format("-hide_banner -i %s", FILE_PATH);

                                S_Log._W_(ACTIVITY_NAME + " :: getFileInfo :: 특정 비디오 파일 정보 (codec , bitrate) 확인 커맨드", new String[]{
                                        "command :: " + command,
                                });


                                // --------------------------------------------
                                // TODO [FFmpegKit.executeAsync 사용해 특정 파일 정보 확인 수행]
                                // --------------------------------------------
                                // TODO implementation 'com.arthenica:smart-exception-java:0.2.1' 라이브러리가 없는 경우 에러가 발생하니 필수 추가 필요
                                // ---------------------------------------------
                                FFmpegKit.executeAsync(command, session -> {

                                    // ---------------------------------------------
                                    // TODO [코덱 정보만 확인할 목적으로 ReturnCode 가 1이어도 무시하고 로그를 파싱]
                                    // ---------------------------------------------
                                    // if (ReturnCode.isSuccess(session.getReturnCode())) {}
                                    // ---------------------------------------------
                                    if (session.getFailStackTrace() != null && C_Util.stringNotNull(String.valueOf(session.getFailStackTrace())) == true) {
                                        S_Log._E_(ACTIVITY_NAME + " :: getFileInfo :: 특정 비디오 파일 정보 (codec , bitrate) 확인 실패", new String[]{
                                                "getSessionId :: " + String.valueOf(session.getSessionId()),
                                                "getCommand :: " + String.valueOf(session.getCommand()),
                                                "getReturnCode :: " + String.valueOf(session.getReturnCode()),
                                                "getFailStackTrace :: " + String.valueOf(session.getFailStackTrace()),
                                        });

                                        // [에러 메시지 삽입]
                                        getFileInfo_Message = C_Format.form_message(new String[]{
                                                "CODE :: " + C_Util.getSourceCodeLine(),
                                                "EXPLANATION :: " + "특정 비디오 파일 정보 (codec , bitrate) 확인에 실패했습니다.",
                                                "ERROR :: " + String.valueOf(session.getFailStackTrace())
                                        });

                                        if (subscriber != null && subscriber.isDisposed() == false){
                                            subscriber.onError(new Throwable(getFileInfo_Message));
                                            subscriber.onComplete();
                                        }

                                    } else {
                                        S_Log._W_(ACTIVITY_NAME + " :: getFileInfo :: 특정 비디오 파일 정보 (codec , bitrate) 확인 성공", new String[]{
                                                "getSessionId :: " + String.valueOf(session.getSessionId()),
                                                "getCommand :: " + String.valueOf(session.getCommand()),
                                                "getAllLogsAsString :: " + "\n\n"+String.valueOf(session.getAllLogsAsString())+"\n\n",
                                        });

                                        /**
                                         * -----------------------------------------------
                                         * TODO [예시 비디오 파일 확인 로그]
                                         * -----------------------------------------------
                                         * Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/storage/emulated/0/Download/video.mp4':
                                         *       Metadata:
                                         *         major_brand     : mp42
                                         *         minor_version   : 0
                                         *         compatible_brands: isommp42
                                         *         creation_time   : 2025-09-02T09:54:42.000000Z
                                         *         com.android.version: 15
                                         *       Duration: 00:00:21.27, start: 0.000000, bitrate: 5316 kb/s
                                         *       Stream #0:0[0x1](eng): Video: h264 (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1280x720, 5302 kb/s, 30 fps, 30 tbr, 90k tbn (default)
                                         *         Metadata:
                                         *           creation_time   : 2025-09-02T09:54:42.000000Z
                                         *           handler_name    : VideoHandle
                                         *           vendor_id       : [0][0][0][0]
                                         *         Side data:
                                         *           displaymatrix: rotation of -90.00 degrees
                                         *       Stream #0:1[0x2](eng): Audio: aac (mp4a / 0x6134706D), 8000 Hz, mono, fltp, 12 kb/s (default)
                                         *         Metadata:
                                         *           creation_time   : 2025-09-02T09:54:42.000000Z
                                         *           handler_name    : SoundHandle
                                         *           vendor_id       : [0][0][0][0]
                                         *     At least one output file must be specified
                                         * -----------------------------------------------
                                         * */

                                        // [리턴 반환 수행]
                                        returnData.put("Command", String.valueOf(session.getCommand()));
                                        returnData.put("FileLog", String.valueOf(session.getAllLogsAsString()));

                                        if (subscriber != null && subscriber.isDisposed() == false){
                                            subscriber.onNext(returnData);
                                            subscriber.onComplete();
                                        }
                                    }
                                });
                            }
                            else {
                                S_Log._E_(ACTIVITY_NAME + " :: getFileInfo :: 특정 비디오 파일 정보 (codec , bitrate) 확인 에러", new String[]{"FILE EXISTS FALSE)"});

                                // [에러 메시지 삽입]
                                getFileInfo_Message = C_Format.form_message(new String[]{
                                        "CODE :: " + C_Util.getSourceCodeLine(),
                                        "EXPLANATION :: " + "파일 존재 여부 확인이 필요합니다.",
                                        "ERROR :: FILE EXISTS FALSE"
                                });

                                if (subscriber != null && subscriber.isDisposed() == false){
                                    subscriber.onError(new Throwable(getFileInfo_Message));
                                    subscriber.onComplete();
                                }
                            }

                        } catch (Exception es) {
                            es.printStackTrace();

                            S_Log._E_(ACTIVITY_NAME + " :: getFileInfo :: 특정 비디오 파일 정보 (codec , bitrate) 확인 실패", new String[]{String.valueOf(es.getMessage())});

                            // [에러 메시지 삽입]
                            getFileInfo_Message = C_Format.form_message(new String[]{
                                    "CODE :: " + C_Util.getSourceCodeLine(),
                                    "EXPLANATION :: " + "특정 비디오 파일 정보 (codec , bitrate) 확인 중 예외 상황이 발생했습니다.",
                                    "EXCEPTION [1] :: " + String.valueOf(es.getMessage())
                            });

                            if (subscriber != null && subscriber.isDisposed() == false){
                                subscriber.onError(new Throwable(getFileInfo_Message));
                                subscriber.onComplete();
                            }

                        }

                    }
                }.start();

            }
            else {
                S_Log._E_(ACTIVITY_NAME + " :: getFileInfo :: 특정 비디오 파일 정보 (codec , bitrate) 확인 에러", new String[]{"Input Data Is Null (FILE_PATH)"});

                // [에러 메시지 삽입]
                getFileInfo_Message = C_Format.form_message(new String[]{
                        "CODE :: " + C_Util.getSourceCodeLine(),
                        "EXPLANATION :: " + "특정 비디오 파일 정보 (codec , bitrate) 확인에 필요한 필수 값을 확인해주세요.",
                        "ERROR :: Input Data Is Null (FILE_PATH)"
                });

                if (subscriber != null && subscriber.isDisposed() == false){
                    subscriber.onError(new Throwable(getFileInfo_Message));
                    subscriber.onComplete();
                }

            }

        });
    }


} // TODO [클래스 종료]

-----------------------------------------------------------------------------------------





-----------------------------------------------------------------------------------------
[참고 사이트]
-----------------------------------------------------------------------------------------

[IT 용어] FFmpeg (미디어 포맷 변환 도구) 용어 설명

https://blog.naver.com/kkh0977/222912178602?trackingCode=blog_bloghome_searchlist


[간단 소스] 안드로이드 arthenica ffmpeg 미디오 포맷 변환 라이브러리 사용해 mp4 파일을 mkv 파일로 변환 수행

https://blog.naver.com/kkh0977/223990678835


[간단 소스] 안드로이드 arthenica ffmpeg 라이브러리 사용해 특정 비디오 파일 코덱 (Codec) 및 bitrate 정보 확인

https://blog.naver.com/kkh0977/224004448029

-----------------------------------------------------------------------------------------
 
728x90
반응형
Comments