SDL播放视频

// PlayVideo.cpp : Defines the entry point for the console application.

//







extern "C"

{

#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 <windows.h>

#include <stdio.h>

#include <math.h>

#include <SDL.h>

#include <SDL_thread.h>

#include <SDL_main.h>





#pragma comment(lib, "libmingwex.lib")

#pragma comment(lib, "libgcc.lib")

#pragma comment(lib, "avformat.lib")

#pragma comment(lib, "avutil.lib")

#pragma comment(lib, "avcodec.lib")

#pragma comment(lib, "swresample.lib")

#pragma comment(lib, "swscale.lib")



#pragma comment(lib, "SDL.lib")

#pragma comment(lib, "SDLmain.lib")



//speedloss is decided by scale

//#define PICTURE_W 640  

//#define PICTURE_H 352  

//#define PICTURE_PW 640  

//#define PICTURE_PH 352  

//change 100 

#define MAX_QUEUE_SIZE     50



#define VIDEO_PICTURE_QUEUE_SIZE 2  









typedef struct VideoPicture_t{  

	SDL_Overlay *bmp;  

	double pts;  

	double duration;  

	int width,height;  

}VideoPicture;  





struct play_info_t{  

	//先注释掉同步

	double audio_clock;//解码时钟  

	double audio_current_pts;//当前音频的pts  

	double vedio_clock;//解码时钟  

	double last_frame_pts;//上一帧的PTS值  

	double last_frame_delay;//上一帧的延时  

	double frame_timer;  



	AVCodecContext *pVideoCodecCtx;  

	AVCodecContext *pAudioCodecCtx;  



	VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];  

	int pictq_size, pictq_rindex, pictq_windex;  

	SDL_mutex *pictq_mutex;  

	SDL_cond *pictq_cond;  



	struct SwrContext *audio_conv;  

	struct SwsContext *video_conv;  

};  



struct play_info_t g_play_info;  









typedef struct PacketQueue {  

	AVPacketList *first_pkt, *last_pkt;  

	int nb_packets;  

	int size;  

	SDL_mutex *mutex;  

	SDL_cond *cond;  

} PacketQueue;  



PacketQueue audio_queue;  

PacketQueue video_queue;  

int quit = 0;  

SDL_Surface *screen;  

SDL_Overlay *bmp;  

SDL_Rect rect;  



int global_video_pkt_pts=0;  

int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) {  

	int ret = avcodec_default_get_buffer(c, pic);  

	uint64_t *pts = (uint64_t *)av_malloc(sizeof(uint64_t));  

	*pts = global_video_pkt_pts;  

	pic->opaque = pts;  

	return ret;  

}  

void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) {  

	if(pic) av_freep(&pic->opaque);  

	avcodec_default_release_buffer(c, pic);  

}  



double get_audio_clock(void)  

{  

	return g_play_info.audio_current_pts;  

} 



double get_video_clock(void)  

{  

	return g_play_info.vedio_clock;  

} 



void picture_queque_init(struct play_info_t *play)  

{  

	int i;  

	play->pictq_cond=SDL_CreateCond();  

	play->pictq_mutex=SDL_CreateMutex();  

	play->pictq_rindex=play->pictq_windex=0;  

	play->pictq_size=0;  

	memset(play->pictq,0,sizeof(play->pictq));  

	for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE;i++){  //注释掉   g_play_info.pVideoCodecCtx->height

		//play->pictq[i].bmp=SDL_CreateYUVOverlay(PICTURE_W,PICTURE_H,SDL_YUY2_OVERLAY,screen);  

		play->pictq[i].bmp=SDL_CreateYUVOverlay(g_play_info.pVideoCodecCtx->width,g_play_info.pVideoCodecCtx->height,SDL_YUY2_OVERLAY,screen);  

	}  

}  



void picture_queque_destroy(struct play_info_t *play)  

{  

	int i;  

	for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE;i++){  

		SDL_FreeYUVOverlay(play->pictq[i].bmp);  

	}  

}  







void packet_queue_init(PacketQueue *q) {  

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

	q->mutex = SDL_CreateMutex();  

	q->cond = SDL_CreateCond();  

}  



