




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
1、linux c 廣播流媒體文件(一) 概要實現(xiàn)的功能,廣播流媒體文件,發(fā)送端和接收端,發(fā)送端分為200個頻道,其中有一個菜單頻道,和其他的數(shù)據(jù)頻道,接收端通過客戶端通過菜單選擇數(shù)據(jù)頻道,并且通過mpg123播放(二)模塊劃分發(fā)送端菜單發(fā)送模塊: 負(fù)責(zé)發(fā)送菜單,包括頻道號,以及該頻道相應(yīng)的描述,以供接收端選擇頻道媒體數(shù)據(jù)發(fā)送模塊: 負(fù)責(zé)發(fā)送各個頻道的媒體數(shù)據(jù)媒體資源處理模塊: 負(fù)責(zé)從媒體目錄中相應(yīng)文件里面讀取媒體數(shù)據(jù)提交給媒體數(shù)據(jù)發(fā)送模塊令牌桶: 負(fù)責(zé)控制發(fā)送數(shù)據(jù)的速度,以確保不會因為發(fā)送的速度過快導(dǎo)致接收端緩沖區(qū)滿溢出而丟包發(fā)送和接收時的數(shù)據(jù)結(jié)構(gòu)程序的主體(三)實現(xiàn)細(xì)節(jié)3.0 發(fā)送和接收時的數(shù)
2、據(jù)包結(jié)構(gòu)./src/include/proto.h./src/include/site_types.h#defien CHNNR 200 /* 頻道個數(shù) */#define LISTCHNID 0 /* 節(jié)目單的頻道號 */#define MINCHNID 1 /* 最小頻道號 */#define MAXCHNID (MINCHNID+CHNNR-1) /* 最大頻道號 */#define MSG_CHANNEL_MAX (65536-20-8) /* 一個最大的字節(jié)數(shù) */typedef uint8_t chnid_t; /* 頻道號 */* 數(shù)據(jù)頻道發(fā)送的包結(jié)構(gòu) */struct msg_
3、channel_st chnid_t id; /* MUST BETWEEN MINCHIND, MAXCHIND 頻道號 */ uint8_t data1; /* 流媒體數(shù)據(jù), 當(dāng)做可邊長類型使用 */ _attribute_(packed); /* 不進行結(jié)構(gòu)體內(nèi)存對齊,避免網(wǎng)絡(luò)通信中,雙方對齊方式不一樣 */#define MSG_LIST_MAX (65536-20-8) struct msg_listentry_st /* 每個頻道的頻道號以及對應(yīng)的頻道描述 */ chnid_t id; /* MUST BETWEEN MINCHIND, MAXCHIND 頻道號 */ uint16
4、_t len; /* 保存當(dāng)前這個包的大小,以便得到下一個包的地址 */ uint8_t desrc1; /* 頻道描述, 當(dāng)做可變長字節(jié)數(shù)組 */ _attribute_(packed);/* 菜單 */struct msg_list_st /* 菜單頻道的包結(jié)構(gòu) */ chnid_t id; /* MUST BE LISTCHNID 頻道號 */ struct msg_listentry_st entry1; /* 各個頻道的頻道號以及頻道信息, 當(dāng)做可變長字節(jié)數(shù)組 */ _attribute_(packed);新遇到的函數(shù)或用法不要任意直接使用常量,如果這個常量有意義,那就定義宏來使用,
5、便于后期的維護和代碼的可讀性在兩臺電腦上通過網(wǎng)絡(luò)傳輸數(shù)據(jù)的時候,不僅要注意大小端的問題,還要注意兩臺電腦之間的內(nèi)存對齊問題struct list struct list *next; char data0;在32位下,sizeof(struct list)是4個字節(jié),data里面沒有元素,所以是0個字節(jié),但是data的地址是緊貼著struct list末尾的地址的,所以可以這樣使用struct list *ptr = malloc(sizeof(struct list)+20);這個結(jié)構(gòu)體指針指向的空間多出20個字節(jié)來,可以通過data來得到這塊內(nèi)存的首地址,這樣就實現(xiàn)了一個類似一個可變長字節(jié)
6、的結(jié)構(gòu)體3.1 令牌桶./src/server/mytbf.h ./src/server/mytbf.c 大體的功能是有一個容器,存有多個令牌,當(dāng)去讀文件的時候先去令牌桶拿令牌,拿到多少個就讀出多少個字節(jié), 規(guī)定令牌桶的最大容量(burst),每秒鐘能拿多少個令牌(字節(jié))(cps),當(dāng)前桶內(nèi)剩余的令牌數(shù)(token);桶內(nèi)的令牌會自動增長,每秒增長cps個令牌/* 主要的數(shù)據(jù)結(jié)構(gòu) */ struct mytbf_st int cps; /* 每秒多少字節(jié) */ int burst; /* 桶的容量 字節(jié)數(shù)*/ int token; /* 當(dāng)前桶的大小 字節(jié)數(shù)*/ pthread_mutex_t
7、 mut; /* 對token進行加鎖 */ pthread_cond_t cond; /*如果取得時候,令牌桶里面沒有令牌,就會阻塞在這個條件變量上*/ ;tpyedef void mytbf_t; /令牌桶的處理句柄static struct mytbf_st *tbfTBFMAX; /* 令牌桶指針數(shù)組 */static pthread_mutex_t mut_tbf = PTHREAD_MUTEX_INITIALIZER; 用于鎖這個令牌桶數(shù)組static pthread_t tid_timer;static pthread_once_t init_once = PTHREAD_ONC
8、E_INIT;mytbf_t *mytbf_init(int cps, int burst); /初始化化令牌桶,設(shè)置這個令牌桶的cps和burst static void module_load(void); /加載時鐘線程, static void *thr_timer_func(void *sed); /這個線程主要同于令牌桶中令牌的自增 static void *module_unload(void); /銷毀和回收時鐘線程,使用的是atexit調(diào)用的,當(dāng)進程結(jié)束時運行 static int get_free_pos(); /從令牌桶數(shù)組中得帶沒有使用的令牌桶下標(biāo) 返回令牌桶的地址,當(dāng)
9、做句柄int mytbf_fetchtoken(mytbf_t *,int n); /從桶里面取令牌,如果有就取走,沒有就阻塞在條件變量上等待int mytbf_returntoken(mytbf_t *, int n); /如果取得令牌沒有用完,就把剩余的還回去int mytbf_destroy(mytbf_t *); /銷毀令牌桶令牌桶指針數(shù)組的存在是為了令牌桶自增方便,把所有的令牌桶統(tǒng)計一下,通過一個線程輪詢的方式自增,就避免每個令牌桶都使用單獨的自增線程,節(jié)省資源,便于管理.當(dāng)然,這顯然是一個需要多個線程同時操作的變量,所以需要加鎖新遇到的函數(shù)或用法 /睡眠一秒 struct time
10、spec t; t.tv_sec = 1; /m t.tv_nsec = 0; /ns /* 第一個參數(shù)是要睡多少秒,第二個參數(shù)是如果睡眠被信號打斷,剩余的時間 ,放到循環(huán)里,知道1s全部執(zhí)行完,當(dāng)跳出循環(huán)*/ while (nanosleep(&t, &t) != 0) if (errno != EINTR) syslog(LOG_ERR, "nanosleep(): %s", strerror(errno); exit(1); /設(shè)置程序進程結(jié)束時調(diào)用的函數(shù)(返回值void,沒有參數(shù)) int atexit(void (*function)(void);
11、 /在線程函數(shù)中,只執(zhí)行一次這個函數(shù)(返回值void,沒有參數(shù)) 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文件(缺一不可),我們根據(jù)滿足要求的子文件的個數(shù)來確
12、定頻道的個數(shù),頻道數(shù)新定位200個 /* 主要的數(shù)據(jù)結(jié)構(gòu) */struct mlib_listentry_st /* 每個頻道的描述的結(jié)構(gòu)體 */ chnid_t id; /頻道id char *desc; /頻道的描述,也就是desc.test中的內(nèi)容;struct channel_context_st /* 存儲頻道的內(nèi)容信息*/ chnid_t id; /頻道id char *desc; /頻道描述 glob_t mp3glob; /在頻道子目錄中符合相應(yīng)規(guī)則("*.mp3")的文件名的集合 int pos; /當(dāng)前讀取的文件名在mp3glob這個文件名的下標(biāo) off
13、_t offset; /讀取當(dāng)前文件的指針偏移 int fd; /當(dāng)前文件的文件描述符 mytbf_t *tbf; /讀這個文件的令牌桶句柄;/*API*/static struct channel_context_st channelMAXCHNID+1; /存儲所有的頻道內(nèi)容信息的數(shù)組/* 通過媒體資源目錄,得到當(dāng)前的頻道列表信息和頻道個數(shù) */int mlib_getchnlist(struct mlib_listentry_st *, int *); static struct channel_context_st *path2entry(const char *path) /把頻道媒
14、體數(shù)據(jù)的子目錄路徑傳進去,返回一個當(dāng)前頻道內(nèi)容信息的結(jié)構(gòu)體指針 mytbf_t *mytbf_init(int cps, int burst); /初始化這個頻道的令牌桶/根據(jù)頻道號找到相對應(yīng)的資源文件,讀到buf里,返回讀取到的字節(jié)數(shù)ssize_t mlib_readchn(chnid_t cid, void* buf, ssize_t len); int mytbf_fetchtoken(mytbf_t *,int n); /從桶里面取令牌,確定可以讀取的字節(jié)數(shù) static int open_next(chnid_t id); /如果read返回值是0,說明已經(jīng)讀完,自動讀取下一個文件
15、int mytbf_returntoken(mytbf_t *, int n); /讀取內(nèi)容成功,如果又沒用完的令牌,就還回去int mlib_freechnlist(struct mlib_listentry_st *); /釋放channel數(shù)組資源函數(shù)的實現(xiàn)細(xì)節(jié)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這個就是默認(rèn)的資源路徑("/var/media")(絕對路徑) 再使用glob(path, 0, NULL, &globres)得到符合這個通配符的所有文件名信息,放到globres里面 再根據(jù)globres.gl_pathc符合條件的文件路徑個數(shù)確定一共需要創(chuàng)建的頻道個數(shù),在malloc內(nèi)存相應(yīng)的菜單項內(nèi)存 通過輪詢globres.gl_pathvi,范圍globres.gl_pathc,來遍歷每個頻道的媒體文件, 再通過res = path2entry(globres.gl_pathvi);獲取每個頻道的
17、內(nèi)容(id,描述,流媒體文件等), 然后把path2entry返回的內(nèi)容轉(zhuǎn)化成mlib_listentry_st,都放到一個數(shù)組中 然后把頻道的菜單列表和菜單的大小傳出去.ssize_t mlib_readchn(chnid_t cid, void* buf, ssize_t len); 讀取媒體數(shù)據(jù) tbfsize = mytbf_fetchtoken(channelid.tbf, size);首先從;令牌桶獲取令牌 len = pread(channelid.fd, buf, tbfsize, channelid.offset); 從offset的偏移位置讀取數(shù)據(jù) mytbf_return
18、token(channelid.tbf, tbfsize-len);把剩余的令牌換回去新遇到的函數(shù)或用法#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里面有多少個符合規(guī)則的文件名 Count of paths matched so far */ char *gl_pathv; /* 字符串的內(nèi)容 List
19、of matched pathnames. */ size_t gl_offs; /* Slots to reserve in gl_pathv'. 為GLOB_DOOFS保留的 */ glob_t;/* glob函數(shù)搜索匹配函數(shù)pattern中的參數(shù),如/*是匹配根文件下的所有文件(不包含隱藏文件,要找到隱藏文件需要從新匹配),然后將匹配出的結(jié)果存放到pglob,即第4個參數(shù)中,第二個參數(shù)能選擇匹配模式,如是否排序,或者在函數(shù)第二次調(diào)用時,是否將匹配內(nèi)容追加到pglob中等,第三個參數(shù)是饞看錯誤信息,一般置為NULL; 使用glob的遞歸調(diào)用可以找到系統(tǒng)任意路徑的所有文件 */#in
20、clude <string.h>char *strdup(const char *s); strdup會先用malloc()配置與參數(shù)s字符串相同的空間大小,然后將參數(shù)s字符串的內(nèi)容復(fù)制到該內(nèi)存地址,然后把返回.返回的地址最后可以利用free來釋放ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset); 和read差不多,不過是從offset這個偏置的位置開始讀文件,而不是從文件指針位置3.3 菜單發(fā)送模塊./src/server/thr_list.h ./src/server/thr_list.c 把通過媒
21、體資源處理模塊得到的菜單信息通過組播發(fā)送出去,每秒一次/* 創(chuàng)建菜單發(fā)送線程 */int thr_list_create(struct mlib_listentry_st *listp, int nr_ent) 把listp和nr_ent傳入到線程函數(shù)中去,這里是用的全局變量的形式 當(dāng)前的菜單信息是struct mlib_listentry_st這個結(jié)構(gòu)體的,要把它轉(zhuǎn)化成菜單發(fā)送的數(shù)據(jù)包struct msg_list_st 首先根據(jù)listp確定struct msg_list_st菜單發(fā)送數(shù)據(jù)包的大小.然后malloc內(nèi)存 然后把菜單項拷過去 死循環(huán)發(fā)送菜單包 間隔1s3.4 菜單發(fā)送模塊給每
22、個頻道創(chuàng)建一個子線程發(fā)送各自頻道的數(shù)據(jù)int thr_channel_create(struct mlib_listentry_st *ptr) 創(chuàng)建一個線程函數(shù) static void *thr_channel_func(void *ptr) 通過len = mlib_readchn(ent->id, sbufp->data, datasize);來讀取流媒體內(nèi)容,發(fā)送出去新遇到的函數(shù)或用法#include <sched.h> int sched_yield(void);當(dāng)線程調(diào)用這個函數(shù)時,會讓主動出CPU使用權(quán),讓其他線程得到執(zhí)行(四) 程序的主體主要的業(yè)務(wù)流程i
23、nt serversd; /socket文件描述符,全局的struct sockaddr_in sndaddr; /組播地址/發(fā)送端的各種默認(rèn)配置,都在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, /網(wǎng)卡名'eth0' .runmode = run_daemo
24、n /守護進程;主要的業(yè)務(wù)流程1. 首先是設(shè)置日志文件2. 讀取命令行參數(shù),修改相應(yīng)的server_conf的值3. 是否變成守護進程4. socket環(huán)境初始化,創(chuàng)建socket文件,UPD,組播地址,端口號,端口可復(fù)用,5. 獲取加載媒體資源,得到菜單列表, mlib_getchnlist6. 創(chuàng)建發(fā)送菜單的線程7. 循環(huán)創(chuàng)建發(fā)送各個頻道數(shù)據(jù)的線程8. 主線程暫定,由各個線程向組播地址發(fā)送數(shù)據(jù),等待信號終止程序新遇到的函數(shù)或用法守護進程需要設(shè)置信號捕捉函數(shù),以便結(jié)束進程是不會丟失文件或數(shù)據(jù)/系統(tǒng)提供的日志記錄接口,相應(yīng)日志記錄到系統(tǒng)的日志文件中去#include <syslog.h> void openlog(const char *ident, int option, int facility); /程序名稱,
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 理財規(guī)劃服務(wù)合同的客體是
- 濱州2025年山東濱州市沾化區(qū)事業(yè)單位招聘67人筆試歷年參考題庫附帶答案詳解
- 工程材料選型與采購作業(yè)指導(dǎo)書
- 機械設(shè)計制造及其自動化練習(xí)題集及解析
- 2025年二級建造師《礦業(yè)工程管理與實物》全真模擬卷
- 2023年全國碩士研究生招生考試《經(jīng)濟類聯(lián)考綜合能力》真題及解析
- 多學(xué)科綜合呼吸康復(fù)對老年重度慢性阻塞性肺疾病康復(fù)的影響
- 藥物臨床試驗研究
- 智慧解決方案如何提高學(xué)校管理效能
- 2025年關(guān)于家庭安全的教育標(biāo)準(zhǔn)教案
- 開原市石場子村鉀長石礦采礦權(quán)價款評估報告
- 丁字帳表模板
- 貝朗CRRT報警處理-問題-精品醫(yī)學(xué)課件
- 太原市修繕土建工程預(yù)算定額
- 小額納稅人證明模板
- 付款申請函正式函
- 北京市商業(yè)地圖
- 首發(fā)業(yè)務(wù)若干問題解答
- 面包生產(chǎn)工藝流程圖
- 曲線坐標(biāo)計算(交點法]斷鏈
- 員工考勤表(通用版)
評論
0/150
提交評論