音視頻同步代碼

#include<stdio.h>

#include<assert.h>

#include<math.h>

#include<SDL.h>

#include<libavcodec/avcodec.h>

#include?<libavformat/avformat.h>

?#include?<libswscale/swscale.h>

#include<libswresample/swresample.h>

// compatibility with newer API

#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)

#define av_frame_alloc avcodec_alloc_frame

#define av_frame_free avcodec_free_frame

#endif

#define SDL_AUDIO_BUFFER_SIZE1024

#define MAX_AUDIO_FRAME_SIZE 192000 //channels(2) * data_size(2) * sample_rate(48000)

#define MAX_AUDIOQ_SIZE (5*16*1024)

#define MAX_VIDEOQ_SIZE (5*256*1024)

#define AV_SYNC_THRESHOLD0.01

#define AV_NOSYNC_THRESHOLD10.0

#define SAMPLE_CORRECTION_PERCENT_MAX10

#define AUDIO_DIFF_AVG_NB20

#define FF_REFRESH_EVENT (SDL_USEREVENT)

#define FF_QUIT_EVENT (SDL_USEREVENT +1)

#define VIDEO_PICTURE_QUEUE_SIZE1

#define DEFAULT_AV_SYNC_TYPE AV_SYNC_AUDIO_MASTER//AV_SYNC_VIDEO_MASTER

typedef struct PacketQueue {

? AVPacketList *first_pkt, *last_pkt;

? intnb_packets;

? intsize;

? SDL_mutex *mutex;

? SDL_cond *cond;

} PacketQueue;

typedef struct VideoPicture {

? AVPicture *bmp;

? int width, height; /* source height & width */

? intallocated;

? doublepts;

} VideoPicture;

typedef struct VideoState {

? //multi-media file

? ? ? ? //多媒體文件的名字

? char? ? ? ? ? ? filename[1024];

? ? ? ? //多媒體文件上下文

? AVFormatContext *pFormatCtx;

? ? //音頻 視頻流

? int? ? ? ? ? ? videoStream, audioStream;

? //sync

? int? ? ? ? ? ? av_sync_type;

? double? ? ? ? ? external_clock;/* external clock base */

? int64_t? ? ? ? external_clock_time;

? double? ? ? ? ? audio_diff_cum;/* used for AV difference average computation */

? double? ? ? ? ? audio_diff_avg_coef;

? double? ? ? ? ? audio_diff_threshold;

? int? ? ? ? ? ? audio_diff_avg_count;

//記錄當前音頻播放的時間

? double? ? ? ? ? audio_clock;

? ? //下次要回掉的時間值是多少

? double? ? ? ? ? frame_timer;

? ? //上一次播放的視頻幀的pds

? double? ? ? ? ? frame_last_pts;

? ? //上一次播放的視頻幀 增加的delay時間

? double? ? ? ? ? frame_last_delay;

//記錄視頻下一幀將要播放的pts時間

? double? ? ? ? ? video_clock;///

? double? ? ? ? ? video_current_pts;///

? int64_t? ? ? ? video_current_pts_time;? ///

? //audio

?? ? //音頻流

? AVStream? ? ? ? *audio_st;

?? ? //音頻上下文

? AVCodecContext? *audio_ctx;

? ? ? ? //音頻隊列

? PacketQueue? ? audioq;

? ? ? ? //解碼后的音頻緩沖區

? uint8_t? ? ? ? audio_buf[(MAX_AUDIO_FRAME_SIZE *3) /2];

?? ? ? //緩沖區大小

? unsignedint? ? audio_buf_size;

? ? ? //使用了多少字節

? unsignedint? ? audio_buf_index;

?? ? ? //解碼后的音頻幀

? AVFrame? ? ? ? audio_frame;

? ? ? //解碼之前的音頻包

? AVPacket? ? ? ? audio_pkt;

?? ? ? //解碼之前數據的指針

? uint8_t? ? ? ? *audio_pkt_data;

? ? ? ? //解碼之前數據的大小

? int? ? ? ? ? ? audio_pkt_size;


? int? ? ? ? ? ? audio_hw_buf_size;

? //video

?? ? //視頻流

? AVStream? ? ? ? *video_st;

?? ? //視頻上下文

? AVCodecContext? *video_ctx;

? ? ? //視頻流隊列

? PacketQueue? ? videoq;

? structSwsContext*video_sws_ctx;

? ? //音頻重采樣上下文

? ? //打開音頻設備的時候給音頻設備設置了固定參數。采樣率多少 通道數 采樣大小

? ? //多媒體文件各種各樣的大小 放到音頻設備統一的播放出來 我們需要進行重采樣

? ? //將我們所有的多媒體格式 重采樣到 打開音頻設備的時候給音頻設備設置了固定參數

? structSwrContext*audio_swr_ctx;

//解碼后的視頻

? VideoPicture? ? pictq[VIDEO_PICTURE_QUEUE_SIZE];

? ? ? //pictq_size 解碼后的視頻大小 pictq_rindex 取視頻幀的位置 pictq_windex存放視頻幀所在位置

? int? ? ? ? ? ? pictq_size, pictq_rindex, pictq_windex;

?? ? ? //視頻幀隊列鎖

? SDL_mutex? ? ? *pictq_mutex;

? SDL_cond? ? ? ? *pictq_cond;

? ? //解復用線程

? SDL_Thread? ? ? *parse_tid;

?? ? ? //視頻解碼線程

? SDL_Thread? ? ? *video_tid;

? //結束窗口 退出事件

? int? ? ? ? ? ? quit;

} VideoState;

