Сжатие видео с использованием FFMPEG и JNI

Я хочу создать приложение для Android, которое может найти видеофайл (более 300 мб) и сжать его до файла mp4 меньшего размера.

Я уже пытался сделать это с помощью этого

Этот учебник очень эффективен, так как вы сжимаете видео небольшого размера (менее 100 мб)

Итак, я попытался реализовать его с помощью JNI.

Мне удалось построить ffmpeg с помощью this

Но в настоящее время я хочу сжать видео. Я не очень хорошо разбираюсь в JNI. Но я попытался понять это, используя следующую ссылку

Если кто-то может направить меня на шаги, чтобы сжать видео после открытого файла, используя JNI, который действительно здорово, спасибо

Ответ 1

Предполагая, что у вас есть путь к строкам входного файла, мы можем выполнить вашу задачу довольно легко. Я предполагаю, что у вас есть понимание основ NDK: Как подключить собственный .c файл к native методам в соответствующем .java файле (дайте мне знать, если эта часть вашего вопроса). Вместо этого я сосредоточусь на том, как использовать FFmpeg в контексте Android/JNI.

Обзор высокого уровня:

#include <jni.h>
#include <android/log.h>
#include <string.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"

#define LOG_TAG "FFmpegWrapper"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)


void Java_com_example_yourapp_yourJavaClass_compressFile(JNIEnv *env, jobject obj, jstring jInputPath, jstring jInputFormat, jstring jOutputPath, jstring JOutputFormat){
  // One-time FFmpeg initialization
  av_register_all();
  avformat_network_init();
  avcodec_register_all();

  const char* inputPath = (*env)->GetStringUTFChars(env, jInputPath, NULL);
  const char* outputPath = (*env)->GetStringUTFChars(env, jOutputPath, NULL);
  // format names are hints. See available options on your host machine via $ ffmpeg -formats
  const char* inputFormat = (*env)->GetStringUTFChars(env, jInputFormat, NULL);
  const char* outputFormat = (*env)->GetStringUTFChars(env, jOutputFormat, NULL);

  AVFormatContext *outputFormatContext = avFormatContextForOutputPath(outputPath, outputFormat);
  AVFormatContext *inputFormatContext = avFormatContextForInputPath(inputPath, inputFormat /* not necessary since file can be inspected */);

  copyAVFormatContext(&outputFormatContext, &inputFormatContext);
  // Modify outputFormatContext->codec parameters per your liking
  // See http://ffmpeg.org/doxygen/trunk/structAVCodecContext.html

  int result = openFileForWriting(outputFormatContext, outputPath);
  if(result < 0){
      LOGE("openFileForWriting error: %d", result);
  }

  writeFileHeader(outputFormatContext);

  // Copy input to output frame by frame
  AVPacket *inputPacket;
  inputPacket = av_malloc(sizeof(AVPacket));

  int continueRecording = 1;
  int avReadResult = 0;
  int writeFrameResult = 0;
  int frameCount = 0;
  while(continueRecording == 1){
      avReadResult = av_read_frame(inputFormatContext, inputPacket);
      frameCount++;
      if(avReadResult != 0){
        if (avReadResult != AVERROR_EOF) {
            LOGE("av_read_frame error: %s", stringForAVErrorNumber(avReadResult));
        }else{
            LOGI("End of input file");
        }
        continueRecording = 0;
      }

      AVStream *outStream = outputFormatContext->streams[inputPacket->stream_index];
      writeFrameResult = av_interleaved_write_frame(outputFormatContext, inputPacket);
      if(writeFrameResult < 0){
          LOGE("av_interleaved_write_frame error: %s", stringForAVErrorNumber(avReadResult));
      }
  }

  // Finalize the output file
  int writeTrailerResult = writeFileTrailer(outputFormatContext);
  if(writeTrailerResult < 0){
      LOGE("av_write_trailer error: %s", stringForAVErrorNumber(writeTrailerResult));
  }
  LOGI("Wrote trailer");
}

Для полного содержимого всех вспомогательных функций (в camelCase), см. мой полный проект Github. Есть вопросы? Я рад разработать.