ffplay程序运行流程分析

http://blog.csdn.net/yangping1220/article/details/11232419

1、main()开始:

 

分别注册编解码器,复用以及解复用器


[cpp]  view plain copy print ?
  1. avcodec_register_all(); //register codec  
  2. avdevice_register_all();  
  3. av_register_all();  //register demux and mux  


接着就是一些分配内存空间的代码

代码 略

[csharp]  view plain copy print ?
  1. parse_options(argc, argv, options, opt_input_file);  //分析是否带有选项?  
  2. flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;   //设置SDL的参数,我们选择了显示图像声音,并注册了一个定时器  
  3. SDL_Init (flags)          <span style="white-space:pre">            </span>//初始化SDL  
  4. SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE);  //设置SDL的响应事件  
  5. SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);  
  6. SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);  
  7. SDL_EventState(SDL_USEREVENT, SDL_IGNORE);  
  8. av_init_packet(&flush_pkt);         //初始化一个AVPacket  


这个是重点:

[csharp]  view plain copy print ?
  1. cur_stream = stream_open(input_filename, file_iformat);  //打开一个流,可以是file,tcp,rtp,udp,http等  


2、具体分析stream_open()函数:

 

首先初始化了显示视频要的互斥锁,条件变量


[csharp]  view plain copy print ?
  1. is->pictq_mutex = SDL_CreateMutex();  
  2. is->pictq_cond = SDL_CreateCond();  
  3.   
  4. is->subpq_mutex = SDL_CreateMutex();  
  5. is->subpq_cond = SDL_CreateCond();  
  6. /* add the refresh timer to draw the picture */  
  7. schedule_refresh(is, 40);   
  8. is->parse_tid = SDL_CreateThread(decode_thread, is); //创建解码线程  


stream_open()函数调用结束。    

 

3、接着分析decode_thread线程:

开始会初始化VideoState的部分参数

