0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

全志R128芯片基础组件开发指南—RTOS多媒体编码

冬至子 来源:丨budboool 作者:丨budboool 2023-10-11 14:54 次阅读

RTOS 多媒体编码

介绍 FreeRTOS 下如何使用 xrecorder 的接口来开发录制应用程序,方便录制应用开发人员快速正确地开发,以及录制应用测试人员如何根据该文档对基于 xrecord 的录制应用进行验证测试。

编码支持情况

目前 RTOS 平台多媒体编码应用支持的编码格式分别为:pcm、amr、mp3、speex、opus。

其中 pcm、amr、mp3 可通过 xrecorder 进行编码以及录制;speex 和 opus 可通过第三方示例工程进行编码。

xrecorder 状态图

在这里插入图片描述

这张状态转换图清晰地描述了 xrecorder 的各个状态,也列举了主要的方法的调用时序,每种方法只能在一些特定的状态下使用,否则会出错。

Init 状态

Idle 状态:当调用 XRecordCreate() 创建一个 xrecord 时,处于 idle 状态。

Prepared 状态

调用 XRecordPrepare() 函数并返回后,xrecorder 处于 Prepared 状态。在这个状态下说明所有的资源都已经就绪了,调用 XRecordStart() 函数就可以进行录制。

Started 状态

xrecorder prepare 完成后,调用 XRecordStart() 进行录制,当应用开始录制后,xrecorder 就处于 Started 状态,这表明 xrecorder 正在录制文件。

Stopped 状态

Started 状态下均可调用 XrecordStop() 停止 xrecorder,而处于 Stop 状态的 xrecorder 要想重新录制,需要通过 XRecorderPrepare() 回到先前的 Prepared 状态重新开始才可以。

Destroyed 状态

通过 XRecordDestroy() 的方法可以进入 Destroyed 状态,只要 xrecorder 不再被使用,就应当尽快将其 destroy 掉。

接口函数

创建一个 XRecord

XRecord *XRecordCreate()

参数

返回值:

设置录制音频的编码格式

int XRecordSetAudioEncodeType(XRecord *p, XRECODER_AUDIO_ENCODE_TYPE type, XRecordConfig *config)

参数:

  • p: 通过 XRecordCreate 创建的 XRecord 指针
  • type: 已支持的编码格式
  • config: 上层应用对音频属性的配置

返回值:

  • 成功: 0; 失败: ‑1

获取指针

获取指向音频设备管理模块的指针,用于录制音频

void XRecordSetAudioCap(XRecord* p, const CaptureCtrl* audioSrc)

参数:

  • p: 通过 XRecordCreate 创建的 XRecord 指针
  • audioSrc: 由上层应用获取的音频设备管理模块的指针

返回值:

audioSrc 可在上层应用通过调用 cedarx 的音频设备管理模块的 RTCaptureDeviceCreate 来创建。

设置录制后文件的保存的路径

int XRecordSetDataDstUrl(XRecord* p, const char* pUrl, void* arg, const CdxKeyedVectorT* pHeaders)

参数:

  • p: 通过 XRecordCreate 创建的 XRecord 指针
  • pUrl:url 地址

返回值:

  • 成功:0;失败:‑1

将 XRecord 置为准备状态, 准备 Muxer

int XRecordPrepare(XRecord* p)

参数:

  • p:通过 XRecordCreate 创建的 XRecord 指针

返回值:

  • 成功:0;失败:‑1

将 XRecord 置为启动状态

int XRecordStart(XRecord* p)

参数:

  • p:通过 XRecordCreate 创建的 XRecord 指针

返回值:

  • 成功:0;失败:‑1

将 XRecord 置为停止状态

int XRecordStop(XRecord* p)

参数:

  • p:通过 XRecordCreate 创建的 XRecord 指针

返回值:

  • 成功: 0;失败:‑1

编码数据入队封装

提供接口给下层编码模块,将编码数据放进缓存队列中等待封装

int onAudioDataEnc(XRecord* app, CdxMuxerPacketT* buff)

参数:

  • app: xrecorder 的环境句柄;
  • buff:编码后的缓存数据

