FFmepg中文例子—指導3:模塊化

#include "libavformat/avformat.h"  
#include "libswscale/swscale.h"  
//#include <windows.h>  
 
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <math.h>  
#include <SDL/SDL.h>  
#include <SDL/SDL_thread.h>  
 
#ifdef main  
#undef main  
#endif  
 
#define SDL_AUDIO_BUFFER_SIZE 1024  
  
#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)  
#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)  
 
#define FF_ALLOC_EVENT   (SDL_USEREVENT)  
#define FF_REFRESH_EVENT (SDL_USEREVENT + 1)  
#define FF_QUIT_EVENT (SDL_USEREVENT + 2)  
#define VIDEO_PICTURE_QUEUE_SIZE 1  
#define SDL_AUDIO_BUFFER_SIZE 1024  
  
static int sws_flags = SWS_BICUBIC;  
  
typedef struct PacketQueue   
{   
    AVPacketList *first_pkt, *last_pkt;   
    int nb_packets;   
    int size;   
    SDL_mutex *mutex;   
    SDL_cond *cond;   
} PacketQueue;   
  
typedef struct VideoPicture   
{  
    SDL_Overlay *bmp;  
    int width, height;   
    int allocated;  
} VideoPicture;  
  
typedef struct VideoState   
{  
    AVFormatContext *pFormatCtx;  
    int             videoStream, audioStream;  
    AVStream        *audio_st;  
    PacketQueue     audioq;  
    uint8_t         audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];  
    unsigned int    audio_buf_size;  
    unsigned int    audio_buf_index;  
    AVPacket        audio_pkt;  
    uint8_t         *audio_pkt_data;  
    int             audio_pkt_size;  
    AVStream        *video_st;  
    PacketQueue     videoq;  
  
    VideoPicture    pictq[VIDEO_PICTURE_QUEUE_SIZE];  
    int             pictq_size, pictq_rindex, pictq_windex;  
    SDL_mutex       *pictq_mutex;  
    SDL_cond        *pictq_cond;  
  
    SDL_Thread      *parse_tid;  
    SDL_Thread      *video_tid;  
  
    char            filename[1024];  
    int             quit;  
} VideoState;  
  
SDL_Surface     *screen;  
  
  
VideoState *global_video_state;  
  
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;   
}   
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)   
{   
    AVPacketList *pkt1;   
    int ret;   
    SDL_LockMutex(q->mutex);   
    for(;;)   
    {   
        if(global_video_state->quit)   
        {   
            ret = -1;   
            break;   
        }   
        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 audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size)  
{  
    int len1, data_size;  
    AVPacket *pkt = &is->audio_pkt;  
  
    for(;;)   
    {  
        while(is->audio_pkt_size > 0)   
        {  
            data_size = buf_size;  
            len1 = avcodec_decode_audio3(is->audio_st->codec,   
                (int16_t *)audio_buf, &data_size,   
                pkt);  
            if(len1 < 0)   
            {  
                  
                is->audio_pkt_size = 0;  
                break;  
            }  
            is->audio_pkt_data += len1;  
            is->audio_pkt_size -= len1;  
            if(data_size <= 0)   
            {  
                  
                continue;  
            }  
              
            return data_size;  
        }  
        if(pkt->data)  
            av_free_packet(pkt);  
  
        if(is->quit)   
        {  
            return -1;  
        }  
          
        if(packet_queue_get(&is->audioq, pkt, 1) < 0)   
        {  
            return -1;  
        }  
        is->audio_pkt_data = pkt->data;  
        is->audio_pkt_size = pkt->size;  
    }  
}  
  
void audio_callback(void *userdata, Uint8 *stream, int len)  
{  
    VideoState *is = (VideoState *)userdata;  
    int len1, audio_size;  
  
    while(len > 0)   
    {  
        if(is->audio_buf_index >= is->audio_buf_size)   
        {  
              
            audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf));  
            if(audio_size < 0)   
            {  
                  
                is->audio_buf_size = 1024;  
                memset(is->audio_buf, 0, is->audio_buf_size);  
            }   
            else   
            {  
                is->audio_buf_size = audio_size;  
            }  
            is->audio_buf_index = 0;  
        }  
        len1 = is->audio_buf_size - is->audio_buf_index;  
        if(len1 > len)  
            len1 = len;  
        memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);  
        len -= len1;  
        stream += len1;  
        is->audio_buf_index += len1;  
    }  
}  
  
  
static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque)   
{  
    //printf("sdl_refresh_timer_cb called:interval--%d\n",interval);  
    SDL_Event event;  
    event.type = FF_REFRESH_EVENT;  
    event.user.data1 = opaque;  
    SDL_PushEvent(&event);      //派發FF_REFRESH_EVENT事件  
    return 0;   
}  
  
  
static void schedule_refresh(VideoState *is, int delay)   
{  
    //printf("schedule_refresh called:delay--%d\n",delay);  
    SDL_AddTimer(delay, sdl_refresh_timer_cb, is);      //sdl_refresh_timer_cb函數在延時delay毫秒後,只會被執行一次,is是sdl_refresh_timer_cb的參數  
}  
  