[csharp]  view plain copy print ?
  1. static int decode_thread(void *arg)  
  2. {  
  3.     VideoState *is = arg;  
  4.     AVFormatContext *ic;  
  5.     int err, i, ret, video_index, audio_index, subtitle_index;  
  6.     AVPacket pkt1, *pkt = &pkt1;  
  7.     AVFormatParameters params, *ap = ¶ms;  
  8.   
  9.     video_index = -1;  
  10.     audio_index = -1;  
  11.     subtitle_index = -1;  
  12.     is->video_stream = -1;  
  13.     is->audio_stream = -1;  
  14.     is->subtitle_stream = -1;  
  15.   
  16.     global_video_state = is;  
  17.     url_set_interrupt_cb(decode_interrupt_cb);  
  18.   
  19.     memset(ap, 0, sizeof(*ap));  
  20.   
  21.     ap->width = frame_width;  
  22.     ap->height= frame_height;  
  23.     ap->time_base= (AVRational){1, 25};  
  24.     ap->pix_fmt = frame_pix_fmt;  
  25.   
  26.     err = av_open_input_file(&ic, is->filename, is->iformat, 0, ap);  //要求打开文件或流  
  27.   
  28.     if (err < 0) {  
  29.         print_error(is->filename, err);  
  30.         ret = -1;  
  31.         goto fail;  
  32.     }  
  33.     is->ic = ic;  
  34.   
  35.     if(genpts)  
  36.         ic->flags |= AVFMT_FLAG_GENPTS;  
  37.   
  38.     err = av_find_stream_info(ic);        //从文件中找到媒体即填充AVFormatContext结构体  
  39.   
  40.     if (err < 0) {  
  41.         fprintf(stderr, "%s: could not find codec parameters\n"is->filename);  
  42.         ret = -1;  
  43.         goto fail;  
  44.     }  
  45.     if(ic->pb)  
  46.         ic->pb->eof_reached= 0; //FIXME hack, ffplay maybe should not use url_feof() to test for the end  
  47.   
  48.     /* if seeking requested, we execute it */  
  49.     if (start_time != AV_NOPTS_VALUE) {  
  50.         int64_t timestamp;  
  51.   
  52.         timestamp = start_time;  
  53.         /* add the stream start time */  
  54.         if (ic->start_time != AV_NOPTS_VALUE)  
  55.             timestamp += ic->start_time;  
  56.         ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD);  
  57.         if (ret < 0) {  
  58.             fprintf(stderr, "%s: could not seek to position %0.3f\n",  
  59.                     is->filename, (double)timestamp / AV_TIME_BASE);  
  60.         }  
  61.     }  
  62.   
  63.     for(i = 0; i < ic->nb_streams; i++) {           //从流中探测是否有音频流和视频流,字幕流,并将找到的流ID赋值给video  
  64.         AVCodecContext *enc = ic->streams[i]->codec;//_index,探测完所有的流后,退出  
  65.         ic->streams[i]->discard = AVDISCARD_ALL;  
  66.         switch(enc->codec_type) {  
  67.         case CODEC_TYPE_AUDIO:  
  68.             if (wanted_audio_stream-- >= 0 && !audio_disable)  
  69.                 audio_index = i;  //found audio stream  
  70.             break;  
  71.         case CODEC_TYPE_VIDEO:  
  72.             if (wanted_video_stream-- >= 0 && !video_disable)  
  73.                 video_index = i; //founf video stream  
  74.             break;  
  75.         case CODEC_TYPE_SUBTITLE:  
  76.             if (wanted_subtitle_stream-- >= 0 && !video_disable)  
  77.                 subtitle_index = i; //found subtile stream  
  78.             break;  
  79.         default:  
  80.             break;  
  81.         }  
  82.     }  
  83.     if (show_status) {  
  84.         dump_format(ic, 0, is->filename, 0);//  
  85.         dump_stream_info(ic);  
  86.     }  
  87.   
  88.     /* open the streams */  
  89.     if (audio_index >= 0) {  
  90.         stream_component_open(is, audio_index);  //此函数里,会新建一个解码线程,用于音频解码  
  91.     }  
  92.   
  93.     if (video_index >= 0) {  
  94.         stream_component_open(is, video_index);   //此函数里,会新建一个解码线程,用于视频解码  
  95.   
  96.     } else {  
  97.         if (!display_disable)  
  98.             is->show_audio = 1;  
  99.     }  
  100.   
  101.     if (subtitle_index >= 0) {  
  102.         stream_component_open(is, subtitle_index);  
  103.     }  
  104.   
  105.     if (is->video_stream < 0 && is->audio_stream < 0) {  
  106.         fprintf(stderr, "%s: could not open codecs\n"is->filename);  
  107.         ret = -1;  
  108.         goto fail;  
  109.     }  
  110.   
  111.     for(;;) {  
  112.         if (is->abort_request)  
  113.             break;  
  114.         if (is->paused != is->last_paused) {  
  115.             is->last_paused = is->paused;  
  116.             if (is->paused)  
  117.                 av_read_pause(ic);  
  118.             else  
  119.                 av_read_play(ic);  
  120.         }  
  121. #if CONFIG_RTSP_DEMUXER  
  122.         if (is->paused && !strcmp(ic->iformat->name, "rtsp")) {  
  123.             /* wait 10 ms to avoid trying to get another packet */  
  124.             /* XXX: horrible */  
  125.             SDL_Delay(10);  
  126.             continue;  
  127.         }  
  128. #endif  
  129.         if (is->seek_req) {  
  130.             int stream_index= -1;  
  131.             int64_t seek_target= is->seek_pos;  
  132.   
  133.             if     (is->   video_stream >= 0) stream_index= is->   video_stream;  
  134.             else if(is->   audio_stream >= 0) stream_index= is->   audio_stream;  
  135.             else if(is->subtitle_stream >= 0) stream_index= is->subtitle_stream;  
  136.   
  137.             if(stream_index>=0){  
  138.                 seek_target= av_rescale_q(seek_target, AV_TIME_BASE_Q, ic->streams[stream_index]->time_base); //av_rescale_q(a,b,c)是用来把时间戳从一个时基调整到另外一个时基时候用的函数  
  139.             }  
  140.   
  141.             ret = av_seek_frame(is->ic, stream_index, seek_target, is->seek_flags); //移动IC的指针(实现快进快退的功能)  
  142.             if (ret < 0) {  
  143.                 fprintf(stderr, "%s: error while seeking\n"is->ic->filename);  
  144.             }else{  
  145.                 if (is->audio_stream >= 0) {  
  146.                     packet_queue_flush(&is->audioq);          //清空pkt队列缓冲区 ,这是因为is->seek_req==1,用户要实现视频片段的跳转。在跳转到一个新的片段时,之前的缓冲区需要清空  
  147.                     packet_queue_put(&is->audioq, &flush_pkt); //队列已经被清空,需要将flush_pkt包(清空包)加入到队列,相当于一个链表的header(这样说不知道是不是正确)  
  148.                 }  
  149.                 if (is->subtitle_stream >= 0) {  
  150.                     packet_queue_flush(&is->subtitleq);  
  151.                     packet_queue_put(&is->subtitleq, &flush_pkt);  
  152.                 }  
  153.                 if (is->video_stream >= 0) {  
  154.                     packet_queue_flush(&is->videoq);  
  155.                     packet_queue_put(&is->videoq, &flush_pkt);  
  156.                 }  
  157.             }  
  158.             is->seek_req = 0;  
  159.         }  
  160.   
  161.         /* if the queue are full, no need to read more */  
  162.         if (is->audioq.size > MAX_AUDIOQ_SIZE ||  
  163.             is->videoq.size > MAX_VIDEOQ_SIZE ||  
  164.             is->subtitleq.size > MAX_SUBTITLEQ_SIZE) {  
  165.             /* wait 10 ms */  
  166.             SDL_Delay(10);  
  167.             continue;  
  168.         }  
  169.         if(url_feof(ic->pb)) {  
  170.             av_init_packet(pkt);  
  171.             pkt->data=NULL;  
  172.             pkt->size=0;  
  173.             pkt->stream_index= is->video_stream;  
  174.             packet_queue_put(&is->videoq, pkt);  
  175.             continue;  
  176.         }  
  177.         ret = av_read_frame(ic, pkt);   //读取一帧数据到pkt  
  178.         if (ret < 0) {  
  179.             if (ret != AVERROR_EOF && url_ferror(ic->pb) == 0) {  
  180.                 SDL_Delay(100); /* wait for user event */  
  181.                 continue;  
  182.             } else  
  183.                 break;  
  184.         }  
  185.         if (pkt->stream_index == is->audio_stream) {   //如果该帧为音频帧,则将该包加入到音频队列中  
  186.             packet_queue_put(&is->audioq, pkt);  
  187.         } else if (pkt->stream_index == is->video_stream) {  
  188.             packet_queue_put(&is->videoq, pkt);  
  189.         } else if (pkt->stream_index == is->subtitle_stream) {  
  190.             packet_queue_put(&is->subtitleq, pkt);  
  191.         } else {  
  192.             av_free_packet(pkt);                      //如果不为以上的包,则释放该包。  
  193.         }  
  194.     }  
  195.     /* wait until the end */  
  196.     while (!is->abort_request) {  
  197.         SDL_Delay(100);  
  198.     }  
  199.   
  200.     ret = 0;  
  201.  fail:  
  202.     /* disable interrupting */  
  203.     global_video_state = NULL;  
  204.   
  205.     /* close each stream */  
  206.     if (is->audio_stream >= 0)  
  207.         stream_component_close(isis->audio_stream);    
  208.     if (is->video_stream >= 0)  
  209.         stream_component_close(isis->video_stream);  
  210.     if (is->subtitle_stream >= 0)  
  211.         stream_component_close(isis->subtitle_stream);  
  212.     if (is->ic) {  
  213.         av_close_input_file(is->ic);  
  214.         is->ic = NULL; /* safety */  
  215.     }  
  216.     url_set_interrupt_cb(NULL);  
  217.   
  218.     if (ret != 0) {  
  219.         SDL_Event event;  
  220.   
  221.         event.type = FF_QUIT_EVENT;  
  222.         event.user.data1 = is;  
  223.         SDL_PushEvent(&event);  
  224.     }  
  225.     return 0;  
  226. }  