SDL_mutex? ? *text_mutex;

SDL_Window? *win = NULL;

SDL_Renderer *renderer;

SDL_Texture? *texture;

enum {

? AV_SYNC_AUDIO_MASTER,

? AV_SYNC_VIDEO_MASTER,

? AV_SYNC_EXTERNAL_MASTER,

};

staticintscreen_left= SDL_WINDOWPOS_CENTERED;

staticintscreen_top= SDL_WINDOWPOS_CENTERED;

static int screen_width = 0;

static int screen_height = 0;

static int resize = 1;

FILE *yuvfd = NULL;

FILE *audiofd = NULL;

/* Since we only have one decoding thread, the Big Struct

?? can be global in case we need it. */

VideoState *global_video_state;

voidpacket_queue_init(PacketQueue *q) {

? memset(q,0,sizeof(PacketQueue));

? q->mutex = SDL_CreateMutex();

? q->cond = SDL_CreateCond();

}

//入隊函數 第一個是隊列指針 第二個是插入元素的Packet

intpacket_queue_put(PacketQueue *q, AVPacket *pkt) {

? AVPacketList *pkt1;

? if(av_dup_packet(pkt) <0) {

? ? return-1;

? }

? ? ? ? //分配一個元素

? 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;

? ? //入隊成功 發一個信號 等待的線程進入

? ? ? //SDL_CondSignal 解鎖 發信號 在加鎖

? SDL_CondSignal(q->cond);


? SDL_UnlockMutex(q->mutex);

? return 0;

}

//出隊 block代表阻塞還是非阻塞

intpacket_queue_get(PacketQueue *q, AVPacket *pkt,intblock)

{

? AVPacketList *pkt1;

? intret;

//加鎖

? SDL_LockMutex(q->mutex);


? for(;;) {


? ? if(global_video_state->quit) {

? ? ? ret = -1;

? ? ? break;

? ? }

//拿到隊列頭取

? ? 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;//獲得的pkt

? ? ? av_free(pkt1);

? ? ? ret =1;

? ? ? break;

? ? }elseif(!block) {

? ? ? ret =0;

? ? ? break;

? ? }else {// 沒有數據 那么等待

?? ? ? ? ? ? //??解鎖 等待 加鎖

? ? ? SDL_CondWait(q->cond, q->mutex);

? ? }

? }

? SDL_UnlockMutex(q->mutex);

? returnret;

}

doubleget_audio_clock(VideoState *is) {

? doublepts;

? inthw_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->audio_ctx->channels *2;

? if(is->audio_st) {

? ? bytes_per_sec = is->audio_ctx->sample_rate * n;

? }

? if(bytes_per_sec) {

? ? pts -= (double)hw_buf_size / bytes_per_sec;

? }

? returnpts;

}

doubleget_video_clock(VideoState *is) {

? doubledelta;

? delta = (av_gettime() - is->video_current_pts_time) /1000000.0;

? returnis->video_current_pts + delta;

}

doubleget_external_clock(VideoState *is) {

? returnav_gettime() /1000000.0;

}

doubleget_master_clock(VideoState *is) {

? ? //get_video_clock 拿到正在播放音頻的時間

? if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) {

? ? returnget_video_clock(is);

? }elseif(is->av_sync_type == AV_SYNC_AUDIO_MASTER) {

? ? returnget_audio_clock(is);

? }else{

? ? returnget_external_clock(is);

? }

}

/* Add or subtract samples to get a better sync, return new

?? audio buffer size */

intsynchronize_audio(VideoState *is,short*samples,

? ? ? intsamples_size,doublepts) {

? intn;

? doubleref_clock;

? n =2* is->audio_ctx->channels;


? if(is->av_sync_type != AV_SYNC_AUDIO_MASTER) {

? ? doublediff, avg_diff;

? ? intwanted_size, min_size, max_size/*, nb_samples */;


? ? ref_clock = get_master_clock(is);

? ? ? //我們要展示視頻幀還是不要展示

? ? diff = get_audio_clock(is) - ref_clock;

? ? if(diff < AV_NOSYNC_THRESHOLD) {//超過閥值

? ? ? // accumulate the diffs

? ? ? is->audio_diff_cum = diff + is->audio_diff_avg_coef

* is->audio_diff_cum;

? ? ? if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) {

is->audio_diff_avg_count++;

? ? ? }else{

avg_diff = is->audio_diff_cum * (1.0- is->audio_diff_avg_coef);

if(fabs(avg_diff) >= is->audio_diff_threshold) {

? wanted_size = samples_size + ((int)(diff * is->audio_ctx->sample_rate) * n);

? min_size = samples_size * ((100- SAMPLE_CORRECTION_PERCENT_MAX) /100);

? max_size = samples_size * ((100+ SAMPLE_CORRECTION_PERCENT_MAX) /100);

? if(wanted_size < min_size) {

? ? wanted_size = min_size;

? }elseif(wanted_size > max_size) {

? ? wanted_size = max_size;

? }

? if(wanted_size < samples_size) {

? ? /* remove samples */

? ? samples_size = wanted_size;

? }elseif(wanted_size > samples_size) {

? ? uint8_t *samples_end, *q;

? ? intnb;

? ? /* add samples by copying final sample*/

? ? nb = (samples_size - wanted_size);

? ? samples_end = (uint8_t *)samples + samples_size - n;

? ? q = samples_end + n;

? ? while(nb >0) {

? ? ? memcpy(q, samples_end, n);

? ? ? q += n;

? ? ? nb -= n;

? ? }

? ? samples_size = wanted_size;

? }

}

? ? ? }

? ? }else{

? ? ? /* difference is TOO big; reset diff stuff */

? ? ? is->audio_diff_avg_count =0;

? ? ? is->audio_diff_cum =0;

? ? }

? }

? returnsamples_size;

}

intaudio_decode_frame(VideoState *is, uint8_t *audio_buf,intbuf_size,double*pts_ptr) {

? intlen1, data_size =0;

? AVPacket *pkt = &is->audio_pkt;

? doublepts;

? intn;

? for(;;) {

? ? ? //音頻流還有數據

? ? while(is->audio_pkt_size >0) {

? ? ? intgot_frame =0;

? ? ? ? //解碼

? ? ? len1 = avcodec_decode_audio4(is->audio_ctx, &is->audio_frame, &got_frame, pkt);

? ? ? if(len1 <0) {//出錯

/* if error, skip frame */

is->audio_pkt_size =0;

break;

? ? ? }

? ? ? data_size =0;

? ? ? if(got_frame) {

? ? ? ? /*

data_size = av_samples_get_buffer_size(NULL,?

? ? ? is->audio_ctx->channels,

? ? ? is->audio_frame.nb_samples,

? ? ? is->audio_ctx->sample_fmt,

? ? ? 1);

? ? ? ? */

? ? ? ? data_size =2* is->audio_frame.nb_samples *2;

assert(data_size <= buf_size);

//進行重采樣

? ? ? ? swr_convert(is->audio_swr_ctx,

? ? ? ? ? ? ? ? ? ? ? ? &audio_buf,

? ? ? ? ? ? ? ? ? ? ? ? MAX_AUDIO_FRAME_SIZE*3/2,

? ? ? ? ? ? ? ? ? ? ? ? (constuint8_t **)is->audio_frame.data,

? ? ? ? ? ? ? ? ? ? ? ? is->audio_frame.nb_samples);

? ? ? ? fwrite(audio_buf,1, data_size, audiofd);

//memcpy(audio_buf, is->audio_frame.data[0], data_size);

? ? ? }

? ? ? is->audio_pkt_data += len1;

? ? ? is->audio_pkt_size -= len1;

? ? ? if(data_size <=0) {

/* No data yet, get more frames */

continue;

? ? ? }

? ? ? pts = is->audio_clock;

? ? ? *pts_ptr = pts;

? ? ? n =2* is->audio_ctx->channels;

? ? ? is->audio_clock += (double)data_size /

(double)(n * is->audio_ctx->sample_rate);

? ? ? /* We have data, return it and come back for more later */

? ? ? returndata_size;

? ? }

? ? if(pkt->data)

? ? ? av_free_packet(pkt);

? ? if(is->quit) {

? ? ? return-1;

? ? }

? ? /* next packet */

? ? ? //音頻隊列中取出包

? ? if(packet_queue_get(&is->audioq, pkt,1) <0) {

? ? ? return-1;

? ? }

? ? is->audio_pkt_data = pkt->data;

? ? is->audio_pkt_size = pkt->size;

? ? /* if update, update the audio clock w/pts */

? ? if(pkt->pts != AV_NOPTS_VALUE) {

? ? ? is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;

? ? }

? }

}

