在最简单的音视频播放器中,由于解复用和解码是在不同的线程中,存放包的队列是公共资源,需要互斥。解复用向队列添加包,解码从队列取包,也需要同步。所以队列的入队和出队操作,采用了互斥量和条件变量。
1、创建互斥量
SDL_mutex *mutex; mutex = SDL_CreateMutex();创建的互斥量默认是未上锁的。
2、上锁和解锁
if (SDL_LockMutex(mutex) == 0) { /* Do stuff while mutex is locked */ SDL_UnlockMutex(mutex); } else { fprintf(stderr, "Couldn't lock mutex\n"); }成功返回0,否则返回<0
3、销毁锁
SDL_DestroyMutex(mutex);
1、创建条件变量
SDL_cond* SDL_CreateCond(void)
int SDL_CondWait(SDL_cond* cond, SDL_mutex* mutex)
信号激活后,返回0,否则返回错误代码。
SDL_ConWait必须在互斥量锁住之后才能调用。该函数会解锁锁住的互斥量,并等待拥有该锁的线程激活信号。激活后,会重新上锁。
3、激活信号int SDL_CondSignal(SDL_cond* cond)成功返回 0,否则返回错误代码。
SDL_ConSignal会激活等待的一个线程(根据优先级),而不是所有等待的线程。
4、销毁条件变量
void SDL_DestroyCond(SDL_cond* cond)
<pre name="code" class="cpp">extern"C"{ #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include "include/sdl/SDL.h" #include "include/sdl/SDL_thread.h" #include "include/libavutil/time.h" #include "include/libavutil/avstring.h" #include "libswresample/swresample.h" } #pragma comment(lib, "lib/avformat.lib") #pragma comment(lib, "lib/avcodec.lib") #pragma comment(lib, "lib/avutil.lib") #pragma comment(lib, "lib/swscale.lib") #pragma comment(lib, "lib/swresample.lib") #pragma comment(lib, "lib/SDL.lib") #pragma comment(lib, "lib/SDLmain.lib") #include <stdio.h> #include<stdlib.h> #include<string.h> #include <assert.h> /*包队列*/ typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; int nb_packets;//包个数 int size;//包大小 SDL_mutex *mutex;//互斥量 SDL_cond *cond;//条件量 } PacketQueue; /* 初始化队列 */ void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } /* 入队操作 */ int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; if (av_dup_packet(pkt) < 0) { return -1; } pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); if (!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL; SDL_LockMutex(q->mutex); if (!q->last_pkt) q->first_pkt = pkt1; else q->last_pkt->next = pkt1; q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0; } /* 出队操作 */ int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { AVPacketList *pkt1; int ret; SDL_LockMutex(q->mutex); for (;;) { pkt1 = q->first_pkt; if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) q->last_pkt = NULL; q->nb_packets--; q->size -= pkt1->pkt.size; *pkt = pkt1->pkt; av_free(pkt1); ret = 1; break; } else if (!block) { ret = 0; break; } else { SDL_CondWait(q->cond, q->mutex); } } SDL_UnlockMutex(q->mutex); return ret; } //生产线程 int produce(void * qq) { PacketQueue *q = (PacketQueue*)qq; while (1) { AVPacket* pkt = (AVPacket*)malloc(sizeof(AVPacket)); if (packet_queue_put(q, pkt)==0) { printf("produce %d product!\n",q->nb_packets); } SDL_Delay(2);//等待2ms } return 0; } //消费线程 int consume(void * qq) { PacketQueue *q = (PacketQueue*)qq; while (1) { int ret = packet_queue_get(q, pkt, 0); <span style="white-space:pre"> </span>if (ret ==1) <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>printf("consume a product\n"); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>else if (ret == 0) <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>printf("no product ,wait!\n"); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span> <span style="white-space:pre"> </span>SDL_Delay(1); } return 0; } int main(int argc, char *argv[]) { SDL_Thread * threadA; SDL_Thread * threadB; PacketQueue * q = (PacketQueue*)malloc(sizeof(PacketQueue)); packet_queue_init(q); threadA = SDL_CreateThread(produce, q); threadB = SDL_CreateThread(consume, q); SDL_Delay(500); SDL_KillThread(threadA); SDL_KillThread(threadB); return 0; }
从图中可以看到,当生产者向队列放入1产品后,消费者消费了一个产品。当消费者再次取产品的时候,发现没有产品,所以等待。
在程序中,我让生产者线程生产的速度低于消费的速度(一个delay了2毫秒,一个delay了1毫秒),所以才会出现消费者等待的情形。
现在假设队列的容量为3,并当队列满的时候,不允许再向队列放入产品,等待消费者消费了之后再放入产品。实现起来也不难,只需要在入队时做个判断就可以了。源代码如下:
extern"C"{ #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include "include/sdl/SDL.h" #include "include/sdl/SDL_thread.h" #include "include/libavutil/time.h" #include "include/libavutil/avstring.h" #include "libswresample/swresample.h" } #pragma comment(lib, "lib/avformat.lib") #pragma comment(lib, "lib/avcodec.lib") #pragma comment(lib, "lib/avutil.lib") #pragma comment(lib, "lib/swscale.lib") #pragma comment(lib, "lib/swresample.lib") #pragma comment(lib, "lib/SDL.lib") #pragma comment(lib, "lib/SDLmain.lib") #include <stdio.h> #include<stdlib.h> #include<string.h> #include <assert.h> /*包队列*/ typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; int nb_packets;//包个数 int size;//包大小 SDL_mutex *mutex;//互斥量 SDL_cond *cond;//条件量 int max_packets; } PacketQueue; void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); q->max_packets = 3;//设置队列容量 q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } int packet_queue_put(PacketQueue *q, AVPacket *pkt) { if (q->nb_packets>=q->max_packets)//队列满则返回 { return 1; } AVPacketList *pkt1; if (av_dup_packet(pkt) < 0) { return -1; } pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); if (!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL; SDL_LockMutex(q->mutex); if (!q->last_pkt) q->first_pkt = pkt1; else q->last_pkt->next = pkt1; q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0; } int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { AVPacketList *pkt1; int ret; SDL_LockMutex(q->mutex); for (;;) { pkt1 = q->first_pkt; if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) q->last_pkt = NULL; q->nb_packets--; q->size -= pkt1->pkt.size; *pkt = pkt1->pkt; av_free(pkt1); ret = 1; break; } else if (!block) { ret = 0; break; } else { SDL_CondWait(q->cond, q->mutex); } } SDL_UnlockMutex(q->mutex); return ret; } int produce(void * qq) { PacketQueue *q = (PacketQueue*)qq; while (1) { AVPacket* pkt = (AVPacket*)malloc(sizeof(AVPacket)); int ret = packet_queue_put(q, pkt); if (ret==0) { printf("produce %dth product!\n",q->nb_packets); } else if (ret==1) { printf("Queue is full,wait!\n"); SDL_Delay(1); } } return 0; } int consume(void * qq) { PacketQueue *q = (PacketQueue*)qq; while (1) { AVPacket* pkt = (AVPacket*)malloc(sizeof(AVPacket)); int ret = packet_queue_get(q, pkt, 0); if (ret ==1) { printf("consume a product\n"); } else if (ret == 0) { printf("no product ,wait!\n"); SDL_Delay(1); } } return 0; } int main(int argc, char *argv[]) { SDL_Thread * threadA; SDL_Thread * threadB; PacketQueue * q = (PacketQueue*)malloc(sizeof(PacketQueue)); packet_queue_init(q); threadA = SDL_CreateThread(produce, q); threadB = SDL_CreateThread(consume, q); SDL_Delay(500); SDL_KillThread(threadA); SDL_KillThread(threadB); return 0; }