Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2116825
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-05-23 17:14:06

http://dranger.com/ffmpeg/tutorial05.html
1. 根据上面的教程学习音视频同步

2. 代码基于ffmpeg-3.0.1
2.1 注:同步新加的部分用绿色标出
  1. #include <libavcodec/avcodec.h>
  2. #include <libavformat/avformat.h>
  3. #include <libswscale/swscale.h>
  4. #include <libswresample/swresample.h>
  5. #include <libavutil/avstring.h>
  6. #include <libavutil/pixfmt.h>
  7. #include <libavutil/log.h>
  8. #include <libavutil/time.h>              //要使用函数av_gettime
  9. #include <SDL/SDL.h>
  10. #include <SDL/SDL_thread.h>
  11. #include <stdio.h>
  12. #include <math.h>

  13. #define SDL_AUDIO_BUFFER_SIZE 4096
  14. #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000

  15. #define MAX_AUDIOQ_SIZE (* 16 * 1024)
  16. #define MAX_VIDEOQ_SIZE (* 256 * 1024)

  17. #define AV_SYNC_THRESHOLD 0.01
  18. #define AV_NOSYNC_THRESHOLD 10.0

  19. #define FF_REFRESH_EVENT (SDL_USEREVENT)
  20. #define FF_QUIT_EVENT (SDL_USEREVENT + 1)

  21. typedef struct PacketQueue
  22. {
  23.     AVPacketList * first_pkt, *last_pkt;
  24.     int nb_packets;
  25.     int size;
  26.     SDL_mutex *mutex;
  27.     SDL_cond * cond;
  28. }PacketQueue;

  29. static int signal_quit = 0;
  30. SDL_mutex *affmutex;
  31. SDL_Event sdlevent;

  32. #define VIDEO_PICTURE_QUEUE_SIZE 1

  33. typedef struct VideoState {
  34.     int videoindex;
  35.     int sndindex;
  36.     int frameFinished;
  37.     int wanted_freq;
  38.     int wanted_samples;
  39.     int wanted_channels;
  40.     int pictq_size, pictq_rindex, pictq_windex;
  41.     unsigned int audio_buf_size;
  42.     unsigned int audio_buf_index;
  43.     double frame_timer;
  44.     double frame_last_pts;
  45.     double now_video_pts;
  46.     double frame_last_delay;
  47.     double video_clock;
  48.     double              audio_clock;
  49.     SDL_Thread* video_tid;
  50.     AVFormatContext* pFormatCtx;
  51.     AVCodecContext* vdoCodecCtx;
  52.     AVCodecContext* sndCodecCtx;
  53.     AVCodec* vdoCodec;
  54.     AVCodec* sndCodec;
  55.     AVStream* vdoStream;
  56.     AVStream* sndStream;
  57.     AVFrame* pFrameYUV;
  58.     AVPacket* packet;
  59.     struct SwsContext *img_convert_ctx;
  60.     struct SwrContext *swr_ctx;
  61.     SDL_mutex *pictq_mutex;
  62.     SDL_cond *pictq_cond;
  63.     SDL_Surface* psscreen;
  64.     SDL_Overlay* overlay;
  65.     SDL_Rect rect;
  66.     SDL_mutex *screen_mutex;
  67.     enum AVSampleFormat wanted_fmt;
  68.     int64_t wanted_channel_layout;
  69.     PacketQueue audioq;
  70.     PacketQueue videoq;
  71.     DECLARE_ALIGNED(16,uint8_t,audio_buf2) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
  72. }VideoState;

  73. static void video_refresh_timer(void* arg);

  74. double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) ;  //这两个函数是新加的
  75. double get_audio_clock(VideoState *is);
  76. static int sdl_event_thread(void* data)
  77. {
  78.     while((0==signal_quit))
  79.     {
  80.         SDL_LockMutex(affmutex);
  81.         while(SDL_PollEvent(&sdlevent))
  82.         {
  83.             switch(sdlevent.type)
  84.             {
  85.                 case FF_QUIT_EVENT:
  86.                 case SDL_QUIT:
  87.                     {
  88.                         signal_quit = 1;
  89.                         SDL_Quit();
  90.                         exit(0);
  91.                     }
  92.                     break;
  93.                 case FF_REFRESH_EVENT:
  94.                   video_refresh_timer(sdlevent.user.data1);
  95.                   break;
  96.                 default:
  97.                     break;
  98.             }
  99.         }
  100.         SDL_UnlockMutex(affmutex);
  101.     }
  102.     return 0;
  103. }

  104. static void packet_queue_init(PacketQueue *q)
  105. {
  106.     memset(q, 0, sizeof(PacketQueue));
  107.     q->mutex = SDL_CreateMutex();
  108.     q->cond = SDL_CreateCond();
  109. }

  110. static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
  111. {
  112.     AVPacketList *pkt1;
  113.     pkt1 = av_malloc(sizeof(AVPacketList));
  114.     if (!pkt1)
  115.         return -1;
  116.     pkt1->pkt = *pkt;
  117.     pkt1->next = NULL;

  118.     SDL_LockMutex(q->mutex);
  119.     if (!q->last_pkt)
  120.         q->first_pkt = pkt1;
  121.     else
  122.         q->last_pkt->next = pkt1;
  123.     q->last_pkt = pkt1;
  124.     q->nb_packets++;
  125.     q->size += pkt1->pkt.size;
  126.     //dbmsg("put_queue and send singal");
  127.     SDL_CondSignal(q->cond);
  128.     SDL_UnlockMutex(q->mutex);
  129.     return 0;
  130. }

  131. static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
  132. {
  133.     AVPacketList *pkt1;
  134.     int ret;
  135.     SDL_LockMutex(q->mutex);
  136.     for(;;)
  137.     {
  138.         pkt1 = q->first_pkt;
  139.         if (pkt1)
  140.         {
  141.             q->first_pkt = pkt1->next;
  142.             if (!q->first_pkt)
  143.                 q->last_pkt = NULL;
  144.             q->nb_packets--;
  145.             q->size -= pkt1->pkt.size;
  146.             *pkt = pkt1->pkt;
  147.             av_free(pkt1);
  148.             ret = 1;
  149.             break;
  150.         } else if (!block) {
  151.             ret = 0;
  152.             break;
  153.         } else {
  154.             SDL_CondWait(q->cond, q->mutex);
  155.          }
  156.     }
  157.     SDL_UnlockMutex(q->mutex);
  158.     return ret;
  159. }
  160. double get_audio_clock(VideoState *is) {
  161.     double pts;
  162.     int hw_buf_size, bytes_per_sec, n;

  163.     pts = is->audio_clock; /* maintained in the audio thread */
  164.     hw_buf_size = is->audio_buf_size - is->audio_buf_index;
  165.     bytes_per_sec = 0;
  166.     n = is->sndCodecCtx->channels * 2;
  167.     bytes_per_sec = is->sndCodecCtx->sample_rate * n;
  168.     if(bytes_per_sec) {
  169.         pts -= (double)hw_buf_size / bytes_per_sec;
  170.     }
  171.     return pts;
  172. }

  173. static int audio_decode_frame(VideoState* is)
  174. {
  175.     int len1,len2, data_size, got_frame;
  176.     int new_packet;
  177.     int n;
  178.     int64_t dec_channel_layout;
  179.     uint8_t *out[] = { is->audio_buf2 };
  180.     AVPacket *pkt = av_mallocz(sizeof(AVPacket));
  181.     AVPacket *pkt_temp = av_mallocz(sizeof(AVPacket));
  182.     AVFrame *frame = av_frame_alloc();
  183.     for(;;)
  184.     {
  185.         while(pkt_temp->size>|| (!pkt_temp->data && new_packet))
  186.         {
  187.             if(frame)
  188.             {
  189.                 //dbmsg("av_get default frame");
  190.                 av_frame_unref(frame); //reset frame
  191.             }
  192.             new_packet = 0;
  193.             len1 = avcodec_decode_audio4(is->sndCodecCtx, frame, &got_frame, pkt_temp); //decode data is store in frame
  194.             if(len1 < 0)
  195.             {
  196.                 pkt_temp->size = 0;
  197.                 break;
  198.             }
  199.             //dbmsg("len1=%d, linesize=%d",len1, frame->linesize[0]);

  200.             pkt_temp->data += len1;
  201.             pkt_temp->size -= len1;

  202.             if(got_frame <= 0) /* No data yet, get more frames */
  203.                 continue;
  204.             data_size = av_samples_get_buffer_size(NULL, is->sndCodecCtx->channels, frame->nb_samples, is->sndCodecCtx->sample_fmt, 1);
  205.             dec_channel_layout = (frame->channel_layout && frame->channels==av_get_channel_layout_nb_channels(frame->channel_layout))?
  206.                 frame->channel_layout: is->wanted_channel_layout;
  207. #if 0
  208.             dbmsg("format=%d:%d,layout=%lld:%lld,rate=%d:%d,samples=%d:%d",frame->format,is->wanted_fmt,
  209.                    (long long) dec_channel_layout,(long long)av_get_default_channel_layout(is->wanted_channels),
  210.               frame->sample_rate,is->wanted_freq,frame->nb_samples,is->wanted_samples );

  211.             dbmsg("wanted_channel_layout=%lld", (long long)is->wanted_channel_layout);
  212.             dbmsg("wanted_fmt=%d", is->wanted_fmt);
  213.             dbmsg("spec.freq=%d", is->wanted_freq);
  214. #endif
  215.             //check: format,channel_layout,rate,sample
  216.             if((NULL==is->swr_ctx) && ( (frame->format!=is->wanted_fmt) || (dec_channel_layout!=av_get_default_channel_layout(is->wanted_channels)) ||
  217.               (frame->sample_rate!=is->wanted_freq) || (frame->nb_samples!=is->wanted_samples)) )
  218.             {
  219.                 if(is->swr_ctx != NULL)
  220.                     swr_free(&is->swr_ctx);
  221. #if 0
  222.                 dbmsg("wanted_channel_layout=%lld, wanted_fmt=%d, wanted_sample=%d, dec_channel_layout=%lld, frame->format=%d, frame->sample_rate=%d",
  223.                 (long long)is->wanted_channel_layout, is->wanted_fmt, is->wanted_samples, (long long)dec_channel_layout, frame->format, frame->sample_rate);
  224. #endif
  225.                 is->swr_ctx = swr_alloc_set_opts(NULL, is->wanted_channel_layout, is->wanted_fmt, is->wanted_freq, dec_channel_layout, frame->format, frame->sample_rate, 0, NULL);
  226.                 if(is->swr_ctx == NULL)
  227.                 {
  228.                     dbmsg("swr_ctx == NULL");
  229.                 }
  230.                 swr_init(is->swr_ctx);
  231.             }
  232.             if(is->swr_ctx)
  233.             {
  234.                 len2 = swr_convert(is->swr_ctx, out, sizeof(is->audio_buf2)/is->wanted_channels/av_get_bytes_per_sample(frame->format),(const uint8_t **)frame->extended_data, frame->nb_samples);
  235.                 data_size = len2 * is->wanted_channels * av_get_bytes_per_sample(is->wanted_fmt);
  236.             }else {
  237.                 memcpy(is->audio_buf2, frame->data[0], frame->linesize[0]);
  238.             }
  239.             n = 2*is->sndCodecCtx->channels;
  240.             is->audio_clock += (double)data_size/(double)(n*is->sndCodecCtx->sample_rate);    //下面获取的audio的pts与下一个sample的时间相加得到下一个sample的pts
  241.             dbmsg("data_size=%d,sample_rate=%d, audio_clock=%lf, n=%d",data_size,is->sndCodecCtx->sample_rate, is->audio_clock, n);
  242.             return data_size;
  243.         }
  244.         if(pkt->data)
  245.             av_freep(pkt);
  246.         memset(pkt_temp, 0, sizeof(*pkt_temp));
  247.         if(signal_quit)
  248.             return -1;
  249.         if((new_packet = packet_queue_get(&is->audioq, pkt, 1)) < 0)
  250.         {
  251.             dbmsg("get packet=%d", new_packet);
  252.             return -1;
  253.         }
  254.         if(pkt->pts != AV_NOPTS_VALUE)
  255.         {
  256.             is->audio_clock = av_q2d(is->sndCodecCtx->time_base)*pkt->pts;   //这儿获取的是audio的pts单位是s
  257.             dbmsg("audio_clock=%lf", is->audio_clock);
  258.         }

  259.         *pkt_temp = *pkt;
  260.     }
  261. }

  262. static void audio_callback(void *userdata, Uint8 *stream, int len)
  263. {
  264.     VideoState* is = (VideoState*)userdata;
  265.     int len1, audio_size;

  266.     //static unsigned int audio_buf_size = 0;
  267.     //static unsigned int audio_buf_index = 0;

  268.     while(len > 0)
  269.     {
  270.         if(is->audio_buf_index >= is->audio_buf_size)
  271.         {
  272.             audio_size = audio_decode_frame(is); //decode data is store in is->audio_buf2
  273.             //dbmsg("audio_size=%d", audio_size);
  274.             if(audio_size < 0)
  275.             {
  276.                 /* If error, output silence */
  277.                 is->audio_buf_size = 1024;
  278.                 memset(is->audio_buf2, 0, is->audio_buf_size);
  279.             } else {
  280.                 is->audio_buf_size = audio_size;
  281.             }
  282.             is->audio_buf_index = 0;
  283.         }
  284.         //dbmsg("len=%d, audio_buf_size=%d, audio_buf_index=%d", len, audio_buf_size, audio_buf_index);
  285.         len1 = is->audio_buf_size - is->audio_buf_index;
  286.         //dbmsg("len1=%d", len1);
  287.         if(len1 > len)
  288.             len1 = len;
  289.         //dbmsg("len1 = %d", len1);
  290.         memcpy(stream, (uint8_t *)is->audio_buf2 + is->audio_buf_index, len1);
  291.         len -= len1;
  292.         stream += len1;
  293.         is->audio_buf_index += len1;
  294.     }
  295. }

  296. static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) {
  297.   SDL_Event event;
  298.   event.type = FF_REFRESH_EVENT;
  299.   event.user.data1 = opaque;
  300.   SDL_PushEvent(&event);
  301.   return 0; /* 0 means stop timer */
  302. }

  303. /* schedule a video refresh in 'delay' ms */
  304. static void schedule_refresh(VideoState *is, int delay) {
  305.   dbmsg("schedule_delay = %d", delay);
  306.   SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
  307. }

  308. static void video_display(VideoState *is) {
  309.     SDL_LockMutex(is->screen_mutex);
  310.     SDL_DisplayYUVOverlay(is->overlay, &is->rect);
  311.     SDL_UnlockMutex(is->screen_mutex);
  312. }

  313. static void video_refresh_timer(void *data)
  314. {
  315.     VideoState *is = (VideoState *)data;
  316.     double actual_delay, delay, sync_threshold, ref_clock, diff;
  317.     if(is->pictq_size == 0) {
  318.         schedule_refresh(is, 1);
  319.     } else {
  320.         delay = is->now_video_pts - is->frame_last_pts;   //当前帧的pts与上一帧的pts的差就是两帧的时间间隔,单位是s
  321.         dbmsg("delay=%lf", delay);
  322.         if(delay<=|| delay>=1.0)                        //前后两帧的间隔不能小于0,也不能大于1s
  323.         {
  324.             delay = is->frame_last_delay;
  325.             dbmsg("delay=is->frame_last_delay=%lf", delay);
  326.         }
  327.         is->frame_last_delay = delay;                     //保存上一次的delay结果
  328.         is->frame_last_pts = is->now_video_pts;           //保存上一次的pts结果 

  329.         ref_clock = get_audio_clock(is);                  //获取当前audio的pts
  330.         dbmsg("now_video_pts=%lf, ref_clock=%lf",is->now_video_pts, ref_clock);
  331.         diff = is->now_video_pts - ref_clock;             //视频相对于音频提前或落后了多长时间

  332.         //每次同步的时间间隔不能小于这个阀值
  333.         sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD;
  334.         if( fabs(diff) < AV_NOSYNC_THRESHOLD)
  335.         {
  336.             if(diff <= -sync_threshold)           //视频比音频慢了,就让视频快点跑delay=0
  337.                 delay = 0;
  338.             else if( diff >= sync_threshold)      //视频比音频快了,就让视频慢点跑delay=2*delay
  339.                 delay = 2 * delay;
  340.         }
  341.         is->frame_timer += delay;
  342.         actual_delay = is->frame_timer - (av_gettime() / 1000000.0); //单位是s
  343.         if(actual_delay < 0.010) {
  344.                actual_delay = 0.010;
  345.         }
  346.         dbmsg("actual_delay=%lf", actual_delay);
  347.         schedule_refresh(is, (int)(actual_delay * 1000 + 0.5));     //刷新的单位是ms,这儿加0.5的作用是四舍五入吗?
  348.         //schedule_refresh(is, 40);
  349.         video_display(is);

  350.         SDL_LockMutex(is->pictq_mutex);
  351.         is->pictq_size--;
  352.         SDL_CondSignal(is->pictq_cond);
  353.         SDL_UnlockMutex(is->pictq_mutex);
  354.     }
  355. }

  356. static int queue_picture(VideoState *is, AVFrame *pFrame)
  357. {
  358.     /* wait until we have space for a new pic */
  359.     SDL_LockMutex(is->pictq_mutex);
  360.     while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && (0==signal_quit)) {
  361.         SDL_CondWait(is->pictq_cond, is->pictq_mutex);
  362.     }
  363.     SDL_UnlockMutex(is->pictq_mutex);

  364.     if(signal_quit)
  365.       return -1;

  366.     /* We have a place to put our picture on the queue */
  367.     SDL_LockYUVOverlay(is->overlay);
  368.     is->pFrameYUV->data[0] = is->overlay->pixels[0];
  369.     is->pFrameYUV->data[1] = is->overlay->pixels[2];
  370.     is->pFrameYUV->data[2] = is->overlay->pixels[1];
  371.     is->pFrameYUV->linesize[0] = is->overlay->pitches[0];
  372.     is->pFrameYUV->linesize[1] = is->overlay->pitches[2];
  373.     is->pFrameYUV->linesize[2] = is->overlay->pitches[1];
  374.     sws_scale(is->img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0,
  375.     is->vdoCodecCtx->height, is->pFrameYUV->data, is->pFrameYUV->linesize);
  376.     SDL_UnlockYUVOverlay(is->overlay);
  377.     SDL_LockMutex(is->pictq_mutex);
  378.     is->pictq_size++;
  379.     SDL_UnlockMutex(is->pictq_mutex);
  380.     return 0;
  381. }

  382. double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) {
  383.     double frame_delay;
  384.     //函数的作用是为了修正video_clock,对需要重复的帧修正video_clock
  385.     if(pts != 0) {
  386.         /* if we have pts, set video clock to it */
  387.         is->video_clock = pts;
  388.     } else {
  389.         /* if we aren't given a pts, set it to the clock */
  390.         pts = is->video_clock;         //但是video_clock只在这儿用了一次,当pts==0时把pts设为vidoe_clock
  391.     }                                  //pts只有在第1帧时pts才会为0,那时video_clock也是0,作用就是把0赋给pts,也就是没有用处  
  392.     /* update the video clock */       //所以感觉这个函数有没有都无所谓,现在是这么理解的,不知对不对?
  393.     frame_delay = av_q2d(is->vdoStream->time_base);
  394.     /* if we are repeating a frame, adjust clock accordingly */
  395.     frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
  396.     is->video_clock += frame_delay;
  397.     return pts;
  398. }
  399. static int video_thread(void* data)
  400. {
  401.     VideoState *is = (VideoState *)data;
  402.     AVPacket pkt1, *packet = &pkt1;
  403.     AVFrame *pFrame;
  404.     double pts;
  405.     int ret;
  406.     pFrame = av_frame_alloc();

  407.    for(;;)
  408.    {
  409.       if(signal_quit)
  410.           break;
  411.       if(packet_queue_get(&is->videoq, packet, 1) < 0) {
  412.           // means we quit getting packets
  413.           dbmsg("packet_queue_get < 0");
  414.           break;
  415.         }
  416.         // Decode video frame
  417.         if((ret=avcodec_decode_video2(is->vdoCodecCtx, pFrame, &is->frameFinished, packet)) < 0)
  418.         {
  419.             dbmsg("decocode video error");
  420.             continue;
  421.         }
  422.         if((pts = av_frame_get_best_effort_timestamp(pFrame)) == AV_NOPTS_VALUE) {
  423.             pts = 0;
  424.         }
  425.         //这儿的pts是0 3003 5005 6006 8008这样的
  426.         dbmsg("video: before_pts = %lf", pts);
  427.         pts *= av_q2d(is->vdoStream->time_base);     //time_base[1,24002],即pts=pts*(1/24002),此时pts的单位是s
  428.         dbmsg("time_base[%d,%d]", is->vdoStream->time_base.den,is->vdoStream->time_base.num);

  429.         dbmsg("video: after_pts = %lf", pts);
  430.         if(is->frameFinished) {
  431.          is->now_video_pts = synchronize_video(is, pFrame, pts);    //保存当前帧的pts到is->now_vieo_pts
  432.          //dbmsg("is->now_video_pts = %lf", is->now_video_pts);
  433.           if(queue_picture(is, pFrame) < 0) {
  434.             break;
  435.           }
  436.         }
  437.         av_freep(packet);
  438.     }
  439.     av_frame_free(&pFrame);
  440.     return 0;
  441. }

  442. static int decode_thread(void* data)
  443. {
  444.     VideoState* is = (VideoState*)data;
  445.     is->packet = (AVPacket*)av_malloc(sizeof(AVPacket));
  446.     while( (av_read_frame(is->pFormatCtx, is->packet)>=0) && (signal_quit==0))
  447.     {
  448.         if(is->packet->stream_index == is->videoindex)
  449.         {
  450.             packet_queue_put(&is->videoq, is->packet);
  451.         }

  452.         if(is->packet->stream_index == is->sndindex)
  453.         {
  454.             packet_queue_put(&is->audioq, is->packet);
  455.         }
  456.     }
  457.     return 0;
  458. }

  459. static int init_ffmpeg_and_SDL(VideoState* is, const char* video_file)
  460. {
  461.     int i=0;
  462.     SDL_AudioSpec wanted_spec, real_spec;
  463.     is->videoindex = -1;
  464.     is->sndindex = -1;
  465.     if(NULL == video_file)
  466.     {
  467.         dbmsg("input file is NULL");
  468.         return -1;
  469.     }
  470.     SDL_Init(SDL_INIT_EVERYTHING);
  471.     avcodec_register_all();
  472.     //avfilter_register_all();
  473.     av_register_all();

  474.     is->pFormatCtx = avformat_alloc_context();

  475.     if(avformat_open_input(&is->pFormatCtx, video_file, NULL, NULL)!=0)
  476.         return -1;

  477.     if(avformat_find_stream_info(is->pFormatCtx, NULL)<0)
  478.         return -1;
  479.     av_dump_format(is->pFormatCtx,0, 0, 0);

  480.     for(i=0; i<is->pFormatCtx->nb_streams; i++)
  481.     {
  482.         if(is->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
  483.         {
  484.             is->videoindex= i;
  485.             is->vdoStream =is->pFormatCtx->streams[i];                //保存video的stream以后要用到time_base
  486.         }
  487.         if(is->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
  488.         {
  489.             is->sndindex= i;
  490.             is->sndStream =is->pFormatCtx->streams[i];
  491.         }
  492.     }
  493.     if(is->videoindex== -1)
  494.         dbmsg("no video stream found!");
  495.     if(is->sndindex== -1)
  496.         dbmsg("no sound stream found!");
  497.     dbmsg("videoindex=%d, sndindex=%d", is->videoindex, is->sndindex);

  498.     if(is->videoindex != -1)
  499.     {
  500.         is->vdoCodecCtx = is->pFormatCtx->streams[is->videoindex]->codec;
  501.         is->vdoCodec = avcodec_find_decoder(is->vdoCodecCtx->codec_id);
  502.         if(is->vdoCodec == NULL)
  503.         {
  504.             dbmsg("Codec not found");
  505.             return -1;
  506.         }
  507.         if(avcodec_open2(is->vdoCodecCtx, is->vdoCodec, NULL) < 0)
  508.             return -1;
  509.         is->pFrameYUV = av_frame_alloc();
  510.         is->img_convert_ctx = sws_getContext(is->vdoCodecCtx->width, is->vdoCodecCtx->height, is->vdoCodecCtx->pix_fmt,
  511.               is->vdoCodecCtx->width, is->vdoCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
  512.         if(is->img_convert_ctx == NULL)
  513.         {
  514.             dbmsg("img_convert error");
  515.             return -1;
  516.         }
  517.         is->psscreen = SDL_SetVideoMode(is->vdoCodecCtx->width, is->vdoCodecCtx->height, 0, SDL_SWSURFACE);
  518.         SDL_WM_SetCaption( "FFMPEG Window", NULL);
  519.         is->overlay = SDL_CreateYUVOverlay(is->vdoCodecCtx->width, is->vdoCodecCtx->height, SDL_YV12_OVERLAY, is->psscreen);
  520.         is->rect.= 0;
  521.         is->rect.= 0;
  522.      is->rect.= is->vdoCodecCtx->width;
  523.         is->rect.= is->vdoCodecCtx->height;
  524.         packet_queue_init(&is->videoq);
  525.         is->video_tid = SDL_CreateThread(video_thread, is);
  526.         is->frame_timer = (double)av_gettime() / 1000000.0;      //获取当前时间单位是s,因为av_gettime的单位是us
  527.         is->frame_last_delay = 40e-3;
  528.     }
  529.     if(is->sndindex != -1)
  530.     {
  531.         is->sndCodecCtx = is->pFormatCtx->streams[is->sndindex]->codec;
  532.         is->sndCodec = avcodec_find_decoder(is->sndCodecCtx->codec_id);
  533.         if(is->sndCodec == NULL)
  534.         {
  535.             dbmsg("Codec not found");
  536.             return -1;
  537.         }
  538.         if(avcodec_open2(is->sndCodecCtx, is->sndCodec, NULL) < 0)
  539.             return -1;

  540.         wanted_spec.freq = is->sndCodecCtx->sample_rate;
  541.         wanted_spec.format = AUDIO_S16SYS;
  542.         wanted_spec.channels = is->sndCodecCtx->channels;
  543.         wanted_spec.silence = 0;
  544.         wanted_spec.samples = 2048;
  545.         wanted_spec.callback = audio_callback;
  546.         wanted_spec.userdata = is;
  547.         is->audio_buf_size = 0;
  548.         is->audio_buf_index = 0;
  549.         if(SDL_OpenAudio(&wanted_spec, &real_spec) < 0)
  550.         {
  551.             dbmsg("SDL_OpenAudio:%s", SDL_GetError());
  552.             return -1;
  553.         }
  554.         //store infomation for audio swr_convert
  555.         is->wanted_fmt = AV_SAMPLE_FMT_S16; //AUDIO_S16SYS;
  556.         is->wanted_channel_layout = av_get_default_channel_layout(is->sndCodecCtx->channels);
  557.         is->wanted_freq = real_spec.freq;
  558.         is->wanted_samples = real_spec.samples;
  559.         is->wanted_channels = real_spec.channels;
  560.         //dbmsg("freq=%d, channels=%d, samples=%d", is->wanted_freq, is->wanted_channels, is->wanted_samples);
  561.         packet_queue_init(&is->audioq);
  562.         SDL_PauseAudio(0);
  563.     }
  564.     return 0;
  565. }


  566. int main(int argc, char **argv)
  567. {
  568.     int ret;
  569.     SDL_Thread* sdl_thread;
  570.     SDL_Thread* decode_thread_id;
  571.     VideoState* is = (VideoState*) av_mallocz(sizeof(VideoState));
  572.     if(argc < 2)
  573.         dbmsg("usage: ./ffplay \n");

  574.     if( (ret=init_ffmpeg_and_SDL(is, argv[1])) != 0)
  575.     {
  576.         dbmsg("init_ffmpeg_and SDL error");
  577.         return -1;
  578.     }

  579.     is->pictq_mutex = SDL_CreateMutex();
  580.     is->pictq_cond = SDL_CreateCond();

  581.     //sdl get signal quit
  582.     affmutex = SDL_CreateMutex();
  583.     decode_thread_id = SDL_CreateThread(decode_thread, is);
  584.     sdl_thread = SDL_CreateThread(sdl_event_thread, NULL);

  585.     schedule_refresh(is, 40);

  586.     SDL_WaitThread(sdl_thread, &ret);
  587.     SDL_WaitThread(decode_thread_id, &ret);
  588.     SDL_WaitThread(is->video_tid,&ret);
  589.     SDL_DestroyMutex(affmutex);
  590.     av_freep(is->packet);
  591.     av_free(is->pFrameYUV);
  592.     avcodec_close(is->vdoCodecCtx);
  593.     avcodec_close(is->sndCodecCtx);
  594.     avformat_close_input(&is->pFormatCtx);
  595.     return 0;
  596. }
2.2 Makefile
  1. EXE=sync
  2. CC=gcc
  3. FFMPEG=/work/ffmpeg/ffmpeg-3.0.1/install/
  4. CFLAGS=-g -O0 -I$(FFMPEG)/include
  5. LDFLAGS = -L$(FFMPEG)/lib/ -lz -lswscale -lswresample -lavformat -lavdevice -lavcodec -lavutil -lavfilter -lm -lSDL -pthread -lz -lbz2
  6. SRC=$(wildcard *.c)
  7. OBJ=$(patsubst %.c,%.o,$(SRC))
  8. DEP=$(patsubst %.c,.%.d,$(SRC))
  9. $(EXE):$(OBJ)
  10.     $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)

  11. $(DEP):.%.d:%.c
  12.     @set -e; rm -f $@; \
  13.     $(CC) -MM $< > $@.$$$$; \
  14.     sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; \
  15.     rm -f $@.$$$$

  16. -include $(DEP)
  17. clean:
  18.     @rm $(EXE) $(OBJ) $(DEP) -f
  19. run:
  20.     export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(FFMPEG)/lib/ \
  21.     && ./$(EXE) ../../san.mp4
  22.     #export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/work/ffmpeg/out/lib/
Makefile也需要修改一下。

3.代码
5sync.rar(下载后改名为5sync.tar.gz)
3.1 几个编译问题
a. -pthread,不加这个会出现以下报错
  1. /usr/bin/ld: /work/ffmpeg/ffmpeg-3.0.1/install//lib//libavcodec.a(h264.o): undefined reference to symbol 'pthread_once@@GLIBC_2.2.5'
  2. //lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from command line
  3. collect2: error: ld returned 1 exit status
b.  -lz,不加这个会出现以下报错
  1. /usr/bin/ld: /work/ffmpeg/ffmpeg-3.0.1/install//lib//libavformat.a(http.o): undefined reference to symbol 'inflateInit2_'
  2. /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libz.so: error adding symbols: DSO missing from command line
c.-lbz2不加这个会出现以下报错
  1. /work/ffmpeg/ffmpeg-3.0.1/install//lib//libavformat.a(matroskadec.o): In function `matroska_decode_buffer':
  2. /work/ffmpeg/ffmpeg-3.0.1/libavformat/matroskadec.c:1327: undefined reference to `BZ2_bzDecompressInit'
  3. /work/ffmpeg/ffmpeg-3.0.1/libavformat/matroskadec.c:1342: undefined reference to `BZ2_bzDecompress'
  4. /work/ffmpeg/ffmpeg-3.0.1/libavformat/matroskadec.c:1335: undefined reference to `BZ2_bzDecompressEnd'
  5. /work/ffmpeg/ffmpeg-3.0.1/libavformat/matroskadec.c:1345: undefined reference to `BZ2_bzDecompressEnd
3.2 pkg-config更新Makefile
a. 重新编译ffmpeg
这儿注意:   
    加入  --enable-shared 生成动态链接库
  
     --prefix=/work/ffmpeg/ffmpeg-3.0.1/install  后面跟完整路径名,这样pkg-config后面也是完整的路径,
       否则pkg-config是相对路径,把5sync的代码放在其它的目录中就会找不到库
cong@msi:/work/ffmpeg/ffmpeg-3.0.1$ ./configure --enable-shared --prefix=/work/ffmpeg/ffmpeg-3.0.1/install --disable-ffmpeg --disable-ffprobe --disable-ffserver
cong@msi:/work/ffmpeg/ffmpeg-3.0.1$ make -j16 && make install 
b. ffmpge编译并install之后,需要加入pkg-config的查找路径
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/work/ffmpeg/ffmpeg-3.0.1/install/lib/pkgconfig/
测试一下pkg-config --libs libavfilter
c
. 重写了一个Makefile
cong@msi:/work/ffmpeg/test/5sync$ cat Makefile 
  1. EXE=sync
  2. FFMPEG=/work/ffmpeg/ffmpeg-3.0.1/install
  3. CC=gcc
  4. SRC:=$(wildcard *.c) $(wildcard *.cpp)
  5. OBJ:=$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SRC)))
  6. FFMPEG_LIBS= libavdevice \
  7.                 libavformat \
  8.                 libavfilter \
  9.                 libavcodec \
  10.                 libswresample \
  11.                 libswscale \
  12.                 libavutil \

  13. CFLAGS += -Wall -g
  14. CPPFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(shell pkg-config --cflags sdl)
  15. LDFLAGS:= $(shell pkg-config --libs $(FFMPEG_LIBS)) $(shell pkg-config --libs sdl)
  16. DEP:=$(patsubst %.c,.%.d,$(patsubst %.cpp,.%.d,$(SRC)))

  17. $(EXE):$(OBJ)
  18.     $(CC) $^ -o $@ $(LDFLAGS)
  19.   
  20. %.o: %.c
  21.     $(CC) $(CPPFLAGS) -c $< -o $@
  22.   
  23. %.o: %.cpp
  24.     $(CC) $(CPPFLAGS) -c $< -o $@
  25.   
  26. -include $(DEP)
  27.   
  28. define gen_dep
  29. @set -e; rm -f $@; \
  30. $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
  31. sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
  32. rm -f $@.$$$$
  33. endef
  34.   
  35. .%.d: %.c
  36.     $(gen_dep)
  37.   
  38. .%.d: %.cpp
  39.     $(gen_dep)
  40.   
  41. .PHONY: clean
  42. clean:
  43.     rm -f all $(OBJ) $(DEP)
  44.   
  45. echo:
  46.     @echo SRC=$(SRC)
  47.     @echo OBJ=$(OBJ)
  48.     @echo DEP=$(DEP)
  49.     @echo CPPFLAGS=$(CPPFLAGS)
  50. run:
  51.     export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(FFMPEG)/lib/ \
  52.     && ./$(EXE) ../../san.mp4
  53.     #export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/work/ffmpeg/out/lib/
这样5sync的代码在任何地方都可以编译了

阅读(3631) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~