voidaudio_callback(void*userdata, Uint8 *stream,intlen) {

? VideoState *is = (VideoState *)userdata;

? intlen1, audio_size;

? doublepts;

? SDL_memset(stream,0, len);

? while(len >0) {

? ? ? //現在索取的數據 大于audio_buf_size 說明 緩沖區沒有數據

? ? if(is->audio_buf_index >= is->audio_buf_size) {

? ? ? /* We have already sent all our data; get more */

? ? ? ? //解碼

? ? ? audio_size = audio_decode_frame(is, is->audio_buf,sizeof(is->audio_buf), &pts);

? ? ? if(audio_size <0) {

/* If error, output silence */

is->audio_buf_size =1024*2*2;

memset(is->audio_buf,0, is->audio_buf_size);

? ? ? }else{

audio_size = synchronize_audio(is, (int16_t *)is->audio_buf,

? ? ? audio_size, pts);

is->audio_buf_size = audio_size;

? ? ? }

? ? ? is->audio_buf_index =0;

? ? }

?? ? // 解碼后的長度

? ? len1 = is->audio_buf_size - is->audio_buf_index;

? ? if(len1 > len) //解碼后的長度和 聲卡需要的長度比較

? ? ? len1 = len;

? ? ? //扔給聲卡

? ? SDL_MixAudio(stream,(uint8_t *)is->audio_buf + is->audio_buf_index, len1, SDL_MIX_MAXVOLUME);

? ? //memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);

? ? len -= len1;

? ? stream += len1;

? ? is->audio_buf_index += len1;

? }

}

staticUint32sdl_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 */

staticvoidschedule_refresh(VideoState *is,intdelay) {

? SDL_AddTimer(delay, sdl_refresh_timer_cb, is);

}

voidvideo_display(VideoState *is) {

? SDL_Rect rect;

? VideoPicture *vp;

? floataspect_ratio;

? intw, h, x, y;

? inti;

? if(screen_width && resize){

? SDL_SetWindowSize(win, screen_width, screen_height);

? SDL_SetWindowPosition(win, screen_left, screen_top);?

? SDL_ShowWindow(win);

? //IYUV: Y + U + V? (3 planes)

? //YV12: Y + V + U? (3 planes)

? Uint32 pixformat= SDL_PIXELFORMAT_IYUV;

? //create texture for render

? texture = SDL_CreateTexture(renderer,

? pixformat,

? SDL_TEXTUREACCESS_STREAMING,

? screen_width,

? screen_height);

? resize =0;

? }

? vp = &is->pictq[is->pictq_rindex];

? if(vp->bmp) {

//解碼后的視頻幀放入紋理中去

? ? SDL_UpdateYUVTexture( texture,NULL,?

? ? ? ? ? ? ? ? ? ? ? ? ? vp->bmp->data[0], vp->bmp->linesize[0],

? ? ? ? ? ? ? ? ? ? ? ? ? vp->bmp->data[1], vp->bmp->linesize[1],

? ? ? ? ? ? ? ? ? ? ? ? ? vp->bmp->data[2], vp->bmp->linesize[2]);

? ? rect.x =0;

? ? rect.y =0;

? ? rect.w = is->video_ctx->width;

? ? rect.h = is->video_ctx->height;

? ? SDL_LockMutex(text_mutex);

? ? ? //刷一下屏

? ? SDL_RenderClear( renderer );

? ? ? //

? ? SDL_RenderCopy( renderer, texture,NULL, &rect);

? ? SDL_RenderPresent( renderer );

? ? SDL_UnlockMutex(text_mutex);

? }

}

//隔多長時間刷新

