您的位置:首页 > 编程语言 > PHP开发

dtplayer如何添加stream

2014-03-25 20:09 471 查看
目录

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,¶,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的时候会用到

[b]3 dtstream
添加步骤
[/b]

这里以刚添加的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中添加一行
#dtstreamSRCS_COMMON-$(DT_STREAM) +=dtstream/dtstream_api.cSRCS_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。

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

联系开发者:peter_future@outlook.com

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

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

由于后面随着开发的进行文章会进行细节的更新,因此为了保证读者随时读到最新的内容,文章禁止转载,多谢大家支持。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: