




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、linux c 广播流媒体文件(一) 概要实现的功能,广播流媒体文件,发送端和接收端,发送端分为200个频道,其中有一个菜单频道,和其他的数据频道,接收端通过客户端通过菜单选择数据频道,并且通过mpg123播放(二)模块划分发送端菜单发送模块: 负责发送菜单,包括频道号,以及该频道相应的描述,以供接收端选择频道媒体数据发送模块: 负责发送各个频道的媒体数据媒体资源处理模块: 负责从媒体目录中相应文件里面读取媒体数据提交给媒体数据发送模块令牌桶: 负责控制发送数据的速度,以确保不会因为发送的速度过快导致接收端缓冲区满溢出而丢包发送和接收时的数据结构程序的主体(三)实现细节3.0 发送和接收时的数
2、据包结构./src/include/proto.h./src/include/site_types.h#defien CHNNR 200 /* 频道个数 */#define LISTCHNID 0 /* 节目单的频道号 */#define MINCHNID 1 /* 最小频道号 */#define MAXCHNID (MINCHNID+CHNNR-1) /* 最大频道号 */#define MSG_CHANNEL_MAX (65536-20-8) /* 一个最大的字节数 */typedef uint8_t chnid_t; /* 频道号 */* 数据频道发送的包结构 */struct msg_
3、channel_st chnid_t id; /* MUST BETWEEN MINCHIND, MAXCHIND 频道号 */ uint8_t data1; /* 流媒体数据, 当做可边长类型使用 */ _attribute_(packed); /* 不进行结构体内存对齐,避免网络通信中,双方对齐方式不一样 */#define MSG_LIST_MAX (65536-20-8) struct msg_listentry_st /* 每个频道的频道号以及对应的频道描述 */ chnid_t id; /* MUST BETWEEN MINCHIND, MAXCHIND 频道号 */ uint16
4、_t len; /* 保存当前这个包的大小,以便得到下一个包的地址 */ uint8_t desrc1; /* 频道描述, 当做可变长字节数组 */ _attribute_(packed);/* 菜单 */struct msg_list_st /* 菜单频道的包结构 */ chnid_t id; /* MUST BE LISTCHNID 频道号 */ struct msg_listentry_st entry1; /* 各个频道的频道号以及频道信息, 当做可变长字节数组 */ _attribute_(packed);新遇到的函数或用法不要任意直接使用常量,如果这个常量有意义,那就定义宏来使用,
5、便于后期的维护和代码的可读性在两台电脑上通过网络传输数据的时候,不仅要注意大小端的问题,还要注意两台电脑之间的内存对齐问题struct list struct list *next; char data0;在32位下,sizeof(struct list)是4个字节,data里面没有元素,所以是0个字节,但是data的地址是紧贴着struct list末尾的地址的,所以可以这样使用struct list *ptr = malloc(sizeof(struct list)+20);这个结构体指针指向的空间多出20个字节来,可以通过data来得到这块内存的首地址,这样就实现了一个类似一个可变长字节
6、的结构体3.1 令牌桶./src/server/mytbf.h ./src/server/mytbf.c 大体的功能是有一个容器,存有多个令牌,当去读文件的时候先去令牌桶拿令牌,拿到多少个就读出多少个字节, 规定令牌桶的最大容量(burst),每秒钟能拿多少个令牌(字节)(cps),当前桶内剩余的令牌数(token);桶内的令牌会自动增长,每秒增长cps个令牌/* 主要的数据结构 */ struct mytbf_st int cps; /* 每秒多少字节 */ int burst; /* 桶的容量 字节数*/ int token; /* 当前桶的大小 字节数*/ pthread_mutex_t
7、 mut; /* 对token进行加锁 */ pthread_cond_t cond; /*如果取得时候,令牌桶里面没有令牌,就会阻塞在这个条件变量上*/ ;tpyedef void mytbf_t; /令牌桶的处理句柄static struct mytbf_st *tbfTBFMAX; /* 令牌桶指针数组 */static pthread_mutex_t mut_tbf = PTHREAD_MUTEX_INITIALIZER; 用于锁这个令牌桶数组static pthread_t tid_timer;static pthread_once_t init_once = PTHREAD_ONC
8、E_INIT;mytbf_t *mytbf_init(int cps, int burst); /初始化化令牌桶,设置这个令牌桶的cps和burst static void module_load(void); /加载时钟线程, static void *thr_timer_func(void *sed); /这个线程主要同于令牌桶中令牌的自增 static void *module_unload(void); /销毁和回收时钟线程,使用的是atexit调用的,当进程结束时运行 static int get_free_pos(); /从令牌桶数组中得带没有使用的令牌桶下标 返回令牌桶的地址,当
9、做句柄int mytbf_fetchtoken(mytbf_t *,int n); /从桶里面取令牌,如果有就取走,没有就阻塞在条件变量上等待int mytbf_returntoken(mytbf_t *, int n); /如果取得令牌没有用完,就把剩余的还回去int mytbf_destroy(mytbf_t *); /销毁令牌桶令牌桶指针数组的存在是为了令牌桶自增方便,把所有的令牌桶统计一下,通过一个线程轮询的方式自增,就避免每个令牌桶都使用单独的自增线程,节省资源,便于管理.当然,这显然是一个需要多个线程同时操作的变量,所以需要加锁新遇到的函数或用法 /睡眠一秒 struct time
10、spec t; t.tv_sec = 1; /m t.tv_nsec = 0; /ns /* 第一个参数是要睡多少秒,第二个参数是如果睡眠被信号打断,剩余的时间 ,放到循环里,知道1s全部执行完,当跳出循环*/ while (nanosleep(&t, &t) != 0) if (errno != EINTR) syslog(LOG_ERR, "nanosleep(): %s", strerror(errno); exit(1); /设置程序进程结束时调用的函数(返回值void,没有参数) int atexit(void (*function)(void);
11、 /在线程函数中,只执行一次这个函数(返回值void,没有参数) pthread_once_t once_control = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *once_control, void (*init_routine)(void);3.2 媒体资源处理模块./src/server/medialib.c ./src/server/medialib.h媒体文件的存储方式是在./tmp/media文件中,一个单独的文件,里面有存储着.MP3流媒体文件和desc.test文件(缺一不可),我们根据满足要求的子文件的个数来确
12、定频道的个数,频道数新定位200个 /* 主要的数据结构 */struct mlib_listentry_st /* 每个频道的描述的结构体 */ chnid_t id; /频道id char *desc; /频道的描述,也就是desc.test中的内容;struct channel_context_st /* 存储频道的内容信息*/ chnid_t id; /频道id char *desc; /频道描述 glob_t mp3glob; /在频道子目录中符合相应规则("*.mp3")的文件名的集合 int pos; /当前读取的文件名在mp3glob这个文件名的下标 off
13、_t offset; /读取当前文件的指针偏移 int fd; /当前文件的文件描述符 mytbf_t *tbf; /读这个文件的令牌桶句柄;/*API*/static struct channel_context_st channelMAXCHNID+1; /存储所有的频道内容信息的数组/* 通过媒体资源目录,得到当前的频道列表信息和频道个数 */int mlib_getchnlist(struct mlib_listentry_st *, int *); static struct channel_context_st *path2entry(const char *path) /把频道媒
14、体数据的子目录路径传进去,返回一个当前频道内容信息的结构体指针 mytbf_t *mytbf_init(int cps, int burst); /初始化这个频道的令牌桶/根据频道号找到相对应的资源文件,读到buf里,返回读取到的字节数ssize_t mlib_readchn(chnid_t cid, void* buf, ssize_t len); int mytbf_fetchtoken(mytbf_t *,int n); /从桶里面取令牌,确定可以读取的字节数 static int open_next(chnid_t id); /如果read返回值是0,说明已经读完,自动读取下一个文件
15、int mytbf_returntoken(mytbf_t *, int n); /读取内容成功,如果又没用完的令牌,就还回去int mlib_freechnlist(struct mlib_listentry_st *); /释放channel数组资源函数的实现细节int mlib_getchnlist(struct mlib_listentry_st *result, int *resnum) 通过snprintf(path, PATHSIZE, "%s/*", server_conf.media_dir);这一步拼接出"/var/media/*"带
16、有通配符字符串,server_conf.media_dir这个就是默认的资源路径("/var/media")(绝对路径) 再使用glob(path, 0, NULL, &globres)得到符合这个通配符的所有文件名信息,放到globres里面 再根据globres.gl_pathc符合条件的文件路径个数确定一共需要创建的频道个数,在malloc内存相应的菜单项内存 通过轮询globres.gl_pathvi,范围globres.gl_pathc,来遍历每个频道的媒体文件, 再通过res = path2entry(globres.gl_pathvi);获取每个频道的
17、内容(id,描述,流媒体文件等), 然后把path2entry返回的内容转化成mlib_listentry_st,都放到一个数组中 然后把频道的菜单列表和菜单的大小传出去.ssize_t mlib_readchn(chnid_t cid, void* buf, ssize_t len); 读取媒体数据 tbfsize = mytbf_fetchtoken(channelid.tbf, size);首先从;令牌桶获取令牌 len = pread(channelid.fd, buf, tbfsize, channelid.offset); 从offset的偏移位置读取数据 mytbf_return
18、token(channelid.tbf, tbfsize-len);把剩余的令牌换回去新遇到的函数或用法#include <glob.h>int glob(const char *pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob);typedef struct size_t gl_pathc; /*gl_pathv里面有多少个符合规则的文件名 Count of paths matched so far */ char *gl_pathv; /* 字符串的内容 List
19、of matched pathnames. */ size_t gl_offs; /* Slots to reserve in gl_pathv'. 为GLOB_DOOFS保留的 */ glob_t;/* glob函数搜索匹配函数pattern中的参数,如/*是匹配根文件下的所有文件(不包含隐藏文件,要找到隐藏文件需要从新匹配),然后将匹配出的结果存放到pglob,即第4个参数中,第二个参数能选择匹配模式,如是否排序,或者在函数第二次调用时,是否将匹配内容追加到pglob中等,第三个参数是馋看错误信息,一般置为NULL; 使用glob的递归调用可以找到系统任意路径的所有文件 */#in
20、clude <string.h>char *strdup(const char *s); strdup会先用malloc()配置与参数s字符串相同的空间大小,然后将参数s字符串的内容复制到该内存地址,然后把返回.返回的地址最后可以利用free来释放ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset); 和read差不多,不过是从offset这个偏置的位置开始读文件,而不是从文件指针位置3.3 菜单发送模块./src/server/thr_list.h ./src/server/thr_list.c 把通过媒
21、体资源处理模块得到的菜单信息通过组播发送出去,每秒一次/* 创建菜单发送线程 */int thr_list_create(struct mlib_listentry_st *listp, int nr_ent) 把listp和nr_ent传入到线程函数中去,这里是用的全局变量的形式 当前的菜单信息是struct mlib_listentry_st这个结构体的,要把它转化成菜单发送的数据包struct msg_list_st 首先根据listp确定struct msg_list_st菜单发送数据包的大小.然后malloc内存 然后把菜单项拷过去 死循环发送菜单包 间隔1s3.4 菜单发送模块给每
22、个频道创建一个子线程发送各自频道的数据int thr_channel_create(struct mlib_listentry_st *ptr) 创建一个线程函数 static void *thr_channel_func(void *ptr) 通过len = mlib_readchn(ent->id, sbufp->data, datasize);来读取流媒体内容,发送出去新遇到的函数或用法#include <sched.h> int sched_yield(void);当线程调用这个函数时,会让主动出CPU使用权,让其他线程得到执行(四) 程序的主体主要的业务流程i
23、nt serversd; /socket文件描述符,全局的struct sockaddr_in sndaddr; /组播地址/发送端的各种默认配置,都在server_conf.h以及proto.h中定义,struct server_conf_st server_conf = .rcvport = DEFAULT_RCVPORT, /端口 .mgroup = DEFAULT_MGROUP, /组播ip .media_dir = DEFAULT_MEDIADIR, /媒体目录 .ifname = DEFAULT_IF, /网卡名'eth0' .runmode = run_daemo
24、n /守护进程;主要的业务流程1. 首先是设置日志文件2. 读取命令行参数,修改相应的server_conf的值3. 是否变成守护进程4. socket环境初始化,创建socket文件,UPD,组播地址,端口号,端口可复用,5. 获取加载媒体资源,得到菜单列表, mlib_getchnlist6. 创建发送菜单的线程7. 循环创建发送各个频道数据的线程8. 主线程暂定,由各个线程向组播地址发送数据,等待信号终止程序新遇到的函数或用法守护进程需要设置信号捕捉函数,以便结束进程是不会丢失文件或数据/系统提供的日志记录接口,相应日志记录到系统的日志文件中去#include <syslog.h> void openlog(const char *ident, int option, int facility); /程序名称,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 行政管理中的流程优化案例研究试题及答案
- 行政管理的法治思维试题及答案
- 行政管理中的决策支持系统试题及答案
- 行政管理议题研究试题及答案
- 2025正规的合租房屋租赁合同样本
- 2025快餐店临时工雇佣合同
- 建筑工程现场安全管理的新方法试题及答案
- 行政管理自考实务问题试题及答案
- 2025设备产品买卖合同模板
- 2025企业茶叶收购管理经营承包合同模板
- 2025年河北省秦皇岛市海港区中考一模数学试卷(原卷版+解析版)
- 2025年注册测绘师考试测绘地理信息数据处理与应用试题
- 二手车货车合同协议书
- 测井试题及答案完整版
- 外贸英语词汇
- 中级出版专业技术人员职业资格2025年笔试题库附答案
- 江苏南通2025年公开招聘农村(村务)工作者笔试题带答案分析
- 东南地区周代冶金考古研究新进展
- 2025年浙江省衢州市中考一模英语试题(原卷版+解析版)
- 中南大学毕业答辩学术论文模板
- 专利代缴年费合同协议
评论
0/150
提交评论