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 開發流程
XRecordCreate()
//創建一個錄制應用XRecordSetAudioCap()
//設置音頻采集設備;可先調用RTCaptureDeviceCreate
創建。XRecordSetDataDstUrl()
//設置錄制后文件保存位置XRecordSetAudioEncodeType()
//設置音頻數據的編碼格式XRecordPrepare()
//設置 Muxer,讓 xrecorder 進入準備狀態XRecordStart()
//開始錄制XRecordStop()
//停止錄制XRecordDestroy()
//當不需要進行錄制的時候,銷毀 xrecorder
注意事項
- 在調用 XRecordSetAudioCap 設置音頻采集設備之前,需先打開音頻采集設備來獲取句柄。在 rtos 平臺可調用 libcedarx 提供的音頻采集設備控制模塊 rtosCaptureControl.c 中的
RTCaptureDeviceCreate
來創建句柄。 - recorder 應用未支持暫停錄制。
- 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文章
485瀏覽量
63497 -
音頻編碼器
+關注
關注
0文章
17瀏覽量
9216 -
緩存器
+關注
關注
0文章
63瀏覽量
11806 -
vfs
+關注
關注
0文章
14瀏覽量
5344 -
R128
+關注
關注
0文章
41瀏覽量
224
發布評論請先 登錄
相關推薦
全志R128基礎組件開發指南—RTOS多媒體解碼

詳解全志R128 RTOS安全方案功能
全志R128入門編寫HelloWorld
全志R128硬件設計指南①
全志R128 SDK架構與目錄結構
全志R128 Devkit開發板原理圖模塊介紹及使用說明
全志R128芯片 基礎組件開發指南——RTOS 多媒體解碼
全志R128芯片應用開發案例——按鍵輸入
全志R128芯片 基礎組件開發指南——RTOS 多媒體編碼
全志R128芯片 基礎組件開發指南——RTOS 多媒體編碼
全志R128軟件配置——RTOS 軟件包配置
DshanMCU-R128s2 SDK 架構與目錄結構

評論