首页 > 其他 > 详细

dtplayer如何添加stream

时间:2014-03-25 21:47:42      阅读:462      评论:0      收藏:0      [点我收藏+]

目录

1 dtstream介绍

2 dtstream接口说明

3 dtstream添加步骤


stream模块和demuxer模块是播放器的数据入口,本篇主要介绍如何添加一个新的stream类型,下篇会介绍如何添加一个新的demuxer。


1 dtstream 介绍


dtstream的主要作用是依据传入的参数. 建立与源的连接,并为dtdemuxer模块提供服务,主要接口有read seek等,

在设计之初由于ffmpeg中demuxer和stream结合的比较紧密,本来是没有这个模块的,后来考虑到整个播放器的模块化以及后续的扩展性

添加了dtstream的模块,用户可以直接在dtstream框架下添加新的stream,如添加一些ffmpeg本身不支持的stream类型: rtsp 等

但由于还未提供dtdemuxer中的demuxer_ffmpeg直接使用dtsteam的接口,后面会添加此功能。


与dtstream模块相关的源代码主要在dtstream目录下,主要有

dtstream_api.c     对外接口

dtstream.c           对下层stream实现的封装,包括选择stream、调用对应stream的方法等

stream/*.c           实际stream的实现,如file rtsp http等


下面介绍下dtstream的代码执行过程。

1.1 register

在播放器开头的初始化阶段,会register所有的stream demuxer decoder render等,具体代码在dtplayer/dtplayer.c中

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

具体实现在dtstream/dtstream.c中

static void stream_register_all ()
{    
    REGISTER_STREAM (FILE, file);
}

由于现在只添加了一个file类型,因此只注册了一个

注册过程也很简单,类似ffmpeg中的av_register_all,就是将所有的stream挂载到一个全局的链表g_stream中


1.2 dtstream_open

dtstream是依托dtdemux而存在的,为dtdemux提供服务,因此后续的控制等都由dtdemux来发起,代码在dtdemux/dtdemuxer.c:

int demuxer_open (dtdemuxer_context_t * dem_ctx)

{

    int ret = 0;

    /* open stream */

    dtstream_para_t para;

    para.stream_name = dem_ctx->file_name;

    ret = dtstream_open(&dem_ctx->stream_priv,&para,dem_ctx);

    if(ret != DTERROR_NONE)

    {

        dt_error (TAG, "stream open failed \n");

        //return -1;

    }

    else

    {

        //open stream failed, then we use ffmpeg only

        int64_t old_pos = dtstream_tell(dem_ctx->stream_priv);

        ret = buf_init(&dem_ctx->probe_buf,PROBE_BUF_SIZE);

        if(ret < 0)

            return -1;

        ret = dtstream_read(dem_ctx->stream_priv,dem_ctx->probe_buf.data,PROBE_BUF_SIZE);

        if(ret <= 0)

            return -1;

        dem_ctx->probe_buf.level = ret;

        dtstream_seek(dem_ctx->stream_priv,old_pos,SEEK_SET);

    }

......

    return 0;

}

可以看到在demuxer_open的实现中会构造并初始化dtstream_para_t结构体变量,并调用dtstream_open来初始化dtstream模块

【dtstream/dtstream_api.c】

int dtstream_open (void **priv, dtstream_para_t * para, void *parent)
{
    dtstream_context_t *ctx = (dtstream_context_t *)malloc(sizeof(dtstream_context_t));
    if(!ctx)
    {
        dt_error(TAG,"STREAM CTX MALLOC FAILED \n");
        return -1;
    }
    memset (ctx, 0, sizeof (dtstream_context_t));
    ctx->stream_name = para->stream_name;
    if(stream_open(ctx) == -1)
    {
        dt_error(TAG,"STREAM CONTEXT OPEN FAILED \n");
        free(ctx);
        *priv = NULL;
        return -1;
    }
    *priv = (void *)ctx;
    ctx->parent = parent;
    dt_info(TAG,"STREAM CTX OPEN SUCCESS\n");
    return 0;
}

简单说下,初始化首先构造一个context,之后是选择stream,并调用stream_open

stream_open就是实际stream的封装,这里就不展开了,比较简单。

初始化成功后,基本上就可以为dtdemux提供服务了,剩下的接口大部分播放器都是一致的,read seek skip close等

这里后面介绍实际的stream的时候再说


2 dtstream api介绍


dtstream_api.c dtstream.c可以认为是一个对外,一个对内,对外提供dtstream接口

对内管理内部的各个stream,并在初始化的时候选择合适的stream提供实际的服务。

而这里只介绍 一个典型的stream应该实现哪些接口

在dtstream模块中,主要是实现一个stream_wrapper_t的结构体,定义:

【dtstream/dtstream.h】