int packet_queue_put(PacketQueue *q, AVPacket *pkt) {  

	AVPacketList *pkt1;  

	if(av_dup_packet(pkt) < 0) {  

		return -1;  

	}  

	pkt1 = (AVPacketList *)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(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(;;) {  

		if(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;  

			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;  

}  



int decode_interrupt_cb(void) {  

	return quit;  

}  



//计算正确的pts值  

double sync_video(struct play_info_t *play,AVFrame* frame,double pts)  

{  

	double frame_delay;  

	if(pts!=0)  

		play->vedio_clock=pts;  

	else  

		pts=play->vedio_clock;  

	frame_delay=av_q2d(play->pVideoCodecCtx->time_base);//一帧占用的时间  

	frame_delay+=frame->repeat_pict*(frame_delay*0.5);//计算重复帧  

	play->vedio_clock+=frame_delay;  

	return pts;  

}  



int audio_decode_frame(struct play_info_t* play, uint8_t *audio_buf,int buf_size) {  

	AVCodecContext *aCodecCtx=play->pAudioCodecCtx;  

	AVFrame *pAudioFrame=avcodec_alloc_frame();  

	AVPacket pkt,pkt1;  

	int frame_finished=0;  

	int pkt_pos,pkt_len;  

	int src_len=0,dst_len=0,data_size=0;  

	float pts=0;  

	avcodec_get_frame_defaults(pAudioFrame);  



	uint8_t *out[]={audio_buf};  



	for(;!quit;){  

		if(packet_queue_get(&audio_queue, &pkt, 1) < 0) {  

			av_free(pAudioFrame);  

			return -1;  

		}  

		pkt1=pkt;  

		pkt_pos=0;  

		pkt_len=pkt.size;  



		while(pkt_pos<pkt.size && !quit){  

			if((src_len=avcodec_decode_audio4(aCodecCtx,pAudioFrame,&frame_finished,&pkt1))<0){  

				av_free_packet(&pkt);  

				av_free(pAudioFrame);  

				return -1;  

			}  



		/*	play->audio_clock+=(double)(pAudioFrame->linesize[0])/  

				(aCodecCtx->channels*aCodecCtx->sample_rate*av_get_bytes_per_sample(aCodecCtx->sample_fmt));  */

			if(video_queue.nb_packets>= 100 || audio_queue.nb_packets>= 200){  //改写

				//printf("%d---%d\n",video_queue.nb_packets,audio_queue.nb_packets);  

				Sleep(20); //  change   10000



			} 

			else if(video_queue.nb_packets>= 50 || audio_queue.nb_packets>= 100)

			{

				Sleep(10);



			}



			if(frame_finished){ 

				const uint8_t **in=(const uint8_t **)pAudioFrame->data;

				int len=swr_convert(play->audio_conv,out,buf_size/aCodecCtx->channels/av_get_bytes_per_sample(AV_SAMPLE_FMT_S16),  

					in,pAudioFrame->linesize[0]/aCodecCtx->channels/av_get_bytes_per_sample((AVSampleFormat)pAudioFrame->format));  

				len=len*aCodecCtx->channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);  

				av_free(pAudioFrame);  

				av_free_packet(&pkt);  

				return len;  

			}else{  

				if (!pkt1.data && aCodecCtx->codec->capabilities & CODEC_CAP_DELAY){  

					break;  

				}  

			}  

			pkt_pos+=src_len;//已经解码的长度  

			pkt1.data=pkt.data+pkt_pos;  

			pkt1.size=pkt.size-pkt_pos;  



		





		}  

		av_free_packet(&pkt);  

	}  

	av_free(pAudioFrame);  

	return dst_len;  

}  



//decode the audio data ,and copy the result to stream  

void SDLCALL audio_callback(void *userdata, Uint8 *stream, int len)  

{  

	struct play_info_t *play=(struct play_info_t*)userdata;  

	AVCodecContext *aCodecCtx = play->pAudioCodecCtx;  

	int len1, audio_size;  

	static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];  

	static unsigned int audio_buf_size = 0;  

	static unsigned int audio_buf_index = 0;  

	int bytes_per_sec=2*aCodecCtx->channels*aCodecCtx->sample_rate;  



	while(len > 0 && !quit) {  

		if(audio_buf_index >= audio_buf_size) {  

			audio_size = audio_decode_frame(play, audio_buf,sizeof(audio_buf));  

			if(audio_size < 0) {  

				audio_buf_size = 1024;  

				memset(audio_buf, 0, audio_buf_size);  

			} else {  

				audio_buf_size = audio_size;  

			}  

			audio_buf_index = 0;  

		}  

		len1 = audio_buf_size - audio_buf_index;  



		if(len1 > len)  

			len1 = len;  

		memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);  

		len -= len1;  

		stream += len1;  

		audio_buf_index += len1;  

	}  

	//先注释掉同步,自然播放

	if(audio_buf_size > audio_buf_index){  

		play->audio_current_pts=play->audio_clock-((double)(audio_buf_size-audio_buf_index)/(double)bytes_per_sec);  

		if(play->audio_current_pts<0)  

			play->audio_current_pts=play->audio_clock;  

		printf("audio-pts:%f\n",play->audio_current_pts);  

	}  

}  