返回值:

  • 成功: 0;失败:‑1

销毁一个 XRecord

int XRecordDestroy(XRecord* p)

参数:

  • p:通过 XRecordCreate 创建的 XRecord 指针

返回值:

  • 成功: 0;失败:‑1

XRecorder 开发流程

  1. XRecordCreate() //创建一个录制应用
  2. XRecordSetAudioCap() //设置音频采集设备;可先调用 RTCaptureDeviceCreate 创建。
  3. XRecordSetDataDstUrl() //设置录制后文件保存位置
  4. XRecordSetAudioEncodeType() //设置音频数据的编码格式
  5. XRecordPrepare() //设置 Muxer,让 xrecorder 进入准备状态
  6. XRecordStart() //开始录制
  7. XRecordStop() //停止录制
  8. XRecordDestroy() //当不需要进行录制的时候,销毁 xrecorder

注意事项

  1. 在调用 XRecordSetAudioCap 设置音频采集设备之前,需先打开音频采集设备来获取句柄。在 rtos 平台可调用 libcedarx 提供的音频采集设备控制模块 rtosCaptureControl.c 中的 RTCaptureDeviceCreate 来创建句柄。
  2. recorder 应用未支持暂停录制。
  3. recorder 的录制时长为调用 XRecordStart 至调用 XRecordStop 之间的时长来决定,因此上层应用需要录制指定时长的音频时,录制的步骤应为调用 XRecordStart,等待指定的时间,调用XRecordStop。

示例代码

#include < stdio.h >
#include < stdlib.h >
#include < stdbool.h >
#include < string.h >
#include < aw_common.h >
#include < console.h >

#include "vfs.h"
#include "xrecord.h"

#define RECORDER_LOGD(msg, arg...)      printf("[RECORDER_DBG] < %s : %d > " msg "n", __func__, __LINE__, ##arg)
#define RECORDER_LOGI(msg, arg...)      printf("[RECORDER_INFO] < %s : %d > " msg "n", __func__, __LINE__, ##arg)
#define RECORDER_LOGW(msg, arg...)      printf("[RECORDER_WRN] < %s : %d > " msg "n", __func__, __LINE__, ##arg)
#define RECORDER_LOGE(msg, arg...)      printf("[RECORDER_ERR] < %s : %d > " msg "n", __func__, __LINE__, ##arg)

typedef struct recorder_base recorder_base;

typedef struct rec_cfg
{
    XRECODER_AUDIO_ENCODE_TYPE   type;
    int                 sample_rate;
    int                 chan_num;
    int                 bitrate;
    int                 sampler_bits;
} rec_cfg;

struct recorder_base
{
    int (*start)(recorder_base *base, const char *url, const rec_cfg *cfg);
    int (*stop)(recorder_base *base);
};

struct ExampleCustomerWriterImpl
{
    CdxWriterT base;
    vfs_file_t *vfs;
};

typedef struct recorder
{
    recorder_base base;
    XRecord *xrecorder;
    CaptureCtrl *cap;
} recorder;

recorder_base *recorder_create();
int recorder_destroy(recorder_base *base);

/* Example Customer Writer */
static int __CdxExampleConnect(CdxWriterT *writer)
{
    struct ExampleCustomerWriterImpl *impl;

    impl = (struct ExampleCustomerWriterImpl *)writer;

    vfs_unlink("data/record/2.amr");
    impl- >vfs = vfs_open("data/record/2.amr", VFS_RDWR | VFS_CREAT);
    if (impl- >vfs == NULL) {
        return -1;
    }

    return 0;
}

static int __CdxExampleRead(CdxWriterT *writer, void *buf, int size)
{
    return 0;
}

static int __CdxExampleWrite(CdxWriterT *writer, void *buf, int size)
{
    uint32_t write_len;
    struct ExampleCustomerWriterImpl *impl;

    impl = (struct ExampleCustomerWriterImpl *)writer;

    write_len = vfs_write(impl- >vfs, buf, size);

    return write_len;
}

static long __CdxExampleSeek(CdxWriterT *writer, long moffset, int mwhere)
{
    return 0;
}