void video_refresh_timer(void *userdata) {

? VideoState *is = (VideoState *)userdata;

? VideoPicture *vp;

? doubleactual_delay, delay, sync_threshold, ref_clock, diff;


? if(is->video_st) {

? ? ? //解碼后是否有數據

? ? if(is->pictq_size ==0) {

? ? ? ? //沒有數據 就1ms 不停的查詢是否有數據了

? ? ? schedule_refresh(is,1);

? ? ? //fprintf(stderr, "no picture in the queue!!!\n");

? ? }else{

? ? ? //fprintf(stderr, "get picture from queue!!!\n");

? ? ? ? //解碼后隊列拿到視頻幀

? ? ? vp = &is->pictq[is->pictq_rindex];


? ? ? is->video_current_pts = vp->pts;

? ? ? is->video_current_pts_time = av_gettime();


? ? ? delay = vp->pts - is->frame_last_pts;/* the pts from last time */

? ? ? ? //大于1s

? ? ? if(delay <=0|| delay >=1.0) {

/* if incorrect delay, use previous one */

? ? ? ? ? //上一次delay時間

delay = is->frame_last_delay;

? ? ? }

? ? ? /* save for next time */

? ? ? is->frame_last_delay = delay;

? ? ? is->frame_last_pts = vp->pts;

? ? ? /* update delay to sync to audio if not master source */

? ? ? if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) {

ref_clock = get_master_clock(is);

diff = vp->pts - ref_clock;

/* Skip or repeat the frame. Take delay into account

? FFPlay still doesn't "know if this is the best guess." */

? ? ? ? ? //同步閥值

sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD;

if(fabs(diff) < AV_NOSYNC_THRESHOLD) {

? if(diff <= -sync_threshold) {//閥值小于sync_threshold 視頻時間是在音頻時間之前

? ? delay =0;//立馬展示

? }elseif(diff >= sync_threshold) {//視頻展示的時間還沒到 等待時間加長

? ? delay =2* delay;

? }

}

? ? ? }//音頻的每一個包是10ms

? ? ? ? // 加上系統時間

? ? ? is->frame_timer += delay;

? ? ? /* computer the REAL delay */

? ? ? ? //真正等待時間

? ? ? actual_delay = is->frame_timer - (av_gettime() /1000000.0);

? ? ? if(actual_delay <0.010) {//小于10ms就等待10ms

/* Really it should skip the picture instead */

actual_delay =0.010;

? ? ? }

? ? ? ? //下一次渲染的時間

? ? ? schedule_refresh(is, (int)(actual_delay *1000+0.5));


? ? ? /* show the picture! */

? ? ? video_display(is);


? ? ? /* update queue for next picture! */

? ? ? if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) {

is->pictq_rindex =0;

? ? ? }

? ? ? SDL_LockMutex(is->pictq_mutex);

? ? ? is->pictq_size--;

? ? ? SDL_CondSignal(is->pictq_cond);

? ? ? SDL_UnlockMutex(is->pictq_mutex);

? ? }

? }else{

? ? schedule_refresh(is,100);

? }

}


voidalloc_picture(void*userdata) {

? intret;

? VideoState *is = (VideoState *)userdata;

? VideoPicture *vp;

? vp = &is->pictq[is->pictq_windex];

? if(vp->bmp) {

? ? // we already have one make another, bigger/smaller

? ? avpicture_free(vp->bmp);

? ? free(vp->bmp);

? ? vp->bmp =NULL;

? }

? // Allocate a place to put our YUV image on that screen

? SDL_LockMutex(text_mutex);

? vp->bmp = (AVPicture*)malloc(sizeof(AVPicture));

? ret = avpicture_alloc(vp->bmp, AV_PIX_FMT_YUV420P, is->video_ctx->width, is->video_ctx->height);

? if(ret <0) {

? ? ? fprintf(stderr,"Could not allocate temporary picture: %s\n", av_err2str(ret));

? }

? SDL_UnlockMutex(text_mutex);

? vp->width = is->video_ctx->width;

? vp->height = is->video_ctx->height;

? vp->allocated =1;

}

intqueue_picture(VideoState *is, AVFrame *pFrame,doublepts) {

? VideoPicture *vp;

? /* wait until we have space for a new pic */

? SDL_LockMutex(is->pictq_mutex);

? while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&

!is->quit) {

? ? SDL_CondWait(is->pictq_cond, is->pictq_mutex);

? }

? SDL_UnlockMutex(is->pictq_mutex);

? if(is->quit)

? ? return-1;

? // windex is set to 0 initially

? vp = &is->pictq[is->pictq_windex];

? /* allocate or resize the buffer! */

? if(!vp->bmp ||

?? ? vp->width != is->video_ctx->width ||

?? ? vp->height != is->video_ctx->height) {

? ? vp->allocated =0;

? ? alloc_picture(is);

? ? if(is->quit) {

? ? ? return-1;

? ? }

? }


? /* We have a place to put our picture on the queue */

? if(vp->bmp) {

//

? ? vp->pts = pts;


? ? // Convert the image into YUV format that SDL uses

? ? sws_scale(is->video_sws_ctx, (uint8_tconst*const*)pFrame->data,

? ? ? pFrame->linesize,0, is->video_ctx->height,

? ? ? vp->bmp->data, vp->bmp->linesize);


? ? /* now we inform our display thread that we have a pic ready */

? ? if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) {

? ? ? is->pictq_windex =0;

? ? }

? ? SDL_LockMutex(is->pictq_mutex);

? ? is->pictq_size++;

? ? SDL_UnlockMutex(is->pictq_mutex);

? }

? return 0;

}

