音视频

音视频开发 - 交叉编译

2019-09-29  本文已影响0人  文艺女青年的男人

1.交叉编译原理

先来看一下,如果要在PC上运行一个二进制程序(以源码的方式进行编译,不要以包管理工具的方式来安装),

需要怎样做。首先,要有这个二进制程序的源代码(有可能是直接下载的,也有可能是自己编写的代码),然后

在PC上进行编译链接生成可执行文件,最后在Terminal下面去执行该可执行文件。上述流程中包含了几个角色,

首先是要有源代码,然后是要知道最终运行该二进制程序的机器是哪一个(其实就是本机器),当然,其中最重

要的就是编译器和链接器了,对于C或者C++程序来讲,就是使用gcc和g++,而该编译器是需要预先安装在机器

上的。分析了这么多角色,总结成一句话就是:使用本机器的编译器,将源代码编译链接成为

一个可以在本机器。这就是正常的编译过程,也称为Native Compilation,中文译作本机编

译。

交叉编译

就是在一个平台(如PC)上生成另外一个平台(Android、iOS或者其他嵌

入式设备)的可执行代码。相较于正常编译,下面来看一下交叉编译的相应角色。首先,最终

程序运行的设备就是Android或者iOS设备,源代码就是从第三方开源网站上下载的源代码,编

译机器就是我们的PC,而编译器也必须要安装到该PC上。但是这里对编译器是有特殊需求

的,最终程序运行的系统必须要提供可运行在PC上的编译器,而该编译器就是大家常说的交

叉工具编译链。

编译器的组成:

它们都会提供以下几个工具:CC、AS、AR、LD、NM、GDB。

那么,这几个工具到底是做什么用的呢?下面就来逐一解释一下。

CC:编译器,对C源文件进行编译处理,生成汇编文件。

AS:将汇编文件生成目标文件(汇编文件使用的是指令助记符,AS将它翻译成机器码)。

AR:打包器,用于库操作,可以通过该工具从一个库中删除或者增加目标代码模块。

LD:链接器,为前面生成的目标代码分配地址空间,将多个目标文件链接成一个库或者是可执行文件。

GDB:调试工具,可以对运行过程中的程序进行代码调试工作。

STRIP:以最终生成的可执行文件或者库文件作为输入,然后消除掉其中的源码。

NM:查看静态库文件中的符号表。

Objdump:查看静态库或者动态库的方法签名。

ARM平台

苹果的iOS系统架构都基于ARM平台,但是随着时间的推移,平台也在不断地演进下面就依据iOS设备发布的时间线来逐个看一下。

armv6:iPhone、iPhone 2、iPhone 3G

armv7:iPhone 4、iPhone 4S

armv7s:iPhone 5、iPhone 5S

arm64:iPhone 5S、iPhone 6(P)、iPhone 6S(P)、iPhone7(P)机器对指令集的支持是向下兼容的,因此armv7的指令集是可以运行在iPhone 5S中的,只是效率没那么高而已。

讨论一下iOS项目文件中的一项配置,即Build Settings里面的Architectures选项

Architectures指的是该App支持的指令集,一般情况下,在Xcode中新建一个项目,其默认的
Architectures选项值是Standard architectures(armv7、arm64),表示该App仅支持armv7和
arm64的指令集;

Valid architectures选项指即将编译的指令集,一般设置为armv7、armv7s、arm64,表示一般会编译这三个指令集;

Build ActiveArchitecture Only选项表示是否只编译当前适用的指令集,一般情况下在Debug的时候设置为YES,以便可以更加快速、高效地调试程序,而在Release的情况下设置为NO,以便App在各个机器上都能够以最高效率运行,因为Valid architectures选择的对应指令集是armv7、armv7s和arm64,在Release下会为各个指令集编译对应的代码,因此最后的ipa体积基本上翻了3倍。

2、LAME交叉编译(MP3编码引擎)

LAME是目前非常优秀的一种MP3编码引擎,在业界,转码成MP3格式的音频文件时,最常用

的编码器就是LAME库。当达到320Kbit/s以上时,LAME编码出来的音频质量几乎可以和CD的

音质相媲美,并且还能保证整个音频文件的体积非常小,因此若要在移动端平台上编码MP3文

件,使用LAME便成为唯一的选择。

在iOS上进行交叉编译时,其实在安装iOS的开发环境Xcode时,配套的编译器就已经安装好

了。开发iOS平台下的App就是这么方便,不需要再单独下载交叉工具编译链,接下来直接去

SourceForge下载最新的LAME版本,访问链接如下:

1、https://sourceforge.net/projects/lame/files/lame/3.99/

2、然后去下载外国大神的编译lame的shell脚本:https://github.com/kewlbear/lame-ios-build

3.将lame源码解压到一个文件夹里面,文件夹命名为lame

4.修改shell脚本(底下按需修改指令集)

5.在桌面生成一个文件夹X,将shell脚本和lame文件夹拖入此文件夹中