static long __CdxExampleTell(CdxWriterT *writer)
{
    return 0;
}

static int __CdxExampleClose(CdxWriterT *writer)
{
    struct ExampleCustomerWriterImpl *impl;

    impl = (struct ExampleCustomerWriterImpl *)writer;

    vfs_close(impl- >vfs);
    free(impl);

    return 0;
}

static const struct CdxWriterOps exampleCustomerWriteOps =
{
    .cdxConnect   =  __CdxExampleConnect,
    .cdxRead      =  __CdxExampleRead,
    .cdxWrite     =  __CdxExampleWrite,
    .cdxSeek      =  __CdxExampleSeek,
    .cdxTell      =  __CdxExampleTell,
    .cdxClose     =  __CdxExampleClose
};

CdxWriterT *ExampleCustomerWriterCreat()
{
    struct ExampleCustomerWriterImpl *impl;

    impl = malloc(sizeof(*impl));
    if (impl == NULL) {
        printf("example customer writer create fail.n");
        return NULL;
    }

    memset(impl, 0, sizeof(*impl));

    impl- >base.ops = &exampleCustomerWriteOps;

    return &impl- >base;
}

/* Main App */
static void showHelp(){
    printf("n");
    printf("**************************n");
    printf("* This is a simple audio recoder, when it is started, you can input commands to telln");
    printf("* what you want it to do.n");
    printf("* Usage: n");
    printf("*   cedarx_record amr 10  : this means record 10s amr musicn");
    printf("*   cedarx_record pcm 10  : this means record 10s pcm musicn");
    printf("**************************n");
}

recorder *recorder_singleton = NULL;

static int record_start(recorder_base *base, const char *url, const rec_cfg *cfg)
{
    recorder *impl = container_of(base, recorder, base);

    XRecordConfig audioConfig;

    if (cfg- >type == XRECODER_AUDIO_ENCODE_PCM_TYPE)
    {
        audioConfig.nChan = cfg- >chan_num;
        audioConfig.nSamplerate = cfg- >sample_rate;
        audioConfig.nSamplerBits = cfg- >sampler_bits;
        audioConfig.nBitrate = cfg- >bitrate;
    }
    else if (cfg- >type == XRECODER_AUDIO_ENCODE_AMR_TYPE)
    {
        audioConfig.nChan = 1;
        audioConfig.nSamplerate = 8000;//amr-nb 8000Hz amr-wb 16000Hz
        audioConfig.nSamplerBits = 16;
        audioConfig.nBitrate = 12200;//amr-nb 12200  amr-wb 23850
    } else {
        audioConfig.nChan = cfg- >chan_num;
        audioConfig.nSamplerate = cfg- >sample_rate;
        audioConfig.nSamplerBits = cfg- >sampler_bits;
        audioConfig.nBitrate = cfg- >bitrate;
    }

    XRecordSetDataDstUrl(impl- >xrecorder, url, NULL, NULL);
    XRecordSetAudioEncodeType(impl- >xrecorder, cfg- >type, &audioConfig);

    XRecordPrepare(impl- >xrecorder);
    XRecordStart(impl- >xrecorder);
    RECORDER_LOGI("record start");
    return 0;
}

static int record_stop(recorder_base *base)
{
    recorder *impl = container_of(base, recorder, base);
    XRecordStop(impl- >xrecorder);
    return 0;
}

extern CaptureCtrl* RTCaptureDeviceCreate();

recorder_base *recorder_create()
{
    if (recorder_singleton != NULL)
        return &recorder_singleton- >base;

    recorder *impl = malloc(sizeof(*impl));
    if (impl == NULL)
        return NULL;
    memset(impl, 0, sizeof(*impl));

    impl- >xrecorder = XRecordCreate();
    if (impl- >xrecorder == NULL)
        goto failed;

    impl- >cap = (void *)(uintptr_t)RTCaptureDeviceCreate();
    if (impl- >cap == NULL)
        goto failed;
    XRecordSetAudioCap(impl- >xrecorder, impl- >cap);

    impl- >base.start = record_start;
    impl- >base.stop = record_stop;

    recorder_singleton = impl;

    return &impl- >base;

failed:
    RECORDER_LOGE("recorder create failed");
    if (impl- >xrecorder)
        XRecordDestroy(impl- >xrecorder);
    if (impl)
        free(impl);
    return NULL;
}

