首页 | 互联网 | IT动态 | IT培训 | Cisco | Windows | Linux | Java | .Net | Oracle | 软件测试 | C/C++ | 嵌入式开发 | 存储世界 | 服务器
网络设备 | IDC | 安全 | 求职招聘 | 数字网校 | 网页设计 | 平面设计 | 技术专题 | 电子书下载 | 教学视频 | 源码下载 | 搜索 | 博客 | 论坛
中国IT实验室Linux频道
Google
首页 入门 Linux编程 系统管理 网络管理 Linux认证 Unix/BSD Linux数据库 Linux集群 Linux手册 Linux下载 Ubuntu 论坛 专题 RSS
您现在的位置: 中国IT实验室 >> Linux >> 新手入门 >> 软件使用 >> 正文

基于libmad 的简单MP3流媒体播放器的实现

6.数据接收线程和音乐播放线程

    由于是两个线程并发运行,且音乐播放线程线程运行速度较慢。如果网络速度较快,数据接收线程的接收缓冲区满后,如果当前音乐播放线程正在播放音乐,那么数据接收线程必须停止接收数据。如果不让数据接收线程进入等待状态,它会一直轮训音乐播放线程观察其是否需要数据,简单的轮询会浪费 CPU 资源,所以在这种情况下,有必要让数据接收线程进入等待状态。本文使用信号量机制,来动态控制线程的运行。数据接收缓冲区必须留出一定的空间,存放解码缓冲区中没有被解码的数据。那么要留出多少数据空间呢?至少应该留出一帧数据的空间。这里 8192 字节空间存放剩余的一帧 MPEG 数据,一般情况下应该够用。因此定义:

#define DECODE_BUF_SIZE   (8192*11)
#define GARD_SIZE         (8192*10)
static char decode_buf[DECODE_BUF_SIZE];
static char recv_buf[DECODE_BUF_SIZE];
      

  GARD_SIZE 是一次从 socket 读取数据字节数的最大值,而解码缓冲区的大小应该是比 GARD_SIZE 大 8192 字节,因此定义 DECODE_BUF_SIZE(8192*11)recv_buf 是数据接收缓冲区,decode_buf 是数据解码缓冲区。在拷贝数据到解码缓冲区的时候,上次未解码的数据,还被保存在解码缓冲区的开始部分,故拷贝数据的时候,必须拷贝到剩余数据的后面,程序例子如下:

memcpy(decode_buf + current_remain, recv_buf, current_read);
current_read += current_remain;
      

这里的 current_remain 表示上次解码线程中未解码的不完整 MP3 帧的数据字节数,current_read 表示当前接收线程接收到的实际数据字节数。两个缓冲区之间的数据拷贝操作如下图所示。


图 2:缓冲区之间的数据拷贝操作
缓冲区之间的数据拷贝操作

数据接收线程和音乐播放线程之间的同步

   由于使用了双缓冲区保存数据,所以,在音乐播放线程播放音乐的时候,数据接收线程不能把数据拷贝到数据解码缓冲区,而是需要等待。当数据接收缓冲区满的时候,接收线程自己也需要等待。本文用到了 POSIX 信号量处理函数,实现了线程之间的同步。它们分别是:

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value); 
      

初始化信号量,第三个参数表示初始的信号量的计数。

int sem_wait(sem_t * sem);
      

sem_wait 阻塞当前线程的执行,直到信号量的计数非 0;然后,它会把信号量计数减 1,然后程序继续执行。相当于 P 操作。

int sem_post(sem_t * sem);
      

sem 指向的信号量计数加 1。相当于 V 操作。

int sem_destroy(sem_t * sem);
      

释放信号量对象。

在程序中,信号量定义及初始化为:

static sem_t empty_sem;
static sem_t decode_sem;
static sem_t copy_sem;
sem_init(&empty_sem, 0, 1);
sem_init(&decode_sem, 0, 0);
sem_init(©_sem, 0, 1);
      

 

上一页  [1] [2] [3] [4] [5] [6] [7] 下一页

【责编:Peng】
中国IT教育
相关产品和培训
文章评论
 友情推荐链接
 认证培训