流媒体实现"/>
基于UDP的流媒体实现
基于UDP的流媒体实现
- 项目简介
- 原理图
- 头文件的定义
- 资源解析模块
- 节目单发送线程模块
- 频道发送模块
- 流量控制模块
- 客户端实现
- 客户端头文件
- 服务端头文件
项目简介
我们通过UDP来进行流媒体的传输,主要实现音乐的发送,通过UDP来进行组播,可以对应多个客户端,客户端方面,采用mpg123来进行音月资源的解析。
原理图
服务端采用多线程来实现节目单和节目资源的发送,客户端采用多进程,父进程接收资源,和显示节目单,在选择节目后,将资源发送给子进程,子进程来实现资源的解析。
在server端主要需要解析文件资源模块,节目单发送线程,节目资源发送线程和流量控制模块来控制资源发送的速率,接下来我将分模块讲解一下它的实现。
头文件的定义
先看一下代码吧
#ifndef _PROTO_H_
#define _PROTO_H_#include <stdint.h>#define DEFAULT_MGPPOUP "239.0.0.2" //广播地址
#define DEFAULT_RCVPORT "9000" //端口#define CHNNUM 100 //节目最大数#define LISTCHNID 0 //节目单的id#define MINCHNID 1 //最小节目号
#define MAXCHNID (MINCHNID+CHNNUM-1) //最大节目号#define MSG_CHANNEL_MAX (65536-20-8) //资源最大发送量
#define MAX_DATA (MSG_CHANNEL_MAX-sizeof(uint8_t))#define MSG_LIST_MAX (65536-20-8)
#define MAX_ENTRY (MSG_LIST_MAX-sizeof(uint8_t))struct msg_channel_st //频道信息
{uint8_t chnid; //节目iduint8_t data[1]; //传输的数据
}__attribute__((packed)); //不需要对齐struct msg_listentry_st //节目单信息
{uint8_t chnid; //节目iduint16_t len; //长度char desc[1]; //节目单内容
}__attribute__((packed)); struct msg_list_st //节目单
{uint8_t chnid; struct msg_listentry_st entry[1];}__attribute__((packed)); #endif // !_PROTO_H_
在这里我们定义了发送节目单的结构体,发送频道信息的结构体,来实现资源发送的实现,同时也方便文件的解析,我们主要通过变参的形式保存资源,因为我们不知道资源文件的大小。在这里我们也设置里一些必要条件的宏,因为这个头文件,会被所以的模块所包含,所以在这里定义一些,必要的参数,比如,组播的地址和端口和结构体的参数。
资源解析模块
我们来先看一下代码
/*medialib.h*/typedef void* mytbf_t;struct mlib_listentry_st
{uint8_t chnid; char* desc;};int mlib_getchnlist(struct mlib_listentry_st**,int*); //获取节目资源int mlib_freechnlist(struct mlib_listentry_st*); //释放文件资源int mlib_readchn(uint8_t, void*, size_t); //读取指定的节目资源
这是解析资源文件的头文件,主要就是访问资源文件夹,将节目资源的简介和节目号读取,通过在主线程传入mli_listentry_st的指针,在获取文件资源的函数中,来实现传入参数的赋值,所以获取文件资源的函数中,我们传入里结构体指针的指针,同时,我们也要获取到资源的数量,通过要发送资源的数量,我们来指定资源线程的数量。
节目单发送线程模块
先上代码
/*thr_list.h*/
#pragma once
#include "medialib.h"//创建发送线程
int thr_list_create(struct mlib_listentry_st*,int); //线程的销毁
int thr_list_destroy(void);
节目单发送线程就比较简单了,主要在主线程,我们将解析出来的文件结构体传入,之后我们创建线程,来实现节目单的定时发送,就可以了将节目单资源发送过去了。
频道发送模块
频道发送模块和节目单发送线程在定义的头文件上是差不多的,但是在这里我们增加了线程的指定销毁。因为我们要调用资源解析中用到的函数,所以我们才包含了mediali.h的头文件。
#pragma once
#include "medialib.h"int thr_channel_create(struct mlib_listentry_st*); int thr_channel_destroy(struct mlib_listentry_st*);int thr_channel_destroyall(void);
流量控制模块
/*mytbf.h*/
#pragma once#define MYTBF_MAX 1024
typedef void* mytbf_t;mytbf_t* mytbf_init(int cps, int burst); //速率和上限int mytbf_fetchtoken(mytbf_t*,int); //获取令牌int mytbt_returntoken(mytbf_t*,int); //归还令牌int mytbf_destroy(mytbf_t*); //销毁
流量控制,就是控制发送的速率,我们可以通过mytbf_init来设置每次发送的速率,通过获取令牌,来获得发送的权限,实现资源发送的速率,因为发送流媒体资源,我们要控制资源的发送的速率,来实现音乐的播放。
客户端实现
客户端头文件
#ifndef _CLIENT_H_
#define _CLIENT_H_#define DEFAULT_PLAYERCMD "/usr/bin/mpg123 - > /dev/null" struct client_conf_st
{const char* rcvport;const char* mgroup;const char* player_cmd;
};int writen(int fd, const void* vptr, int n);extern struct client_conf_st client_conf;#endif // !_CLIENT_H_
我们来创建一个客户端的结构体,来指定接收端口,组播地址和媒体解析器,同时我们会在客户端fork出子进程,来调用解析器,解析父进程发送过来的资源文件,父进程主要的功能就是,接收节目单资源和频道资源,并且选择指定的频道资源,将资源发送个子进程。
服务端头文件
#pragma once
#define DEFAULT_MEDIADIR "/var/media"
#define DEFAULT_IF "ens33"
#include <netinet/ip.h>
#include <arpa/inet.h>enum
{run_daemon = 1,run_foregrounp};static int daemonize();struct server_conf_st
{const char* rcvport; //绑定的ipconst char* mgroup; //广播地址const char* media_dir; //资源文件const char* runmode; //运行模式const char* ifname; //网卡信息
};extern int listenfd;
extern struct sockaddr_in clientaddr;
extern socklen_t clientaddr_len;
在后续我会将我的项目发送到github中,在这里就不在多说了。
更多推荐
基于UDP的流媒体实现
发布评论