音视频篇 - 移动端环境搭建 (学习笔记)

本文章是阅读《音视频开发进阶指南基于android与ios平台的实践》一书的学习笔记。


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

目录:

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

 

1. LAME 的交叉编译

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

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

由于我之前弄过很久的 NDK,所以这块就不做赘述,可以根据文档和 NDK 的知识去做交叉编译。或者直接使用别人已经编译好的 lame android 库:https://github.com/naman14/TAndroidLame

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 的音质都要好一些。

这边也有 Android 编译好的现成库:https://github.com/mstorsjo/fdk-aac

3. X264 的交叉编译

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

x264 源码:http://www.videolan.org/developers/x264.html

现成库:https://github.com/sszhangpengfei/android_x264_encoder

4. 使用 LAME 编码 MP3 文件

github: https://github.com/zhanxiaokai/Android-Mp3Encoder

要实现的目标是,在添加好 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);
	}
}

猜你喜欢

转载自blog.csdn.net/u014294681/article/details/106349330