doublesynchronize_video(VideoState *is, AVFrame *src_frame,doublepts) {

? doubleframe_delay;

? if(pts !=0) {

? ? /* if we have pts, set video clock to it */

? ? ? //更新video_clock

? ? is->video_clock = pts;

? }else{

? ? /* if we aren't given a pts, set it to the clock */

? ? ? //使用上一次的

? ? pts = is->video_clock;

? }

? /* update the video clock */

? ? //

? frame_delay = av_q2d(is->video_ctx->time_base);

? /* if we are repeating a frame, adjust clock accordingly */

? ? //解碼后的視頻幀 這個幀要重復的播放

? frame_delay += src_frame->repeat_pict * (frame_delay *0.5);

? ? //下一幀的pts

? is->video_clock += frame_delay;

? returnpts;

}

//視頻解碼線程

int decode_video_thread(void *arg) {

? VideoState *is = (VideoState *)arg;

? AVPacket pkt1, *packet = &pkt1;

? intframeFinished;

? AVFrame *pFrame;

? doublepts;

? pFrame = av_frame_alloc();

? for(;;) {

? ? ? //視頻隊列中取出視頻包

? ? if(packet_queue_get(&is->videoq, packet,1) <0) {

? ? ? // means we quit getting packets

? ? ? break;

? ? }

? ? pts =0;

? ? // Decode video frame

? ? ? //解碼

? ? avcodec_decode_video2(is->video_ctx, pFrame, &frameFinished, packet);

? ? if((pts = av_frame_get_best_effort_timestamp(pFrame)) != AV_NOPTS_VALUE) {

? ? }else{

? ? ? pts =0;

? ? }

? ? ? //時間機換算成s

? ? pts *= av_q2d(is->video_st->time_base);

? ? // Did we get a video frame?

? ? ? //解碼成功后

? ? if(frameFinished) {

? ? ? pts = synchronize_video(is, pFrame, pts);

? ? ? ? //解碼后的視頻幀隊列中

? ? ? if(queue_picture(is, pFrame, pts) <0) {

break;

? ? ? }

? ? }

? ? av_free_packet(packet);

? }

? av_frame_free(&pFrame);

? return 0;

}

//stream_index 哪路流

intstream_component_open(VideoState *is,intstream_index) {

? AVFormatContext *pFormatCtx = is->pFormatCtx;

? AVCodecContext *codecCtx =NULL;

? AVCodec *codec =NULL;

? SDL_AudioSpec wanted_spec, spec;

? if(stream_index <0|| stream_index >= pFormatCtx->nb_streams) {

? ? return-1;

? }

? codecCtx = avcodec_alloc_context3(NULL);

//得到編碼上下文

? intret = avcodec_parameters_to_context(codecCtx, pFormatCtx->streams[stream_index]->codecpar);

? if(ret <0)

? ? return-1;

// 解碼器

? codec = avcodec_find_decoder(codecCtx->codec_id);

? if(!codec) {

? ? fprintf(stderr,"Unsupported codec!\n");

? ? return-1;

? }

?//音頻

? if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) {

//打開音頻設備

? ? ? // Set audio settings from codec info

? ? ? //音頻采樣率

? ? ? wanted_spec.freq = codecCtx->sample_rate;

? ? ? //采樣格式

? ? ? wanted_spec.format = AUDIO_S16SYS;

? ? ? wanted_spec.channels =2;//codecCtx->channels;

? ? ? //禁默音

? ? ? wanted_spec.silence =0;

? ? ? //采樣的個數 每秒中可以多少個數

? ? ? wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;

? ? ? wanted_spec.callback = audio_callback;

? ? ? wanted_spec.userdata = is;

? ? ? fprintf(stderr,"wanted spec: channels:%d, sample_fmt:%d, sample_rate:%d \n",

? ? ? ? ? ? 2, AUDIO_S16SYS, codecCtx->sample_rate);

?//打開解碼器

? ? ? if(SDL_OpenAudio(&wanted_spec, &spec) <0) {

? ? ? ? ? fprintf(stderr,"SDL_OpenAudio: %s\n", SDL_GetError());

? ? ? ? ? return-1;

? ? ? }

? ? ? is->audio_hw_buf_size = spec.size;

? }

? if(avcodec_open2(codecCtx, codec,NULL) <0) {

? ? fprintf(stderr,"Unsupported codec!\n");

? ? return-1;

? }