typedef struct stream_wrapper
{
    char *name;
    int id;
    int (*open) (struct stream_wrapper * wrapper,char *stream_name);
    int64_t (*tell) (struct stream_wrapper * wrapper);
    int (*read) (struct stream_wrapper * wrapper, uint8_t *buf,int len);
    int (*seek) (struct stream_wrapper * wrapper, int64_t pos, int whence);
    int (*close) (struct stream_wrapper * wrapper);
    void *stream_priv;          // point to priv context
    void *parent;               // point to parent, dtstream_context_t
    stream_ctrl_t info;
    struct stream_wrapper *next;
} stream_wrapper_t;

主要是函数指针,各个函数的含义为

open 初始化 

tell 返回当前读取位置

read 在当前位置读取len大小的数据到buf中,返回实际读取的数据量

seek 在流中seek,whence和pos的定义与read的系统调用定义一致

close 关闭stream,释放内存等

各个成员含义为:

name: stream名称

id: stream id

stream_priv 实际stream可定义自己独有的结构保存在此成员中

parent:上一级管理员,这里为dtstream_context_t

info:一些控制信息,如eof等,这样判断eof就不必到实际的stream中,提高了效率

next: 所有的stream都挂载在一条链表中,这里next指的当前stream的下一个,在register的时候会用到


3 dtstream 添加步骤


这里以刚添加的stream_file为例,介绍如何在dtstream框架下添加一个新的stream

具体源文件在dtstream/stream/stream_file.c

注意这里不讲细节,只讲如何添加一个stream并在dtplayer中跑起来


3.1 定义结构体

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

stream_wrapper_t stream_file = {
    .name = "File",
    .id = STREAM_FILE,
    .open = stream_file_open,
    .read = stream_file_read,
    .seek = stream_file_seek,
    .close = stream_file_close,
};

这里可以看下示例建立的结构提变量需要按照一定的明明规则,后面会讲回身么,必须是stream_** 格式

这里每添加一个新的stream,需要定义一个ID,统一定义的位置在dtstream/dtstream.h

typedef enum{
    STREAM_INVALID = -1,
    STREAM_FILE,
    STREAM_FFMPEG,
    STREAM_UNSUPPORT
}stream_format_t;

现在只实现了file ,下一步会添加ffmpe封装


3.2 在注册方法中加入file

代码在dtstream/dtstream.c中

static void stream_register_all (){    REGISTER_STREAM (FILE, file);}

看下宏实现

#define REGISTER_STREAM(X,x) \    if(ENABLE_STREAM_##X)    \    {                         \        extern stream_wrapper_t stream_##x; \        register_stream(&stream_##x);     \    }


之前说stream命名需按照规则,作用就在此处,在注册的时候是按照统一的模式进行注册的,

参数FILE 主要是构造成ENABLE_STREAM_FILE来判断在编译的时候是否配置成可用,如果不可用,则不注册

参数file主要是引用实际的变量,即stream_file调用registere_stream进行注册

static stream_wrapper_t *g_stream = NULL;

static void register_stream (stream_wrapper_t * wrapper)

{

    stream_wrapper_t **p;

    p = &g_stream;

    while (*p != NULL)

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

    *p = wrapper;

    wrapper->next = NULL;

} static int stream_select (dtstream_context_t * stm_ctx) {    if (!g_stream)        return -1;    stm_ctx->stream = g_stream; // select the only one    return 0; }

实现也比较简单,就是挂载在g_stream的链表中,挂载好后,就可以通过stream_select选择对应的stream了

选择好了之后后面调用dtstream_api的接口时,就会路由到实际注册的stream的实现或者通过封装相应的实现来完成。


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

这部分就不展开了,但凡自己添加stream的同仁都知道要实现什么样的接口。

实在不清楚的可以邮件我:)


3.4 配置

最后一步是配置编译系统

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

#dtstream

SRCS_COMMON-$(DT_STREAM) +=dtstream/dtstream_api.c

SRCS_COMMON-$(DT_STREAM) +=dtstream/dtstream.c

+ SRCS_COMMON-$(DT_STREAM_FILE) +=dtstream/stream/stream_file.c

这样在编译整个项目的时候才能编译到你添加的源文件


再有就是配置config.mk,这里主要就是两部分

+DT_CFLAGS += -DENABLE_STREAM_FILE=1
+DT_STREAM_FILE=yes

其中第一个是用于注册的时候用,第二个是上面Makfile中配置将代码加入编译


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


最后介绍下stream选取的思路, 后面会添加比较多的stream,选择的依据是,最先匹配优先,当都不匹配的时候会选择ffmpeg。


联系开发者:peter_future@outlook.com
开源中国地址:http://www.oschina.net/p/dtplayer

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


dtplayer如何添加stream,布布扣,bubuko.com

dtplayer如何添加stream

原文:http://blog.csdn.net/u011350110/article/details/22093145

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!