SDL2+FFMPEG的视音频播放器例子

SDL2+FFMPEG,没做视音频同步,音频效果比前一篇的QT+FFMPEG的要好一些

代码如下,没什么好说的

// sdlplay.cpp : 定义控制台应用程序的入口点。
//

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"

#include "SDL2\SDL.h"

//#define YUV_FILE_OUTPUT

#define THREADEXIT_EVENT  (SDL_USEREVENT + 1)


int thread_exit = 0;
int window_w;
int window_h;

SDL_Window *sdl_window;
SDL_Renderer *sdl_renderer;
SDL_Texture *sdl_texture;

SDL_AudioDeviceID m_AudioDevice;
SDL_AudioSpec wanted_spec, have;

Uint32  audio_len;
Uint8  *audio_pos;

void  fill_audio(void *udata, Uint8 *stream, Uint32 len) {
	//SDL 2.0
	SDL_memset(stream, 0, len);
	if (audio_len == 0)
		return;

	len = (len>audio_len ? audio_len : len);    //  Mix  as  much  data  as  possible  

	SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
	audio_pos += len;
	audio_len -= len;
}


int decode_play(void *data)
{
	char *filepath = (char*)data;
	AVFormatContext	*pFormatCtx;
	unsigned int	i;
	int videoindex, audioindex;
	AVCodec			*pVideoCodec, *pAudioCodec;
	AVFrame	*pFrame, *pFrameYUV;
	AVPacket *packet;

	SDL_Event event;

	int ret, got_picture;

//	av_register_all();
//	avformat_network_init();
	pFormatCtx = avformat_alloc_context();

	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0) {
		printf("Couldn't open input stream.\n");
		return -1;
	}
	if (avformat_find_stream_info(pFormatCtx, NULL)<0) {
		printf("Couldn't find stream information.\n");
		return -1;
	}
	videoindex = -1;
	audioindex = -1;
	videoindex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	audioindex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

	//	video
	AVCodecParameters *video_codecpar = pFormatCtx->streams[videoindex]->codecpar;
	pVideoCodec = avcodec_find_decoder(video_codecpar->codec_id);
	if (pVideoCodec == NULL) {
		printf("Codec not found.\n");
		return -1;
	}

	//	audio
	AVCodecParameters *audio_codecpar = pFormatCtx->streams[audioindex]->codecpar;
	pAudioCodec = avcodec_find_decoder(audio_codecpar->codec_id);

	if (pAudioCodec == NULL) {
		printf("audio Codec not found.\n");
		return -1;
	}

	AVCodecContext *video_codecctx = avcodec_alloc_context3(NULL);
	avcodec_parameters_to_context(video_codecctx, video_codecpar);

	AVCodecContext *audio_codecctx = avcodec_alloc_context3(NULL);
	avcodec_parameters_to_context(audio_codecctx, audio_codecpar);

	struct SwsContext *video_convert_ctx = NULL;
	uint8_t	*video_out_buffer = NULL;
	uint8_t	*audio_out_buffer = NULL;

	struct SwrContext *audio_convert_ctx = NULL;

	//nb_samples: AAC-1024 MP3-1152
	int out_nb_samples = audio_codecctx->frame_size;
	uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
	enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
	int out_sample_rate = audio_codecctx->sample_rate;

	uint64_t in_channel_layout = av_get_default_channel_layout(audio_codecctx->channels);
	int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);

	int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
	audio_out_buffer = (uint8_t *)av_malloc(out_buffer_size);
	
	//SDL_AudioSpec
	wanted_spec.freq = out_sample_rate;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.channels = out_channels;
	wanted_spec.silence = 0;
	wanted_spec.samples = out_nb_samples;
	wanted_spec.callback = (SDL_AudioCallback)fill_audio;
	wanted_spec.userdata = audio_codecctx;

	m_AudioDevice = SDL_OpenAudio(&wanted_spec, NULL);
	
	if (m_AudioDevice < 0) {
		printf("can't open audio.\n");
		return -1;
	}
	SDL_PauseAudio(0);

	audio_convert_ctx = swr_alloc();
	audio_convert_ctx = swr_alloc_set_opts(audio_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,in_channel_layout, audio_codecctx->sample_fmt, audio_codecctx->sample_rate, 0, NULL);

	swr_init(audio_convert_ctx);

	pFrame = av_frame_alloc();
	packet = (AVPacket *)av_malloc(sizeof(AVPacket));
	pFrameYUV = av_frame_alloc();

	video_out_buffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, video_codecctx->width, video_codecctx->height, 1));
	av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, video_out_buffer, AV_PIX_FMT_YUV420P, video_codecpar->width, video_codecpar->height, 1);

	video_codecctx->thread_count = 2;
	video_codecctx->thread_type = FF_THREAD_FRAME;
	video_codecctx->delay = 10;

	if (avcodec_open2(video_codecctx, pVideoCodec, NULL)<0) {
		printf("Could not open video codec.\n");
		return -1;
	}

	if (avcodec_open2(audio_codecctx, pAudioCodec, NULL)<0) {
		printf("Could not open audio codec.\n");
		return -1;
	}

	sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, video_codecctx->width, video_codecctx->height);
	
	
	av_dump_format(pFormatCtx, 0, filepath, 0);