? switch(codecCtx->codec_type) {

? caseAVMEDIA_TYPE_AUDIO:

? ? ? ? ? //音頻流index

? ? is->audioStream = stream_index;

? ? ? ? ? //具體音頻流

? ? is->audio_st = pFormatCtx->streams[stream_index];

? ? ? ? ? //編解碼上下文

? ? is->audio_ctx = codecCtx;

? ? ? ? ? //音頻buf_size

? ? is->audio_buf_size =0;

? ? ? ? ? // 我們使用了多少

? ? is->audio_buf_index =0;

? ? ? ? ? //音頻包

? ? memset(&is->audio_pkt,0,sizeof(is->audio_pkt));

? ? ? ? ? //音頻隊列初始化

? ? packet_queue_init(&is->audioq);

? ? //Out Audio Param

? ? uint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;

? ? //AAC:1024? MP3:1152

? ? intout_nb_samples= is->audio_ctx->frame_size;

? ? //AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;

? ? intout_sample_rate=is->audio_ctx->sample_rate;

? ? intout_channels=av_get_channel_layout_nb_channels(out_channel_layout);

? ? //Out Buffer Size

? ? /*

? ? int out_buffer_size=av_samples_get_buffer_size(NULL,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? out_channels,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? out_nb_samples,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AV_SAMPLE_FMT_S16,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 1);

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? */

? ? //uint8_t *out_buffer=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);

? ? int64_t in_channel_layout=av_get_default_channel_layout(is->audio_ctx->channels);

//音頻重采樣上下文

? ? structSwrContext *audio_convert_ctx;

? ? audio_convert_ctx = swr_alloc();

? ? swr_alloc_set_opts(audio_convert_ctx,

?? ? ? ? ? ? ? ? ? ? ? out_channel_layout,

?? ? ? ? ? ? ? ? ? ? ? AV_SAMPLE_FMT_S16,

?? ? ? ? ? ? ? ? ? ? ? out_sample_rate,

?? ? ? ? ? ? ? ? ? ? ? in_channel_layout,

?? ? ? ? ? ? ? ? ? ? ? is->audio_ctx->sample_fmt,

?? ? ? ? ? ? ? ? ? ? ? is->audio_ctx->sample_rate,

?? ? ? ? ? ? ? ? ? ? ? 0,

?? ? ? ? ? ? ? ? ? ? ? NULL);

? ? fprintf(stderr,"swr opts: out_channel_layout:%lld, out_sample_fmt:%d, out_sample_rate:%d, in_channel_layout:%lld, in_sample_fmt:%d, in_sample_rate:%d",

? ? ? ? ? ? out_channel_layout, AV_SAMPLE_FMT_S16, out_sample_rate, in_channel_layout, is->audio_ctx->sample_fmt, is->audio_ctx->sample_rate);


? ? swr_init(audio_convert_ctx);

? ? is->audio_swr_ctx = audio_convert_ctx;

//播放音頻

? ? SDL_PauseAudio(0);

? ? break;

? caseAVMEDIA_TYPE_VIDEO:

? ? is->videoStream = stream_index;

? ? ? ? ? //具體流

? ? is->video_st = pFormatCtx->streams[stream_index];

? ? ? ? ? //視頻解碼上下文

? ? is->video_ctx = codecCtx;

//獲取系統時間換算成s av_gettime微s

? ? is->frame_timer = (double)av_gettime() /1000000.0;

? ? is->frame_last_delay =40e-3;

? ? is->video_current_pts_time = av_gettime();

//視頻的隊列

? ? packet_queue_init(&is->videoq);

? ? ? ? ? //視頻裁剪上下文

? ? is->video_sws_ctx = sws_getContext(is->video_ctx->width, is->video_ctx->height,

is->video_ctx->pix_fmt, is->video_ctx->width,

is->video_ctx->height, AV_PIX_FMT_YUV420P,

SWS_BILINEAR,NULL,NULL,NULL

);

? ? ? ? ? //創建線程

? ? is->video_tid = SDL_CreateThread(decode_video_thread,"decode_video_thread", is);

? ? break;

? default:

? ? break;

? }

}

int demux_thread(void *arg) {

? interr_code;

? charerrors[1024] = {0,};

? intw, h;

? VideoState *is = (VideoState *)arg;

? AVFormatContext *pFormatCtx =NULL;

? AVPacket pkt1, *packet = &pkt1;

? intvideo_index = -1;

? intaudio_index = -1;

? inti;

? is->videoStream=-1;

? is->audioStream=-1;

? global_video_state = is;

? /* open input file, and allocate format context */

? ? //真正打開多媒體文件

? if((err_code=avformat_open_input(&pFormatCtx, is->filename,NULL,NULL)) <0) {

? ? ? av_strerror(err_code, errors,1024);

? ? ? fprintf(stderr,"Could not open source file %s, %d(%s)\n", is->filename, err_code, errors);

? ? ? return-1;

? }

? is->pFormatCtx = pFormatCtx;


? // Retrieve stream information

? ? //查找流相關的信息

? if(avformat_find_stream_info(pFormatCtx,NULL)<0)

? ? return -1; // Couldn't find stream information


? // Dump information about file onto standard error

? ? //將多媒體文件信息dump出來

? av_dump_format(pFormatCtx,0, is->filename,0);


? // Find the first video stream

? for(i=0; inb_streams; i++) {

? ? if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO &&

?? ? ? video_index <0) {

? ? ? video_index=i;

? ? }

? ? if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&

?? ? ? audio_index <0) {

? ? ? audio_index=i;

? ? }