4、下面再来分析视频解码线程(函数为 ffplay.c / stream_component_open())

下面是解码线程


[csharp]  view plain copy print ?
  1. static int stream_component_open(VideoState *isint stream_index)  
  2. {  
  3.     AVFormatContext *ic = is->ic;  
  4.     AVCodecContext *enc;  
  5.     AVCodec *codec;  
  6.     <strong>SDL_AudioSpec wanted_spec, spec;</strong>  
  7.   
  8.     if (stream_index < 0 || stream_index >= ic->nb_streams)  
  9.         return -1;  
  10.     enc = ic->streams[stream_index]->codec;  
  11.   
  12.     /* prepare audio output */  
  13.     if (enc->codec_type == CODEC_TYPE_AUDIO) {   //如果为音频解码,需要初始化参数  
  14.         if (enc->channels > 0) {  
  15.             enc->request_channels = FFMIN(2, enc->channels);  
  16.         } else {  
  17.             enc->request_channels = 2;  
  18.         }  
  19.     }  
  20.   
  21.     codec = avcodec_find_decoder(enc->codec_id);  //根据id找到具体的解码器  
  22.     enc->debug_mv = debug_mv;  
  23.     enc->debug = debug;  
  24.     enc->workaround_bugs = workaround_bugs;  
  25.     enc->lowres = lowres;  
  26.     if(lowres) enc->flags |= CODEC_FLAG_EMU_EDGE;  
  27.     enc->idct_algo= idct;  
  28.     if(fast) enc->flags2 |= CODEC_FLAG2_FAST;  
  29.     enc->skip_frame= skip_frame;  
  30.     enc->skip_idct= skip_idct;  
  31.     enc->skip_loop_filter= skip_loop_filter;  
  32.     enc->error_recognition= error_recognition;  
  33.     enc->error_concealment= error_concealment;  
  34.   
  35.     set_context_opts(enc, avctx_opts[enc->codec_type], 0); //set  decode  option  
  36.   
  37.     if (!codec ||  
  38.         avcodec_open(enc, codec) < 0) //open decode  
  39.         return -1;  
  40.   
  41.     /* prepare audio output */  
  42.     if (enc->codec_type == CODEC_TYPE_AUDIO) {  
  43.         wanted_spec.freq = enc->sample_rate;  
  44.         wanted_spec.format = AUDIO_S16SYS;  
  45.         wanted_spec.channels = enc->channels;  
  46.         wanted_spec.silence = 0;  
  47.         wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  
  48.         wanted_spec.callback = <strong><span style="color:#ff0000;">sdl_audio_callback</span></strong>; //此处初始化了一个音频解码线程(线程是在SDL中实现的,此接口是对用户提供了一个回调函数接口)  
  49.         wanted_spec.userdata = is;  
  50.         if (SDL_OpenAudio(&wanted_spec, &spec) < 0) {  
  51.             fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());  
  52.             return -1;  
  53.         }  
  54.         is->audio_hw_buf_size = spec.size;  
  55.         is->audio_src_fmt= SAMPLE_FMT_S16;  
  56.     }  
  57.   
  58.     if(thread_count>1)  
  59.         avcodec_thread_init(enc, thread_count);  //解码线程初始化  
  60.     enc->thread_count= thread_count;  
  61.     ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;  
  62.     switch(enc->codec_type) {  
  63.     case CODEC_TYPE_AUDIO:  
  64.         is->audio_stream = stream_index;  
  65.         is->audio_st = ic->streams[stream_index];  
  66.         is->audio_buf_size = 0;  
  67.         is->audio_buf_index = 0;  
  68.   
  69.         /* init averaging filter */  
  70.         is->audio_diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB);  
  71.         is->audio_diff_avg_count = 0;  
  72.         /* since we do not have a precise anough audio fifo fullness, 
  73.            we correct audio sync only if larger than this threshold */  
  74.         is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / enc->sample_rate;  
  75.   
  76.         memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));  
  77.         packet_queue_init(&is->audioq);  
  78.         SDL_PauseAudio(0);  
  79.         break;  
  80.     case CODEC_TYPE_VIDEO:  
  81.         is->video_stream = stream_index;  
  82.         is->video_st = ic->streams[stream_index];  
  83.   
  84.         is->frame_last_delay = 40e-3;  
  85.         is->frame_timer = (double)av_gettime() / 1000000.0;  
  86.         is->video_current_pts_time = av_gettime();  
  87.   
  88.         packet_queue_init(&is->videoq);  
  89.         is->video_tid = SDL_CreateThread(video_thread, is); //如果为视频,则创建一个视频处理线程  
  90.         break;  
  91.     case CODEC_TYPE_SUBTITLE:  
  92.         is->subtitle_stream = stream_index;  
  93.         is->subtitle_st = ic->streams[stream_index];  
  94.         packet_queue_init(&is->subtitleq);  
  95.   
  96.         is->subtitle_tid = SDL_CreateThread(subtitle_thread, is);  
  97.         break;  
  98.     default:  
  99.         break;  
  100.     }  
  101.     return 0;  
  102. }  