#ifdef YUV_FILE_OUTPUT
	char yuvfile[1024] = "";
	sprintf_s(yuvfile, 1024, "Hunantv_%d_%d.yuv", video_codecctx->width, video_codecctx->height);
	int y_size;
	FILE *fpout;
	fopen_s(&fpout, yuvfile, "wb+");
#endif
	SDL_Rect sdlRect;

	while (!thread_exit) {
		if (av_read_frame(pFormatCtx, packet) < 0)
		{
			event.type = THREADEXIT_EVENT;
			SDL_PushEvent(&event);
			break;
		}
		if (packet->stream_index == videoindex) {
#if 1

	//		Uint32 tick0 = SDL_GetTicks();
			ret = avcodec_send_packet(video_codecctx, packet);
			if (ret < 0) {
				printf("Decode Error.\n");
				break;
			}
			got_picture = avcodec_receive_frame(video_codecctx, pFrame);
			/*
			switch (pFrame->pict_type)
			{
			case AV_PICTURE_TYPE_I:
				printf("I\n");
				break;
			case AV_PICTURE_TYPE_P:
				printf("P\n");
				break;
			case AV_PICTURE_TYPE_B:
				printf("B\n");
				break;
			default:
				printf("No picture type\n");
				break;

			}
			*/

	//		Uint32 tick1 = SDL_GetTicks();
	//		printf("decode time [%d] ms\n", tick1 - tick0);

			if (!got_picture) 
			{
					if (pFrameYUV)
					{
	//					Uint32 tick2 = SDL_GetTicks();
						
						video_convert_ctx = sws_getContext(video_codecctx->width, video_codecctx->height, video_codecctx->pix_fmt, video_codecctx->width, video_codecctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
						sws_scale(video_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, video_codecctx->height, pFrameYUV->data, pFrameYUV->linesize);
						sws_freeContext(video_convert_ctx);
	//					Uint32 tick3 = SDL_GetTicks();
	//					printf("sws_scale time [%d] ms\n", tick3 - tick2);
#ifdef YUV_FILE_OUTPUT					
						y_size = video_codecctx->width * video_codecctx->height;
						fwrite(pFrame->data[0], 1, y_size,  fpout);
						fwrite(pFrame->data[1], 1, y_size / 4, fpout);
						fwrite(pFrame->data[2], 1, y_size / 4, fpout);	
#endif

						SDL_UpdateTexture(sdl_texture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);

						int x, y;
						SDL_GetWindowSize(sdl_window, &x, &y);
						sdlRect.x = 0;
						sdlRect.y = 0;
						sdlRect.w = x ;
						sdlRect.h = y ;
	
						SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, &sdlRect);

						SDL_RenderPresent(sdl_renderer);

	//					Uint32 tick4 = SDL_GetTicks();
	//					printf("present over time [%d] ms\n", tick4 - tick3);
						
					}
			}
#endif
		}
		else if (packet->stream_index == audioindex)
		{
			ret = avcodec_send_packet(audio_codecctx, packet);
			if (ret < 0) {
				printf("Decode Error.\n");
				break;
			}
			got_picture = avcodec_receive_frame(audio_codecctx, pFrame);

//			printf("avcodec_decode_video2 size=[%d]-got_picture=[%d]\n", ret, got_picture);
			if (got_picture == 0)
			{

				swr_convert(audio_convert_ctx, &audio_out_buffer, pFrame->nb_samples, (const uint8_t **)pFrame->data, pFrame->nb_samples); // 转换音频
			
				audio_pos = (uint8_t *)audio_out_buffer;
				audio_len = out_buffer_size;

				while (audio_len > 0) {
					SDL_Delay(1);//延迟播放
				}

			}
		}
	}