6.打开终端,输入指令

(1)cd 到文件夹X

(2)chmod 777 build-lame.sh

(3)sudo -s //提升到root权限,好像不用提升权限也可以,省掉(3)(4)

(4)输入系统密码

(5)./build-lame.sh

开始编译,编译完成之后。生成fat-lame目录和thin-lame目录,分别存放合并所有指令集的静态库,以及各指令集的静态库.

根据所需,copy lame.h和libmp3lame.a文件到project里,就可以正常使用了,如果发现Build Phases 的Compile Sources是否有lame.h,否则将.h添加进来。

2-1

在这个过程中可能会遇到一些问题:再执行完上面的命令之后,提示找不到xcodebuild

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

需要更新一下xcode的路径

解决方法:在终端输入命令

xcode-select -switch 新的xcode路径

例如:

$ sudo xcode-select --switch /Applications/Xcode\ 5.1.app/Contents/Developer/

3、FDK_AAC交叉编译(ACC音频编码)

FDK_AAC是用来编码和解码AAC格式音频文件的开源库,Android系统编码和解码AAC所用

的就是这个库。开发者Fraunhofer IIS是AAC音频规范的核心制定者(MP3时代Fraunhofer IIS

也是MP3规范的制定者)。AAC有很多种Profile,而FDK_AAC几乎支持大部分的Profile,并

且支持CBR和VBR这两种模式,在同等码率下FDK_AAC比NeroAAC以及faac和voaac的音

都要好一些。

1、下载地址:

https://sourceforge.net/p/opencore-amr/fdk-aac/ci/v0.1.4/tree/

2、下载完成后将其放到桌面一个新建的文件夹fdk-aac下,FDK_AAC的配置选项中要求比LAME多

配置一项AS参数,并且需要安装gas-preprocessor,首先进入下方链接:

https://github.com/applexiaohao/gas-preprocessor

下载gas-preprocessor.pl,然后复制到/usr/local/bin/目录下,修改/usr/local/bin/gas-preprocessor.pl的文件权限为可执行权限:

chmod 777 /usr/local/bin/gas-preprocessor.pl

这样gas-preprocessor.pl就安装成功了,

3、脚本下载路径:https://github.com/kewlbear/fdk-aac-build-script-for-iOS,将下载的build-fdk-aac.sh文件放到文件夹fdk-aac下,

4、终端执行:brew install automake libtool .

5、修改build-fdk-aac.sh 文件里:SOURCE="fdk-aac名称"和ARCHS="arm64 x86_64 i386 armv7 armv7s",打开文本编辑修改就行

6、终端cd 到文件夹fdk-aac,再执行./build-fdk-aac.sh就好了


2-2

7、再cd 到 fdk-aac-ios文件夹下的lib文件夹 执行:file libfdk-aac.a

出现:

libfdk-aac.a: Mach-O universal binary with 5 architectures: [i386:current ar archive] [arm64]

libfdk-aac.a (for architecture i386):   current ar archive

libfdk-aac.a (for architecture armv7):  current ar archive

libfdk-aac.a (for architecture armv7s): current ar archive

libfdk-aac.a (for architecture x86_64): current ar archive

libfdk-aac.a (for architecture arm64):  current ar archive

就好了

4、X264交叉编译(视频编码)

X264是一个开源的H.264/MPEG-4 AVC视频编码函数库,是最好的有损视频编码器之一。一

般的输入是视频帧的YUV表示,输出是编码之后的H264的数据包,并且支持CBR、VBR模

式,可以在编码的过程中直接改变码率的设置,这在直播的场景中是非常实用的(直播场景下

利用该特点可以做码率自适应)。

1、获取X264源码,还是放到一个x264的文件夹中

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

2、下载脚本,放到x264文件夹中
https://github.com/tangyi1234/x264-iOS-build-script

3、由于已经下载了gas-preprocessor,所以不需要执行相应的下载步骤

4、打开终端进入cd到集成脚本目录,执行./build-x264.sh,从lib中查看.a文件的支持架构

libx264.a (for architecture armv7): current ar archive random library
libx264.a (for architecture armv7s):    current ar archive random library
libx264.a (for architecture arm64): current ar archive random library

执行完之后的x264文件夹结构


2-3

5、脚本中可能涉及到的几个重要的字段

上面提到的三个脚本其实内部分为了很多部分,包括单独架构的.a文件生成、合并不同编译器架构的.a文件,其中在生成.a文件的时候用到了几个关键字段:

./configure \
--disable-shared \
--disable-frontend \
--host=arm-apple-darwin \
--prefix="./thin/armv7" \
CC="xcrun -sdk iphoneos clang -arch armv7" \
CFLAGS="-arch armv7 -fembed-bitcode -miphoneos-version-min=7.0" \
LDFLAGS="-arch armv7 -fembed-bitcode -miphoneos-version-min=7.0"
make clean
make -j8
make instal