void video_display(VideoState *is)   
{  
    //printf("video_display called\n");  
    SDL_Rect rect;  
    VideoPicture *vp;  
    AVPicture pict;  
    float aspect_ratio;  
    int w, h, x, y;  
    int i;  
  
    vp = &is->pictq[is->pictq_rindex];  
    if(vp->bmp)   
    {  
        if(is->video_st->codec->sample_aspect_ratio.num == 0)   
        {  
            aspect_ratio = 0;  
        }   
        else   
        {  
            aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio) *  
                is->video_st->codec->width / is->video_st->codec->height;  
        }  
        if(aspect_ratio <= 0.0)      //aspect_ratio 寬高比  
        {  
            aspect_ratio = (float)is->video_st->codec->width /  
                (float)is->video_st->codec->height;  
        }  
        h = screen->h;  
        w = ((int)(h * aspect_ratio)) & -3;  
        if(w > screen->w)   
        {  
            w = screen->w;  
            h = ((int)(w / aspect_ratio)) & -3;  
        }  
        x = (screen->w - w) / 2;  
        y = (screen->h - h) / 2;  
  
        rect.x = x;  
        rect.y = y;  
        rect.w = w;  
        rect.h = h;  
        SDL_DisplayYUVOverlay(vp->bmp, &rect);  
    }  
}  
  
void video_refresh_timer(void *userdata)   
{  
    VideoState *is = (VideoState *)userdata;  
    VideoPicture *vp;  
  
    if(is->video_st)   
    {  
        if(is->pictq_size == 0)   
        {  
            schedule_refresh(is, 1);  
        }   
        else   
        {  
            vp = &is->pictq[is->pictq_rindex];  
              
            schedule_refresh(is, 80);  
  
              
            video_display(is);  
  
              
            if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE)   
            {  
                is->pictq_rindex = 0;  
            }  
            SDL_LockMutex(is->pictq_mutex);  
            is->pictq_size--;  
            SDL_CondSignal(is->pictq_cond);  
            SDL_UnlockMutex(is->pictq_mutex);  
        }  
    }   
    else   
    {  
        schedule_refresh(is, 100);  
    }  
}  
  
void alloc_picture(void *userdata)   
{  
    VideoState *is = (VideoState *)userdata;  
    VideoPicture *vp;  
  
    vp = &is->pictq[is->pictq_windex];  
    if(vp->bmp)   
    {  
        // we already have one make another, bigger/smaller  
        SDL_FreeYUVOverlay(vp->bmp);  
    }  
    // Allocate a place to put our YUV image on that screen  
    vp->bmp = SDL_CreateYUVOverlay(is->video_st->codec->width,  
        is->video_st->codec->height,  
        SDL_YV12_OVERLAY,  
        screen);  
    vp->width = is->video_st->codec->width;  
    vp->height = is->video_st->codec->height;  
  
    SDL_LockMutex(is->pictq_mutex);  
    vp->allocated = 1;  
    SDL_CondSignal(is->pictq_cond);  
    SDL_UnlockMutex(is->pictq_mutex);  
  
}  
  