5、接着分析video_thread线程:

[csharp]  view plain copy print ?
  1. static int video_thread(void *arg)  
  2. {  
  3.     VideoState *is = arg;  
  4.     AVPacket pkt1, *pkt = &pkt1;  
  5.     int len1, got_picture;  
  6.     AVFrame *frame= avcodec_alloc_frame();  
  7.     double pts;  
  8.   
  9.     for(;;) {  
  10.         while (is->paused && !is->videoq.abort_request) {  
  11.             SDL_Delay(10);  
  12.         }  
  13.         if (packet_queue_get(&is->videoq, pkt, 1) < 0)  //从队列获取一帧数据  
  14.             break;  
  15.   
  16.         if(pkt->data == flush_pkt.data){               //如果当前包的数据等于清空包的数据,则刷新解码器的缓冲  
  17.             avcodec_flush_buffers(is->video_st->codec);  
  18.             continue;  
  19.         }  
  20.   
  21.         /* NOTE: ipts is the PTS of the _first_ picture beginning in 
  22.            this packet, if any */  
  23.         is->video_st->codec->reordered_opaque= pkt->pts;  
  24.         len1 = avcodec_decode_video(is->video_st->codec,   //解码视频  
  25.                                     frame, &got_picture,  
  26.                                     pkt->data, pkt->size);  
  27.   
  28.         if(   (decoder_reorder_pts || pkt->dts == AV_NOPTS_VALUE)  //解码or播放的时间 dts、pts  
  29.            && frame->reordered_opaque != AV_NOPTS_VALUE)  
  30.             pts= frame->reordered_opaque;  
  31.         else if(pkt->dts != AV_NOPTS_VALUE)  
  32.             pts= pkt->dts;  
  33.         else  
  34.             pts= 0;  
  35.         pts *= av_q2d(is->video_st->time_base);  
  36.   
  37. //            if (len1 < 0)  
  38. //                break;  
  39.         if (got_picture) {  
  40.             if (output_picture2(is, frame, pts) < 0)    //输出图像  
  41.                 goto the_end;  
  42.         }  
  43.         av_free_packet(pkt);                              //释放缓冲  
  44.         if (step)  
  45.             if (cur_stream)  
  46.                 stream_pause(cur_stream);  
  47.     }  
  48.  the_end:  
  49.     av_free(frame);  
  50.     return 0;  
  51. }  


