dtplayer如何添加audio decoder

目录

1 dtaudio介绍

2 audio decoder接口说明

3 audio decoder添加步骤


本篇文章主要介绍如何在dtaudio的模块中添加一个decoder。 之前我们添加了file stream 以及 aac demuxer。

本次添加一个aac 的decoder,利用第三方库faad2 来解码aac

这样用户就可以体验一下去除ffmpeg的dtplayer。之前没有decoder dtplayer不得不借助ffmpeg的libavcode来解码

完成添加aac decoder之后,就可以变身成为一个简单的 aac 音乐播放器。


1 dtaudio介绍

先介绍下dtaudio模块,之前在结构设计的文章中有对dtaudio的定位。dtaudio模块是dtplayer框架中的音频处理模块,

涵盖了音频解码、音频后处理(未添加)、音频输出,本篇主要介绍音频解码器的添加。

与dtaudio相关的代码主要在dtaudio目录下,主要有

dtaudio_api.c              dtaudio模块的对外接口

dtaudio.c                    dtaudio中间模块,向上支持对外接口,向下管理decoder render

dtaudio_decoder.c       decoder管理模块

dtaudio_filter.c            filter管理模块(暂未实现)

dtaudio_output.c         render模块

audio_decoder/           实际decoder的代码实现

audio_out/                  实际render的代码实现


【dtaudio_api.h】


int dtaudio_init (void **audio_priv, dtaudio_para_t * para, void *parent);

int dtaudio_start (void *audio_priv);

int dtaudio_pause (void *audio_priv);

int dtaudio_resume (void *audio_priv);

int dtaudio_stop (void *audio_priv);

int64_t dtaudio_get_pts (void *audio_priv);

int dtaudio_drop (void *audio_priv, int64_t target_pts);

int64_t dtaudio_get_first_pts (void *audio_priv);

int dtaudio_get_state (void *audio_priv, dec_state_t * dec_state);

int dtaudio_get_out_closed (void *audio_priv);

dtaudio对外提供的接口,具体含义如下

dtaudio_init  初始化audio模块,包括后面的decoder和render

dtaudio_start 启动播放,  render模块开始输出

dtaudio_pause dtaudio_resume  暂停 恢复

dtaudio_stop 结束播放

dtaudio_get_pts 获取当前audio pts

dtaudio_drop 播放开头丢掉部分数据

dtaudio_get_first_pts 判断第一帧pts是否已经拿到

dtaudio_get_state 获取audio状态,主要是decoder以及render的状态

dtaudio_get_out_closed 判断数据是否播放完毕,在播放结束的时候使用


【dtaudio.h】

void audio_register_all();

int audio_read_frame (void *priv, dt_av_frame_t * frame);

int audio_output_read (void *priv, uint8_t * buf, int size);

int64_t audio_get_current_pts (dtaudio_context_t * actx);

int64_t audio_get_first_pts (dtaudio_context_t * actx);

int audio_drop (dtaudio_context_t * actx, int64_t target_pts);

void audio_update_pts (void *priv);

int audio_get_dec_state (dtaudio_context_t * actx, dec_state_t * dec_state);

int audio_get_out_closed (dtaudio_context_t * actx);

int audio_start (dtaudio_context_t * actx);

int audio_pause (dtaudio_context_t * actx);

int audio_resume (dtaudio_context_t * actx);

int audio_stop (dtaudio_context_t * actx);

int audio_init (dtaudio_context_t * actx);

audio_register_all负责注册所有的解码器和render

audio_read_frame 从dthost模块读取一包数据解码

audio_output_read render模块从audio 缓冲区读取pcm 输出

audio_get_current_pts 计算当前audio pts

audio_get_first_pts 计算第一包pts是否已经拿到

audio_drop 播放开头丢掉部分数据

audio_update_pts 刷新dthost中的audio pts信息

audio_get_dec_state 获取解码器状态

audio_get_out_closed 判断数据是否render完毕

audio_start audio_pause audio_resume audio_stop audio_init 状态机控制


这里从api可以看出dtaudio_api 主要是对dtaudio的封装,实际的实现由dtaudio完成。同理dtaudio分别又是对dtaudio_decoder dtaudio_filter dtaudio_output的封装

