#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;
}