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;  
}

程序结构分析:

     main函数主要做了三件事:

           1.创建了一个线程decode_Thread

           2.调用了schedule_refresh函数

           3.创建了一个无限循环,处理程序运行过程中派发的退出播放事件,内存分配事件,屏幕刷新事件

先从简单地schedule_refresh函数说起

      这个函数调用了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函数。

video_refresh_timer函数

      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关键字。

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函数来阻塞和解除阻塞

你可能感兴趣的:(ffmpeg)