dtplayer如何添加demuxer

目录

1 dtdemux介绍

2 demux接口说明

3 demuxer添加步骤


上篇文章,介绍了如何在dtplayer框架下添加一个新的stream,后面又添加了封装ffmpeg的stream,使得dtplayer支持的stream类型大大增加。

而且用户添加demuxer的时候测试也方便了很多(未添加stream_ffmpeg之前只能测试本地文件,现在已经支持部分网络流)。

本篇主要介绍下dtdemuxer,主要的接口等,还会以aac demuxer为例介绍如何在dtplayer框架下添加一个新的demuxer。


1 dtdemux模块介绍

dtdemux模块在整个播放框架中起着至关重要的作用,主要工作是对stream过来的流进行解析,向上为player模块提供完整帧数据。

与dtdemuxer相关的文件主要在dtdemux目录下,主要有:

dtdemuxer_api.c  dtdemux模块对外提供的api

dtdemuxer.c        中间模块,向上支持对外接口,向下管理实际的demuxer

demuxer/*.c        实际demuxer的实现,当然最重要的是demuxer_ffmpeg.c


先介绍下对外的接口,可参考dtdemuxer_api.h 头文件中的声明


int dtdemuxer_open (void **priv, dtdemuxer_para_t * para, void *parent);

dt_media_info_t *dtdemuxer_get_media_info (void *priv);

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

int dtdemuxer_seekto (void *priv, int timestamp);

int dtdemuxer_close (void *priv);

接口非常简单,简单介绍下

dtdemuxer_open 初始化demuxer,申请资源等,同时在此接口中会open stream

dtdemuxer_get_media_info 获取流信息,包括audio track , video track 等信息

dtdemuxer_read_frame 读取一帧数据

dtdemuxer_seekto 实现seek操作,player等获取到seek命令后实际执行的是demuxer模块

dtdemuxer_close 播放结束时调用close进行资源的释放当


在看下dtdemuxer.h中提供的接口


void demuxer_register_all();

int demuxer_open (dtdemuxer_context_t * dem_ctx);

int demuxer_read_frame (dtdemuxer_context_t * dem_ctx, dt_av_frame_t * frame);

int demuxer_seekto (dtdemuxer_context_t * dem_ctx, int timestamp);

int demuxer_close (dtdemuxer_context_t * dem_ctx);

基本与对外的接口是一致的,dtdemuxer负责管理实际的demuxer操作,对外的接口都是通过封装dtdemuxer的方法来实现的,具体可参考实际代码

这里就不介绍了。


下面介绍下实际的工作流程

dtdemux模块主要是由dtplayer模块进行管理,由dtplayer的方法负责调用。这里根据实际代码看下:

1.1 register

使用之前需要先注册

入口在dtplayer/dtplayer_api.c


int dtplayer_init (void **player_priv, dtplayer_para_t * para)

{

    int ret = 0;

    if (!para)

        return -1;

    player_register_all();

......

    *player_priv = dtp_ctx;

    return 0;

}

->dtplayer/dtplayer.c

void player_register_all(){    stream_register_all();    demuxer_register_all();    audio_register_all();    video_register_all();}

->dtdemux/dtdemuxer.c


void demuxer_register_all ()

{

    REGISTER_DEMUXER (AAC, aac);

    REGISTER_DEMUXER (FFMPEG, ffmpeg);

}

主要有两个demuxer, aac(可作为例子)以及ffmpeg,注册之后就可以在demuxer_select中选择并工作了。


1.2 dtdemuxer_open

入口:dtplayer.c


int player_init (dtplayer_context_t * dtp_ctx)

{

    int ret = 0;

    pthread_t tid;

    set_player_status (dtp_ctx, PLAYER_STATUS_INIT_ENTER);

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

    dtp_ctx->file_name = dtp_ctx->player_para.file_name;

    dtp_ctx->update_cb = dtp_ctx->player_para.update_cb;

    /* init server */

    player_server_init (dtp_ctx);

    dtdemuxer_para_t demux_para;

    demux_para.file_name = dtp_ctx->file_name;

    ret = dtdemuxer_open (&dtp_ctx->demuxer_priv, &demux_para, dtp_ctx);

    if (ret < 0)

    {

        ret = -1;

        goto ERR1;

    }

    dtp_ctx->media_info = dtdemuxer_get_media_info (dtp_ctx->demuxer_priv);    ......

}


这里在player_init会执行两个操作 open 以及 get_medie_info 分别代表初始化以及获取媒体信息,进入dtdemuxer看下实现

int dtdemuxer_open (void **priv, dtdemuxer_para_t * para, void *parent)