#ifdef YUV_FILE_OUTPUT
	fclose(fpout);
#endif

	av_frame_free(&pFrameYUV);
	av_frame_free(&pFrame);
	avcodec_close(video_codecctx);
	avcodec_close(audio_codecctx);
	avformat_close_input(&pFormatCtx);

	return 0;
}

#if 1
#undef main
int main(int argc, char *argv[])
{

	int quit = 0;
	char file[1024] = "";
	if (argc == 1)
		sprintf_s(file, 1024, "%s", "rtmp://58.200.131.2:1935/livetv/hunantv");
//		sprintf_s(file, 1024, "%s", "D:\\123.mp4");
	else
		sprintf_s(file, 1024, "%s", argv[1]);

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0)
	{
		return 1;
	}
	
	window_w = 1024;
	window_h = 576;
	sdl_window = SDL_CreateWindow("SDL-test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_w, window_h, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);

	int displayindex;
	SDL_DisplayMode mode0;
	displayindex = SDL_GetWindowDisplayIndex(sdl_window);
	SDL_GetCurrentDisplayMode(displayindex, &mode0);

	SDL_Surface *iconSurface = SDL_LoadBMP(".\\face.bmp");
	SDL_SetWindowIcon(sdl_window, iconSurface);

	//	0 SDL_VIDEO_RENDER_D3D
	//	1 SDL_VIDEO_RENDER_D3D11
	//	2 SDL_VIDEO_RENDER_OGL
	//	3 SDL_VIDEO_RENDER_OGL_ES2
	sdl_renderer = SDL_CreateRenderer(sdl_window, 0, SDL_RENDERER_PRESENTVSYNC);

	SDL_Thread *play_thread = SDL_CreateThread(decode_play, NULL, file);
	SDL_Event event;
	
	while (quit != 1)
	{
		while (SDL_WaitEvent(&event))
		{
		
			if (event.type == SDL_WINDOWEVENT)
			{
			}
			else if (event.type == SDL_MOUSEMOTION)
			{
//				printf("Receive MOUSEMOTION event, x=[%d]-y=[%d]-xrel=[%d]-yrel=[%d]\n", event.motion.x, event.motion.y, event.motion.xrel, event.motion.yrel);
			}
			else if (event.type == SDL_MOUSEBUTTONDOWN)
			{
			}
			else if (event.type == SDL_KEYDOWN)
			{
				if ((SDL_GetModState() & KMOD_SHIFT) && event.key.keysym.scancode == SDL_SCANCODE_F12) {
					quit = 1;
				}

//				printf("Receive KEYDOWN event keycode=[%d]-mod=[%x]--key=[%s]\n", event.key.keysym.scancode, event.key.keysym.mod, SDL_GetScancodeName(event.key.keysym.scancode));

			}
			else if (event.type == SDL_KEYUP)
			{
	//			printf("Receive KEYUP event keycode=[%d]-mod=[%x]--key=[%s]\n", event.key.keysym.scancode, event.key.keysym.mod, SDL_GetScancodeName(event.key.keysym.scancode));

			}
			else if (event.type = SDL_MOUSEWHEEL) 
			{
				printf("Recv MOUSEWHEEL  direction=[%d],x=[%d],y=[%d]", event.wheel.direction, event.wheel.x, event.wheel.y);
			}
			else if (event.type == SDL_QUIT)
			{
				thread_exit = 1;
				quit = 1;
			}
			else if (event.type == THREADEXIT_EVENT)
			{

			}
			else
			{
//				printf("event.type=[%d]\n", event.type);

			}
		}

	}
	SDL_Quit();

    return 0;
}

#endif

编译命令如下

gcc -o sdlplay sdlplay.c  -I../ffmpeg_install/include -L../ffmpeg_install/lib  -lavcodec -lavformat -lavutil -lswresample -lswscale -lavfilter `pkg-config --cflags --libs SDL2`

SDL2创建的窗口,就为了显示,所以没什么按钮啊之类的

效果图

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值