? }

? if(audio_index >=0) {

? ? ? //設置其音頻相關參數

? ? stream_component_open(is, audio_index);

? }

? if(video_index >=0) {

? ? stream_component_open(is, video_index);

? }? ?

//音頻和視頻是否都是ok

? if(is->videoStream <0|| is->audioStream <0) {

? ? fprintf(stderr,"%s: could not open codecs\n", is->filename);

? ? gotofail;

? }

? screen_width = is->video_ctx->width;

? screen_height = is->video_ctx->height;

? // main decode loop

? for(;;) {

? ? if(is->quit) {

? ? ? break;

? ? }

? ? // seek stuff goes here

? ? if(is->audioq.size > MAX_AUDIOQ_SIZE ||

?? ? ? is->videoq.size > MAX_VIDEOQ_SIZE) {

? ? ? SDL_Delay(10);

? ? ? continue;

? ? }

? ? ? //解復用

? ? if(av_read_frame(is->pFormatCtx, packet) <0) {

? ? ? if(is->pFormatCtx->pb->error ==0) {

SDL_Delay(100); /* no error; wait for user input */

continue;

? ? ? }else{

break;

? ? ? }

? ? }

? ? // Is this a packet from the video stream?

? ? ? //讀取的是視頻

? ? if(packet->stream_index == is->videoStream) {

? ? ? ? // 放入視頻隊列

? ? ? packet_queue_put(&is->videoq, packet);

? ? }elseif(packet->stream_index == is->audioStream) {

? ? ? ? ? ? // 放入音頻隊列去

? ? ? packet_queue_put(&is->audioq, packet);

? ? }else{

? ? ? av_free_packet(packet);

? ? }

? }

? /* all done - wait for it */

? while(!is->quit) {

? ? SDL_Delay(100);

? }

?fail:

? if(1){

? ? SDL_Event event;

? ? event.type = FF_QUIT_EVENT;

? ? event.user.data1 = is;

? ? SDL_PushEvent(&event);

? }

? return 0;

}

intmain(intargc,char*argv[]) {

? SDL_Event? ? ? event;

//播放器 音頻視頻都保存在這個結構體里面

? VideoState? ? ? *is;

?? //核心結構體分配空間

? is = av_mallocz(sizeof(VideoState));

? if(argc <2) {

? ? fprintf(stderr,"Usage: test <file>\n");

? ? exit(1);

? }

? yuvfd = fopen("testout.yuv","wb+");

? audiofd = fopen("testout.pcm","wb+");

? // Register all formats and codecs

? av_register_all();

? if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {

? ? fprintf(stderr,"Could not initialize SDL - %s\n", SDL_GetError());

? ? exit(1);

? }

? //creat window from SDL

? win = SDL_CreateWindow("Media Player",

?? ? ? ? ? ? ? ? ? ? ? ? 100,

?? ? ? ? ? ? ? ? ? ? ? ? 100,

640,480,

?? ? ? ? ? ? ? ? ? ? ? ? //is->video_ctx->width, is->video_ctx->height,

?? ? ? ? ? ? ? ? ? ? ? ? SDL_WINDOW_RESIZABLE);

? if(!win) {

? ? ? fprintf(stderr,"\nSDL: could not set video mode:%s - exiting\n", SDL_GetError());

? ? ? exit(1);

? }

? renderer = SDL_CreateRenderer(win, -1,0);

? text_mutex = SDL_CreateMutex();

? av_strlcpy(is->filename, argv[1],sizeof(is->filename));

? ? //解碼視頻隊列創建一個鎖

? is->pictq_mutex = SDL_CreateMutex();

? ? ? ? //解碼視頻隊列創建一個信號量

? is->pictq_cond = SDL_CreateCond();

? //set timer? 40ms回掉一次一幀 視頻渲染

? schedule_refresh(is,40);

? is->av_sync_type = DEFAULT_AV_SYNC_TYPE;

? ? //創建一個解復用線程

? is->parse_tid = SDL_CreateThread(demux_thread,"demux_thread", is);

? if(!is->parse_tid) {

? ? av_free(is);

? ? return-1;

? }

? for(;;) {

//等待事件

? ? SDL_WaitEvent(&event);

? ? switch(event.type) {

? ? caseFF_QUIT_EVENT:

? ? caseSDL_QUIT:

? ? ? is->quit =1;

? ? ? SDL_Quit();

? ? ? return0;

? ? ? break;

? ? caseFF_REFRESH_EVENT:

? ? ? video_refresh_timer(event.user.data1);

? ? ? break;

? ? default:

? ? ? break;

? ? }

? }

? fclose(yuvfd);

? fclose(audiofd);

? return 0;

}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。