关于音视频的移动端环境搭建

作者: 不正经的创作者 | 来源:发表于2020-06-08 16:07 被阅读0次

本章主要目标:使用 LAME 这个开源的 MP3 编码库在 Android 平台上将一个 PCM 文件编码为 MP3 文件,最终将编码后的 MP3文件发送到电脑上即可进行播放。

目录:

  • LAME 的交叉编译
  • FDK_AAC 的交叉编译
  • X264 的交叉编译
  • 使用 LAME 编码 MP3 文件
1. LAME 的交叉编译

LAME 是目前非常优秀的一种 MP3 编码引擎,在业界,转码成 MP3 格式的音频文件时,最常用的编码器就是 LAME 库。当达到320Kbit/s 以上时,LAME 编码出来的音频质量几乎可以和 CD 的音质相媲美,并且还能保证整个音频文件的体积非常小,因此若要在移动端平台上编码 MP3 文件,使用 LAME 便成为唯一的选择。

LAME 官网:https://lame.sourceforge.io

2. FDK_AAC 的交叉编译

FDK_AAC 是用来编码和解码 AAC 格式音频文件的开源库,Android 系统编码和解码 AAC 所用的就是这个库。开发者 Fraunhofer IIS 是 AAC 音频规范的核心制定者(MP3 时代 Fraunhofer IIS 也是 MP3 规范的制定者)。前面章节中已经介绍过 AAC 有很多种Profile,而 FDK_AAC 几乎支持大部分的 Profile,并且支持 CBR 和 VBR 这两种模式,根据笔者个人的听感和频谱分析,在同等码率下 FDK_AAC 比 NeroAAC 以及 faac 和 voaac 的音质都要好一些。

3. X264 的交叉编译

X264 是一个开源的 H.264/MPEG-4 AVC 视频编码函数库,是最好的有损视频编码器之一。一般的输入是视频帧的 YUV 表示,输出是编码之后的 H264 的数据包,并且支持 CBR、VBR 模式,可以在编码的过程中直接改变码率的设置,这在直播的场景中是非常实用的 (直播场景下利用该特点可以做码率自适应)。

4. 使用 LAME 编码 MP3 文件

要实现的目标是,在添加好 C++ 支持的项目中加入编码 MP3 文件的功能。当点击按钮的时候,输入的是一个 PCM 文件的路径和一个 MP3 的路径,等运行完毕,电脑上的播放器直接就可以播放该 MP3 文件。

Java 代码:

  public class MainActivity extends Activity {
 
      static {
          System.loadLibrary("audioencoder");
      }
 
      private final String TAG = "MainActivity";
 
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          findViewById(R.id.mp3_encoder_btn).setOnClickListener(new 
   OnClickListener() {
              @Override
              public void onClick(View v) {
                  Mp3Encoder encoder = new Mp3Encoder();
                  // 通道数
                  int audioChannels = 2;
                  // 比特率
                  int bitRate = 128 * 1024;
                  // 采样率
                  int sampleRate = 44100;
                  String audioPath = 
   Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
                  // 进行编码初始化
                  int ret = encoder.init(audioPath + "vocal.pcm", audioChannels, bitRate, 
   sampleRate, audioPath + "vocal.mp3");
                  if (ret >= 0) {
                      // 编码
                      encoder.encode();
                      // 销毁
                      encoder.destroy();
                      Log.i(TAG, "Encode Mp3 Success");
                  } else {
                      Log.i(TAG, "Encoder Initialized Failed...");
                  }
              }
          });
      }
  }

JNI 类:

  package com.phuket.tour.studio;
 
  public class Mp3Encoder {
 
    public native int init(String pcmPath, int audioChannels, int bitRate, int 
  sampleRate, String mp3Path);
 
    public native void encode();
 
    public native void destroy();
  }

C++ 实现:

  #include "mp3_encoder.h"
 
  Mp3Encoder::Mp3Encoder() {
  }
 
  Mp3Encoder::~Mp3Encoder() {
  }
 
  int Mp3Encoder::Init(const char* pcmFilePath, const char *mp3FilePath, int 
  sampleRate, int channels, int bitRate) {
      int ret = -1;
      pcmFile = fopen(pcmFilePath, "rb");
      if(pcmFile) {
          mp3File = fopen(mp3FilePath, "wb");
          if(mp3File) {
                 lameClient = lame_init();
             lame_set_in_samplerate(lameClient, sampleRate);
             lame_set_out_samplerate(lameClient, sampleRate);
             lame_set_num_channels(lameClient, channels);
             lame_set_brate(lameClient, bitRate / 1000);
             lame_init_params(lameClient);
             ret = 0;
          }
       }
     return ret;
  }
 
  void Mp3Encoder::Encode() {
      int bufferSize = 1024 * 256;
      short* buffer = new short[bufferSize / 2];
      short* leftBuffer = new short[bufferSize / 4];
      short* rightBuffer = new short[bufferSize / 4];
      uint8_t* mp3_buffer = new uint8_t[bufferSize];
      int readBufferSize = 0;
      while ((readBufferSize = fread(buffer, 2, bufferSize / 2, pcmFile)) > 0) {
          for (int i = 0; i < readBufferSize; i++) {
              if (i % 2 == 0) {
                  leftBuffer[i / 2] = buffer[i];
              } else {
                  rightBuffer[i / 2] = buffer[i];
              }
          }
          int wroteSize = lame_encode_buffer(lameClient, (short int *) leftBuffer, 
    (short int *) rightBuffer, readBufferSize / 2, mp3_buffer, bufferSize);
          fwrite(mp3_buffer, 1, wroteSize, mp3File);
      }
      delete[] buffer;
      delete[] leftBuffer;
      delete[] rightBuffer;
      delete[] mp3_buffer;
  }
 
  void Mp3Encoder::Destory() {
      if(pcmFile) {
          fclose(pcmFile);
      }
      if(mp3File) {
          fclose(mp3File);
          lame_close(lameClient);
      }
  }

关于音视频的知识还会有更新,浏览到最后的同学可以点个赞。

相关文章

网友评论

    本文标题:关于音视频的移动端环境搭建

    本文链接:https://www.haomeiwen.com/subject/ltcbzhtx.html