configure是符合GNU标准的软件包发布所必备的命令,所以这里是通过configure的方式来生成Makefile文件,然后使用make和make install编译和安装整个库。可使用configure-h命令来查看一下configure的帮助文档,了解LAME的可选配置项,具体如下。

--prefix:指定将编译好的库放到哪个目录下,这是GNU大部分库的标准配置。

--host:指定最终库要运行的平台。

CC:指定交叉工具编译链的路径,其实这里就是指定gcc的路径。

CFLAGS:指定编译时所带的参数。Shell脚本中指定-march是armv7平台,代表编译的库运行的目标平台是armv7平台;另外Shell脚本中也指定了打开bitcode选项,这使得使用编译出来的这个库的工程,可以将enable-bitcode选项设置为YES,如果没有打开该选项,那么其在Xcode中只能设置为NO,而这对于最终App的运行性能会有一定的影响。Shell脚本中同时也指定了编译出来的这个库所支持的最低iOS版本是7.0,如果不配置该参数的话,则默认是iOS 9.0版本,而所使用的编译出来的这个库的工程,若所支持的最低iOS版本不是9.0的话,Xcode就会给出警告。

LDFLAGS:指定链接过程中的参数,同样也要带上bitcode的选项以及开发者期望App支持的
最低iOS版本的选项参数。

--disable-shared:通常是GNU标准中关闭动态链接库的选项,一般是在编译出命令行工具的时候,期望命令行工具可以单独使用而不需要动态链接库的配置。

--disable-frontend:不编译出LAME的可执行文件。

bitcode:表明当开发者提交应用(App)到App Store上的时候,Xcode会将程序编译为一个中间表现形式(bitcode)。App Store会将该bitcode中间表现形式的代码进行编译优化,链接为64位或者32位的程序。如果程序中用到了第三方静态库,则必须在编译第三方静态库的时候也开启bitcode,否则在Xcode的Build Setting中必须要关闭bitcode,这对于App来讲可能会造成性能的降低。

6、利用LAME进行pcm转MP3

1、创建Xcode项目WKLameDemo,并引入编译好的lame.h和libmp3lame.a

2、首先新建两个文件:mp3_encoder.h和mp3_encoder.cpp,当前的两个文件是创建c++文件时

将.hpp改为.h,先看一下头文件应该如何编写:

头文件其实就是用于定义该类对外提供的接口。这里提供的是一个Init接口,输入的是一个

PCM FilePath和一个MP3FilePath,会判定输入文件是否存在、初始化LAME以及初始化输出

文件的资源,返回值是该函数是否成功初始化了所有的相关资源,成功则返回true,否则返回

false。此外,还要再提供一个encode方法,负责读取PCM数据,并且调用LAME进行编码,

然后将编码之后的数据写入文件。最后再对外提供一个销毁资源的接口destroy方法,用于关

闭所有的资源。

#include "lame.h"

class Mp3Encoder {
private:
    FILE* pcmFile;
    FILE* mp3File;
    lame_t lameClient;
public:
    Mp3Encoder();
    ~Mp3Encoder();
    int Init(const char* pcmFilePath, const char *mp3FilePath, int sampleRate, int channels, int bitRate);
    void Encode();
    void Destory();
};

接下来实现文件中进行具体实现:

#include "mp3_encoder.h"

//以读的方式”rb“打开pcm文件,以写的方式”wb“打开mp3文件,初始化Lame
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;
}

//encode编码,主体是一个循环,每次都会读取一段bufferSize
void Mp3Encoder::Encode(){
   //每次都会读取一段bufferSize大小
    int bufferSize = 1024*256;
    short* buffer = new short[bufferSize/2];
    short* leftBuffer = new short[bufferSize/4];
    short* rightBuffer = new short[bufferSize/4];
    unsigned char* mp3_buffer = new unsigned char[bufferSize];
    size_t readBufferSize = 0;
    while ((readBufferSize = fread(buffer, 2, bufferSize/2, pcmFile)) > 0){
        //将buffer的左右声道()分开
        for (int i = 0; readBufferSize; i++) {
            if (i%2==0) {
                leftBuffer[i/2] = buffer[i];
            }else{
                rightBuffer[i/2] = buffer[i];
            }
        }
        //送入到lame编码器
        size_t wroteSize = lame_encode_buffer(lameClient, (short int *)leftBuffer, (short int *)rightBuffer, (int)(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);
    }
}

3、在ViewController中开始将pcm编码成mp3

ViewController要改成.mm支持c++,将vocal.pcm文件放入沙河,并开始执行encode任务,最终可以在电脑上导出真机中的沙河mp3文件进行播放。

    [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"vocal.pcm"];
    
    
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject  stringByAppendingPathComponent:@"vocal.mp3"];
    

    Mp3Encoder* encoder = new Mp3Encoder;
    encoder->Encode();
上一篇下一篇

猜你喜欢

热点阅读