int queue_picture(VideoState *is, AVFrame *pFrame)   
{  
    //printf("queue_picture called\n");  
    VideoPicture *vp;  
    int dst_pix_fmt;  
    AVPicture pict;  
    static struct SwsContext *img_convert_ctx;  
    if (img_convert_ctx == NULL)   
    {  
        img_convert_ctx = sws_getContext(is->video_st->codec->width, is->video_st->codec->height,  
                                         is->video_st->codec->pix_fmt,  
                                         is->video_st->codec->width, is->video_st->codec->height,  
                                         PIX_FMT_YUV420P,  
                                         sws_flags, NULL, NULL, NULL);  
        if (img_convert_ctx == NULL)   
        {  
            fprintf(stderr, "Cannot initialize the conversion context\n");  
            exit(1);  
        }  
    }  
  
      
    SDL_LockMutex(is->pictq_mutex);  
    while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&  
        !is->quit)   
    {  
        SDL_CondWait(is->pictq_cond, is->pictq_mutex);  
    }  
    SDL_UnlockMutex(is->pictq_mutex);  
  
    if(is->quit)  
        return -1;  
  
    // windex is set to 0 initially  
    vp = &is->pictq[is->pictq_windex];  
      
      
    if(!vp->bmp ||  
        vp->width != is->video_st->codec->width ||  
        vp->height != is->video_st->codec->height)   
    {  
        SDL_Event event;  
  
        vp->allocated = 0;  
          
        event.type = FF_ALLOC_EVENT;  
        event.user.data1 = is;  
        SDL_PushEvent(&event);  
  
          
        SDL_LockMutex(is->pictq_mutex);  
        while(!vp->allocated && !is->quit)   
        {  
            SDL_CondWait(is->pictq_cond, is->pictq_mutex);    //沒有獲得消息時解鎖,獲得消息後加鎖,和SDL_CondSignal配對使用  
        }  
        SDL_UnlockMutex(is->pictq_mutex);  
        if(is->quit)   
        {  
            return -1;  
        }  
    }  
      
  
    if(vp->bmp)   
    {  
        SDL_LockYUVOverlay(vp->bmp);  
        dst_pix_fmt = PIX_FMT_YUV420P;  
          
  
        pict.data[0] = vp->bmp->pixels[0];  
        pict.data[1] = vp->bmp->pixels[2];  
        pict.data[2] = vp->bmp->pixels[1];  
  
        pict.linesize[0] = vp->bmp->pitches[0];  
        pict.linesize[1] = vp->bmp->pitches[2];  
        pict.linesize[2] = vp->bmp->pitches[1];  
  
        // Convert the image into YUV format that SDL uses  
        sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize,  
                    0, is->video_st->codec->height, pict.data, pict.linesize);  
        SDL_UnlockYUVOverlay(vp->bmp);  
          
        if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE)   
        {  
            is->pictq_windex = 0;  
        }  
        SDL_LockMutex(is->pictq_mutex);  
        is->pictq_size++;  
        SDL_UnlockMutex(is->pictq_mutex);  
    }  
    return 0;  
}  
  
int video_thread(void *arg)   
{  
    //printf("video_thread called");  
    VideoState *is = (VideoState *)arg;  
    AVPacket pkt1, *packet = &pkt1;  
    int len1, frameFinished;  
    AVFrame *pFrame;  
  
    pFrame = avcodec_alloc_frame();  
  
    for(;;)   
    {  
        if(packet_queue_get(&is->videoq, packet, 1) < 0)   
        {  
            // means we quit getting packets  
            break;  
        }  
        // Decode video frame  
        len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished,   
            packet);  
  
        // Did we get a video frame?  
        if(frameFinished)   
        {  
            if(queue_picture(is, pFrame) < 0)   
            {  
                break;  
            }  
        }  
        av_free_packet(packet);  
    }  
    av_free(pFrame);  
    return 0;  
}  
  
int stream_component_open(VideoState *is, int stream_index)   
{  
    AVFormatContext *pFormatCtx = is->pFormatCtx;  
    AVCodecContext *codecCtx;  
    AVCodec *codec;  
    SDL_AudioSpec wanted_spec, spec;  
  
    if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams)   
    {  
        return -1;  
    }  
  
    // Get a pointer to the codec context for the video stream  
    codecCtx = pFormatCtx->streams[stream_index]->codec;  
  
    if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO)   
    {  
        // Set audio settings from codec info  
        wanted_spec.freq = codecCtx->sample_rate;  
        wanted_spec.format = AUDIO_S16SYS;  
        wanted_spec.channels = codecCtx->channels;  
        wanted_spec.silence = 0;  
        wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  
        wanted_spec.callback = audio_callback;  
        wanted_spec.userdata = is;  
  
        if(SDL_OpenAudio(&wanted_spec, &spec) < 0)   
        {  
            fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());  
            return -1;  
        }  
    }  
    codec = avcodec_find_decoder(codecCtx->codec_id);  
    if(!codec || (avcodec_open(codecCtx, codec) < 0))   
    {  
        fprintf(stderr, "Unsupported codec!\n");  
        return -1;  
    }  
  
    switch(codecCtx->codec_type)   
    {  
    case AVMEDIA_TYPE_AUDIO:  
        is->audioStream = stream_index;  
        is->audio_st = pFormatCtx->streams[stream_index];  
        is->audio_buf_size = 0;  
        is->audio_buf_index = 0;  
        memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));  
        packet_queue_init(&is->audioq);  
        SDL_PauseAudio(0);  
        break;  
    case AVMEDIA_TYPE_VIDEO:  
        is->videoStream = stream_index;  
        is->video_st = pFormatCtx->streams[stream_index];  
  
        packet_queue_init(&is->videoq);  
        is->video_tid = SDL_CreateThread(video_thread, is);  
        break;  
    default:  
        break;  
    }  
}  
  
