http://dranger.com/ffmpeg/tutorial05.html
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;
-
}
2.2 Makefile
-
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/
Makefile也需要修改一下。
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
b.
-lz,不加这个会出现以下报错
-
/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
c.
-lbz2,
不加这个会出现以下报错
-
/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/
测试一下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/
这样5sync的代码在任何地方都可以编译了
阅读(3721) | 评论(0) | 转发(0) |