int SDLCALL video_decode_callback(void* arg)  

{  

	printf("---in video decode thread ---\n");  

	struct play_info_t* play=(struct play_info_t*)arg;  

	AVCodecContext *pVideoCodecCtx=play->pVideoCodecCtx;  

	double pts;  

	VideoPicture *vp;  

	AVPacket pkt;  

	int frame_finished;  

	AVPicture pict = { { 0 } };  

	AVFrame *pVideoFrame=avcodec_alloc_frame();  

	if(pVideoFrame==NULL){  

		printf("can't alloc video frame!\n");  

		return -1;  

	}  



	while(!quit){  

		if(packet_queue_get(&video_queue, &pkt, 1) < 0) {  

			return -1;  

		}  

		pts = 0;  

		// Save global pts to be stored in pFrame in first call  

		global_video_pkt_pts = pkt.pts;  

		avcodec_decode_video2(pVideoCodecCtx,pVideoFrame,&frame_finished,&pkt);  

		if(pkt.dts == AV_NOPTS_VALUE && pVideoFrame->opaque && *(uint64_t*)pVideoFrame->opaque != AV_NOPTS_VALUE){  

			pts = *(uint64_t *)pVideoFrame->opaque;  

		} else if(pkt.dts!=AV_NOPTS_VALUE){  

			pts=pkt.dts;  

		}else{  

			pts=0;  

		}  

		pts*=av_q2d(pVideoCodecCtx->time_base);  



		av_free_packet(&pkt);  

		if(video_queue.nb_packets>= 100 || audio_queue.nb_packets>= 200){  //改写

			printf("%d---%d\n",video_queue.nb_packets,audio_queue.nb_packets);  

			Sleep(20); //  change   10000

	

		} 

		else if(video_queue.nb_packets>= 50 || audio_queue.nb_packets>= 100)

		{

			Sleep(10);

	

		}

		if(frame_finished){  

			SDL_LockMutex(play->pictq_mutex);  

			while(play->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !quit){  

				SDL_CondWait(play->pictq_cond,play->pictq_mutex);  

			}  

			SDL_UnlockMutex(play->pictq_mutex);  

			if(quit)  

				goto out;  



			vp=&play->pictq[play->pictq_windex];  

			SDL_Overlay *bmp=vp->bmp; //定义 

			//序列化帧  

			pts=sync_video(&g_play_info,pVideoFrame,pts);  

			vp->pts=pts;  

			printf("video-pts:%f\n",pts);  



			SDL_LockYUVOverlay(bmp);  //加锁

			pict.data[0]=bmp->pixels[0];  

			pict.data[1]=bmp->pixels[2];  

			pict.data[2]=bmp->pixels[1];  

			pict.linesize[0]=bmp->pitches[0];  

			pict.linesize[1]=bmp->pitches[2];  

			pict.linesize[2]=bmp->pitches[1];  

			sws_scale(play->video_conv,pVideoFrame->data,pVideoFrame->linesize,0,  

				pVideoFrame->height,pict.data,pict.linesize);  

			SDL_UnlockYUVOverlay(bmp);  





			play->pictq_windex=(play->pictq_windex+1)%VIDEO_PICTURE_QUEUE_SIZE;  

			SDL_LockMutex(play->pictq_mutex);  

			play->pictq_size++;  

			SDL_UnlockMutex(play->pictq_mutex);  



		}  

	}  

out:  

	av_free(pVideoFrame);  

	printf("---- exit video_decode_callback ----\n");  

	return 0;  

}  



void video_image_display(struct play_info_t *play)  

