小麦克 发表于 2024-10-25 08:56

《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
```

Jacktang 发表于 2024-10-27 09:16

<p>CMake交叉编译这全是代码,真是看不懂</p>
页: [1]
查看完整版本: 《CMake构建实战》CMake实践-交叉编译APP