int recorder_destroy(recorder_base *base)
{
    recorder *impl = container_of(base, recorder, base);

    if (impl- >xrecorder) {
        XRecordDestroy(impl- >xrecorder);
    }

    free(impl);

    recorder_singleton = NULL;

    return 0;
}

static int cedarx_record_test(int argc, char **argv)
{
    recorder_base *recorder;
    rec_cfg cfg;
    char music_url[64];
    char file_url[64];
    CdxWriterT *writer;
    memset(file_url, 0, 64);
    if(argc == 3){
        if( !strncmp("amr", argv[1], sizeof("amr")-1) ){
            cfg.type = XRECODER_AUDIO_ENCODE_AMR_TYPE;
            snprintf(file_url, 64, "file://data/%ds.amr", atoi(argv[2]));
            cfg.sample_rate = 8000;//8000
            cfg.chan_num = 1;//1
            cfg.bitrate = 12200;
            cfg.sampler_bits = 16;
        }
        else if( !strncmp("pcm", argv[1], sizeof("pcm")-1) ){
            cfg.type = XRECODER_AUDIO_ENCODE_PCM_TYPE;
            snprintf(file_url, 64, "file://data/%ds.pcm", atoi(argv[2]));
            cfg.sample_rate = 8000;//8000
            cfg.chan_num = 1;//1
            cfg.bitrate = 12200;
            cfg.sampler_bits = 16;
        }
        else if( !strncmp("mp3", argv[1], sizeof("mp3")-1) ){
            cfg.type = XRECODER_AUDIO_ENCODE_MP3_TYPE;
            snprintf(file_url, 64, "file://data/%ds.mp3", atoi(argv[2]));
            cfg.sample_rate = 16000;
            cfg.chan_num = 1;
            cfg.bitrate = 32000;
            cfg.sampler_bits = 16;
        } else {
            printf("now support!n");
            return -1;
        }
    }else{
        printf("the parameter is error,usage is as following:n");
        showHelp();
        return -1;
    }

    recorder = recorder_create();
    if (recorder == NULL) {
        printf("recorder create fail, exitn");
        return -1;
    }

    printf("===start record %s now, last for %d s===n", argv[1], atoi(argv[2]));
    recorder- >start(recorder, file_url, &cfg);
    sleep(atoi(argv[2]));
    recorder- >stop(recorder);
    printf("record %s over.n", argv[1]);
exit:
    return recorder_destroy(recorder);
}

FINSH_FUNCTION_EXPORT_CMD(cedarx_record_test, cedarx_record, cedarx record test demo);
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FreeRTOS
    +关注

    关注

    12

    文章

    484

    浏览量

    62139
  • 音频编码器
    +关注

    关注

    0

    文章

    14

    浏览量

    9131
  • 缓存器
    +关注

    关注

    0

    文章

    63

    浏览量

    11658
  • vfs
    vfs
    +关注

    关注

    0

    文章

    14

    浏览量

    5255
  • R128
    +关注

    关注

    0

    文章

    41

    浏览量

    105
