http://dranger.com/ffmpeg/tutorial05.html
1. 根据上面的教程学习音视频同步
2. 代码基于ffmpeg-3.0.1
2.1 注:同步新加的部分用绿色标出
2.2 Makefile
Makefile也需要修改一下。
3.代码
5sync.rar (下载后改名为5sync.tar.gz)
3.1 几个编译问题
a. -pthread,不加这个会出现以下报错
b.
-lz,不加这个会出现以下报错
c.
-lbz2
,
不加这个会出现以下报错
1. 根据上面的教程学习音视频同步
2. 代码基于ffmpeg-3.0.1
2.1 注:同步新加的部分用绿色标出
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libswscale/swscale.h>
- #include <libswresample/swresample.h>
- #include <libavutil/avstring.h>
- #include <libavutil/pixfmt.h>
- #include <libavutil/log.h>
- #include <libavutil/time.h> //要使用函数av_gettime
- #include <SDL/SDL.h>
- #include <SDL/SDL_thread.h>
- #include <stdio.h>
- #include <math.h>
-
- #define SDL_AUDIO_BUFFER_SIZE 4096
- #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
-
- #define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
- #define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
-
- #define AV_SYNC_THRESHOLD 0.01
- #define AV_NOSYNC_THRESHOLD 10.0
-
- #define FF_REFRESH_EVENT (SDL_USEREVENT)
- #define FF_QUIT_EVENT (SDL_USEREVENT + 1)
-
- typedef struct PacketQueue
- {
- AVPacketList * first_pkt, *last_pkt;
- int nb_packets;
- int size;
- SDL_mutex *mutex;
- SDL_cond * cond;
- }PacketQueue;
-
- static int signal_quit = 0;
- SDL_mutex *affmutex;
- SDL_Event sdlevent;
-
- #define VIDEO_PICTURE_QUEUE_SIZE 1
-
- typedef struct VideoState {
- int videoindex;
- int sndindex;
- int frameFinished;
- int wanted_freq;
- int wanted_samples;
- int wanted_channels;
- int pictq_size, pictq_rindex, pictq_windex;
- unsigned int audio_buf_size;
- unsigned int audio_buf_index;
- double frame_timer;
- double frame_last_pts;
- double now_video_pts;
- double frame_last_delay;
- double video_clock;
- double audio_clock;
- SDL_Thread* video_tid;
- AVFormatContext* pFormatCtx;
- AVCodecContext* vdoCodecCtx;
- AVCodecContext* sndCodecCtx;
- AVCodec* vdoCodec;
- AVCodec* sndCodec;
- AVStream* vdoStream;
- AVStream* sndStream;
- AVFrame* pFrameYUV;
- AVPacket* packet;
- struct SwsContext *img_convert_ctx;
- struct SwrContext *swr_ctx;
- SDL_mutex *pictq_mutex;
- SDL_cond *pictq_cond;
- SDL_Surface* psscreen;
- SDL_Overlay* overlay;
- SDL_Rect rect;
- SDL_mutex *screen_mutex;
- enum AVSampleFormat wanted_fmt;
- int64_t wanted_channel_layout;
- PacketQueue audioq;
- PacketQueue videoq;
- DECLARE_ALIGNED(16,uint8_t,audio_buf2) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
- }VideoState;
-
- static void video_refresh_timer(void* arg);
-
- double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) ; //这两个函数是新加的
- double get_audio_clock(VideoState *is);
- static int sdl_event_thread(void* data)
- {
- while((0==signal_quit))
- {
- SDL_LockMutex(affmutex);
- while(SDL_PollEvent(&sdlevent))
- {
- switch(sdlevent.type)
- {
- case FF_QUIT_EVENT:
- case SDL_QUIT:
- {
- signal_quit = 1;
- SDL_Quit();
- exit(0);
- }
- break;
- case FF_REFRESH_EVENT:
- video_refresh_timer(sdlevent.user.data1);
- break;
- default:
- break;
- }
- }
- SDL_UnlockMutex(affmutex);
- }
- return 0;
- }
-
- static void packet_queue_init(PacketQueue *q)
- {
- memset(q, 0, sizeof(PacketQueue));
- q->mutex = SDL_CreateMutex();
- q->cond = SDL_CreateCond();
- }
-
- static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
- {
- AVPacketList *pkt1;
- pkt1 = av_malloc(sizeof(AVPacketList));
- if (!pkt1)
- return -1;
- pkt1->pkt = *pkt;
- pkt1->next = NULL;
-
- SDL_LockMutex(q->mutex);
- if (!q->last_pkt)
- q->first_pkt = pkt1;
- else
- q->last_pkt->next = pkt1;
- q->last_pkt = pkt1;
- q->nb_packets++;
- q->size += pkt1->pkt.size;
- //dbmsg("put_queue and send singal");
- SDL_CondSignal(q->cond);
- SDL_UnlockMutex(q->mutex);
- return 0;
- }
-
- static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
- {
- AVPacketList *pkt1;
- int ret;
- SDL_LockMutex(q->mutex);
- for(;;)
- {
- pkt1 = q->first_pkt;
- if (pkt1)
- {
- q->first_pkt = pkt1->next;
- if (!q->first_pkt)
- q->last_pkt = NULL;
- q->nb_packets--;
- q->size -= pkt1->pkt.size;
- *pkt = pkt1->pkt;
- av_free(pkt1);
- ret = 1;
- break;
- } else if (!block) {
- ret = 0;
- break;
- } else {
- SDL_CondWait(q->cond, q->mutex);
- }
- }
- SDL_UnlockMutex(q->mutex);
- return ret;
- }
- double get_audio_clock(VideoState *is) {
- double pts;
- int hw_buf_size, bytes_per_sec, n;
-
- pts = is->audio_clock; /* maintained in the audio thread */
- hw_buf_size = is->audio_buf_size - is->audio_buf_index;
- bytes_per_sec = 0;
- n = is->sndCodecCtx->channels * 2;
- bytes_per_sec = is->sndCodecCtx->sample_rate * n;
- if(bytes_per_sec) {
- pts -= (double)hw_buf_size / bytes_per_sec;
- }
- return pts;
- }
-
- static int audio_decode_frame(VideoState* is)
- {
- int len1,len2, data_size, got_frame;
- int new_packet;
- int n;
- int64_t dec_channel_layout;
- uint8_t *out[] = { is->audio_buf2 };
- AVPacket *pkt = av_mallocz(sizeof(AVPacket));
- AVPacket *pkt_temp = av_mallocz(sizeof(AVPacket));
- AVFrame *frame = av_frame_alloc();
- for(;;)
- {
- while(pkt_temp->size>0 || (!pkt_temp->data && new_packet))
- {
- if(frame)
- {
- //dbmsg("av_get default frame");
- av_frame_unref(frame); //reset frame
- }
- new_packet = 0;
- len1 = avcodec_decode_audio4(is->sndCodecCtx, frame, &got_frame, pkt_temp); //decode data is store in frame
- if(len1 < 0)
- {
- pkt_temp->size = 0;
- break;
- }
- //dbmsg("len1=%d, linesize=%d",len1, frame->linesize[0]);
-
- pkt_temp->data += len1;
- pkt_temp->size -= len1;
-
- if(got_frame <= 0) /* No data yet, get more frames */
- continue;
- data_size = av_samples_get_buffer_size(NULL, is->sndCodecCtx->channels, frame->nb_samples, is->sndCodecCtx->sample_fmt, 1);
- dec_channel_layout = (frame->channel_layout && frame->channels==av_get_channel_layout_nb_channels(frame->channel_layout))?
- frame->channel_layout: is->wanted_channel_layout;
- #if 0
- dbmsg("format=%d:%d,layout=%lld:%lld,rate=%d:%d,samples=%d:%d",frame->format,is->wanted_fmt,
- (long long) dec_channel_layout,(long long)av_get_default_channel_layout(is->wanted_channels),
- frame->sample_rate,is->wanted_freq,frame->nb_samples,is->wanted_samples );
-
- dbmsg("wanted_channel_layout=%lld", (long long)is->wanted_channel_layout);
- dbmsg("wanted_fmt=%d", is->wanted_fmt);
- dbmsg("spec.freq=%d", is->wanted_freq);
- #endif
- //check: format,channel_layout,rate,sample
- if((NULL==is->swr_ctx) && ( (frame->format!=is->wanted_fmt) || (dec_channel_layout!=av_get_default_channel_layout(is->wanted_channels)) ||
- (frame->sample_rate!=is->wanted_freq) || (frame->nb_samples!=is->wanted_samples)) )
- {
- if(is->swr_ctx != NULL)
- swr_free(&is->swr_ctx);
- #if 0
- dbmsg("wanted_channel_layout=%lld, wanted_fmt=%d, wanted_sample=%d, dec_channel_layout=%lld, frame->format=%d, frame->sample_rate=%d",
- (long long)is->wanted_channel_layout, is->wanted_fmt, is->wanted_samples, (long long)dec_channel_layout, frame->format, frame->sample_rate);
- #endif
- 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);
- if(is->swr_ctx == NULL)
- {
- dbmsg("swr_ctx == NULL");
- }
- swr_init(is->swr_ctx);
- }
- if(is->swr_ctx)
- {
- 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);
- data_size = len2 * is->wanted_channels * av_get_bytes_per_sample(is->wanted_fmt);
- }else {
- memcpy(is->audio_buf2, frame->data[0], frame->linesize[0]);
- }
- n = 2*is->sndCodecCtx->channels;
- is->audio_clock += (double)data_size/(double)(n*is->sndCodecCtx->sample_rate); //下面获取的audio的pts与下一个sample的时间相加得到下一个sample的pts
- dbmsg("data_size=%d,sample_rate=%d, audio_clock=%lf, n=%d",data_size,is->sndCodecCtx->sample_rate, is->audio_clock, n);
- return data_size;
- }
- if(pkt->data)
- av_freep(pkt);
- memset(pkt_temp, 0, sizeof(*pkt_temp));
- if(signal_quit)
- return -1;
- if((new_packet = packet_queue_get(&is->audioq, pkt, 1)) < 0)
- {
- dbmsg("get packet=%d", new_packet);
- return -1;
- }
- if(pkt->pts != AV_NOPTS_VALUE)
- {
- is->audio_clock = av_q2d(is->sndCodecCtx->time_base)*pkt->pts; //这儿获取的是audio的pts单位是s
- dbmsg("audio_clock=%lf", is->audio_clock);
- }
-
- *pkt_temp = *pkt;
- }
- }
-
- static void audio_callback(void *userdata, Uint8 *stream, int len)
- {
- VideoState* is = (VideoState*)userdata;
- int len1, audio_size;
-
- //static unsigned int audio_buf_size = 0;
- //static unsigned int audio_buf_index = 0;
-
- while(len > 0)
- {
- if(is->audio_buf_index >= is->audio_buf_size)
- {
- audio_size = audio_decode_frame(is); //decode data is store in is->audio_buf2
- //dbmsg("audio_size=%d", audio_size);
- if(audio_size < 0)
- {
- /* If error, output silence */
- is->audio_buf_size = 1024;
- memset(is->audio_buf2, 0, is->audio_buf_size);
- } else {
- is->audio_buf_size = audio_size;
- }
- is->audio_buf_index = 0;
- }
- //dbmsg("len=%d, audio_buf_size=%d, audio_buf_index=%d", len, audio_buf_size, audio_buf_index);
- len1 = is->audio_buf_size - is->audio_buf_index;
- //dbmsg("len1=%d", len1);
- if(len1 > len)
- len1 = len;
- //dbmsg("len1 = %d", len1);
- memcpy(stream, (uint8_t *)is->audio_buf2 + is->audio_buf_index, len1);
- len -= len1;
- stream += len1;
- is->audio_buf_index += len1;
- }
- }
-
- static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) {
- SDL_Event event;
- event.type = FF_REFRESH_EVENT;
- event.user.data1 = opaque;
- SDL_PushEvent(&event);
- return 0; /* 0 means stop timer */
- }
-
- /* schedule a video refresh in 'delay' ms */
- static void schedule_refresh(VideoState *is, int delay) {
- dbmsg("schedule_delay = %d", delay);
- SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
- }
-
- static void video_display(VideoState *is) {
- SDL_LockMutex(is->screen_mutex);
- SDL_DisplayYUVOverlay(is->overlay, &is->rect);
- SDL_UnlockMutex(is->screen_mutex);
- }
-
- static void video_refresh_timer(void *data)
- {
- VideoState *is = (VideoState *)data;
- double actual_delay, delay, sync_threshold, ref_clock, diff;
- if(is->pictq_size == 0) {
- schedule_refresh(is, 1);
- } else {
- delay = is->now_video_pts - is->frame_last_pts; //当前帧的pts与上一帧的pts的差就是两帧的时间间隔,单位是s
- dbmsg("delay=%lf", delay);
- if(delay<=0 || delay>=1.0) //前后两帧的间隔不能小于0,也不能大于1s
- {
- delay = is->frame_last_delay;
- dbmsg("delay=is->frame_last_delay=%lf", delay);
- }
- is->frame_last_delay = delay; //保存上一次的delay结果
- is->frame_last_pts = is->now_video_pts; //保存上一次的pts结果
-
- ref_clock = get_audio_clock(is); //获取当前audio的pts
- dbmsg("now_video_pts=%lf, ref_clock=%lf",is->now_video_pts, ref_clock);
- diff = is->now_video_pts - ref_clock; //视频相对于音频提前或落后了多长时间
-
- //每次同步的时间间隔不能小于这个阀值
- sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD;
- if( fabs(diff) < AV_NOSYNC_THRESHOLD)
- {
- if(diff <= -sync_threshold) //视频比音频慢了,就让视频快点跑delay=0
- delay = 0;
- else if( diff >= sync_threshold) //视频比音频快了,就让视频慢点跑delay=2*delay
- delay = 2 * delay;
- }
- is->frame_timer += delay;
- actual_delay = is->frame_timer - (av_gettime() / 1000000.0); //单位是s
- if(actual_delay < 0.010) {
- actual_delay = 0.010;
- }
- dbmsg("actual_delay=%lf", actual_delay);
- schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); //刷新的单位是ms,这儿加0.5的作用是四舍五入吗?
- //schedule_refresh(is, 40);
- video_display(is);
-
- SDL_LockMutex(is->pictq_mutex);
- is->pictq_size--;
- SDL_CondSignal(is->pictq_cond);
- SDL_UnlockMutex(is->pictq_mutex);
- }
- }
-
- static int queue_picture(VideoState *is, AVFrame *pFrame)
- {
- /* wait until we have space for a new pic */
- SDL_LockMutex(is->pictq_mutex);
- while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && (0==signal_quit)) {
- SDL_CondWait(is->pictq_cond, is->pictq_mutex);
- }
- SDL_UnlockMutex(is->pictq_mutex);
-
- if(signal_quit)
- return -1;
-
- /* We have a place to put our picture on the queue */
- SDL_LockYUVOverlay(is->overlay);
- is->pFrameYUV->data[0] = is->overlay->pixels[0];
- is->pFrameYUV->data[1] = is->overlay->pixels[2];
- is->pFrameYUV->data[2] = is->overlay->pixels[1];
- is->pFrameYUV->linesize[0] = is->overlay->pitches[0];
- is->pFrameYUV->linesize[1] = is->overlay->pitches[2];
- is->pFrameYUV->linesize[2] = is->overlay->pitches[1];
- sws_scale(is->img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0,
- is->vdoCodecCtx->height, is->pFrameYUV->data, is->pFrameYUV->linesize);
- SDL_UnlockYUVOverlay(is->overlay);
- SDL_LockMutex(is->pictq_mutex);
- is->pictq_size++;
- SDL_UnlockMutex(is->pictq_mutex);
- return 0;
- }
-
- double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) {
- double frame_delay;
- //函数的作用是为了修正video_clock,对需要重复的帧修正video_clock
- if(pts != 0) {
- /* if we have pts, set video clock to it */
- is->video_clock = pts;
- } else {
- /* if we aren't given a pts, set it to the clock */
- pts = is->video_clock; //但是video_clock只在这儿用了一次,当pts==0时把pts设为vidoe_clock
- } //pts只有在第1帧时pts才会为0,那时video_clock也是0,作用就是把0赋给pts,也就是没有用处
- /* update the video clock */ //所以感觉这个函数有没有都无所谓,现在是这么理解的,不知对不对?
- frame_delay = av_q2d(is->vdoStream->time_base);
- /* if we are repeating a frame, adjust clock accordingly */
- frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
- is->video_clock += frame_delay;
- return pts;
- }
- static int video_thread(void* data)
- {
- VideoState *is = (VideoState *)data;
- AVPacket pkt1, *packet = &pkt1;
- AVFrame *pFrame;
- double pts;
- int ret;
- pFrame = av_frame_alloc();
-
- for(;;)
- {
- if(signal_quit)
- break;
- if(packet_queue_get(&is->videoq, packet, 1) < 0) {
- // means we quit getting packets
- dbmsg("packet_queue_get < 0");
- break;
- }
- // Decode video frame
- if((ret=avcodec_decode_video2(is->vdoCodecCtx, pFrame, &is->frameFinished, packet)) < 0)
- {
- dbmsg("decocode video error");
- continue;
- }
- if((pts = av_frame_get_best_effort_timestamp(pFrame)) == AV_NOPTS_VALUE) {
- pts = 0;
- }
- //这儿的pts是0 3003 5005 6006 8008这样的
- dbmsg("video: before_pts = %lf", pts);
- pts *= av_q2d(is->vdoStream->time_base); //time_base[1,24002],即pts=pts*(1/24002),此时pts的单位是s
- dbmsg("time_base[%d,%d]", is->vdoStream->time_base.den,is->vdoStream->time_base.num);
-
- dbmsg("video: after_pts = %lf", pts);
- if(is->frameFinished) {
- is->now_video_pts = synchronize_video(is, pFrame, pts); //保存当前帧的pts到is->now_vieo_pts
- //dbmsg("is->now_video_pts = %lf", is->now_video_pts);
- if(queue_picture(is, pFrame) < 0) {
- break;
- }
- }
- av_freep(packet);
- }
- av_frame_free(&pFrame);
- return 0;
- }
-
- static int decode_thread(void* data)
- {
- VideoState* is = (VideoState*)data;
- is->packet = (AVPacket*)av_malloc(sizeof(AVPacket));
- while( (av_read_frame(is->pFormatCtx, is->packet)>=0) && (signal_quit==0))
- {
- if(is->packet->stream_index == is->videoindex)
- {
- packet_queue_put(&is->videoq, is->packet);
- }
-
- if(is->packet->stream_index == is->sndindex)
- {
- packet_queue_put(&is->audioq, is->packet);
- }
- }
- return 0;
- }
-
- static int init_ffmpeg_and_SDL(VideoState* is, const char* video_file)
- {
- int i=0;
- SDL_AudioSpec wanted_spec, real_spec;
- is->videoindex = -1;
- is->sndindex = -1;
- if(NULL == video_file)
- {
- dbmsg("input file is NULL");
- return -1;
- }
- SDL_Init(SDL_INIT_EVERYTHING);
- avcodec_register_all();
- //avfilter_register_all();
- av_register_all();
-
- is->pFormatCtx = avformat_alloc_context();
-
- if(avformat_open_input(&is->pFormatCtx, video_file, NULL, NULL)!=0)
- return -1;
-
- if(avformat_find_stream_info(is->pFormatCtx, NULL)<0)
- return -1;
- av_dump_format(is->pFormatCtx,0, 0, 0);
-
- for(i=0; i<is->pFormatCtx->nb_streams; i++)
- {
- if(is->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
- {
- is->videoindex= i;
- is->vdoStream =is->pFormatCtx->streams[i]; //保存video的stream以后要用到time_base
- }
- if(is->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
- {
- is->sndindex= i;
- is->sndStream =is->pFormatCtx->streams[i];
- }
- }
- if(is->videoindex== -1)
- dbmsg("no video stream found!");
- if(is->sndindex== -1)
- dbmsg("no sound stream found!");
- dbmsg("videoindex=%d, sndindex=%d", is->videoindex, is->sndindex);
-
- if(is->videoindex != -1)
- {
- is->vdoCodecCtx = is->pFormatCtx->streams[is->videoindex]->codec;
- is->vdoCodec = avcodec_find_decoder(is->vdoCodecCtx->codec_id);
- if(is->vdoCodec == NULL)
- {
- dbmsg("Codec not found");
- return -1;
- }
- if(avcodec_open2(is->vdoCodecCtx, is->vdoCodec, NULL) < 0)
- return -1;
- is->pFrameYUV = av_frame_alloc();
- is->img_convert_ctx = sws_getContext(is->vdoCodecCtx->width, is->vdoCodecCtx->height, is->vdoCodecCtx->pix_fmt,
- is->vdoCodecCtx->width, is->vdoCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
- if(is->img_convert_ctx == NULL)
- {
- dbmsg("img_convert error");
- return -1;
- }
- is->psscreen = SDL_SetVideoMode(is->vdoCodecCtx->width, is->vdoCodecCtx->height, 0, SDL_SWSURFACE);
- SDL_WM_SetCaption( "FFMPEG Window", NULL);
- is->overlay = SDL_CreateYUVOverlay(is->vdoCodecCtx->width, is->vdoCodecCtx->height, SDL_YV12_OVERLAY, is->psscreen);
- is->rect.x = 0;
- is->rect.y = 0;
- is->rect.w = is->vdoCodecCtx->width;
- is->rect.h = is->vdoCodecCtx->height;
- packet_queue_init(&is->videoq);
- is->video_tid = SDL_CreateThread(video_thread, is);
- is->frame_timer = (double)av_gettime() / 1000000.0; //获取当前时间单位是s,因为av_gettime的单位是us
- is->frame_last_delay = 40e-3;
- }
- if(is->sndindex != -1)
- {
- is->sndCodecCtx = is->pFormatCtx->streams[is->sndindex]->codec;
- is->sndCodec = avcodec_find_decoder(is->sndCodecCtx->codec_id);
- if(is->sndCodec == NULL)
- {
- dbmsg("Codec not found");
- return -1;
- }
- if(avcodec_open2(is->sndCodecCtx, is->sndCodec, NULL) < 0)
- return -1;
-
- wanted_spec.freq = is->sndCodecCtx->sample_rate;
- wanted_spec.format = AUDIO_S16SYS;
- wanted_spec.channels = is->sndCodecCtx->channels;
- wanted_spec.silence = 0;
- wanted_spec.samples = 2048;
- wanted_spec.callback = audio_callback;
- wanted_spec.userdata = is;
- is->audio_buf_size = 0;
- is->audio_buf_index = 0;
- if(SDL_OpenAudio(&wanted_spec, &real_spec) < 0)
- {
- dbmsg("SDL_OpenAudio:%s", SDL_GetError());
- return -1;
- }
- //store infomation for audio swr_convert
- is->wanted_fmt = AV_SAMPLE_FMT_S16; //AUDIO_S16SYS;
- is->wanted_channel_layout = av_get_default_channel_layout(is->sndCodecCtx->channels);
- is->wanted_freq = real_spec.freq;
- is->wanted_samples = real_spec.samples;
- is->wanted_channels = real_spec.channels;
- //dbmsg("freq=%d, channels=%d, samples=%d", is->wanted_freq, is->wanted_channels, is->wanted_samples);
- packet_queue_init(&is->audioq);
- SDL_PauseAudio(0);
- }
- return 0;
- }
-
-
- int main(int argc, char **argv)
- {
- int ret;
- SDL_Thread* sdl_thread;
- SDL_Thread* decode_thread_id;
- VideoState* is = (VideoState*) av_mallocz(sizeof(VideoState));
- if(argc < 2)
- dbmsg("usage: ./ffplay \n");
-
- if( (ret=init_ffmpeg_and_SDL(is, argv[1])) != 0)
- {
- dbmsg("init_ffmpeg_and SDL error");
- return -1;
- }
-
- is->pictq_mutex = SDL_CreateMutex();
- is->pictq_cond = SDL_CreateCond();
-
- //sdl get signal quit
- affmutex = SDL_CreateMutex();
- decode_thread_id = SDL_CreateThread(decode_thread, is);
- sdl_thread = SDL_CreateThread(sdl_event_thread, NULL);
-
- schedule_refresh(is, 40);
-
- SDL_WaitThread(sdl_thread, &ret);
- SDL_WaitThread(decode_thread_id, &ret);
- SDL_WaitThread(is->video_tid,&ret);
- SDL_DestroyMutex(affmutex);
- av_freep(is->packet);
- av_free(is->pFrameYUV);
- avcodec_close(is->vdoCodecCtx);
- avcodec_close(is->sndCodecCtx);
- avformat_close_input(&is->pFormatCtx);
- return 0;
- }
- EXE=sync
- CC=gcc
- FFMPEG=/work/ffmpeg/ffmpeg-3.0.1/install/
- CFLAGS=-g -O0 -I$(FFMPEG)/include
- LDFLAGS = -L$(FFMPEG)/lib/ -lz -lswscale -lswresample -lavformat -lavdevice -lavcodec -lavutil -lavfilter -lm -lSDL -pthread -lz -lbz2
- SRC=$(wildcard *.c)
- OBJ=$(patsubst %.c,%.o,$(SRC))
- DEP=$(patsubst %.c,.%.d,$(SRC))
- $(EXE):$(OBJ)
- $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
-
- $(DEP):.%.d:%.c
- @set -e; rm -f $@; \
- $(CC) -MM $< > $@.$$$$; \
- sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; \
- rm -f $@.$$$$
-
- -include $(DEP)
- clean:
- @rm $(EXE) $(OBJ) $(DEP) -f
- run:
- export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(FFMPEG)/lib/ \
- && ./$(EXE) ../../san.mp4
- #export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/work/ffmpeg/out/lib/
3.代码
5sync.rar (下载后改名为5sync.tar.gz)
3.1 几个编译问题
a. -pthread,不加这个会出现以下报错
- /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'
- //lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from command line
- collect2: error: ld returned 1 exit status
- /usr/bin/ld: /work/ffmpeg/ffmpeg-3.0.1/install//lib//libavformat.a(http.o): undefined reference to symbol 'inflateInit2_'
- /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libz.so: error adding symbols: DSO missing from command line
- /work/ffmpeg/ffmpeg-3.0.1/install//lib//libavformat.a(matroskadec.o): In function `matroska_decode_buffer':
- /work/ffmpeg/ffmpeg-3.0.1/libavformat/matroskadec.c:1327: undefined reference to `BZ2_bzDecompressInit'
- /work/ffmpeg/ffmpeg-3.0.1/libavformat/matroskadec.c:1342: undefined reference to `BZ2_bzDecompress'
- /work/ffmpeg/ffmpeg-3.0.1/libavformat/matroskadec.c:1335: undefined reference to `BZ2_bzDecompressEnd'
- /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/
cong@msi:/work/ffmpeg/test/5sync$ cat Makefile
这样5sync的代码在任何地方都可以编译了
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
- EXE=sync
- FFMPEG=/work/ffmpeg/ffmpeg-3.0.1/install
- CC=gcc
- SRC:=$(wildcard *.c) $(wildcard *.cpp)
- OBJ:=$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SRC)))
- FFMPEG_LIBS= libavdevice \
- libavformat \
- libavfilter \
- libavcodec \
- libswresample \
- libswscale \
- libavutil \
-
- CFLAGS += -Wall -g
- CPPFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(shell pkg-config --cflags sdl)
- LDFLAGS:= $(shell pkg-config --libs $(FFMPEG_LIBS)) $(shell pkg-config --libs sdl)
- DEP:=$(patsubst %.c,.%.d,$(patsubst %.cpp,.%.d,$(SRC)))
-
- $(EXE):$(OBJ)
- $(CC) $^ -o $@ $(LDFLAGS)
-
- %.o: %.c
- $(CC) $(CPPFLAGS) -c $< -o $@
-
- %.o: %.cpp
- $(CC) $(CPPFLAGS) -c $< -o $@
-
- -include $(DEP)
-
- define gen_dep
- @set -e; rm -f $@; \
- $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
- sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
- rm -f $@.$$$$
- endef
-
- .%.d: %.c
- $(gen_dep)
-
- .%.d: %.cpp
- $(gen_dep)
-
- .PHONY: clean
- clean:
- rm -f all $(OBJ) $(DEP)
-
- echo:
- @echo SRC=$(SRC)
- @echo OBJ=$(OBJ)
- @echo DEP=$(DEP)
- @echo CPPFLAGS=$(CPPFLAGS)
- run:
- export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(FFMPEG)/lib/ \
- && ./$(EXE) ../../san.mp4
- #export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/work/ffmpeg/out/lib/