ffmpeg+sdl教程----编写一个简单的播放器4(让程序更模块化)


      上个教程为视频加入了音频,但是音频和视频间完全没有同步,各自播放各自的数据帧,我们将在下一个教程实现音频和视频之间的同步,再那之前需要先做一些准备工作。

      为了让程序更模块化,便于扩展,需要把原来main函数中的各个功能模块代码分离出来放在相应的函数中。该教程和上个教程相比代码量和难度都增加很多,比上个教程使用了更多的线程,一定要理解清楚各个函数和数据结构之间的关联以及线程之间如何协同工作。

[c-sharp]  view plain copy
  1. // ffmpegExe.cpp: 主项目文件。  
  2. #include "libavformat/avformat.h"  
  3. #include "libswscale/swscale.h"  
  4. //#include <windows.h>  
  5.  
  6. #include <stdlib.h>  
  7. #include <stdio.h>  
  8. #include <string.h>  
  9. #include <math.h>  
  10. #include <SDL/SDL.h>  
  11. #include <SDL/SDL_thread.h>  
  12.  
  13. #ifdef main  
  14. #undef main  
  15. #endif  
  16.  
  17. #define SDL_AUDIO_BUFFER_SIZE 1024  
  18.  
  19. #define MAX_AUDIOQ_SIZE (5 * 16 * 1024)  
  20. #define MAX_VIDEOQ_SIZE (5 * 256 * 1024)  
  21.  
  22. #define FF_ALLOC_EVENT   (SDL_USEREVENT)  
  23. #define FF_REFRESH_EVENT (SDL_USEREVENT + 1)  
  24. #define FF_QUIT_EVENT (SDL_USEREVENT + 2)  
  25. #define VIDEO_PICTURE_QUEUE_SIZE 1  
  26. #define SDL_AUDIO_BUFFER_SIZE 1024  
  27.   
  28. static int sws_flags = SWS_BICUBIC;  
  29.   
  30. typedef struct PacketQueue   
  31. {   
  32.     AVPacketList *first_pkt, *last_pkt;   
  33.     int nb_packets;   
  34.     int size;   
  35.     SDL_mutex *mutex;   
  36.     SDL_cond *cond;   
  37. } PacketQueue;   
  38.   
  39. typedef struct VideoPicture   
  40. {  
  41.     SDL_Overlay *bmp;  
  42.     int width, height; /* source height & width */  
  43.     int allocated;  
  44. } VideoPicture;  
  45.   
  46. typedef struct VideoState   
  47. {  
  48.     AVFormatContext *pFormatCtx;  
  49.     int             videoStream, audioStream;  
  50.     AVStream        *audio_st;  
  51.     PacketQueue     audioq;  
  52.     uint8_t         audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];  
  53.     unsigned int    audio_buf_size;  
  54.     unsigned int    audio_buf_index;  
  55.     AVPacket        audio_pkt;  
  56.     uint8_t         *audio_pkt_data;  
  57.     int             audio_pkt_size;  
  58.     AVStream        *video_st;  
  59.     PacketQueue     videoq;  
  60.   
  61.     VideoPicture    pictq[VIDEO_PICTURE_QUEUE_SIZE];  
  62.     int             pictq_size, pictq_rindex, pictq_windex;  
  63.     SDL_mutex       *pictq_mutex;  
  64.     SDL_cond        *pictq_cond;  
  65.   
  66.     SDL_Thread      *parse_tid;  
  67.     SDL_Thread      *video_tid;  
  68.   
  69.     char            filename[1024];  
  70.     int             quit;  
  71. } VideoState;  
  72.   
  73. SDL_Surface     *screen;  
  74.   
  75. /* Since we only have one decoding thread, the Big Struct 
  76. can be global in case we need it. */  
  77. VideoState *global_video_state;  
  78.   
  79. void packet_queue_init(PacketQueue *q)  
  80. {   
  81.     memset(q, 0, sizeof(PacketQueue));   
  82.     q->mutex = SDL_CreateMutex();   
  83.     q->cond = SDL_CreateCond();   
  84. }   
  85. int packet_queue_put(PacketQueue *q, AVPacket *pkt)   
  86. {   
  87.     AVPacketList *pkt1;   
  88.     if(av_dup_packet(pkt) < 0)   
  89.     {   
  90.         return -1;   
  91.     }   
  92.     pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));   
  93.     if (!pkt1)   
  94.         return -1;   
  95.     pkt1->pkt = *pkt;   
  96.     pkt1->next = NULL;   
  97.     SDL_LockMutex(q->mutex);   
  98.     if (!q->last_pkt)   
  99.         q->first_pkt = pkt1;   
  100.     else   
  101.         q->last_pkt->next = pkt1;   
  102.     q->last_pkt = pkt1;   
  103.     q->nb_packets++;   
  104.     q->size += pkt1->pkt.size;   
  105.     SDL_CondSignal(q->cond);   
  106.     SDL_UnlockMutex(q->mutex);   
  107.     return 0;   
  108. }   
  109. static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)   
  110. {   
  111.     AVPacketList *pkt1;   
  112.     int ret;   
  113.     SDL_LockMutex(q->mutex);   
  114.     for(;;)   
  115.     {   
  116.         if(global_video_state->quit)   
  117.         {   
  118.             ret = -1;   
  119.             break;   
  120.         }   
  121.         pkt1 = q->first_pkt;   
  122.         if (pkt1)   
  123.         {  
  124.             q->first_pkt = pkt1->next;   
  125.             if (!q->first_pkt)   
  126.                 q->last_pkt = NULL;   
  127.             q->nb_packets--;   
  128.             q->size -= pkt1->pkt.size;   
  129.             *pkt = pkt1->pkt;   
  130.             av_free(pkt1);   
  131.             ret = 1;   
  132.             break;   
  133.         }   
  134.         else if (!block)   
  135.         {   
  136.             ret = 0;   
  137.             break;   
  138.         }   
  139.         else   
  140.         {   
  141.             SDL_CondWait(q->cond, q->mutex);   
  142.         }   
  143.     }   
  144.     SDL_UnlockMutex(q->mutex);   
  145.     return ret;   
  146. }   
  147.   
  148. int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size)  
  149. {  
  150.     int len1, data_size;  
  151.     AVPacket *pkt = &is->audio_pkt;  
  152.   
  153.     for(;;)   
  154.     {  
  155.         while(is->audio_pkt_size > 0)   
  156.         {  
  157.             data_size = buf_size;  
  158.             len1 = avcodec_decode_audio2(is->audio_st->codec,   
  159.                 (int16_t *)audio_buf, &data_size,   
  160.                 is->audio_pkt_data, is->audio_pkt_size);  
  161.             if(len1 < 0)   
  162.             {  
  163.                 /* if error, skip frame */  
  164.                 is->audio_pkt_size = 0;  
  165.                 break;  
  166.             }  
  167.             is->audio_pkt_data += len1;  
  168.             is->audio_pkt_size -= len1;  
  169.             if(data_size <= 0)   
  170.             {  
  171.                 /* No data yet, get more frames */  
  172.                 continue;  
  173.             }  
  174.             /* We have data, return it and come back for more later */  
  175.             return data_size;  
  176.         }  
  177.         if(pkt->data)  
  178.             av_free_packet(pkt);  
  179.   
  180.         if(is->quit)   
  181.         {  
  182.             return -1;  
  183.         }  
  184.         /* next packet */  
  185.         if(packet_queue_get(&is->audioq, pkt, 1) < 0)   
  186.         {  
  187.             return -1;  
  188.         }  
  189.         is->audio_pkt_data = pkt->data;  
  190.         is->audio_pkt_size = pkt->size;  
  191.     }  
  192. }  
  193.   
  194. void audio_callback(void *userdata, Uint8 *stream, int len)  
  195. {  
  196.     VideoState *is = (VideoState *)userdata;  
  197.     int len1, audio_size;  
  198.   
  199.     while(len > 0)   
  200.     {  
  201.         if(is->audio_buf_index >= is->audio_buf_size)   
  202.         {  
  203.             /* We have already sent all our data; get more */  
  204.             audio_size = audio_decode_frame(isis->audio_buf, sizeof(is->audio_buf));  
  205.             if(audio_size < 0)   
  206.             {  
  207.                 /* If error, output silence */  
  208.                 is->audio_buf_size = 1024;  
  209.                 memset(is->audio_buf, 0, is->audio_buf_size);  
  210.             }   
  211.             else   
  212.             {  
  213.                 is->audio_buf_size = audio_size;  
  214.             }  
  215.             is->audio_buf_index = 0;  
  216.         }  
  217.         len1 = is->audio_buf_size - is->audio_buf_index;  
  218.         if(len1 > len)  
  219.             len1 = len;  
  220.         memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);  
  221.         len -= len1;  
  222.         stream += len1;  
  223.         is->audio_buf_index += len1;  
  224.     }  
  225. }  
  226.   
  227.   
  228. static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque)   
  229. {  
  230.     //printf("sdl_refresh_timer_cb called:interval--%d/n",interval);  
  231.     SDL_Event event;  
  232.     event.type = FF_REFRESH_EVENT;  
  233.     event.user.data1 = opaque;  
  234.     SDL_PushEvent(&event);      //派发FF_REFRESH_EVENT事件  
  235.     return 0; /* 0 means stop timer */  
  236. }  
  237.   
  238. /* schedule a video refresh in 'delay' ms */  
  239. static void schedule_refresh(VideoState *isint delay)   
  240. {  
  241.     //printf("schedule_refresh called:delay--%d/n",delay);  
  242.     SDL_AddTimer(delay, sdl_refresh_timer_cb, is);      //sdl_refresh_timer_cb函数在延时delay毫秒后,只会被执行一次,is是sdl_refresh_timer_cb的参数  
  243. }  
  244.   
  245. void video_display(VideoState *is)   
  246. {  
  247.     //printf("video_display called/n");  
  248.     SDL_Rect rect;  
  249.     VideoPicture *vp;  
  250.     AVPicture pict;  
  251.     float aspect_ratio;  
  252.     int w, h, x, y;  
  253.     int i;  
  254.   
  255.     vp = &is->pictq[is->pictq_rindex];  
  256.     if(vp->bmp)   
  257.     {  
  258.         if(is->video_st->codec->sample_aspect_ratio.num == 0)   
  259.         {  
  260.             aspect_ratio = 0;  
  261.         }   
  262.         else   
  263.         {  
  264.             aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio) *  
  265.                 is->video_st->codec->width / is->video_st->codec->height;  
  266.         }  
  267.         if(aspect_ratio <= 0.0)      //aspect_ratio 宽高比  
  268.         {  
  269.             aspect_ratio = (float)is->video_st->codec->width /  
  270.                 (float)is->video_st->codec->height;  
  271.         }  
  272.         h = screen->h;  
  273.         w = ((int)(h * aspect_ratio)) & -3;  
  274.         if(w > screen->w)   
  275.         {  
  276.             w = screen->w;  
  277.             h = ((int)(w / aspect_ratio)) & -3;  
  278.         }  
  279.         x = (screen->w - w) / 2;  
  280.         y = (screen->h - h) / 2;  
  281.   
  282.         rect.x = x;  
  283.         rect.y = y;  
  284.         rect.w = w;  
  285.         rect.h = h;  
  286.         SDL_DisplayYUVOverlay(vp->bmp, &rect);  
  287.     }  
  288. }  
  289.   
  290. void video_refresh_timer(void *userdata)   
  291. {  
  292.     VideoState *is = (VideoState *)userdata;  
  293.     VideoPicture *vp;  
  294.   
  295.     if(is->video_st)   
  296.     {  
  297.         if(is->pictq_size == 0)   
  298.         {  
  299.             schedule_refresh(is, 1);  
  300.         }   
  301.         else   
  302.         {  
  303.             vp = &is->pictq[is->pictq_rindex];  
  304.             /* Now, normally here goes a ton of code 
  305.             about timing, etc. we're just going to 
  306.             guess at a delay for now. You can 
  307.             increase and decrease this value and hard code 
  308.             the timing - but I don't suggest that ;) 
  309.             We'll learn how to do it for real later. 
  310.             */  
  311.             schedule_refresh(is, 80);  
  312.   
  313.             /* show the picture! */  
  314.             video_display(is);  
  315.   
  316.             /* update queue for next picture! */  
  317.             if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE)   
  318.             {  
  319.                 is->pictq_rindex = 0;  
  320.             }  
  321.             SDL_LockMutex(is->pictq_mutex);  
  322.             is->pictq_size--;  
  323.             SDL_CondSignal(is->pictq_cond);  
  324.             SDL_UnlockMutex(is->pictq_mutex);  
  325.         }  
  326.     }   
  327.     else   
  328.     {  
  329.         schedule_refresh(is, 100);  
  330.     }  
  331. }  
  332.   
  333. void alloc_picture(void *userdata)   
  334. {  
  335.     VideoState *is = (VideoState *)userdata;  
  336.     VideoPicture *vp;  
  337.   
  338.     vp = &is->pictq[is->pictq_windex];  
  339.     if(vp->bmp)   
  340.     {  
  341.         // we already have one make another, bigger/smaller  
  342.         SDL_FreeYUVOverlay(vp->bmp);  
  343.     }  
  344.     // Allocate a place to put our YUV image on that screen  
  345.     vp->bmp = SDL_CreateYUVOverlay(is->video_st->codec->width,  
  346.         is->video_st->codec->height,  
  347.         SDL_YV12_OVERLAY,  
  348.         screen);  
  349.     vp->width = is->video_st->codec->width;  
  350.     vp->height = is->video_st->codec->height;  
  351.   
  352.     SDL_LockMutex(is->pictq_mutex);  
  353.     vp->allocated = 1;  
  354.     SDL_CondSignal(is->pictq_cond);  
  355.     SDL_UnlockMutex(is->pictq_mutex);  
  356.   
  357. }  
  358.   
  359. int queue_picture(VideoState *is, AVFrame *pFrame)   
  360. {  
  361.     //printf("queue_picture called/n");  
  362.     VideoPicture *vp;  
  363.     int dst_pix_fmt;  
  364.     AVPicture pict;  
  365.     static struct SwsContext *img_convert_ctx;  
  366.     if (img_convert_ctx == NULL)   
  367.     {  
  368.         img_convert_ctx = sws_getContext(is->video_st->codec->width, is->video_st->codec->height,  
  369.                                          is->video_st->codec->pix_fmt,  
  370.                                          is->video_st->codec->width, is->video_st->codec->height,  
  371.                                          PIX_FMT_YUV420P,  
  372.                                          sws_flags, NULL, NULL, NULL);  
  373.         if (img_convert_ctx == NULL)   
  374.         {  
  375.             fprintf(stderr, "Cannot initialize the conversion context/n");  
  376.             exit(1);  
  377.         }  
  378.     }  
  379.   
  380.     /* wait until we have space for a new pic */  
  381.     SDL_LockMutex(is->pictq_mutex);  
  382.     while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&  
  383.         !is->quit)   
  384.     {  
  385.         SDL_CondWait(is->pictq_cond, is->pictq_mutex);  
  386.     }  
  387.     SDL_UnlockMutex(is->pictq_mutex);  
  388.   
  389.     if(is->quit)  
  390.         return -1;  
  391.   
  392.     // windex is set to 0 initially  
  393.     vp = &is->pictq[is->pictq_windex];  
  394.       
  395.     /* allocate or resize the buffer! */  
  396.     if(!vp->bmp ||  
  397.         vp->width != is->video_st->codec->width ||  
  398.         vp->height != is->video_st->codec->height)   
  399.     {  
  400.         SDL_Event event;  
  401.   
  402.         vp->allocated = 0;  
  403.         /* we have to do it in the main thread */  
  404.         event.type = FF_ALLOC_EVENT;  
  405.         event.user.data1 = is;  
  406.         SDL_PushEvent(&event);  
  407.   
  408.         /* wait until we have a picture allocated */  
  409.         SDL_LockMutex(is->pictq_mutex);  
  410.         while(!vp->allocated && !is->quit)   
  411.         {  
  412.             SDL_CondWait(is->pictq_cond, is->pictq_mutex);    //没有得到消息时解锁,得到消息后加锁,和SDL_CondSignal配对使用  
  413.         }  
  414.         SDL_UnlockMutex(is->pictq_mutex);  
  415.         if(is->quit)   
  416.         {  
  417.             return -1;  
  418.         }  
  419.     }  
  420.     /* We have a place to put our picture on the queue */  
  421.   
  422.     if(vp->bmp)   
  423.     {  
  424.         SDL_LockYUVOverlay(vp->bmp);  
  425.         dst_pix_fmt = PIX_FMT_YUV420P;  
  426.         /* point pict at the queue */  
  427.   
  428.         pict.data[0] = vp->bmp->pixels[0];  
  429.         pict.data[1] = vp->bmp->pixels[2];  
  430.         pict.data[2] = vp->bmp->pixels[1];  
  431.   
  432.         pict.linesize[0] = vp->bmp->pitches[0];  
  433.         pict.linesize[1] = vp->bmp->pitches[2];  
  434.         pict.linesize[2] = vp->bmp->pitches[1];  
  435.   
  436.         // Convert the image into YUV format that SDL uses  
  437.         sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize,  
  438.                     0, is->video_st->codec->height, pict.data, pict.linesize);  
  439.         SDL_UnlockYUVOverlay(vp->bmp);  
  440.         /* now we inform our display thread that we have a pic ready */  
  441.         if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE)   
  442.         {  
  443.             is->pictq_windex = 0;  
  444.         }  
  445.         SDL_LockMutex(is->pictq_mutex);  
  446.         is->pictq_size++;  
  447.         SDL_UnlockMutex(is->pictq_mutex);  
  448.     }  
  449.     return 0;  
  450. }  
  451.   
  452. int video_thread(void *arg)   
  453. {  
  454.     //printf("video_thread called");  
  455.     VideoState *is = (VideoState *)arg;  
  456.     AVPacket pkt1, *packet = &pkt1;  
  457.     int len1, frameFinished;  
  458.     AVFrame *pFrame;  
  459.   
  460.     pFrame = avcodec_alloc_frame();  
  461.   
  462.     for(;;)   
  463.     {  
  464.         if(packet_queue_get(&is->videoq, packet, 1) < 0)   
  465.         {  
  466.             // means we quit getting packets  
  467.             break;  
  468.         }  
  469.         // Decode video frame  
  470.         len1 = avcodec_decode_video(is->video_st->codec, pFrame, &frameFinished,   
  471.             packet->data, packet->size);  
  472.   
  473.         // Did we get a video frame?  
  474.         if(frameFinished)   
  475.         {  
  476.             if(queue_picture(is, pFrame) < 0)   
  477.             {  
  478.                 break;  
  479.             }  
  480.         }  
  481.         av_free_packet(packet);  
  482.     }  
  483.     av_free(pFrame);  
  484.     return 0;  
  485. }  
  486.   
  487. int stream_component_open(VideoState *isint stream_index)   
  488. {  
  489.     AVFormatContext *pFormatCtx = is->pFormatCtx;  
  490.     AVCodecContext *codecCtx;  
  491.     AVCodec *codec;  
  492.     SDL_AudioSpec wanted_spec, spec;  
  493.   
  494.     if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams)   
  495.     {  
  496.         return -1;  
  497.     }  
  498.   
  499.     // Get a pointer to the codec context for the video stream  
  500.     codecCtx = pFormatCtx->streams[stream_index]->codec;  
  501.   
  502.     if(codecCtx->codec_type == CODEC_TYPE_AUDIO)   
  503.     {  
  504.         // Set audio settings from codec info  
  505.         wanted_spec.freq = codecCtx->sample_rate;  
  506.         wanted_spec.format = AUDIO_S16SYS;  
  507.         wanted_spec.channels = codecCtx->channels;  
  508.         wanted_spec.silence = 0;  
  509.         wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  
  510.         wanted_spec.callback = audio_callback;  
  511.         wanted_spec.userdata = is;  
  512.   
  513.         if(SDL_OpenAudio(&wanted_spec, &spec) < 0)   
  514.         {  
  515.             fprintf(stderr, "SDL_OpenAudio: %s/n", SDL_GetError());  
  516.             return -1;  
  517.         }  
  518.     }  
  519.     codec = avcodec_find_decoder(codecCtx->codec_id);  
  520.     if(!codec || (avcodec_open(codecCtx, codec) < 0))   
  521.     {  
  522.         fprintf(stderr, "Unsupported codec!/n");  
  523.         return -1;  
  524.     }  
  525.   
  526.     switch(codecCtx->codec_type)   
  527.     {  
  528.     case CODEC_TYPE_AUDIO:  
  529.         is->audioStream = stream_index;  
  530.         is->audio_st = pFormatCtx->streams[stream_index];  
  531.         is->audio_buf_size = 0;  
  532.         is->audio_buf_index = 0;  
  533.         memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));  
  534.         packet_queue_init(&is->audioq);  
  535.         SDL_PauseAudio(0);  
  536.         break;  
  537.     case CODEC_TYPE_VIDEO:  
  538.         is->videoStream = stream_index;  
  539.         is->video_st = pFormatCtx->streams[stream_index];  
  540.   
  541.         packet_queue_init(&is->videoq);  
  542.         is->video_tid = SDL_CreateThread(video_thread, is);  
  543.         break;  
  544.     default:  
  545.         break;  
  546.     }  
  547. }  
  548.   
  549. int decode_interrupt_cb(void)   
  550. {  
  551.     return (global_video_state && global_video_state->quit);  
  552. }  
  553.   
  554. int decode_thread(void *arg)   
  555. {  
  556.     VideoState *is = (VideoState *)arg;  
  557.     AVFormatContext *pFormatCtx;  
  558.     AVPacket pkt1, *packet = &pkt1;  
  559.   
  560.     int video_index = -1;  
  561.     int audio_index = -1;  
  562.     int i;  
  563.   
  564.     is->videoStream=-1;  
  565.     is->audioStream=-1;  
  566.   
  567.     global_video_state = is;  
  568.     // will interrupt blocking functions if we quit!  
  569.     url_set_interrupt_cb(decode_interrupt_cb);  
  570.   
  571.     // Open video file  
  572.     if(av_open_input_file(&pFormatCtx, is->filename, NULL, 0, NULL)!=0)  
  573.         return -1; // Couldn't open file  
  574.   
  575.     is->pFormatCtx = pFormatCtx;  
  576.   
  577.     // Retrieve stream information  
  578.     if(av_find_stream_info(pFormatCtx)<0)  
  579.         return -1; // Couldn't find stream information  
  580.   
  581.     // Dump information about file onto standard error  
  582.     dump_format(pFormatCtx, 0, is->filename, 0);  
  583.   
  584.     // Find the first video stream  
  585.   
  586.     for(i=0; i<pFormatCtx->nb_streams; i++)  
  587.     {  
  588.         if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO &&  
  589.             video_index < 0)   
  590.         {  
  591.                 video_index=i;  
  592.         }  
  593.         if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO &&  
  594.             audio_index < 0)   
  595.         {  
  596.                 audio_index=i;  
  597.         }  
  598.     }  
  599.     if(audio_index >= 0)   
  600.     {  
  601.         stream_component_open(is, audio_index);  
  602.     }  
  603.     if(video_index >= 0)   
  604.     {  
  605.         stream_component_open(is, video_index);  
  606.     }     
  607.   
  608.     if(is->videoStream < 0 || is->audioStream < 0)   
  609.     {  
  610.         fprintf(stderr, "%s: could not open codecs/n"is->filename);  
  611.         goto fail;  
  612.     }  
  613.   
  614.     // main decode loop  
  615.     for(;;)   
  616.     {  
  617.         if(is->quit)   
  618.         {  
  619.             break;  
  620.         }  
  621.         // seek stuff goes here  
  622.         if(is->audioq.size > MAX_AUDIOQ_SIZE ||  
  623.             is->videoq.size > MAX_VIDEOQ_SIZE)   
  624.         {  
  625.                 SDL_Delay(10);  
  626.                 continue;  
  627.         }  
  628.         if(av_read_frame(is->pFormatCtx, packet) < 0)   
  629.         {  
  630.             if(url_ferror(pFormatCtx->pb) == 0)   
  631.             {  
  632.                 SDL_Delay(100); /* no error; wait for user input */  
  633.                 continue;  
  634.             }   
  635.             else   
  636.             {  
  637.                 break;  
  638.             }  
  639.         }  
  640.         // Is this a packet from the video stream?  
  641.         if(packet->stream_index == is->videoStream)  
  642.         {  
  643.             packet_queue_put(&is->videoq, packet);  
  644.         }   
  645.         else if(packet->stream_index == is->audioStream)  
  646.         {  
  647.             packet_queue_put(&is->audioq, packet);  
  648.         }   
  649.         else  
  650.         {  
  651.             av_free_packet(packet);  
  652.         }  
  653.     }  
  654.     /* all done - wait for it */  
  655.     while(!is->quit)   
  656.     {  
  657.         SDL_Delay(100);  
  658.     }  
  659. fail:  
  660.     SDL_Event event;  
  661.     event.type = FF_QUIT_EVENT;  
  662.     event.user.data1 = is;  
  663.     SDL_PushEvent(&event);  
  664.     return 0;  
  665. }  
  666.   
  667. int main(int argc, char *argv[])  
  668. {  
  669.     SDL_Event       event;  
  670.     VideoState      *is;  
  671.     is = (VideoState *)av_mallocz(sizeof(VideoState));  
  672.     if(argc < 2)   
  673.     {  
  674.         fprintf(stderr, "Usage: test <file>/n");  
  675.         exit(1);  
  676.     }  
  677.     // Register all formats and codecs  
  678.     av_register_all();  
  679.   
  680.     if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))   
  681.     {  
  682.         fprintf(stderr, "Could not initialize SDL - %s/n", SDL_GetError());  
  683.         exit(1);  
  684.     }  
  685.   
  686.     // Make a screen to put our video  
  687. #ifndef __DARWIN__  
  688.     screen = SDL_SetVideoMode(640, 480, 0, 0);  
  689. #else  
  690.     screen = SDL_SetVideoMode(640, 480, 24, 0);  
  691. #endif  
  692.     if(!screen)  
  693.     {  
  694.         fprintf(stderr, "SDL: could not set video mode - exiting/n");  
  695.         exit(1);  
  696.     }  
  697.   
  698.     //pstrcpy(is->filename, sizeof(is->filename), argv[1]);  
  699.     strcpy(is->filename,argv[1]);  
  700.     is->pictq_mutex = SDL_CreateMutex();  
  701.     is->pictq_cond = SDL_CreateCond();  
  702.   
  703.     schedule_refresh(is, 40);  
  704.     is->parse_tid = SDL_CreateThread(decode_thread, is);  
  705.   
  706.     if(!is->parse_tid)  
  707.     {  
  708.         av_free(is);  
  709.         return -1;  
  710.     }  
  711.     for(;;)  
  712.     {  
  713.         SDL_WaitEvent(&event);  
  714.         switch(event.type)   
  715.         {  
  716.         case FF_QUIT_EVENT:  
  717.             printf("FF_QUIT_EVENT recieved");  
  718.         case SDL_QUIT:  
  719.             printf("SDL_QUIT recieved");  
  720.             is->quit = 1;  
  721.             SDL_Quit();  
  722.             return 0;  
  723.             break;  
  724.         case FF_ALLOC_EVENT:  
  725.             alloc_picture(event.user.data1);  
  726.             break;  
  727.         case FF_REFRESH_EVENT:  
  728.             video_refresh_timer(event.user.data1);  
  729.             break;  
  730.         default:  
  731.             break;  
  732.         }  
  733.     }  
  734.     return 0;  
  735. }  

程序结构分析:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值