收藏 人收藏

    评论

    相关推荐

    R128基础组件开发指南RTOS多媒体解码

    介绍 FreeRTOS 下如何使用 rtplayer 的接口来开发播放器应用程序,方便播放器开发人员快速正确地开发,以及播放器测试人员如何根据该文档对 rtplayer 播放器进行验证测试。
    的头像 发表于 10-10 16:28 2475次阅读
    <b class='flag-5'>全</b><b class='flag-5'>志</b><b class='flag-5'>R128</b>基础<b class='flag-5'>组件</b><b class='flag-5'>开发指南</b>—<b class='flag-5'>RTOS</b><b class='flag-5'>多媒体</b>解码

    R128 BLE最高吞吐量测试正确配置测试方法

    R128使用前我们需要了解BLE的最高吞吐量,以方便评估相关功能的开发
    的头像 发表于 10-27 17:17 1328次阅读
    <b class='flag-5'>全</b><b class='flag-5'>志</b><b class='flag-5'>R128</b> BLE最高吞吐量测试正确配置测试方法

    R128内存泄漏调试案例分享

    硬件:R128 软件:FreeRTOS + rtplayer\_test(Cedarx)+ AudioSystem
    的头像 发表于 11-20 17:27 1014次阅读
    <b class='flag-5'>全</b><b class='flag-5'>志</b><b class='flag-5'>R128</b>内存泄漏调试案例分享

    详解R128 RTOS安全方案功能

    介绍 R128 下安全方案的功能。安全完整的方案基于标准方案扩展,覆盖硬件安全、硬件加解密引擎、安全启动、安全系统、安全存储等方面。 配置文件相关本文涉及到一些配置文件,在此进行说明。 env
    发表于 12-28 15:59

    R128入门编写HelloWorld

    本文将介绍使用 R128 开发板从串口输出 HelloWorld 的方式介绍 SDK 软件开发流程。 载入方案我们使用的开发板是 R128-
    发表于 12-29 09:39

    R128硬件设计指南

    (ANT pin)无需匹配电路,但可预留天线 PI 型匹配电路。如上图所示。为了方便天线PI型匹配电路调试,需在射频输出端口与天线间预留 0Ω电阻 WR1。如图所示。 因 R128 芯片射频前端已
    发表于 01-04 09:23

    R128 SDK架构与目录结构

    R128 S2 是提供的一款 M33(ARM)+C906(RISCV-64)+HIFI5(Xtensa) 三核异构 SoC,同时芯片内部 SIP 有 1M SRAM、8M LSPS
    发表于 01-05 10:05

    R128 Devkit开发板原理图模块介绍及使用说明

    :CH341SER.EXE 购买链接 百问科技淘宝店 - R128 DevKit 原理图模块介绍R128 模组R128 模组使用 SMT
    发表于 01-17 09:45

    R128芯片 基础组件开发指南——RTOS 多媒体解码

    RTOS 多媒体解码 介绍 FreeRTOS 下如何使用 rtplayer 的接口来开发播放器应用程序,方便播放器开发人员快速正确地开发,以
    发表于 10-10 13:52

    R128芯片应用开发案例——按键输入

    按键3脚 载入方案 我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择r128s2_module_c906 $ source envse
    发表于 10-10 14:34

    R128芯片 基础组件开发指南——RTOS 多媒体编码

    RTOS 多媒体编码 介绍 FreeRTOS 下如何使用 xrecorder 的接口来开发录制应用程序,方便录制应用开发人员快速正确地
    发表于 10-11 09:52

    R128芯片 基础组件开发指南——RTOS 多媒体编码

    RTOS 多媒体编码 介绍 FreeRTOS 下如何使用 xrecorder 的接口来开发录制应用程序,方便录制应用开发人员快速正确地
    发表于 10-12 15:49

    R128软件配置——RTOS 软件包配置

    说明 了解了menuconfig的基本操作,我们再来了解一下 RTOS 的 menuconfig 具体都有是么内容。 Build target sunxi arch 分别选择芯片的系列,对于R128
    发表于 10-20 15:31

    DshanMCU-R128s2 R128 EVT 开发套件

    针对 R128 模组,百问科技推出了 R128 EVT 开发套件作为快速开发评估工具。
    的头像 发表于 12-22 15:16 776次阅读
    DshanMCU-<b class='flag-5'>R128</b>s2 <b class='flag-5'>R128</b> EVT <b class='flag-5'>开发</b>套件

    DshanMCU-R128s2 SDK 架构与目录结构

    R128 S2 是提供的一款 M33(ARM)+C906(RISCV-64)+HIFI5(Xtensa) 三核异构 SoC,同时芯片内部 SIP 有 1M SRAM、8M LSPS
    的头像 发表于 12-22 15:57 739次阅读
    DshanMCU-<b class='flag-5'>R128</b>s2 SDK 架构与目录结构