int decode_interrupt_cb(void)   
{  
    return (global_video_state && global_video_state->quit);  
}  
  
int decode_thread(void *arg)   
{  
    VideoState *is = (VideoState *)arg;  
    AVFormatContext *pFormatCtx;  
    AVPacket pkt1, *packet = &pkt1;  
  
    int video_index = -1;  
    int audio_index = -1;  
    int i;  
  
    is->videoStream=-1;  
    is->audioStream=-1;  
  
    global_video_state = is;  
    // will interrupt blocking functions if we quit!  
    url_set_interrupt_cb(decode_interrupt_cb);  
  
    // Open video file  
    if(avformat_open_input(&pFormatCtx, is->filename,  0, NULL)!=0)  
        return -1; // Couldn't open file  
  
    is->pFormatCtx = pFormatCtx;  
  
    // Retrieve stream information  
    if(av_find_stream_info(pFormatCtx)<0)  
        return -1; // Couldn't find stream information  
  
    // Dump information about file onto standard error  
    av_dump_format(pFormatCtx, 0, is->filename, 0);  
  
    // Find the first video stream  
  
    for(i=0; i<pFormatCtx->nb_streams; i++)  
    {  
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO &&  
            video_index < 0)   
        {  
                video_index=i;  
        }  
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&  
            audio_index < 0)   
        {  
                audio_index=i;  
        }  
    }  
    if(audio_index >= 0)   
    {  
        stream_component_open(is, audio_index);  
    }  
    if(video_index >= 0)   
    {  
        stream_component_open(is, video_index);  
    }     
  
    if(is->videoStream < 0 || is->audioStream < 0)   
    {  
        fprintf(stderr, "%s: could not open codecs\n", is->filename);  
        goto fail;  
    }  
  
    // main decode loop  
    for(;;)   
    {  
        if(is->quit)   
        {  
            break;  
        }  
        // seek stuff goes here  
        if(is->audioq.size > MAX_AUDIOQ_SIZE ||  
            is->videoq.size > MAX_VIDEOQ_SIZE)   
        {  
                SDL_Delay(10);  
                continue;  
        }  
        if(av_read_frame(is->pFormatCtx, packet) < 0)   
        {  
            if(url_ferror(pFormatCtx->pb) == 0)   
            {  
                SDL_Delay(100);   
                continue;  
            }   
            else   
            {  
                break;  
            }  
        }  
        // Is this a packet from the video stream?  
        if(packet->stream_index == is->videoStream)  
        {  
            packet_queue_put(&is->videoq, packet);  
        }   
        else if(packet->stream_index == is->audioStream)  
        {  
            packet_queue_put(&is->audioq, packet);  
        }   
        else  
        {  
            av_free_packet(packet);  
        }  
    }  
      
    while(!is->quit)   
    {  
        SDL_Delay(100);  
    }  
fail:  
    SDL_Event event;  
    event.type = FF_QUIT_EVENT;  
    event.user.data1 = is;  
    SDL_PushEvent(&event);  
    return 0;  
}  
  
int main(int argc, char *argv[])  
{  
    SDL_Event       event;  
    VideoState      *is;  
    is = (VideoState *)av_mallocz(sizeof(VideoState));  
    if(argc < 2)   
    {  
        fprintf(stderr, "Usage: test <file>\n");  
        exit(1);  
    }  
    // Register all formats and codecs  
    av_register_all();  
  
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))   
    {  
        fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());  
        exit(1);  
    }  
  
    // Make a screen to put our video  
#ifndef __DARWIN__  
    screen = SDL_SetVideoMode(640, 480, 0, 0);  
#else  
    screen = SDL_SetVideoMode(640, 480, 24, 0);  
#endif  
    if(!screen)  
    {  
        fprintf(stderr, "SDL: could not set video mode - exiting\n");  
        exit(1);  
    }  
  
    //pstrcpy(is->filename, sizeof(is->filename), argv[1]);  
    strcpy(is->filename,argv[1]);  
    is->pictq_mutex = SDL_CreateMutex();  
    is->pictq_cond = SDL_CreateCond();  
  
    schedule_refresh(is, 40);  
    is->parse_tid = SDL_CreateThread(decode_thread, is);  
  
    if(!is->parse_tid)  
    {  
        av_free(is);  
        return -1;  
    }  
    for(;;)  
    {  
        SDL_WaitEvent(&event);  
        switch(event.type)   
        {  
        case FF_QUIT_EVENT:  
            printf("FF_QUIT_EVENT recieved");  
        case SDL_QUIT:  
            printf("SDL_QUIT recieved");  
            is->quit = 1;  
            SDL_Quit();  
            return 0;  
            break;  
        case FF_ALLOC_EVENT:  
            alloc_picture(event.user.data1);  
            break;  
        case FF_REFRESH_EVENT:  
            video_refresh_timer(event.user.data1);  
            break;  
        default:  
            break;  
        }  
    }  
    return 0;  
}

