《CMake构建实战》CMake实践-交叉编译APP
## CMake实践-交叉编译APP之前在这篇[帖子](https://bbs.eeworld.com.cn/thread-1291655-1-1.html)写过使用buildroot生成的sdk开发包开发应用程序。在这个基础上来实践CMake交叉编译。
这里以编译引用`pjsip`库的应用程序为例,通过`buildroot`编译会自动生成 `libpjproject.pc`
测试一下。在工具链目录搜索`libpjproject.pc`,这个文件后面的CMake会用到。
```bash
$ find -name "libpjproject.pc"
./aarch64-buildroot-linux-gnu/sysroot/usr/lib/pkgconfig/libpjproject.pc
```
CMake开发环境
```bash
# 生效buildroot
source ~/Documents/aarch64-buildroot-linux-gnu_sdk-buildroot/environment-setup
```
创建`CMakeLists.txt`
```cmake
cmake_minimum_required(VERSION 3.10)
project(PJSUA2App)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PJSIP REQUIRED libpjproject)
add_definitions(-DPJ_AUTOCONF=1)
add_executable(pjsua2_app pjsip-call.cpp)
target_link_libraries(pjsua2_app ${PJSIP_LIBRARIES})
```
通过`libpjproject` 找到 `pjsip` 相关的库
### 测试例子
新建如下目录结构
```bash
$ tree
.
├── build
├── CMakeLists.txt
└── pjsip-call.cpp
2 directories, 3 files
```
pjsip-call.cpp文件
```c++
#include <pjsua2.hpp>
#include <iostream>
#include <pj/file_access.h>
#define THIS_FILE "pjsua2_demo.cpp"
using namespace pj;
class MyEndpoint : public Endpoint
{
public:
MyEndpoint() : Endpoint() {};
virtual pj_status_t onCredAuth(OnCredAuthParam &prm)
{
PJ_UNUSED_ARG(prm);
std::cout << "*** Callback onCredAuth called ***" << std::endl;
/* Return PJ_ENOTSUP to use
* pjsip_auth_create_aka_response()/<b>libmilenage</b> (default),
* if PJSIP_HAS_DIGEST_AKA_AUTH is defined.
*/
return PJ_ENOTSUP;
}
};
class MyAccount;
class MyAudioMediaPort: public AudioMediaPort
{
virtual void onFrameRequested(MediaFrame &frame)
{
// Give the input frame here
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
// frame.buf.assign(frame.size, 'c');
}
virtual void onFrameReceived(MediaFrame &frame)
{
PJ_UNUSED_ARG(frame);
// Process the incoming frame here
}
};
class MyCall : public Call
{
private:
MyAccount *myAcc;
AudioMediaPlayer *wav_player;
AudioMediaPort *med_port;
public:
MyCall(Account &acc, int call_id = PJSUA_INVALID_ID)
: Call(acc, call_id)
{
wav_player = NULL;
med_port = NULL;
myAcc = (MyAccount *)&acc;
}
~MyCall()
{
if (wav_player)
delete wav_player;
if (med_port)
delete med_port;
}
virtual void onCallState(OnCallStateParam &prm);
virtual void onCallTransferRequest(OnCallTransferRequestParam &prm);
virtual void onCallReplaceRequest(OnCallReplaceRequestParam &prm);
virtual void onCallMediaState(OnCallMediaStateParam &prm);
};
class MyAccount : public Account
{
public:
std::vector<Call *> calls;
public:
MyAccount()
{}
~MyAccount()
{
std::cout << "*** Account is being deleted: No of calls="
<< calls.size() << std::endl;
for (std::vector<Call *>::iterator it = calls.begin();
it != calls.end(); )
{
delete (*it);
it = calls.erase(it);
}
}
void removeCall(Call *call)
{
for (std::vector<Call *>::iterator it = calls.begin();
it != calls.end(); ++it)
{
if (*it == call) {
calls.erase(it);
break;
}
}
}
virtual void onRegState(OnRegStateParam &prm)
{
AccountInfo ai = getInfo();
std::cout << (ai.regIsActive? "*** Register: code=" : "*** Unregister: code=")
<< prm.code << std::endl;
}
virtual void onIncomingCall(OnIncomingCallParam &iprm)
{
Call *call = new MyCall(*this, iprm.callId);
CallInfo ci = call->getInfo();
CallOpParam prm;
std::cout << "*** Incoming Call: " <<ci.remoteUri << " ["
<< ci.stateText << "]" << std::endl;
calls.push_back(call);
prm.statusCode = (pjsip_status_code)200;
call->answer(prm);
}
};
void MyCall::onCallState(OnCallStateParam &prm)
{
PJ_UNUSED_ARG(prm);
CallInfo ci = getInfo();
std::cout << "*** Call: " <<ci.remoteUri << " [" << ci.stateText
<< "]" << std::endl;
if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
myAcc->removeCall(this);
/* Delete the call */
delete this;
}
}
void MyCall::onCallMediaState(OnCallMediaStateParam &prm)
{
PJ_UNUSED_ARG(prm);
CallInfo ci = getInfo();
for (unsigned i = 0; i < ci.media.size(); i++) {
std::cout << "ci.media: " << i << std::endl;
if (ci.media.type == PJMEDIA_TYPE_AUDIO) {
try {
AudioMedia aud_med = getAudioMedia(i);
// Connect the call audio media to sound device
AudDevManager& mgr = Endpoint::instance().audDevManager();
std::cout << "mgr capture dev:" << mgr.getCaptureDev() << ", playback dev: " << mgr.getPlaybackDev() << std::endl;
aud_med.startTransmit(mgr.getPlaybackDevMedia());
mgr.getCaptureDevMedia().startTransmit(aud_med);
}
catch(const Error &e) {
// Handle invalid or not audio media error here
std::cout << "onCallMediaState Exception: " << e.info() << std::endl;
}
}
}
}
void MyCall::onCallTransferRequest(OnCallTransferRequestParam &prm)
{
/* Create new Call for call transfer */
prm.newCall = new MyCall(*myAcc);
}
void MyCall::onCallReplaceRequest(OnCallReplaceRequestParam &prm)
{
/* Create new Call for call replace */
prm.newCall = new MyCall(*myAcc);
}
static void mainProg1(MyEndpoint &ep)
{
// Init library
EpConfig ep_cfg;
ep_cfg.logConfig.level = 4;
ep.libInit( ep_cfg );
// Transport
TransportConfig tcfg;
tcfg.port = 5060;
ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
// Start library
ep.libStart();
std::cout << "*** PJSUA2 STARTED ***" << std::endl;
// Add account
AccountConfig acc_cfg;
acc_cfg.idUri = "sip:testclient@192.168.1.188";
acc_cfg.regConfig.registrarUri = "sip:sip.pjsip.org";
#if PJSIP_HAS_DIGEST_AKA_AUTH
AuthCredInfo aci("Digest", "*", "test", PJSIP_CRED_DATA_EXT_AKA | PJSIP_CRED_DATA_PLAIN_PASSWD, "passwd");
aci.akaK = "passwd";
#else
AuthCredInfo aci("digest", "*", "test1", 0, "test1");
#endif
acc_cfg.sipConfig.authCreds.push_back(aci);
MyAccount *acc(new MyAccount);
try {
acc->create(acc_cfg);
} catch (...) {
std::cout << "Adding account failed" << std::endl;
}
pj_thread_sleep(2000);
std::cout << "*** Make outgoing call ***" << std::endl;
// Make outgoing call
Call *call = new MyCall(*acc);
acc->calls.push_back(call);
CallOpParam prm(true);
prm.opt.audioCount = 1;
prm.opt.videoCount = 0;
call->makeCall("sip:test1@192.168.1.222", prm);
// Hangup all calls
pj_thread_sleep(1000*1000);
ep.hangupAllCalls();
pj_thread_sleep(4000);
// Destroy library
std::cout << "*** PJSUA2 SHUTTING DOWN ***" << std::endl;
delete acc; /* Will delete all calls too */
}
extern "C"
int main()
{
int ret = 0;
MyEndpoint ep;
try {
ep.libCreate();
mainProg1(ep);
ret = PJ_SUCCESS;
} catch (Error & err) {
std::cout << "Exception: " << err.info() << std::endl;
ret = 1;
}
try {
ep.libDestroy();
} catch(Error &err) {
std::cout << "Exception: " << err.info() << std::endl;
ret = 1;
}
if (ret == PJ_SUCCESS) {
std::cout << "Success" << std::endl;
} else {
std::cout << "Error Found" << std::endl;
}
return ret;
}
```
编译
```bash
# 先准备好交叉编译环境
$ source ~/Documents/aarch64-buildroot-linux-gnu_sdk-buildroot/environment-setup
$ cd build
$ cmake ..
-- The C compiler identification is GNU 13.3.0
-- The CXX compiler identification is GNU 13.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/bruce/Documents/aarch64-buildroot-linux-gnu_sdk-buildroot/bin/aarch64-buildroot-linux-gnu-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /home/bruce/Documents/aarch64-buildroot-linux-gnu_sdk-buildroot/bin/aarch64-buildroot-linux-gnu-g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PkgConfig: /home/bruce/Documents/aarch64-buildroot-linux-gnu_sdk-buildroot/bin/pkg-config (found version "1.6.3")
-- Checking for module 'libpjproject'
-- Found libpjproject, version 2.14
-- Configuring done (0.9s)
-- Generating done (0.0s)
-- Build files have been written to: /home/bruce/Documents/pjsip-test/build
# 编译
$ make
# 查看编译出的二进制文件属性,有ARM aarch64说明交叉编译成功
$ file pjsua2_app
```
<p>CMake交叉编译这全是代码,真是看不懂</p>
页:
[1]