再进一步,dtaudio_decoder是对audio_decoder下面实际解码器的封装, dtaudio_output是对audio_out下实际render的封装。

一层一层封装下来,实现音频处理的功能。


这里就不展开讨论audio的完整处理流程了,只介绍写decoder的工作流程

1.1 register

在decoder工作之前,首先需要注册,注册的代码在dtaudio.c中

void audio_register_all(){    
    adec_register_all();    
    aout_register_all();
}

这里主要是将decoder和render全部注册

【dtaudio_decoder.c】

void adec_register_all ()
{
    /*Register all audio_decoder */
    REGISTER_ADEC (FAAD, faad);
    REGISTER_ADEC (FFMPEG, ffmpeg);
    return;
}

这里可以看到,主力还是通过ffmpeg来解码,这里我们也添加了faad来解码aac

1.2 decoder select

【dtaudio_decoder.c】

audio_decoder_init->select_audio_decoder

static int select_audio_decoder (dtaudio_decoder_t * decoder)
{
    dec_audio_wrapper_t **p;
    dtaudio_para_t *para = &(decoder->aparam);
    p = &first_adec;
    while (*p != NULL)
    {
        if ((*p)->afmt != para->afmt && (*p)->afmt != AUDIO_FORMAT_UNKOWN)
            p = &(*p)->next;
        else                    //fmt found, or ffmpeg found
            break;
    }
    if (!*p)
    {
        dt_info (DTAUDIO_LOG_TAG, "[%s:%d]no valid audio decoder found afmt:%d\n", __FUNCTION__, __LINE__, para->afmt);
        return -1;
    }
    decoder->dec_wrapper = *p;
    dt_info (TAG, "[%s:%d] select--%s audio decoder \n", __FUNCTION__, __LINE__, (*p)->name);
    return 0;
}

这里在audio_decoder_init的方法中会调用select_audio_decoder

选择方法比较简单,若有其他解码器并符合格式需求,就选择,否则选择ffmpeg来解码

相信大家都希望使用自己移植的解码器来工作。


1.3 decode流程

当初始化完毕后,会启动audio 解码线程,代码如下


#define MAX_ONE_FRAME_OUT_SIZE 192000

static void *audio_decode_loop (void *arg)

{

    ......

    dt_info (TAG, "[%s:%d] AUDIO DECODE START \n", __FUNCTION__, __LINE__);

    do

    {

        ret = audio_read_frame (decoder->parent, &frame);        

      DECODE_LOOP:       

        /*decode frame */;

        used = dec_wrapper->decode_frame (dec_wrapper, pinfo);        

      REFILL_BUFFER:        

        ret = buf_put (out, pinfo->outptr + fill_size, pinfo->outlen);        

    }

    while (1);

  EXIT:

    dt_info (TAG, "[file:%s][%s:%d]decoder loop thread exit ok\n", __FILE__, __FUNCTION__, __LINE__);

    /* free adec_ctrl_t buf */

    if(pinfo->outptr)

        free(pinfo->outptr);

    pinfo->outlen = pinfo->outsize = 0;

    pthread_exit (NULL);

    return NULL;

}

这里只将解码器循环核心的部分贴了下,

此处是audio_decode_loop 循环执行: 读包 - 解码 - 缓存  等操作来完成解码,

可以想象,render模块同样有个线程执行: 读缓存 - render的操作来完成pcm的消耗


其中,dec_wrapper代表的是实际的解码器,通过调用decode_frame来解码。


2 audio decoder接口说明

下面介绍下audio decoder部分的结构提及接口

移植audio decoder主要是实现dec_audio_wrapper_t的接口及实现

【dtaudio_decoder.h】

typedef enum

{

    ADEC_STATUS_IDLE,

    ADEC_STATUS_RUNNING,

    ADEC_STATUS_PAUSED,

    ADEC_STATUS_EXIT

} adec_status_t;

typedef struct{

    uint8_t *inptr;

    int inlen;

    int consume;

    uint8_t *outptr;

    int outsize; // buffer size

    int outlen;  // buffer level

    int info_change;

    int channels;

    int samplerate;

    int bps;

}adec_ctrl_t;