程序結構分析:windows

     main函數主要作了三件事:安全

           1.建立了一個線程decode_Threadide

           2.調用了schedule_refresh函數函數

           3.建立了一個無限循環,處理程序運行過程當中派發的退出播放事件,內存分配事件,屏幕刷新事件oop

先從簡單地schedule_refresh函數提及ui

      這個函數調用了SDL庫函數SDL_AddTimer(delay, sdl_refresh_timer_cb, is);這個函數會讓sdl_refresh_timer_cb函數延遲delay毫秒後只執行一次,is參數是大結構體VideoState類型,包含 了視頻播放的各類信息,做爲參數傳遞給回調函數sdl_refresh_timer_cb,sdl_refresh_timer_cb函數又會派發事件 FF_REFRESH_EVENT,大結構體類型的數據也跟着派發出去,讓main的事件監聽模塊監聽到,而後調用 video_refresh_timer函數。this

video_refresh_timer函數url

      video_refresh_timer函數主要是負責播放視頻的每一幀,若是幀隊列is->pictq中有能夠播放的圖片幀,就調用 video_display函數播放它,而後改變is->pictq_rindex(影響video_display的播放),改變 is->pictq_size(幀隊列大小)影響queue_picture函數地執行。線程

      video_refresh_timer函數只要被調用,就會讓schedule_refresh函數執行一次,又會派發屏幕刷新事件 FF_REFRESH_EVENT,讓video_refresh_timer又有機會執行,其實是個隱形的循環不斷地調用 video_refresh_timer函數,雖然沒有for或while關鍵字。code

decode_Thread線程函數

     decode_Thread函數首先調用ffmpeg庫函數av_open_input_file打開main函數參數中指定的視頻文件,而後調用 stream_component_open函數分別建立了兩個線程分別去播放音頻和視頻,最後又是一個無限循環不斷地調用庫函數 av_read_frame把數據讀入包變量packet中,而後根據packet->stream_index來分流,分出視頻包和音頻包,分別 放到視頻包隊列is->videoq和音頻包隊列is->audioq中,其中包隊列is->videoq中的數據包還有待進行解碼和 格式轉換,而後在放到幀隊列is->pictq中,讓video_refresh_timer函數有幀可讀,才能播放出圖像。

stream_component_open函數

     會根據參數的不一樣分別建立兩個線程audio_callback進行音頻的解碼和播放,和video_thread進行視頻的解碼和格式轉換(視頻的播放是在video_refresh_timer函數中進行)

video_thread函數

     首先調用packet_queue_get函數獲得視頻數據包,而後調用庫函數avcodec_decode_video解碼數據包,當數據包中的數據解 碼滿一幀後,把該幀的數據pFrame送到queue_picture函數中進行格式轉換,queue_picture函數把格式轉換好後會把該幀存入幀 隊列is->pictq中,應該存到is->pictq的隊前仍是隊後是經過is->pictq_windex來指示的。

queue_picture函數

      queue_picture函數在向幀隊列is->pictq寫入一幀以前會先判斷幀隊列的大小is->pictq_size,若是幀隊列中 有多於一幀的數據,就經過庫函數SDL_CondWait阻塞起來,等待video_refresh_timer函數去播放一幀,而後改變幀隊列大小。

線程間的協調

      整個程序有四個工做線程:main線程,decode_thread線程,audio_callback線程,video_thread線程,這四個線程 若是不加以協調確定會亂套,讓程序沒法正常工做。協調這些線程用到的變量有VideoState結構體中的audioq,videoq來協調 decode_thread線程和他的兩個子線程(線程audio_callback線程、video_thread線程)之間讀包、取包。

      用pictq,pictq_size, 來協調main線程與video_thread線程之間的寫幀、讀幀。

      互斥量pictq_mutex和信號量pictq_cond,經過SDL_LockMutex函數、SDL_UnlockMutex函數保證線程協調變量的讀寫安全;SDL_CondWait函數,SDL_CondSignal函數來阻塞和解除阻塞

相關文章
相關標籤/搜索