1. 程式人生 > 其它 >用JAVACV把圖片轉換成視訊在android上的實現

用JAVACV把圖片轉換成視訊在android上的實現

技術標籤:mediajavacv

javacv下載連結

https://www.softpedia.com/get/Programming/Other-Programming-Files/JavaCV.shtml

javacpp下載連結

https://github.com/bytedeco/javacpp/releases

解壓後把以下jar包加入到專案

gradle

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.example.demo"
        minSdkVersion 18
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        multiDexEnabled true

        ndk {
            // 設定支援的SO庫架構
            abiFilters 'armeabi' , 'armeabi-v7a',  'arm64-v8a'
//            abiFilters 'armeabi-v7a'
        }
    }
    packagingOptions {
        exclude 'META-INF/native-image/android-arm/jnijavacpp/jni-config.json'
        exclude 'META-INF/native-image/android-arm/jnijavacpp/reflect-config.json'
    }
    repositories {
        flatDir {
            dirs 'libs'
        }
    }
    sourceSets{
        main {
            //jni庫的呼叫會到資原始檔夾下libs裡面找so檔案
            jniLibs.srcDirs = ['libs']
        }
    }
//    packagingOptions {
//        exclude 'META-INF/native-image/ios-x86_64/jnijavacpp/'
//    }

    dataBinding  {
        enabled = true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

//    implementation group: 'org.bytedeco', name: 'javacv-platform', version: '1.5.4'
//    implementation 'com.android.support:multidex:1.0.3'
}

功能類:

import android.util.Log;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameRecorder;
import org.bytedeco.javacv.Java2DFrameConverter;

import java.io.File;
import java.util.Map;

import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.opencv_core.IplImage;

import static org.bytedeco.opencv.helper.opencv_imgcodecs.cvLoadImage;

public class Utils {
    /**
     * 錄製成功監聽
     */
    public interface CompleteListener{
        public void onComplete(String path);
    }
    public static void createMp4(String mp4SavePath, Map<Integer, File> imgMap, int width, int height,CompleteListener listener) throws FrameRecorder.Exception {
        //視訊寬高最好是按照常見的視訊的寬高  16:9  或者 9:16
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mp4SavePath, width, height);
        //設定視訊編碼層模式
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        //設定視訊為25幀每秒
        recorder.setFrameRate(25);
        //設定視訊影象資料格式
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        recorder.setFormat("mp4");
        try {
            recorder.start();
            Java2DFrameConverter converter = new Java2DFrameConverter();
            Log.d("test","imgMap.size() = " + imgMap.size());
            //錄製一個imgMap size秒數的的視訊,imgmap容量有多大就錄多少秒
            for (int i = 0; i < imgMap.size(); i++) {
                if(imgMap.get(i) == null){
                    continue;
                }
                IplImage image = cvLoadImage(imgMap.get(i).getAbsolutePath());//
                OpenCVFrameConverter.ToMat converterToMat = new OpenCVFrameConverter.ToMat();
                Frame frame = converterToMat.convert(image);
                //一秒是30幀 所以要記錄30次
                for(int j = 0; j < 30; j++){
                    recorder.record(frame);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //最後一定要結束並釋放資源
            recorder.stop();
            recorder.release();
            if(listener != null){
                listener.onComplete(mp4SavePath);
            }
            Log.d("test","工作已完成 ");
        }
    }

}

activity呼叫:

class ImageToVideoActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_image_to_video)
    }
    fun start(v: View){
        Log.d("ddebug", "---start---")
//        imageToMp4()

        object :Thread(){
            override fun run() {
                super.run()
                //合成的MP4
                //合成的MP4
                val mp4SavePath = "/storage/emulated/0/ScreenRecord/${System.currentTimeMillis()}test_abc.mp4"//"D:\\javacv\\mp4\\img.mp4"
                //圖片地址 這裡面放了幾張圖片
                val img = "/storage/emulated/0/ScreenRecord/temp"
                val width = 1600
                val height = 900
                //讀取所有圖片
                val file = File(img)
                val files = file.listFiles()
                val imgMap: MutableMap<Int, File> = HashMap()
                var num = 0
                for (imgFile in files) {
                    imgMap[num] = imgFile
                    num++
                }
                Utils.createMp4(mp4SavePath, imgMap, width, height,
                    object : Utils.CompleteListener{
                        override fun onComplete(path: String?) {
                            runOnUiThread{
                                val tv:TextView = findViewById(R.id.tv)
                                tv.setText("錄製已完成:$path")
                            }
                        }
                    });

            }
        }.start()
    }

   
}