{

    dtdemuxer_context_t *dem_ctx = (dtdemuxer_context_t *) malloc (sizeof (dtdemuxer_context_t));

    if (!dem_ctx)

    {

        dt_error (TAG, "demuxer context malloc failed \n");

        return -1;

    }

    memset (dem_ctx, 0, sizeof (dtdemuxer_context_t));

    dem_ctx->file_name = para->file_name;

    if (demuxer_open (dem_ctx) == -1)

    {

        dt_error (TAG, "demuxer context open failed \n");

        free(dem_ctx);

        return -1;

    }

    *priv = (void *) dem_ctx;

    dem_ctx->parent = parent;

    dt_info (TAG, "demuxer context open success \n");

    return 0;

}

这里open主要是申请dtdemuxer_context_t结构体变量,传递参数(文件名),并调用dtdemuxer.c中的demuxer_open方法,这里只讲入口

细节读者请自行阅读代码。


dt_media_info_t *dtdemuxer_get_media_info (void *priv)

{

    dtdemuxer_context_t *dem_ctx = (dtdemuxer_context_t *) priv;

    return &(dem_ctx->media_info);

}

再看dtdemuxer_get_media_info,这里只是直接返回了保存在dtdemuxer_context_t中的mediainfo变量,原因是在open的时候 已经完成了对文件的解析

并将解析结果存放在了dem_ctx->media_info结构体中。


1.3 dtdemuxer_read_frame & dtdemuxer_seekto

当初始化完成后,dtdemuxer便可以提供服务了,主要是提供数据读取和seek的功能

read_frame的发起在dtplayer_io.c中,dtplayer会单独启动一个线程进行数据的读取,简化的代码如下:


static void *player_io_thread (dtplayer_context_t * dtp_ctx)

{

    io_loop_t *io_ctl = &dtp_ctx->io_loop;

    dt_av_frame_t frame;

    int frame_valid = 0;

    int ret = 0;

    do

    {

        usleep (10000);

        ret = player_read_frame (dtp_ctx, &frame);

        ret = player_write_frame (dtp_ctx, &frame);

    }

    while (1);

  QUIT:

    dt_info (TAG, "io thread quit ok\n");

    pthread_exit (NULL);

    return 0;

}

在播放器启动后,启动此线程后dtplayer就通过player_read_frame从dtdemux中读取数据并将数据通过player_write_frame写入到dtport模块中保存起来


static int player_read_frame (dtplayer_context_t * dtp_ctx, dt_av_frame_t * frame)

{

    return dtdemuxer_read_frame (dtp_ctx->demuxer_priv, frame);

}


实际就是通过dtdemuxer_read_frame 对外接口来完成的,而对外接口(dtdemuxer_api.c中)则是通过dtdemuxer.c中的内部实现完成

【dtdemuxer_api.c】


int dtdemuxer_read_frame (void *priv, dt_av_frame_t * frame)

{

    dtdemuxer_context_t *dem_ctx = (dtdemuxer_context_t *) priv;

    return demuxer_read_frame (dem_ctx, frame);

}

[dtdemuxer.c]


int demuxer_read_frame (dtdemuxer_context_t * dem_ctx, dt_av_frame_t * frame)

{

    demuxer_wrapper_t *wrapper = dem_ctx->demuxer;

    return wrapper->read_frame (wrapper, frame);

}