typedef struct dec_audio_wrapper

{

    int (*init) (struct dec_audio_wrapper * wrapper,void *parent);

    int (*decode_frame) (struct dec_audio_wrapper * wrapper, adec_ctrl_t *pinfo);

    int (*release) (struct dec_audio_wrapper * wrapper);

    char *name;

    audio_format_t afmt;        //not used, for ffmpeg

    int type;

    void *adec_priv;

    struct dec_audio_wrapper *next;

    void *parent;

} dec_audio_wrapper_t;

简单介绍下接口

init 初始化

decode_frame 解码一包数据

release 释放资源

name 解码器的名称

afmt 解码器格式

adec_priv 解码器私有数据

parent: 这里是dtaudio_decoder_t


函数指针中的参数这里介绍下:

wrapper: 本身

parent: dtaudio_decoder_t变量

pinfo: adec_ctrl_t 结构体变量,比较重要,移植解码器需仔细研究,作用是提供解码器需要的参数: 输入buf 输出buf 输入size 等

详细介绍下adec_ctrl_t 的成员

uint8_t *inptr;     输入buf
int inlen;          输入长度
int consume;        输入buf已经消耗了多少
uint8_t *outptr;    输出buf
int outsize;        输出buf最大长度
int outlen;         输出buf 
int info_change;    音频参数是否发生了改变
int channels;       当前声道数
int samplerate;     当前采样率
int bps;            当前bps(暂时没作用)


3 添加步骤

代码位置:添加的audio decoder代码需统一存放在dtaudio/audio_decoder/下,方便管理

这里以faad为例,介绍添加一个audio decoder的具体示例

代码位置:dtaudio/audio_decoder/dec_audio_faad.c


3.1 定义结构体

【dec_audio_faad.c】


dec_audio_wrapper_t adec_faad_ops = {

    .name = "faad audio decoder",

    .afmt = AUDIO_FORMAT_AAC,

    .type = DT_TYPE_AUDIO,

    .init = faad_init,

    .decode_frame = faad_decode,

    .release = faad_release,

};

结构体主要是初始化name id等,最主要的是实现函数指针接口,这样在dtaudio_decoder.c的 解码线程中才能调用解码


3.2 注册解码器

主要在adec_register_all中添加faad选项

void adec_register_all ()

{

    /*Register all audio_decoder */

    REGISTER_ADEC (FAAD, faad);

    REGISTER_ADEC (FFMPEG, ffmpeg);

    return;

}

3.3 实现解码器各个函数

具体可参考faad的具体实现,比较简单


3.4 配置编译

由于此aac decoder以来第三方解码器,因此需先设置开关

【config.mk】

DT_FAAD= yes

ifeq ($(DT_FAAD),yes)

DT_CFLAGS += -DENABLE_ADEC_FAAD=1

else

DT_CFLAGS += -DENABLE_ADEC_FAAD=0

endif

LDFLAGS-$(DT_FAAD) += -lfaad

这里宏定义ENABLE_ADEC_FAAD由DT_FAAD控制,因此只需要配置DT_FAAD就可以了 

然后是加上faad库依赖,这里faad的库若未安装到公共目录,请修改成绝对路径即可。


最后是加上源码支持

【Makefile】

SRCS_COMMON-$(DT_FAAD) += dtaudio/audio_decoder/dec_audio_faad.c

这里也是由DT_FAAD来控制的



至此一个audio decoder就添加成功了。

这样我们就实现了去除ffmpeg依赖的音频播放器的所有条件

stream: file

demuxer: aac

decoder : faad

render: alsa or sdl1 or sdl2


这样直接在config.mk设置DT_FFMPEG= no去除ffmpeg依赖就可以了。


源代码地址:https://github.com/peterfuture/dtplayer

联系开发者:[email protected]

blog: http://blog.csdn.net/u011350110

开源中国地址:http://www.oschina.net/p/dtplayer


由于后面随着开发的进行文章会进行细节的更新,因此为了保证读者随时读到最新的内容,文章禁止转载,多谢大家支持。

你可能感兴趣的:(dtplayer如何添加audio decoder)