如何在Android用FFmpeg+SDL2.0之同步音频

来源:互联网 发布:CVI ibevent windows 编辑:程序博客网 时间:2024/06/05 23:45


http://blog.csdn.net/tanlon_0308/article/details/40428139

同步音频的原理可以参考:http://dranger.com/ffmpeg/tutorial05.html  本文是在 如何在Android用FFmpeg+SDL2.0之同步视频 的基础上面继续进行将视频和音频同步到外部时钟的工作,同时也包含了修正音频解码的问题。

  1. /* 
  2.  * SDL_Lesson.c 
  3.  * 
  4.  *  Created on: Aug 12, 2014 
  5.  *      Author: clarck 
  6.  */  
  7. #include <jni.h>  
  8. #include <android/native_window_jni.h>  
  9. #include "SDL.h"  
  10. #include "SDL_thread.h"  
  11. #include "SDL_events.h"  
  12. #include "../include/logger.h"  
  13. #include "../ffmpeg/include/libavcodec/avcodec.h"  
  14. #include "../ffmpeg/include/libavformat/avformat.h"  
  15. #include "../ffmpeg/include/libavutil/pixfmt.h"  
  16. #include "../ffmpeg/include/libswscale/swscale.h"  
  17. #include "../ffmpeg/include/libswresample/swresample.h"  
  18.   
  19. #define SDL_AUDIO_BUFFER_SIZE 1024  
  20.   
  21. #define MAX_AUDIOQ_SIZE (5 * 16 * 1024)  
  22. #define MAX_VIDEOQ_SIZE (5 * 256 * 1024)  
  23.   
  24. #define AV_SYNC_THRESHOLD 0.01  
  25. #define AV_NOSYNC_THRESHOLD 10.0  
  26.   
  27. #define SAMPLE_CORRECTION_PERCENT_MAX 10  
  28. #define AUDIO_DIFF_AVG_NB 20  
  29.   
  30. #define FF_ALLOC_EVENT   (SDL_USEREVENT)  
  31. #define FF_REFRESH_EVENT (SDL_USEREVENT + 1)  
  32. #define FF_QUIT_EVENT (SDL_USEREVENT + 2)  
  33.   
  34. #define VIDEO_PICTURE_QUEUE_SIZE 1  
  35.   
  36. #define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER  
  37. #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio  
  38.   
  39. typedef struct PacketQueue {  
  40.     AVPacketList *first_pkt, *last_pkt;  
  41.     int nb_packets;  
  42.     int size;  
  43.     SDL_mutex *mutex;  
  44.     SDL_cond *cond;  
  45. } PacketQueue;  
  46.   
  47. typedef struct VideoPicture {  
  48.     SDL_Window *screen;  
  49.     SDL_Renderer *renderer;  
  50.     SDL_Texture *bmp;  
  51.   
  52.     AVFrame* rawdata;  
  53.     int width, height; /*source height & width*/  
  54.     int allocated;  
  55.     double pts;  
  56. } VideoPicture;  
  57.   
  58. typedef struct VideoState {  
  59.     char filename[1024];  
  60.     AVFormatContext *ic;  
  61.     int videoStream, audioStream;  
  62.     AVStream *audio_st;  
  63.     AVFrame *audio_frame;  
  64.     PacketQueue audioq;  
  65.     unsigned int audio_buf_size;  
  66.     unsigned int audio_buf_index;  
  67.     AVPacket audio_pkt;  
  68.     uint8_t *audio_pkt_data;  
  69.     int audio_pkt_size;  
  70.     uint8_t *audio_buf;  
  71.     DECLARE_ALIGNED(16,uint8_t,audio_buf2) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];  
  72.     enum AVSampleFormat audio_src_fmt;  
  73.     enum AVSampleFormat audio_tgt_fmt;  
  74.     int audio_src_channels;  
  75.     int audio_tgt_channels;  
  76.     int64_t audio_src_channel_layout;  
  77.     int64_t audio_tgt_channel_layout;  
  78.     int audio_src_freq;  
  79.     int audio_tgt_freq;  
  80.     struct SwrContext *swr_ctx;  
  81.   
  82.     AVStream *video_st;  
  83.     PacketQueue videoq;  
  84.   
  85.     VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];  
  86.     int pictq_size, pictq_rindex, pictq_windex;  
  87.     SDL_mutex *pictq_mutex;  
  88.     SDL_cond *pictq_cond;  
  89.   
  90.     SDL_Thread *parse_tid;  
  91.     SDL_Thread *audio_tid;  
  92.     SDL_Thread *video_tid;  
  93.   
  94.     AVIOContext *io_ctx;  
  95.     struct SwsContext *sws_ctx;  
  96.   
  97.     double audio_clock;  
  98.   
  99.     int av_sync_type;  
  100.     double external_clock;/*external clock base*/  
  101.     int64_t external_clock_time;  
  102.   
  103.     int audio_hw_buf_size;  
  104.     double audio_diff_cum;/*used of AV difference average computation*/  
  105.     double audio_diff_avg_coef;  
  106.     double audio_diff_threshold;  
  107.     int audio_diff_avg_count;  
  108.     double frame_timer;  
  109.     double frame_last_pts;  
  110.     double frame_last_delay;  
  111.   
  112.     double video_current_pts; ///<current displayed pts (different from video_clock if frame fifos are used)  
  113.     int64_t video_current_pts_time; ///<time (av_gettime) at which we updated video_current_pts - used to have running video pts  
  114.   
  115.     double video_clock; ///<pts of last decoded frame / predicted pts of next decoded frame  
  116.   
  117.   
  118.     int quit;  
  119. } VideoState;  
  120.   
  121. enum {  
  122.     AV_SYNC_AUDIO_MASTER, AV_SYNC_VIDEO_MASTER, AV_SYNC_EXTERNAL_MASTER,  
  123. };  
  124.   
  125. VideoState *global_video_state;  
  126.   
  127. void packet_queue_init(PacketQueue *q) {  
  128.     memset(q, 0, sizeof(PacketQueue));  
  129.     q->mutex = SDL_CreateMutex();  
  130.     q->cond = SDL_CreateCond();  
  131. }  
  132.   
  133. int packet_queue_put(PacketQueue *q, AVPacket *pkt) {  
  134.     AVPacketList *pkt1;  
  135.   
  136.     pkt1 = (AVPacketList *) av_malloc(sizeof(AVPacketList));  
  137.     if (!pkt1) {  
  138.         return -1;  
  139.     }  
  140.     pkt1->pkt = *pkt;  
  141.     pkt1->next = NULL;  
  142.   
  143.     SDL_LockMutex(q->mutex);  
  144.   
  145.     if (!q->last_pkt) {  
  146.         q->first_pkt = pkt1;  
  147.     } else {  
  148.         q->last_pkt->next = pkt1;  
  149.     }  
  150.   
  151.     q->last_pkt = pkt1;  
  152.     q->nb_packets++;  
  153.     q->size += pkt1->pkt.size;  
  154.     SDL_CondSignal(q->cond);  
  155.     SDL_UnlockMutex(q->mutex);  
  156.     return 0;  
  157. }  
  158.   
  159. static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {  
  160.     AVPacketList *pkt1;  
  161.     int ret;  
  162.   
  163.     SDL_LockMutex(q->mutex);  
  164.   
  165.     for (;;) {  
  166.         if (global_video_state->quit) {  
  167.             ret = -1;  
  168.             break;  
  169.         }  
  170.   
  171.         pkt1 = q->first_pkt;  
  172.         if (pkt1) {  
  173.             q->first_pkt = pkt1->next;  
  174.             if (!q->first_pkt) {  
  175.                 q->last_pkt = NULL;  
  176.             }  
  177.             q->nb_packets--;  
  178.             q->size -= pkt1->pkt.size;  
  179.             *pkt = pkt1->pkt;  
  180.   
  181.             av_free(pkt1);  
  182.             ret = 1;  
  183.             break;  
  184.         } else if (!block) {  
  185.             ret = 0;  
  186.             break;  
  187.         } else {  
  188.             SDL_CondWait(q->cond, q->mutex);  
  189.         }  
  190.     }  
  191.   
  192.     SDL_UnlockMutex(q->mutex);  
  193.   
  194.     return ret;  
  195. }  
  196.   
  197. double get_audio_clock(VideoState *is) {  
  198.     double pts;  
  199.     int hw_buf_size, bytes_per_sec, n;  
  200.   
  201.     pts = is->audio_clock; /* maintained in the audio thread */  
  202.     hw_buf_size = is->audio_buf_size - is->audio_buf_index;  
  203.     bytes_per_sec = 0;  
  204.     n = is->audio_st->codec->channels * 2;  
  205.     if (is->audio_st) {  
  206.         bytes_per_sec = is->audio_st->codec->sample_rate * n;  
  207.     }  
  208.     if (bytes_per_sec) {  
  209.         pts -= (double) hw_buf_size / bytes_per_sec;  
  210.     }  
  211.     return pts;  
  212. }  
  213.   
  214. double get_video_clock(VideoState *is) {  
  215.     double delta;  
  216.   
  217.     delta = (av_gettime() - is->video_current_pts_time) / 1000000.0;  
  218.     return is->video_current_pts + delta;  
  219. }  
  220.   
  221. double get_external_clock(VideoState *is) {  
  222.     return av_gettime() / 1000000.0;  
  223. }  
  224.   
  225. double get_master_clock(VideoState *is) {  
  226.     if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) {  
  227.         return get_video_clock(is);  
  228.     } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) {  
  229.         return get_audio_clock(is);  
  230.     } else {  
  231.         return get_external_clock(is);  
  232.     }  
  233. }  
  234.   
  235. /* Add or subtract samples to get a better sync, return new 
  236.  audio buffer size */  
  237. int synchronize_audio(VideoState *is, short *samples, int samples_size,  
  238.         double pts) {  
  239.     int n;  
  240.     double ref_clock;  
  241.   
  242.     n = 2 * is->audio_st->codec->channels;  
  243.   
  244.     if (is->av_sync_type != AV_SYNC_AUDIO_MASTER) {  
  245.         double diff, avg_diff;  
  246.         int wanted_size, min_size, max_size;  
  247.         //int nb_samples;  
  248.   
  249.         ref_clock = get_master_clock(is);  
  250.         diff = get_audio_clock(is) - ref_clock;  
  251.   
  252.         if (diff < AV_NOSYNC_THRESHOLD) {  
  253.             // accumulate the diffs  
  254.             is->audio_diff_cum = diff  
  255.                     + is->audio_diff_avg_coef * is->audio_diff_cum;  
  256.             if (is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) {  
  257.                 is->audio_diff_avg_count++;  
  258.             } else {  
  259.                 avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef);  
  260.                 if (fabs(avg_diff) >= is->audio_diff_threshold) {  
  261.                     wanted_size = samples_size  
  262.                             + ((int) (diff * is->audio_st->codec->sample_rate)  
  263.                                     * n);  
  264.                     min_size = samples_size  
  265.                             * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100);  
  266.                     max_size = samples_size  
  267.                             * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100);  
  268.                     if (wanted_size < min_size) {  
  269.                         wanted_size = min_size;  
  270.                     } else if (wanted_size > max_size) {  
  271.                         wanted_size = max_size;  
  272.                     }  
  273.                     if (wanted_size < samples_size) {  
  274.                         /* remove samples */  
  275.                         samples_size = wanted_size;  
  276.                     } else if (wanted_size > samples_size) {  
  277.                         uint8_t *samples_end, *q;  
  278.                         int nb;  
  279.   
  280.                         /* add samples by copying final sample*/  
  281.                         nb = (samples_size - wanted_size);  
  282.                         samples_end = (uint8_t *) samples + samples_size - n;  
  283.                         q = samples_end + n;  
  284.                         while (nb > 0) {  
  285.                             memcpy(q, samples_end, n);  
  286.                             q += n;  
  287.                             nb -= n;  
  288.                         }  
  289.                         samples_size = wanted_size;  
  290.                     }  
  291.                 }  
  292.             }  
  293.         } else {  
  294.             /* difference is TOO big; reset diff stuff */  
  295.             is->audio_diff_avg_count = 0;  
  296.             is->audio_diff_cum = 0;  
  297.         }  
  298.     }  
  299.     return samples_size;  
  300. }  
  301.   
  302. int audio_decode_frame(VideoState *is, double *pts_ptr) {  
  303.     int len1, len2, decoded_data_size;  
  304.     AVPacket *pkt = &is->audio_pkt;  
  305.     int got_frame = 0;  
  306.     int64_t dec_channel_layout;  
  307.     int wanted_nb_samples, resampled_data_size, n;  
  308.   
  309.     double pts;  
  310.   
  311.     for (;;) {  
  312.         while (is->audio_pkt_size > 0) {  
  313.             if (!is->audio_frame) {  
  314.                 if (!(is->audio_frame = avcodec_alloc_frame())) {  
  315.                     return AVERROR(ENOMEM);  
  316.                 }  
  317.             } else  
  318.                 avcodec_get_frame_defaults(is->audio_frame);  
  319.   
  320.             len1 = avcodec_decode_audio4(is->audio_st->codec, is->audio_frame,  
  321.                     &got_frame, pkt);  
  322.             if (len1 < 0) {  
  323.                 // error, skip the frame  
  324.                 is->audio_pkt_size = 0;  
  325.                 break;  
  326.             }  
  327.   
  328.             is->audio_pkt_data += len1;  
  329.             is->audio_pkt_size -= len1;  
  330.   
  331.             if (!got_frame)  
  332.                 continue;  
  333.   
  334.             /* 计算解码出来的桢需要的缓冲大小 */  
  335.             decoded_data_size = av_samples_get_buffer_size(NULL,  
  336.                     is->audio_frame->channels, is->audio_frame->nb_samples,  
  337.                     is->audio_frame->format, 1);  
  338.   
  339.             dec_channel_layout =  
  340.                     (is->audio_frame->channel_layout  
  341.                             && is->audio_frame->channels  
  342.                                     == av_get_channel_layout_nb_channels(  
  343.                                             is->audio_frame->channel_layout)) ?  
  344.                             is->audio_frame->channel_layout :  
  345.                             av_get_default_channel_layout(  
  346.                                     is->audio_frame->channels);  
  347.   
  348.             wanted_nb_samples = is->audio_frame->nb_samples;  
  349.   
  350.             if (is->audio_frame->format != is->audio_src_fmt  
  351.                     || dec_channel_layout != is->audio_src_channel_layout  
  352.                     || is->audio_frame->sample_rate != is->audio_src_freq  
  353.                     || (wanted_nb_samples != is->audio_frame->nb_samples  
  354.                             && !is->swr_ctx)) {  
  355.                 if (is->swr_ctx)  
  356.                     swr_free(&is->swr_ctx);  
  357.                 is->swr_ctx = swr_alloc_set_opts(NULL,  
  358.                         is->audio_tgt_channel_layout, is->audio_tgt_fmt,  
  359.                         is->audio_tgt_freq, dec_channel_layout,  
  360.                         is->audio_frame->format, is->audio_frame->sample_rate,  
  361.                         0, NULL);  
  362.                 if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {  
  363.                     fprintf(stderr, "swr_init() failed\n");  
  364.                     break;  
  365.                 }  
  366.                 is->audio_src_channel_layout = dec_channel_layout;  
  367.                 is->audio_src_channels = is->audio_st->codec->channels;  
  368.                 is->audio_src_freq = is->audio_st->codec->sample_rate;  
  369.                 is->audio_src_fmt = is->audio_st->codec->sample_fmt;  
  370.             }  
  371.   
  372.             /* 这里我们可以对采样数进行调整,增加或者减少,一般可以用来做声画同步 */  
  373.             if (is->swr_ctx) {  
  374.                 const uint8_t **in =  
  375.                         (const uint8_t **) is->audio_frame->extended_data;  
  376.                 uint8_t *out[] = { is->audio_buf2 };  
  377.                 if (wanted_nb_samples != is->audio_frame->nb_samples) {  
  378.                     if (swr_set_compensation(is->swr_ctx,  
  379.                             (wanted_nb_samples - is->audio_frame->nb_samples)  
  380.                                     * is->audio_tgt_freq  
  381.                                     / is->audio_frame->sample_rate,  
  382.                             wanted_nb_samples * is->audio_tgt_freq  
  383.                                     / is->audio_frame->sample_rate) < 0) {  
  384.                         fprintf(stderr, "swr_set_compensation() failed\n");  
  385.                         break;  
  386.                     }  
  387.                 }  
  388.   
  389.                 len2 = swr_convert(is->swr_ctx, out,  
  390.                         sizeof(is->audio_buf2) / is->audio_tgt_channels  
  391.                                 / av_get_bytes_per_sample(is->audio_tgt_fmt),  
  392.                         in, is->audio_frame->nb_samples);  
  393.                 if (len2 < 0) {  
  394.                     fprintf(stderr, "swr_convert() failed\n");  
  395.                     break;  
  396.                 }  
  397.                 if (len2  
  398.                         == sizeof(is->audio_buf2) / is->audio_tgt_channels  
  399.                                 / av_get_bytes_per_sample(is->audio_tgt_fmt)) {  
  400.                     fprintf(stderr,  
  401.                             "warning: audio buffer is probably too small\n");  
  402.                     swr_init(is->swr_ctx);  
  403.                 }  
  404.                 is->audio_buf = is->audio_buf2;  
  405.                 resampled_data_size = len2 * is->audio_tgt_channels  
  406.                         * av_get_bytes_per_sample(is->audio_tgt_fmt);  
  407.             } else {  
  408.                 resampled_data_size = decoded_data_size;  
  409.                 is->audio_buf = is->audio_frame->data[0];  
  410.             }  
  411.   
  412.             pts = is->audio_clock;  
  413.             *pts_ptr = pts;  
  414.             n = 2 * is->audio_st->codec->channels;  
  415.             is->audio_clock += (double) resampled_data_size  
  416.                     / (double) (n * is->audio_st->codec->sample_rate);  
  417.   
  418.             // We have data, return it and come back for more later  
  419.             return resampled_data_size;  
  420.         }  
  421.   
  422.         if (pkt->data)  
  423.             av_free_packet(pkt);  
  424.         memset(pkt, 0, sizeof(*pkt));  
  425.         if (is->quit)  
  426.             return -1;  
  427.         if (packet_queue_get(&is->audioq, pkt, 1) < 0)  
  428.             return -1;  
  429.   
  430.         is->audio_pkt_data = pkt->data;  
  431.         is->audio_pkt_size = pkt->size;  
  432.   
  433.         /* if update, update the audio clock w/pts */  
  434.         if (pkt->pts != AV_NOPTS_VALUE) {  
  435.             is->audio_clock = av_q2d(is->audio_st->time_base) * pkt->pts;  
  436.         }  
  437.     }  
  438.   
  439.     return 0;  
  440. }  
  441.   
  442. void audio_callback(void *userdata, Uint8 *stream, int len) {  
  443.     VideoState *is = (VideoState *) userdata;  
  444.     int len1, audio_data_size;  
  445.   
  446.     double pts;  
  447.   
  448.     /*   len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 */  
  449.     while (len > 0) {  
  450.         /*  audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/  
  451.         /*   这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/  
  452.         /*   们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更 
  453.          /*   多的桢数据 */  
  454.   
  455.         if (is->audio_buf_index >= is->audio_buf_size) {  
  456.             audio_data_size = audio_decode_frame(is, &pts);  
  457.             /* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */  
  458.             if (audio_data_size < 0) {  
  459.                 /* silence */  
  460.                 is->audio_buf_size = 1024;  
  461.                 /* 清零,静音 */  
  462.                 memset(is->audio_buf, 0, is->audio_buf_size);  
  463.             } else {  
  464.                 audio_data_size = synchronize_audio(is, (int16_t *) is->audio_buf,  
  465.                         audio_data_size, pts);  
  466.                 is->audio_buf_size = audio_data_size;  
  467.             }  
  468.             is->audio_buf_index = 0;  
  469.         }  
  470.         /*  查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */  
  471.         len1 = is->audio_buf_size - is->audio_buf_index;  
  472.         if (len1 > len) {  
  473.             len1 = len;  
  474.         }  
  475.   
  476.         memcpy(stream, (uint8_t *) is->audio_buf + is->audio_buf_index, len1);  
  477.         len -= len1;  
  478.         stream += len1;  
  479.         is->audio_buf_index += len1;  
  480.     }  
  481. }  
  482.   
  483. static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) {  
  484.     SDL_Event event;  
  485.     event.type = FF_REFRESH_EVENT;  
  486.     event.user.data1 = opaque;  
  487.     SDL_PushEvent(&event);  
  488.     return 0;  
  489. }  
  490.   
  491. static void schedule_refresh(VideoState *is, int delay) {  
  492.     SDL_AddTimer(delay, sdl_refresh_timer_cb, is);  
  493. }  
  494.   
  495. int decode_interrupt_cb(void *opaque) {  
  496.     return (global_video_state && global_video_state->quit);  
  497. }  
  498.   
  499. void video_display(VideoState *is) {  
  500.     SDL_Rect rect;  
  501.     VideoPicture *vp;  
  502.     float aspect_ratio;  
  503.   
  504.     vp = &is->pictq[is->pictq_rindex];  
  505.     if (vp->bmp) {  
  506.         if (is->video_st->codec->sample_aspect_ratio.num == 0) {  
  507.             aspect_ratio = 0;  
  508.         } else {  
  509.             aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio)  
  510.                     * is->video_st->codec->width / is->video_st->codec->height;  
  511.         }  
  512.   
  513.         if (aspect_ratio <= 0.0) {  
  514.             aspect_ratio = (float) is->video_st->codec->width  
  515.                     / (float) is->video_st->codec->height;  
  516.         }  
  517.   
  518.         rect.x = 0;  
  519.         rect.y = 0;  
  520.         rect.w = vp->width;  
  521.         rect.h = vp->height;  
  522.   
  523.         SDL_UpdateYUVTexture(vp->bmp, &rect, vp->rawdata->data[0],  
  524.                 vp->rawdata->linesize[0], vp->rawdata->data[1],  
  525.                 vp->rawdata->linesize[1], vp->rawdata->data[2],  
  526.                 vp->rawdata->linesize[2]);  
  527.   
  528.         SDL_RenderClear(vp->renderer);  
  529.         SDL_RenderCopy(vp->renderer, vp->bmp, &rect, &rect);  
  530.         SDL_RenderPresent(vp->renderer);  
  531.     }  
  532. }  
  533.   
  534. void video_refresh_timer(void *userdata) {  
  535.     VideoState *is = (VideoState *) userdata;  
  536.     VideoPicture *vp;  
  537.     double actual_delay, delay, sync_threshold, ref_clock, diff;  
  538.   
  539.     if (is->video_st) {  
  540.         if (is->pictq_size == 0) {  
  541.             schedule_refresh(is, 1);  
  542.         } else {  
  543.             vp = &is->pictq[is->pictq_rindex];  
  544.   
  545.             is->video_current_pts = vp->pts;  
  546.             is->video_current_pts_time = av_gettime();  
  547.   
  548.             delay = vp->pts - is->frame_last_pts; /* the pts from last time */  
  549.             if (delay <= 0 || delay >= 1.0) {  
  550.                 /* if incorrect delay, use previous one */  
  551.                 delay = is->frame_last_delay;  
  552.             }  
  553.             /* save for next time */  
  554.             is->frame_last_delay = delay;  
  555.             is->frame_last_pts = vp->pts;  
  556.   
  557.             /* update delay to sync to audio */  
  558.             ref_clock = get_audio_clock(is);  
  559.             diff = vp->pts - ref_clock;  
  560.   
  561.             /* update delay to sync to audio if not master source */  
  562.             if (is->av_sync_type != AV_SYNC_VIDEO_MASTER) {  
  563.                 ref_clock = get_master_clock(is);  
  564.                 diff = vp->pts - ref_clock;  
  565.   
  566.                 /* Skip or repeat the frame. Take delay into account 
  567.                  FFPlay still doesn't "know if this is the best guess." */  
  568.                 sync_threshold =  
  569.                         (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD;  
  570.                 if (fabs(diff) < AV_NOSYNC_THRESHOLD) {  
  571.                     if (diff <= -sync_threshold) {  
  572.                         delay = 0;  
  573.                     } else if (diff >= sync_threshold) {  
  574.                         delay = 2 * delay;  
  575.                     }  
  576.                 }  
  577.             }  
  578.             is->frame_timer += delay;  
  579.             /* computer the REAL delay */  
  580.             actual_delay = is->frame_timer - (av_gettime() / 1000000.0);  
  581.             if (actual_delay < 0.010) {  
  582.                 /* Really it should skip the picture instead */  
  583.                 actual_delay = 0.010;  
  584.             }  
  585.             schedule_refresh(is, (int) (actual_delay * 1000 + 0.5));  
  586.   
  587.             /* show the picture! */  
  588.             video_display(is);  
  589.   
  590.             /* update queue for next picture! */  
  591.             if (++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) {  
  592.                 is->pictq_rindex = 0;  
  593.             }  
  594.             SDL_LockMutex(is->pictq_mutex);  
  595.             is->pictq_size--;  
  596.             SDL_CondSignal(is->pictq_cond);  
  597.             SDL_UnlockMutex(is->pictq_mutex);  
  598.         }  
  599.     } else {  
  600.         schedule_refresh(is, 100);  
  601.     }  
  602. }  
  603.   
  604. void alloc_picture(void *userdata) {  
  605.     VideoState *is = (VideoState *) userdata;  
  606.     VideoPicture *vp;  
  607.   
  608.     vp = &is->pictq[is->pictq_windex];  
  609.     if (vp->bmp) {  
  610.         // we already have one make another, bigger/smaller  
  611.         SDL_DestroyTexture(vp->bmp);  
  612.     }  
  613.   
  614.     if(vp->rawdata) {  
  615.         av_free(vp->rawdata);  
  616.     }  
  617.   
  618.     // Allocate a place to put our YUV image on that screen  
  619.     vp->screen = SDL_CreateWindow("My Player Window", SDL_WINDOWPOS_UNDEFINED,  
  620.             SDL_WINDOWPOS_UNDEFINED, is->video_st->codec->width,  
  621.             is->video_st->codec->height,  
  622.             SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);  
  623.   
  624.     vp->renderer = SDL_CreateRenderer(vp->screen, -1, 0);  
  625.     vp->bmp = SDL_CreateTexture(vp->renderer, SDL_PIXELFORMAT_YV12,  
  626.                 SDL_TEXTUREACCESS_STREAMING, is->video_st->codec->width, is->video_st->codec->height);  
  627.   
  628.     vp->width = is->video_st->codec->width;  
  629.     vp->height = is->video_st->codec->height;  
  630.   
  631.   
  632.     AVFrame* pFrameYUV = avcodec_alloc_frame();  
  633.     if (pFrameYUV == NULL)  
  634.         return;  
  635.   
  636.     int numBytes = avpicture_get_size(PIX_FMT_YUV420P, vp->width,  
  637.             vp->height);  
  638.   
  639.     uint8_t* buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));  
  640.   
  641.     avpicture_fill((AVPicture *) pFrameYUV, buffer, PIX_FMT_YUV420P,  
  642.             vp->width, vp->height);  
  643.   
  644.     vp->rawdata = pFrameYUV;  
  645.   
  646.     SDL_LockMutex(is->pictq_mutex);  
  647.     vp->allocated = 1;  
  648.     SDL_CondSignal(is->pictq_cond);  
  649.     SDL_UnlockMutex(is->pictq_mutex);  
  650. }  
  651.   
  652. int queue_picture(VideoState *is, AVFrame *pFrame, double pts) {  
  653.     VideoPicture *vp;  
  654.     //int dst_pic_fmt  
  655.     AVPicture pict;  
  656.   
  657.     /* wait unitl we have space for a new pic */  
  658.     SDL_LockMutex(is->pictq_mutex);  
  659.     while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) {  
  660.         SDL_CondWait(is->pictq_cond, is->pictq_mutex);  
  661.     }  
  662.     SDL_UnlockMutex(is->pictq_mutex);  
  663.   
  664.     if (is->quit)  
  665.         return -1;  
  666.   
  667.     // windex is set to 0 initially  
  668.     vp = &is->pictq[is->pictq_windex];  
  669.   
  670.     /* allocate or resize the buffer ! */  
  671.     if (!vp->bmp || vp->width != is->video_st->codec->width  
  672.             || vp->height != is->video_st->codec->height) {  
  673.         SDL_Event event;  
  674.   
  675.         vp->allocated = 0;  
  676.         /* we have to do it in the main thread */  
  677.         event.type = FF_ALLOC_EVENT;  
  678.         event.user.data1 = is;  
  679.         SDL_PushEvent(&event);  
  680.   
  681.         /* wait until we have a picture allocated */  
  682.         SDL_LockMutex(is->pictq_mutex);  
  683.         while (!vp->allocated && !is->quit) {  
  684.             SDL_CondWait(is->pictq_cond, is->pictq_mutex);  
  685.         }  
  686.     }  
  687.     SDL_UnlockMutex(is->pictq_mutex);  
  688.     if (is->quit) {  
  689.         return -1;  
  690.     }  
  691.   
  692.     /* We have a place to put our picture on the queue */  
  693.     if (vp->rawdata) {  
  694.         // Convert the image into YUV format that SDL uses  
  695.         sws_scale(is->sws_ctx, (uint8_t const * const *) pFrame->data,  
  696.                 pFrame->linesize, 0, is->video_st->codec->height,  
  697.                 vp->rawdata->data, vp->rawdata->linesize);  
  698.   
  699.         vp->pts = pts;  
  700.   
  701.         /* now we inform our display thread that we have a pic ready */  
  702.         if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) {  
  703.             is->pictq_windex = 0;  
  704.         }  
  705.         SDL_LockMutex(is->pictq_mutex);  
  706.         is->pictq_size++;  
  707.         SDL_UnlockMutex(is->pictq_mutex);  
  708.     }  
  709.     return 0;  
  710. }  
  711.   
  712. double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) {  
  713.   
  714.     double frame_delay;  
  715.   
  716.     if (pts != 0) {  
  717.         /* if we have pts, set video clock to it */  
  718.         is->video_clock = pts;  
  719.     } else {  
  720.         /* if we aren't given a pts, set it to the clock */  
  721.         pts = is->video_clock;  
  722.     }  
  723.     /* update the video clock */  
  724.     frame_delay = av_q2d(is->video_st->codec->time_base);  
  725.     /* if we are repeating a frame, adjust clock accordingly */  
  726.     frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);  
  727.     is->video_clock += frame_delay;  
  728.     return pts;  
  729. }  
  730. uint64_t global_video_pkt_pts = AV_NOPTS_VALUE;  
  731.   
  732. /* These are called whenever we allocate a frame 
  733.  * buffer. We use this to store the global_pts in 
  734.  * a frame at the time it is allocated. 
  735.  */  
  736. int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) {  
  737.     int ret = avcodec_default_get_buffer(c, pic);  
  738.     uint64_t *pts = av_malloc(sizeof(uint64_t));  
  739.     *pts = global_video_pkt_pts;  
  740.     pic->opaque = pts;  
  741.     return ret;  
  742. }  
  743.   
  744. void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) {  
  745.     if (pic)  
  746.         av_freep(&pic->opaque);  
  747.     avcodec_default_release_buffer(c, pic);  
  748. }  
  749.   
  750. int video_thread(void *arg) {  
  751.     VideoState *is = (VideoState *) arg;  
  752.     AVPacket pkt1, *packet = &pkt1;  
  753.     int frameFinished;  
  754.     AVFrame *pFrame;  
  755.   
  756.     double pts;  
  757.   
  758.     pFrame = av_frame_alloc();  
  759.   
  760.     for (;;) {  
  761.         if (packet_queue_get(&is->videoq, packet, 1) < 0) {  
  762.             // means we quit getting packets  
  763.             break;  
  764.         }  
  765.   
  766.         pts = 0;  
  767.   
  768.         // Save global pts to be stored in pFrame in first call  
  769.         global_video_pkt_pts = packet->pts;  
  770.   
  771.   
  772.         // Decode video frame  
  773.         avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished,  
  774.                 packet);  
  775.   
  776.         if (packet->dts == AV_NOPTS_VALUE && pFrame->opaque  
  777.                 && *(uint64_t*) pFrame->opaque != AV_NOPTS_VALUE) {  
  778.             pts = *(uint64_t *) pFrame->opaque;  
  779.         } else if (packet->dts != AV_NOPTS_VALUE) {  
  780.             pts = packet->dts;  
  781.         } else {  
  782.             pts = 0;  
  783.         }  
  784.         pts *= av_q2d(is->video_st->time_base);  
  785.   
  786.   
  787.         // Did we get a video frame?  
  788.         if (frameFinished) {  
  789.             pts = synchronize_video(is, pFrame, pts);  
  790.             if (queue_picture(is, pFrame, pts) < 0) {  
  791.                 break;  
  792.             }  
  793.         }  
  794.         av_free_packet(packet);  
  795.     }  
  796.   
  797.     av_free(pFrame);  
  798.     return 0;  
  799. }  
  800.   
  801. int audio_stream_component_open(VideoState *is, int stream_index) {  
  802.     AVFormatContext *ic = is->ic;  
  803.     AVCodecContext *codecCtx;  
  804.     AVCodec *codec;  
  805.     SDL_AudioSpec wanted_spec, spec;  
  806.     int64_t wanted_channel_layout = 0;  
  807.     int wanted_nb_channels;  
  808.     /*  SDL支持的声道数为 1, 2, 4, 6 */  
  809.     /*  后面我们会使用这个数组来纠正不支持的声道数目 */  
  810.     const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 };  
  811.   
  812.     if (stream_index < 0 || stream_index >= ic->nb_streams) {  
  813.         return -1;  
  814.     }  
  815.   
  816.     codecCtx = ic->streams[stream_index]->codec;  
  817.     wanted_nb_channels = codecCtx->channels;  
  818.     if (!wanted_channel_layout  
  819.             || wanted_nb_channels  
  820.                     != av_get_channel_layout_nb_channels(  
  821.                             wanted_channel_layout)) {  
  822.         wanted_channel_layout = av_get_default_channel_layout(  
  823.                 wanted_nb_channels);  
  824.         wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;  
  825.     }  
  826.   
  827.     wanted_spec.channels = av_get_channel_layout_nb_channels(  
  828.             wanted_channel_layout);  
  829.     wanted_spec.freq = codecCtx->sample_rate;  
  830.     if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {  
  831.         fprintf(stderr, "Invalid sample rate or channel count!\n");  
  832.         return -1;  
  833.     }  
  834.     wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分  
  835.     wanted_spec.silence = 0;            // 0指示静音  
  836.     wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  // 自定义SDL缓冲区大小  
  837.     wanted_spec.callback = audio_callback;        // 音频解码的关键回调函数  
  838.     wanted_spec.userdata = is;                    // 传给上面回调函数的外带数据  
  839.   
  840.     /*  打开音频设备,这里使用一个while来循环尝试打开不同的声道数(由上面 */  
  841.     /*  next_nb_channels数组指定)直到成功打开,或者全部失败 */  
  842.     while (SDL_OpenAudio(&wanted_spec, &spec) < 0) {  
  843.         fprintf(stderr, "SDL_OpenAudio (%d channels): %s\n",  
  844.                 wanted_spec.channels, SDL_GetError());  
  845.         wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];  
  846.         if (!wanted_spec.channels) {  
  847.             fprintf(stderr,  
  848.                     "No more channel combinations to tyu, audio open failed\n");  
  849.             return -1;  
  850.         }  
  851.         wanted_channel_layout = av_get_default_channel_layout(  
  852.                 wanted_spec.channels);  
  853.     }  
  854.   
  855.     /* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */  
  856.     if (spec.format != AUDIO_S16SYS) {  
  857.         fprintf(stderr, "SDL advised audio format %d is not supported!\n",  
  858.                 spec.format);  
  859.         return -1;  
  860.     }  
  861.   
  862.     if (spec.channels != wanted_spec.channels) {  
  863.         wanted_channel_layout = av_get_default_channel_layout(spec.channels);  
  864.         if (!wanted_channel_layout) {  
  865.             fprintf(stderr, "SDL advised channel count %d is not supported!\n",  
  866.                     spec.channels);  
  867.             return -1;  
  868.         }  
  869.     }  
  870.   
  871.     is->audio_hw_buf_size = spec.size;  
  872.   
  873.     /* 把设置好的参数保存到大结构中 */  
  874.     is->audio_src_fmt = is->audio_tgt_fmt = AV_SAMPLE_FMT_S16;  
  875.     is->audio_src_freq = is->audio_tgt_freq = spec.freq;  
  876.     is->audio_src_channel_layout = is->audio_tgt_channel_layout =  
  877.             wanted_channel_layout;  
  878.     is->audio_src_channels = is->audio_tgt_channels = spec.channels;  
  879.   
  880.     codec = avcodec_find_decoder(codecCtx->codec_id);  
  881.     if (!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) {  
  882.         fprintf(stderr, "Unsupported codec!\n");  
  883.         return -1;  
  884.     }  
  885.     ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;  
  886.     switch (codecCtx->codec_type) {  
  887.     case AVMEDIA_TYPE_AUDIO:  
  888.         is->audioStream = stream_index;  
  889.         is->audio_st = ic->streams[stream_index];  
  890.         is->audio_buf_size = 0;  
  891.         is->audio_buf_index = 0;  
  892.   
  893.         /* averaging filter for audio sync */  
  894.         is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB));  
  895.         is->audio_diff_avg_count = 0;  
  896.         /* Correct audio only if larger error than this */  
  897.         is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE  
  898.                 / codecCtx->sample_rate;  
  899.   
  900.   
  901.         memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));  
  902.         packet_queue_init(&is->audioq);  
  903.         SDL_PauseAudio(0); // 开始播放静音  
  904.         break;  
  905.     default:  
  906.         break;  
  907.     }  
  908.   
  909.     return 0;  
  910. }  
  911.   
  912. int video_stream_component_open(VideoState *is, int stream_index) {  
  913.     AVFormatContext *pFormatCtx = is->ic;  
  914.     AVCodecContext *codecCtx;  
  915.     AVCodec *codec;  
  916.   
  917.     if (stream_index < 0 || stream_index >= pFormatCtx->nb_streams) {  
  918.         return -1;  
  919.     }  
  920.   
  921.     // Get a pointer to the codec context for the video stream  
  922.     codecCtx = pFormatCtx->streams[stream_index]->codec;  
  923.   
  924.     codec = avcodec_find_decoder(codecCtx->codec_id);  
  925.     if (!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) {  
  926.         fprintf(stderr, "Unsupported codec!\n");  
  927.         return -1;  
  928.     }  
  929.   
  930.     switch (codecCtx->codec_type) {  
  931.     case AVMEDIA_TYPE_VIDEO:  
  932.         is->videoStream = stream_index;  
  933.         is->video_st = pFormatCtx->streams[stream_index];  
  934.         is->sws_ctx = sws_getContext(is->video_st->codec->width,  
  935.                 is->video_st->codec->height, is->video_st->codec->pix_fmt,  
  936.                 is->video_st->codec->width, is->video_st->codec->height,  
  937.                 AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);  
  938.   
  939.         is->frame_timer = (double) av_gettime() / 1000000.0;  
  940.         is->frame_last_delay = 40e-3;  
  941.         is->video_current_pts_time = av_gettime();  
  942.   
  943.         packet_queue_init(&is->videoq);  
  944.         is->video_tid = SDL_CreateThread(video_thread, "video_thread", is);  
  945.   
  946.         codecCtx->get_buffer = our_get_buffer;  
  947.         codecCtx->release_buffer = our_release_buffer;  
  948.         break;  
  949.     default:  
  950.         break;  
  951.     }  
  952.     return 0;  
  953. }  
  954.   
  955. int decode_thread(void *arg) {  
  956.     VideoState *is = (VideoState *) arg;  
  957.     AVFormatContext *pFormatCtx = NULL;  
  958.     AVPacket pkt1, *packet = &pkt1;  
  959.   
  960.     int video_index = -1;  
  961.     int audio_index = -1;  
  962.     int i;  
  963.   
  964.     is->videoStream = -1;  
  965.     is->audioStream = -1;  
  966.   
  967.     AVIOInterruptCB interupt_cb;  
  968.   
  969.     global_video_state = is;  
  970.   
  971.     // will interrup blocking functions if we quit!  
  972.     interupt_cb.callback = decode_interrupt_cb;  
  973.     interupt_cb.opaque = is;  
  974.   
  975.     if (avio_open2(&is->io_ctx, is->filename, 0, &interupt_cb, NULL)) {  
  976.         fprintf(stderr, "Cannot open I/O for %s\n", is->filename);  
  977.         return -1;  
  978.     }  
  979.   
  980.     //Open video file  
  981.     if (avformat_open_input(&pFormatCtx, is->filename, NULL, NULL) != 0) {  
  982.         return -1; //Couldn't open file  
  983.     }  
  984.   
  985.     is->ic = pFormatCtx;  
  986.   
  987.     //Retrieve stream infomation  
  988.     if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {  
  989.         return -1; // Couldn't find stream information  
  990.     }  
  991.   
  992.     //Dump information about file onto standard error  
  993.     av_dump_format(pFormatCtx, 0, is->filename, 0);  
  994.   
  995.     //Find the first video stream  
  996.     for (i = 0; i < pFormatCtx->nb_streams; i++) {  
  997.         if (pFormatCtx->streams[i]->codec->coder_type == AVMEDIA_TYPE_VIDEO  
  998.                 && video_index < 0) {  
  999.             video_index = i;  
  1000.         }  
  1001.   
  1002.         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO  
  1003.                 && audio_index < 0) {  
  1004.             audio_index = i;  
  1005.         }  
  1006.     }  
  1007.   
  1008.     if (audio_index >= 0) {  
  1009.         /* 所有设置SDL音频流信息的步骤都在这个函数里完成 */  
  1010.         audio_stream_component_open(is, audio_index);  
  1011.     }  
  1012.   
  1013.     if (video_index >= 0) {  
  1014.         video_stream_component_open(is, video_index);  
  1015.     }  
  1016.   
  1017.     if (is->videoStream < 0 || is->audioStream <= 0) {  
  1018.         fprintf(stderr, "%s: could not open codec\n", is->filename);  
  1019.         goto fail;  
  1020.     }  
  1021.   
  1022.     //main decode loop  
  1023.     /* 读包的主循环, av_read_frame不停的从文件中读取数据包*/  
  1024.     for (;;) {  
  1025.         if (is->quit) {  
  1026.             break;  
  1027.         }  
  1028.   
  1029.         //seek  stuff goes here  
  1030.         /* 这里audioq.size是指队列中的所有数据包带的音频数据的总量或者视频数据总量,并不是包的数量 */  
  1031.         if (is->audioq.size > MAX_AUDIOQ_SIZE || is->videoq.size > MAX_VIDEOQ_SIZE) {  
  1032.             SDL_Delay(10);  
  1033.             continue;  
  1034.         }  
  1035.         if (av_read_frame(is->ic, packet) < 0) {  
  1036.             if (is->ic->pb->error == 0) {  
  1037.                 SDL_Delay(100); /* no error; wait for user input */  
  1038.                 continue;  
  1039.             } else {  
  1040.                 break;  
  1041.             }  
  1042.         }  
  1043.         // Is this a packet from the video stream?  
  1044.         if (packet->stream_index == is->videoStream) {  
  1045.             packet_queue_put(&is->videoq, packet);  
  1046.         } else if (packet->stream_index == is->audioStream) {  
  1047.             packet_queue_put(&is->audioq, packet);  
  1048.         } else {  
  1049.             av_free_packet(packet);  
  1050.         }  
  1051.     }  
  1052.   
  1053.     /*all done - wait for it*/  
  1054.     while (!is->quit) {  
  1055.         SDL_Delay(100);  
  1056.     }  
  1057.   
  1058.     fail: if (1) {  
  1059.         SDL_Event event;  
  1060.         event.type = FF_QUIT_EVENT;  
  1061.         event.user.data1 = is;  
  1062.         SDL_PushEvent(&event);  
  1063.     }  
  1064.     return 0;  
  1065. }  
  1066.   
  1067. int main(int argc, char *argv[]) {  
  1068.     char *filename = argv[1];  
  1069.     SDL_Event event;  
  1070.   
  1071.     VideoState *is;  
  1072.     is = av_malloc(sizeof(VideoState));  
  1073.   
  1074.     // Register all formats and codecs  
  1075.     av_register_all();  
  1076.   
  1077.     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  
  1078.         fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());  
  1079.         exit(1);  
  1080.     }  
  1081.   
  1082.     av_strlcpy(is->filename, filename, sizeof(is->filename));  
  1083.   
  1084.     is->pictq_mutex = SDL_CreateMutex();  
  1085.     is->pictq_cond = SDL_CreateCond();  
  1086.   
  1087.     schedule_refresh(is, 40);  
  1088.   
  1089.     is->av_sync_type = DEFAULT_AV_SYNC_TYPE;  
  1090.   
  1091.     is->parse_tid = SDL_CreateThread(decode_thread, "parse_thread", is);  
  1092.     if (!is->parse_tid) {  
  1093.         av_free(is);  
  1094.         return -1;  
  1095.     }  
  1096.   
  1097.     for (;;) {  
  1098.         SDL_WaitEvent(&event);  
  1099.         switch (event.type) {  
  1100.         case FF_QUIT_EVENT:  
  1101.         case SDL_QUIT:  
  1102.             SDL_CondSignal(is->audioq.cond);  
  1103.             SDL_CondSignal(is->videoq.cond);  
  1104.             is->quit = 1;  
  1105.             SDL_Quit();  
  1106.             return 0;  
  1107.             break;  
  1108.         case FF_ALLOC_EVENT:  
  1109.             alloc_picture(event.user.data1);  
  1110.             break;  
  1111.   
  1112.         case FF_REFRESH_EVENT:  
  1113.             video_refresh_timer(event.user.data1);  
  1114.             break;  
  1115.         }  
  1116.     }  
  1117.   
  1118.     return 0;  
  1119. }  


0

原创粉丝点击