{  

	double delay,time;  

	VideoPicture *vp;  

	double diff,sync_threshold;  

	if(play->pictq_size>0){  

		vp=&play->pictq[play->pictq_rindex];  

		delay=vp->pts - play->last_frame_pts;  

		if(delay>0 && delay<10){  

			play->last_frame_delay=delay;  

		}  

		delay=play->last_frame_delay;  



		diff=vp->pts-get_audio_clock();  

		sync_threshold = FFMAX(0.01, delay);  

		if (fabs(diff) < 10) {  

			if (diff <= -sync_threshold)  

				delay = 0;  

			else if (diff >= sync_threshold)  

				delay = 2 * delay;  

		}  



		time=av_gettime()/1000000.0;  

		if(time < play->frame_timer+delay)  

			return ;  



		if(delay>0)  

			play->frame_timer+=delay;  

		play->last_frame_pts=vp->pts;  



		SDL_DisplayYUVOverlay(vp->bmp,&rect);  //显示



		play->pictq_rindex=(play->pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE;  



		SDL_LockMutex(play->pictq_mutex);  

		play->pictq_size--;  

		SDL_CondSignal(play->pictq_cond);  

		SDL_UnlockMutex(play->pictq_mutex);  



	}  

}  



int SDLCALL video_show_callback(void* arg)  

{   

	double delay=80;  
struct play_info_t *play=(struct play_info_t*)arg; while(!quit){ video_image_display(play); Sleep(delay); } printf("----exit video_show_callback ----\n"); return 0; } void player_log_callback(void* ptr, int level, const char* fmt, va_list vl) { static int print_prefix = 0; static int count; char line[1024]; av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix); printf("%s",line); } int main(int argc,char *argv[]) { if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)<0){ printf("Init SDL err!\n"); return -1; } av_log_set_callback(player_log_callback); av_register_all(); AVFormatContext *pFormatCtx; pFormatCtx=avformat_alloc_context(); argv[1]="test.mp4";
if(avformat_open_input(&pFormatCtx,argv[1],NULL,NULL)<0){ printf("avformat_open_input err!\n"); return -1; } if(avformat_find_stream_info(pFormatCtx,NULL)<0){ printf("avformat_find_stream_info err!\n"); return -1; } //av_dump_format(pFormatCtx,0,argv[1],0); int video_stream=-1,audio_stream=-1,i; for(i=0;i<pFormatCtx->nb_streams;i++){ if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){ video_stream=i; } if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){ audio_stream=i; } } if(video_stream==-1 || audio_stream==-1){ printf("not find video or audio stream!\n"); avformat_close_input(&pFormatCtx); return -1; } g_play_info.pVideoCodecCtx=pFormatCtx->streams[video_stream]->codec; g_play_info.pAudioCodecCtx=pFormatCtx->streams[audio_stream]->codec; //find the decoder AVCodec *pVideoCodec=avcodec_find_decoder(g_play_info.pVideoCodecCtx->codec_id); if(pVideoCodec==NULL){ printf("not find video decoder!\n"); avformat_close_input(&pFormatCtx); return -1; } AVCodec *pAudioCodec=avcodec_find_decoder(g_play_info.pAudioCodecCtx->codec_id); if(pVideoCodec==NULL){ printf("not find audio decoder!\n"); avformat_close_input(&pFormatCtx); return -1; } //open the codec if(avcodec_open(g_play_info.pVideoCodecCtx,pVideoCodec)<0){ printf("can't open the video decoder!\n"); avformat_close_input(&pFormatCtx); return -1; } if(avcodec_open(g_play_info.pAudioCodecCtx,pAudioCodec)<0){ printf("can't open the audio decoder!\n"); avcodec_close(g_play_info.pVideoCodecCtx); avformat_close_input(&pFormatCtx); return -1; } //setup SDL screen = SDL_SetVideoMode(g_play_info.pVideoCodecCtx->width, g_play_info.pVideoCodecCtx->height, 0, 0);//设置视频模式参数 picture_queque_init(&g_play_info); packet_queue_init(&video_queue); packet_queue_init(&audio_queue); rect.x=0; rect.y=0; rect.w = g_play_info.pVideoCodecCtx->width; rect.h = g_play_info.pVideoCodecCtx->height; //setup the sdl audio SDL_AudioSpec sdl_audio; int64_t wanted_channel_layout = 0; int wanted_nb_channels; wanted_channel_layout = (g_play_info.pAudioCodecCtx->channel_layout && g_play_info.pAudioCodecCtx->channels == av_get_channel_layout_nb_channels(g_play_info.pAudioCodecCtx->channel_layout)) ? g_play_info.pAudioCodecCtx->channel_layout : av_get_default_channel_layout(g_play_info.pAudioCodecCtx->channels); wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout); /* SDL only supports 1, 2, 4 or 6 channels at the moment, so we have to make sure not to request anything else. */ while (wanted_nb_channels > 0 && (wanted_nb_channels == 3 || wanted_nb_channels == 5 || wanted_nb_channels > 6)) { wanted_nb_channels--; wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels); } sdl_audio.channels=av_get_channel_layout_nb_channels(wanted_channel_layout); g_play_info.pAudioCodecCtx->channels=sdl_audio.channels; g_play_info.pAudioCodecCtx->channel_layout=wanted_channel_layout; g_play_info.audio_conv=swr_alloc_set_opts(NULL,wanted_channel_layout,AV_SAMPLE_FMT_S16,g_play_info.pAudioCodecCtx->sample_rate, wanted_channel_layout,g_play_info.pAudioCodecCtx->sample_fmt, g_play_info.pAudioCodecCtx->sample_rate,0,NULL); swr_init(g_play_info.audio_conv); sdl_audio.freq=g_play_info.pAudioCodecCtx->sample_rate; sdl_audio.format=AUDIO_S16SYS; sdl_audio.channels=g_play_info.pAudioCodecCtx->channels; sdl_audio.silence=0; sdl_audio.samples=1024; sdl_audio.callback=audio_callback;//calls when the audio device need more data sdl_audio.userdata=&g_play_info; if(SDL_OpenAudio(&sdl_audio,NULL)<0){ printf("can't open audio device!\n"); goto err_alloc_frame1; } g_play_info.vedio_clock=0; g_play_info.audio_clock=0; g_play_info.audio_current_pts=0; g_play_info.last_frame_pts=0; g_play_info.last_frame_delay=0; g_play_info.frame_timer=av_gettime()/1000000.0; g_play_info.pVideoCodecCtx->get_buffer=our_get_buffer; g_play_info.pVideoCodecCtx->release_buffer=our_release_buffer; SDL_PauseAudio(0);
g_play_info.video_conv=sws_getContext(g_play_info.pVideoCodecCtx->width,g_play_info.pVideoCodecCtx->height, g_play_info.pVideoCodecCtx->pix_fmt, g_play_info.pVideoCodecCtx->width,g_play_info.pVideoCodecCtx->height,PIX_FMT_YUYV422,SWS_POINT,NULL,NULL,NULL); SDL_Thread * video_decode_thread=SDL_CreateThread(video_decode_callback,(void*)&g_play_info);
SDL_Thread * video_show_thread=SDL_CreateThread(video_show_callback,(void*)&g_play_info); SDL_Event sdl_event; AVPacket packet; while(!quit){ SDL_PollEvent(&sdl_event); if(sdl_event.type==SDL_QUIT){ SDL_CondSignal(video_queue.cond); SDL_CondSignal(audio_queue.cond); quit=1; break; } if(av_read_frame(pFormatCtx,&packet)<0){ quit=1; continue; } if(packet.stream_ndex==video_stream){ packet_queue_put(&video_queue,&packet); //change Sleep(25);//change }else if(packet.stream_index==audio_stream){ packet_queue_put(&audio_queue,&packet); //change }else{ av_free_packet(&packet); } //usleep(5000); } SDL_CloseAudio(); SDL_CondSignal(g_play_info.pictq_cond); SDL_WaitThread(video_decode_thread,NULL); SDL_WaitThread(video_show_thread,NULL); picture_queque_destroy(&g_play_info); swr_free(&g_play_info.audio_conv); sws_freeContext(g_play_info.video_conv); SDL_FreeSurface(screen); avcodec_close(g_play_info.pVideoCodecCtx); avcodec_close(g_play_info.pAudioCodecCtx); avformat_close_input(&pFormatCtx); return 0; err_alloc_frame1: avcodec_close(g_play_info.pAudioCodecCtx); avcodec_close(g_play_info.pVideoCodecCtx); avformat_close_input(&pFormatCtx); return -1; }

 

你可能感兴趣的:(视频)