首页 > 其他分享 >全志R128芯片 基础组件开发指南——RTOS 多媒体编码

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

时间:2023-10-11 10:03:02浏览次数:37  
标签:__ R128 RTOS 全志 xrecorder base cfg recorder impl

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 tell\n");
    printf("* what you want it to do.\n");
    printf("* Usage: \n");
    printf("*   cedarx_record amr 10  : this means record 10s amr music\n");
    printf("*   cedarx_record pcm 10  : this means record 10s pcm music\n");
    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, exit\n");
		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);

原贴链接:https://bbs.aw-ol.com/topic/4383/ 资源获取、问题讨论可以到全志在线开发者社区进行:https://www.aw-ol.com 全志及开发者最新动态可以关注全志在线微信公众号

标签:__,R128,RTOS,全志,xrecorder,base,cfg,recorder,impl
From: https://blog.51cto.com/u_15380233/7806742

相关文章

  • FreeRTOS 和 RT-Thread 功能差别对比
    软件定时器FreeRTOS:定时器函数在task中被调用,居于任务调度的定时器RT-Thread:定时器函数既可以配置为在task中被调用,也可以配置为在tick中断中被调用 ......
  • FreeRTOS添加计时器
    最近需要将在Linux上的代码移植到FreeRTOS上,许多系统函数运行不了,其中就包括gettimeofday,以及使用定时器的不同。FreeRTOS的时间管理首先,FreeRTOS的系统时钟节拍可以在配置文件FreeRTOSConfig.h里面设置:#defineconfigTICK_RATE_HZ((TickType_t)1000)//配置系统时......
  • FreeRTOS入门教程(队列详细使用示例)
    (文章目录)前言上篇文章我们已经讲解了队列的概念和队列相关的API函数,那么本篇文章的话就开始带大家来学习使用队列。一、队列基本使用这个例子将会创建三个任务,其中两个任务用来发送数据到队列中,另一个任务用来从队列中读取数据。voidTask1Function(void*param){ intv......
  • 十九、STM32移植FreeRTOS操作系统
    1.环境安装下载FreeRTOS源码2.移植2.1在STM32项目下创建freertos文件夹,然后在文件夹下创建inc、src、port文件夹2.2将FreeRTOS源码目录下的FreeRTOS/Source/include文件夹下的.h头文件全部复制到STM32项目的freertos/inc文件夹下2.3 将FreeRTOS源码目录下的FreeRTOS/Sourc......
  • FreeRTOS入门教程(同步与互斥)
    (文章目录)前言前几篇文章一直在围绕FreeRTOS中的任务创建,删除,优先级,调度算法进行讲解,那么从本篇文章开始将围绕同步与互斥来展开讲解。一、同步与互斥概念当多个任务或线程共享资源并发执行时,同步和互斥是两个关键的概念。1.同步(Synchronization)是指协调多个任务或线程的执......
  • FreeRTOS 原理 --- heap 堆内存的使用
    FreeRTOS一共提供了5种申请内存的方案heap1只申请不释放,内存利用率最高。申请出来的内存块,没有内存块头记录这个内存的大小,所以也无法释放,也正是没有内存块头,内存利用率高使用场景:不需要频繁申请内存heap2能申请能释放,不能合并内存块。每个内存块都有一个内存块头,有一个链表......
  • FreeRTOS 原理 --- 软件定时器
     简介 有一个定时器任务,任务内读队列。启动定时器,会向队列发送消息,定时器任务读到消息后把定时器回调函数等信息作为一个链表项插入链表。当链表有链表项,算出还剩多长时间执行定时器回调函数,这个时间作为定时器任务阻塞时间。所以定时器任务重新运行要么是时间到准备运行定时器......
  • FreeRTOS 原理 --- 任务通知
    简介任务通知核心包含是一个32位的无符号整数和一个8位的通知状态,这两个在任务控制块中,通知任务就是一个任务或者中断改写另外一个任务中的32位的无符号整数,改写这个整数的方式可以有所不同可以让这个整数加1,模拟信号量设置该整数的指定的某些位,模拟事件组直接选择覆盖或者不......
  • FreeRTOS 原理 --- 事件组
    简介信号量,队列。它们都具有一下特性:它们允许在阻塞态下等待单个事件的发送。它们在事件发送时可以取消阻塞的单个任务。事件组是FreeRTOS提供另一种事件传递的方法,它与队列和信号量的不同点:时间组允许任务在阻塞态下等待一个或多个事件。事件发生时,事件组将取消阻塞等待......
  • 第8期ThreadX视频教程:应用实战,将裸机工程移植到RTOS的任务划分,驱动和应用层交互,中断DM
    视频教程汇总帖:https://www.armbbs.cn/forum.php?mod=viewthread&tid=110519 这个是我们初学RTOS面临的最直接问题,很多时候,简单的RTOS机制明白了,API也会调用了,就是添加到RTOS后,总感觉那里不对劲,怎么使用才是正确姿势。针对这些问题,本期视频教程,我们ThreadX内核教程穿插一期实......