<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          FFmpeg音頻處理——音頻混合、拼接、剪切、轉(zhuǎn)碼

          共 22532字,需瀏覽 46分鐘

           ·

          2021-03-10 10:01

          本文是一篇關(guān)于 FFmpeg 音頻處理的文章,轉(zhuǎn)載詳情 可見文章末尾~~

          接觸FFmpeg有一段時間了,它是音視頻開發(fā)的開源庫,幾乎其他所有播放器、直播平臺都基于FFmpeg進(jìn)行二次開發(fā)。

          本篇文章來總結(jié)下采用FFmpeg進(jìn)行音頻處理:音頻混合、音頻剪切、音頻拼接與音頻轉(zhuǎn)碼。

          采用android studio進(jìn)行開發(fā),配置build.gradle文件:

          defaultConfig {
                  ......
                  externalNativeBuild {
                      cmake {
                          cppFlags ""
                      }
                  }
                  ndk {
                      abiFilters "armeabi-v7a"
                  }
              }

          另外指定cmake文件路徑:


              externalNativeBuild {
                  cmake {
                      path "CMakeLists.txt"
                  }
              }
              sourceSets {
                  main {
                      jniLibs.srcDirs = ['libs']
                      jni.srcDirs = []
                  }

          從FFmpeg官網(wǎng)下載源碼,編譯成ffmpeg.so動態(tài)庫,并且導(dǎo)入相關(guān)源文件與頭文件:

          然后配置cMakeLists文件:

          add_library( # Sets the name of the library.
                       audio-handle
           
                       # Sets the library as a shared library.
                       SHARED
           
                       # Provides a relative path to your source file(s).
                       src/main/cpp/ffmpeg_cmd.c
                       src/main/cpp/cmdutils.c
                       src/main/cpp/ffmpeg.c
                       src/main/cpp/ffmpeg_filter.c
                       src/main/cpp/ffmpeg_opt.c)
          add_library( ffmpeg
                       SHARED
                       IMPORTED )
          set_target_properties( ffmpeg
                                 PROPERTIES IMPORTED_LOCATION
                                 ../../../../libs/armeabi-v7a/libffmpeg.so )
          include_directories(src/main/cpp/include)
          find_library( log-lib
                        log )
          target_link_libraries( audio-handle
                                 ffmpeg
                                 ${log-lib} )
                                 

          調(diào)用FFmpeg命令行進(jìn)行音頻處理:



              /**
               * 調(diào)用ffmpeg處理音頻
               * @param handleType handleType
               */
              private void doHandleAudio(int handleType){
                  String[] commandLine = null;
                  switch (handleType){
                      case 0://轉(zhuǎn)碼
                          String transformFile = PATH + File.separator + "transform.aac";
                          commandLine = FFmpegUtil.transformAudio(srcFile, transformFile);
                          break;
                      case 1://剪切
                          String cutFile = PATH + File.separator + "cut.mp3";
                          commandLine = FFmpegUtil.cutAudio(srcFile, 10, 15, cutFile);
                          break;
                      case 2://合并
                          String concatFile = PATH + File.separator + "concat.mp3";
                          commandLine = FFmpegUtil.concatAudio(srcFile, appendFile, concatFile);
                          break;
                      case 3://混合
                          String mixFile = PATH + File.separator + "mix.aac";
                          commandLine = FFmpegUtil.mixAudio(srcFile, appendFile, mixFile);
                          break;
                      default:
                          break;
                  }
                  executeFFmpegCmd(commandLine);
              }

          其中,音頻混音、合并、剪切和轉(zhuǎn)碼的FFmpeg命令行的拼接如下:


           

              /**
               * 使用ffmpeg命令行進(jìn)行音頻轉(zhuǎn)碼
               * @param srcFile 源文件
               * @param targetFile 目標(biāo)文件(后綴指定轉(zhuǎn)碼格式)
               * @return 轉(zhuǎn)碼后的文件
               */
              public static String[] transformAudio(String srcFile, String targetFile){
                  String transformAudioCmd = "ffmpeg -i %s %s";
                  transformAudioCmd = String.format(transformAudioCmd, srcFile, targetFile);
                  return transformAudioCmd.split(" ");//以空格分割為字符串?dāng)?shù)組
              }
           
              /**
               * 使用ffmpeg命令行進(jìn)行音頻剪切
               * @param srcFile 源文件
               * @param startTime 剪切的開始時間(單位為秒)
               * @param duration 剪切時長(單位為秒)
               * @param targetFile 目標(biāo)文件
               * @return 剪切后的文件
               */
              @SuppressLint("DefaultLocale")
              public static  String[] cutAudio(String srcFile, int startTime, int duration, String targetFile){
                  String cutAudioCmd = "ffmpeg -i %s -ss %d -t %d %s";
                  cutAudioCmd = String.format(cutAudioCmd, srcFile, startTime, duration, targetFile);
                  return cutAudioCmd.split(" ");//以空格分割為字符串?dāng)?shù)組
              }
           
              /**
               * 使用ffmpeg命令行進(jìn)行音頻合并
               * @param srcFile 源文件
               * @param appendFile 待追加的文件
               * @param targetFile 目標(biāo)文件
               * @return 合并后的文件
               */
              public static  String[] concatAudio(String srcFile, String appendFile, String targetFile){
                  String concatAudioCmd = "ffmpeg -i concat:%s|%s -acodec copy %s";
                  concatAudioCmd = String.format(concatAudioCmd, srcFile, appendFile, targetFile);
                  return concatAudioCmd.split(" ");//以空格分割為字符串?dāng)?shù)組
              }
           
              /**
               * 使用ffmpeg命令行進(jìn)行音頻混合
               * @param srcFile 源文件
               * @param mixFile 待混合文件
               * @param targetFile 目標(biāo)文件
               * @return 混合后的文件
               */
              public static  String[] mixAudio(String srcFile, String mixFile, String targetFile){
                  String mixAudioCmd = "ffmpeg -i %s -i %s -filter_complex amix=inputs=2:duration=first -strict -2 %s";
                  mixAudioCmd = String.format(mixAudioCmd, srcFile, mixFile, targetFile);
                  return mixAudioCmd.split(" ");//以空格分割為字符串?dāng)?shù)組
              }
              

          FFmpeg處理混音的公式如下,其中sample1為源文件采樣率、sample2為待混合文件采樣率:


          混音公式:value = sample1 + sample2 - (sample1 * sample2 / (pow(2, 16-1) - 1))

          開啟子線程,調(diào)用native方法進(jìn)行音頻處理:


              public static void execute(final String[] commands, final OnHandleListener onHandleListener){
                  new Thread(new Runnable() {
                      @Override
                      public void run() {
                          if(onHandleListener != null){
                              onHandleListener.onBegin();
                          }
                          //調(diào)用ffmpeg進(jìn)行處理
                          int result = handle(commands);
                          if(onHandleListener != null){
                              onHandleListener.onEnd(result);
                          }
                      }
                  }).start();
              }
              private native static int handle(String[] commands);
              

          關(guān)鍵的native方法,是把java傳入的字符串?dāng)?shù)組轉(zhuǎn)成二級指針數(shù)組,然后調(diào)用FFmpeg源碼中的run方法:


           

          JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_FFmpegCmd_handle
          (JNIEnv *env, jclass obj, jobjectArray commands){
              int argc = (*env)->GetArrayLength(env, commands);
              char **argv = (char**)malloc(argc * sizeof(char*));
              int i;
              int result;
              for (i = 0; i < argc; i++) {
                  jstring jstr = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
                  char* temp = (char*) (*env)->GetStringUTFChars(env, jstr, 0);
                  argv[i] = malloc(1024);
                  strcpy(argv[i], temp);
                  (*env)->ReleaseStringUTFChars(env, jstr, temp);
              }
              //執(zhí)行ffmpeg命令
              result =  run(argc, argv);
              //釋放內(nèi)存
              for (i = 0; i < argc; i++) {
                  free(argv[i]);
              }
              free(argv);
              return result;
          }

          關(guān)于FFmpeg的run方法的源碼如下,中間有部分省略:

          int run(int argc, char **argv)
          {
              /****************省略********************/
              //注冊各個模塊
              avcodec_register_all();
          #if CONFIG_AVDEVICE
              avdevice_register_all();
          #endif
              avfilter_register_all();
              av_register_all();
              avformat_network_init();
              show_banner(argc, argv, options);
              term_init();
              /****************省略********************/
              //解析命令選項與打開輸入輸出文件
              int ret = ffmpeg_parse_options(argc, argv);
              if (ret < 0)
                  exit_program(1);
              /****************省略********************/
              //文件轉(zhuǎn)換
              if (transcode() < 0)
                  exit_program(1);
              /****************省略********************/
              //退出程序操作:關(guān)閉文件、釋放內(nèi)存
              exit_program(received_nb_signals ? 255 : main_return_code);
              ffmpeg_cleanup(0);
          }

          其中,最關(guān)鍵的是文件轉(zhuǎn)換部分,源碼如下:



          static int transcode(void)
          {
              int ret, i;
              AVFormatContext *os;
              OutputStream *ost;
              InputStream *ist;
              int64_t timer_start;
              int64_t total_packets_written = 0;
              //轉(zhuǎn)碼方法初始化
              ret = transcode_init();
              if (ret < 0)
                  goto fail;
           
              if (stdin_interaction) {
                  av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
              }
              timer_start = av_gettime_relative();
           
          #if HAVE_PTHREADS
              if ((ret = init_input_threads()) < 0)
                  goto fail;
          #endif
              //transcode循環(huán)處理
              while (!received_sigterm) {
                  int64_t cur_time= av_gettime_relative();
           
                  //如果遇到"q"命令,則退出循環(huán)
                  if (stdin_interaction)
                      if (check_keyboard_interaction(cur_time) < 0)
                          break;
           
                  //判斷是否還有輸出流
                  if (!need_output()) {
                      av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n");
                      break;
                  }
           
                  ret = transcode_step();
                  if (ret < 0 && ret != AVERROR_EOF) {
                      char errbuf[128];
                      av_strerror(ret, errbuf, sizeof(errbuf));
           
                      av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", errbuf);
                      break;
                  }
           
                  //打印音視頻流信息
                  print_report(0, timer_start, cur_time);
              }
          #if HAVE_PTHREADS
              free_input_threads();
          #endif
           
              //文件末尾最后一個stream,刷新解碼器buffer
              for (i = 0; i < nb_input_streams; i++) {
                  ist = input_streams[i];
                  if (!input_files[ist->file_index]->eof_reached && ist->decoding_needed) {
                      process_input_packet(ist, NULL, 0);
                  }
              }
              flush_encoders();
              term_exit();
           
              //寫文件尾,關(guān)閉文件
              for (i = 0; i < nb_output_files; i++) {
                  os = output_files[i]->ctx;
                  if ((ret = av_write_trailer(os)) < 0) {
                      av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s", os->filename, av_err2str(ret));
                      if (exit_on_error)
                          exit_program(1);
                  }
              }
           
              //關(guān)閉所有編碼器
              for (i = 0; i < nb_output_streams; i++) {
                  ost = output_streams[i];
                  if (ost->encoding_needed) {
                      av_freep(&ost->enc_ctx->stats_in);
                  }
                  total_packets_written += ost->packets_written;
              }
           
              if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) {
                  av_log(NULL, AV_LOG_FATAL, "Empty output\n");
                  exit_program(1);
              }
           
              //關(guān)閉所有解碼器
              for (i = 0; i < nb_input_streams; i++) {
                  ist = input_streams[i];
                  if (ist->decoding_needed) {
                      avcodec_close(ist->dec_ctx);
                      if (ist->hwaccel_uninit)
                          ist->hwaccel_uninit(ist->dec_ctx);
                  }
              }
           
              //省略最后的釋放內(nèi)存
              return ret;
          }

          好了,使用FFmpeg進(jìn)行音頻剪切、混音、拼接與轉(zhuǎn)碼介紹完畢。

          如果各位有什么問題或者建議,歡迎交流。

          轉(zhuǎn)載自:https://blog.csdn.net/u011686167/article/details/79135240

          文章源碼地址為:https://github.com/xufuji456/FFmpegAndroid


          技術(shù)交流,歡迎加我微信:ezglumes ,拉你入技術(shù)交流群。

          推薦閱讀:

          音視頻面試基礎(chǔ)題

          OpenGL ES 學(xué)習(xí)資源分享

          開通專輯 | 細(xì)數(shù)那些年寫過的技術(shù)文章專輯

          NDK 學(xué)習(xí)進(jìn)階免費視頻來了

          推薦幾個堪稱教科書級別的 Android 音視頻入門項目

          覺得不錯,點個在看唄~

          瀏覽 89
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美一级A片高清免费播放 | 亚洲第97页 | 色婷婷激情视频 | 干逼逼视频| 影音先锋AV激情理论在线观看 |