从代码可以看出,实际工作的是dtdemuxer/demuxer/*.c等实际的demuxer来完成的。

这里不再跟进了,具体的细节读者应该也不会关心。


seek的原理及流程与read_frame非常类似,理解了read_frame相信读者阅读seek流程也不会有困难。

最后的dtdemuxer_close就不说了,作用是释放资源。


下面介绍下实际demuxer对应的接口


2 demux接口说明

移植demuxer最主要的是实现一个结构体及其函数指针

【dtdemux/dtdemuxer.h】


typedef enum{

    DEMUXER_INVALID = -1,

    DEMUXER_AAC,

    DEMUXER_FFMPEG,

    DEMUXER_UNSUPPORT,

}demuxer_format_t;

typedef struct demuxer_wrapper

{

    char *name;

    int id;

    int (*probe) (struct demuxer_wrapper *wrapper,void *parent);

    int (*open) (struct demuxer_wrapper * wrapper);

    int (*read_frame) (struct demuxer_wrapper * wrapper, dt_av_frame_t * frame);

    int (*setup_info) (struct demuxer_wrapper * wrapper, dt_media_info_t * info);

    int (*seek_frame) (struct demuxer_wrapper * wrapper, int timestamp);

    int (*close) (struct demuxer_wrapper * wrapper);

    void *demuxer_priv;         // point to priv context

    void *parent;               // point to parent, dtdemuxer_context_t

    struct demuxer_wrapper *next;

} demuxer_wrapper_t;

其中demuxer_format_t是支持的demuxer, 也就是对应demuxer_wrapper_t中的id成员

后面介绍写接口含义

probe: 检测函数,与ffmpeg中的probe含义一致

open: 初始化,并完成文件参数解析

setup_info: 组装信息到dt_media_info_t ,demuxer中的信息并不能直接为dtplayer所用,

                     需要构造dtplayer自己的文件参数信息,也就是dt_medie_info_t变量。理由很简单,

                     比如之前用ffmpeg中的libavformat作为demuxer,那么可以使用avformatcontext来保存信息

                     但dtplayer支持去除ffmpeg依赖,因此需要有个统一的接口来实现。

read_frame:读取一帧数据,这里移植demuxer的时候需要注意这里必须返回完整帧

seek_frame: seek到timestamp (单位是秒)

close: 播放结束,释放资源

demuxer_priv: demuxer的私有信息

parent: 这里代表dtdemuxer_context_t



3 demuxer添加步骤

代码位置:添加的demuxer代码需统一存放在dtdemux/demuxer/下,方便管理

这里以aac demuxer为例,介绍添加一个demuxer的具体示例

代码位置:dtdemux/demuxer/demuxer_aac.c


3.1 定义结构体

首先是定义一个demuxer_wrapper_t的结构体,并将其链接到dtdemuxer的全局链表中


demuxer_wrapper_t demuxer_aac = {

    .name = "aac demuxer",

    .id = DEMUXER_AAC,

    .probe = demuxer_aac_probe,

    .open = demuxer_aac_open,

    .read_frame = demuxer_aac_read_frame,

    .setup_info = demuxer_aac_setup_info,

    .seek_frame = demuxer_aac_seek_frame,

    .close = demuxer_aac_close

};


建立结构提变量与dtstream类似需要按照一定的规则,必须是demuxer_** 格式

这里每添加一个新的demuxer,需要定义一个ID,统一定义的位置在dtdemux/dtdemuxer.h之前见过,再贴一次

typedef enum{

    DEMUXER_INVALID = -1,

    DEMUXER_AAC,

    DEMUXER_FFMPEG,

    DEMUXER_UNSUPPORT,

}demuxer_format_t;

现在只实现了aac 和ffmpeg的支持


3.2 在注册方法中加入demuxer

[dtdemux/dtdemuxer.c]


#define REGISTER_DEMUXER(X,x) \

   if(ENABLE_DEMUXER_##X)    \

   {                         \

       extern demuxer_wrapper_t demuxer_##x; \

       register_demuxer(&demuxer_##x);     \

   }

static demuxer_wrapper_t *g_demuxer = NULL;

static void register_demuxer (demuxer_wrapper_t * wrapper)

{

    demuxer_wrapper_t **p;

    p = &g_demuxer;

    while (*p != NULL)

        p = &((*p)->next);

    *p = wrapper;

    wrapper->next = NULL;

}

void demuxer_register_all ()

{

    REGISTER_DEMUXER (AAC, aac);

    REGISTER_DEMUXER (FFMPEG, ffmpeg);

}

比较简单即将新的demuxer挂载到全局链表: g_demuxer中

下面dtdemuxer模块会自动调用probe等选择对应的demuxer


static int demuxer_select (dtdemuxer_context_t * dem_ctx)

{

    if (!g_demuxer)

        return -1;

    int score = 0;

    demuxer_wrapper_t *entry = g_demuxer;

    while(entry != NULL)

    {

        score = (entry)->probe(entry,dem_ctx);

        if(score == 1)

            break;

        entry = entry->next;

    }

    if(!entry)

        return -1;

    dem_ctx->demuxer = entry;

    dt_info(TAG,"SELECT DEMUXER:%s \n",entry->name);

    return 0;

}


3.3 实现demuxer_wrapper_t的各个部分的功能

实现demuxer的所有接口,可直接参考aac的实现



3.4 配置

最后一步是配置编译系统

首先是将源文件加到编译系统中,这里是makefile中添加一行

SRCS_COMMON-$(DT_DEMUXER) +=dtdemux/demuxer/demuxer_aac.c

由于aac的添加没有以来其他库,因此默认是enable的


再有就是配置config.mk,这里由于aac默认enable因此不需要添加开关

这里说明一下: 当添加的模块需要以来第三方库,那么请一定在config.mk中添加开关控制,因为用户的机器可能没有安装此库,导致运行或者安装失败


至此一个demuxer就添加成功了。


下面会介绍如何在dtplayer框架下添加一个audio decoder, 会以faad为例。


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

联系开发者:[email protected]

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

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


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

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