6、最后分析一下main中的event_loop()


[csharp]  view plain copy print ?
  1. /* handle an event sent by the GUI */  
  2. static void event_loop(void)  
  3. {  
  4.     SDL_Event event;  
  5.     double incr, pos, frac;  
  6.   
  7.     for(;;) {  
  8.         SDL_WaitEvent(&event);  
  9.         switch(event.type) {  
  10.         case SDL_KEYDOWN:   //如果为按键事件  
  11.             switch(event.key.keysym.sym) {  
  12.             case SDLK_ESCAPE:  
  13.             case SDLK_q:   //如果按下 Q 则退出  
  14.                 do_exit();  
  15.                 break;  
  16.             case SDLK_f:   //如果按下 F 则全屏  
  17.                 toggle_full_screen();  
  18.                 break;  
  19.             case SDLK_p:  
  20.             case SDLK_SPACE: //如果按下SPACE 则暂停  
  21.                 toggle_pause();  
  22.                 break;  
  23.             case SDLK_s: //S: Step to next frame  
  24.                 step_to_next_frame();  
  25.                 break;  
  26.             case SDLK_a:  
  27.                 if (cur_stream)  
  28.                     stream_cycle_channel(cur_stream, CODEC_TYPE_AUDIO);  
  29.                 break;  
  30.             case SDLK_v:  
  31.                 if (cur_stream)  
  32.                     stream_cycle_channel(cur_stream, CODEC_TYPE_VIDEO);  
  33.                 break;  
  34.             case SDLK_t:  
  35.                 if (cur_stream)  
  36.                     stream_cycle_channel(cur_stream, CODEC_TYPE_SUBTITLE);  
  37.                 break;  
  38.             case SDLK_w:  
  39.                 toggle_audio_display();  
  40.                 break;  
  41.             case SDLK_LEFT:  
  42.                 incr = -10.0;  
  43.                 goto do_seek;  
  44.             case SDLK_RIGHT:  
  45.                 incr = 10.0;  
  46.                 goto do_seek;  
  47.             case SDLK_UP:  
  48.                 incr = 60.0;  
  49.                 goto do_seek;  
  50.             case SDLK_DOWN:  
  51.                 incr = -60.0;  
  52.             do_seek:  
  53.                 if (cur_stream) {  
  54.                     if (seek_by_bytes) {  
  55.                         pos = url_ftell(cur_stream->ic->pb);  
  56.                         if (cur_stream->ic->bit_rate)  
  57.                             incr *= cur_stream->ic->bit_rate / 60.0;  
  58.                         else  
  59.                             incr *= 180000.0;  
  60.                         pos += incr;  
  61.                         stream_seek(cur_stream, pos, incr);  
  62.                     } else {  
  63.                         pos = get_master_clock(cur_stream);  
  64.                         pos += incr;  
  65.                         stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), incr);  
  66.                     }  
  67.                 }  
  68.                 break;  
  69.             default:  
  70.                 break;  
  71.             }  
  72.             break;  
  73.         case SDL_MOUSEBUTTONDOWN:  
  74.             if (cur_stream) {  
  75.                 int ns, hh, mm, ss;  
  76.                 int tns, thh, tmm, tss;  
  77.                 tns = cur_stream->ic->duration/1000000LL;  
  78.                 thh = tns/3600;  
  79.                 tmm = (tns%3600)/60;  
  80.                 tss = (tns%60);  
  81.                 frac = (double)event.button.x/(double)cur_stream->width;  
  82.                 ns = frac*tns;  
  83.                 hh = ns/3600;  
  84.                 mm = (ns%3600)/60;  
  85.                 ss = (ns%60);  
  86.                 fprintf(stderr, "Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d)       \n", frac*100,  
  87.                         hh, mm, ss, thh, tmm, tss);  
  88.                 stream_seek(cur_stream, (int64_t)(cur_stream->ic->start_time+frac*cur_stream->ic->duration), 0);  
  89.             }  
  90.             break;  
  91.         case SDL_VIDEORESIZE:  
  92.             if (cur_stream) {  
  93.                 screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 0,  
  94.                                           SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL);  
  95.                 screen_width = cur_stream->width = event.resize.w;  
  96.                 screen_height= cur_stream->height= event.resize.h;  
  97.             }  
  98.             break;  
  99.         case SDL_QUIT:  
  100.         case FF_QUIT_EVENT:  
  101.             do_exit();  
  102.             break;  
  103.         case FF_ALLOC_EVENT:  
  104.             video_open(event.user.data1);  
  105.             alloc_picture(event.user.data1);  
  106.             break;  
  107.         case FF_REFRESH_EVENT:                          //若是刷新时间,则显示图像  
  108.             video_refresh_timer(event.user.data1);  
  109.             break;  
  110.         default:  
  111.             break;  
  112.         }  
  113.     }  

你可能感兴趣的:(ffplay程序运行流程分析)