FFmpeg基础:SDL渲染显示视频流数据

Simple DirectMedia Layer(SDL)是一个跨平台软件开发库,旨在为计算机多媒体硬件组件提供硬件抽象层。我们可以使用SDL库对视频文件中的视频流和音频流进行渲染呈现。SDL和FFmpeg库解析渲染视频流的流程如下图所示:
FFmpeg基础:SDL渲染显示视频流数据_第1张图片
FFmpeg库+SDL库渲染显示视频文件中的视频流的示例如下所示:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//@1视频文件的地址
int main(int argc, char *argv[]) {

	int       ret = -1;
	int       quit = 0;
	int       videoStream;

	AVFormatContext *pFormatCtx = NULL;

	//视频解码上下文,解码器
	AVCodecContext  *pSourceCodecCtx = NULL;
	AVCodec         *pSourceCodec = NULL;

	//图像转换上下文
	struct SwsContext *sws_ctx = NULL;

	//图像数据格式
	uint8_t *dst_data[4] = { 0 };
	int dst_linesize[4] = { 0 };

	AVFrame         *pFrame = NULL;
	AVPacket        packet;

	//视频宽高
	int     w_width = 640;
	int       w_height = 480;


	//SDL渲染用
	int             pixformat;
	SDL_Rect        rect;
	SDL_Window      *win;
	SDL_Renderer    *renderer;
	SDL_Texture     *texture;
	SDL_Event       event;
	SDL_AudioSpec   wanted_spec, spec;

	if (argc < 2) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Usage: command ");
		return ret;
	}

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not initialize SDL - %s\n", SDL_GetError());
		return ret;
	}

	//打开视频上下文
	if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open multi-media file");
		goto __FAIL; // Couldn't open file
	}

	//获取流信息信息
	if (avformat_find_stream_info(pFormatCtx, NULL)<0) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't find stream information ");
		goto __FAIL;
	}

	//查找视频流
	videoStream = -1;
	for (int i = 0; i<pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
			videoStream < 0) {
			videoStream = i;
		}
	}

	if (videoStream == -1) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, " Didn't find a video stream ");
		goto __FAIL;
	}

	//查找视频流的解码器
	pSourceCodec = avcodec_find_decoder(pFormatCtx->streams[videoStream]->codecpar->codec_id);
	if (pSourceCodec == NULL) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unsupported codec!");
		goto __FAIL;
	}

	//创建解码器的上下文,并拷贝参数
	pSourceCodecCtx = avcodec_alloc_context3(pSourceCodec);
	ret = avcodec_parameters_to_context(pSourceCodecCtx, pFormatCtx->streams[videoStream]->codecpar);
	if (ret < 0) {
		printf("Failed to copy in_stream codecpar to codec context\n");
		goto __FAIL;
	}

	//打开解码器
	if (avcodec_open2(pSourceCodecCtx, pSourceCodec, NULL)<0) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open audio decoder!");
		goto __FAIL;
	}

	pFrame = av_frame_alloc();
	w_width = pSourceCodecCtx->width;
	w_height = pSourceCodecCtx->height;

	//创建输出窗口
	win = SDL_CreateWindow("SDL Video Player",
		SDL_WINDOWPOS_UNDEFINED,
		SDL_WINDOWPOS_UNDEFINED,
		w_width, w_height,
		SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);

	if (!win) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create window!");
		goto __FAIL;
	}

	//SDL渲染器
	renderer = SDL_CreateRenderer(win, -1, 0);
	if (!renderer) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create renderer!");
		goto __FAIL;
	}

	//创建显示帧
	pixformat = SDL_PIXELFORMAT_IYUV;
	texture = SDL_CreateTexture(renderer,
		pixformat,
		SDL_TEXTUREACCESS_STREAMING,
		w_width,
		w_height);

	if (!texture)
	{
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Texture!");
		goto __FAIL;
	}

	//转换图像格式
	sws_ctx = sws_getContext(pSourceCodecCtx->width,
		pSourceCodecCtx->height,
		pSourceCodecCtx->pix_fmt,
		pSourceCodecCtx->width,
		pSourceCodecCtx->height,
		AV_PIX_FMT_YUV420P,
		SWS_BILINEAR,
		NULL,
		NULL,
		NULL);

	ret = av_image_alloc(
		dst_data, dst_linesize, pSourceCodecCtx->width, pSourceCodecCtx->height,
		AV_PIX_FMT_YUV420P, 1);
	
	//读取数据帧并渲染
	while (av_read_frame(pFormatCtx, &packet) >= 0)
	{
		if (packet.stream_index == videoStream)
		{
			ret = avcodec_send_packet(pSourceCodecCtx, &packet);
			if (ret < 0)
			{
				continue;
			}

			ret = avcodec_receive_frame(pSourceCodecCtx, pFrame);
			if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
				ret = 0;
				continue;
			}
			if (ret < 0) {
				continue;
			}

			//对图像进行缩放
			sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
				pFrame->linesize, 0, pSourceCodecCtx->height,
				dst_data, dst_linesize);

			//渲染图像
			SDL_UpdateYUVTexture(texture, NULL,
				dst_data[0], dst_linesize[0],
				dst_data[1], dst_linesize[1],
				dst_data[2], dst_linesize[2]);

			rect.x = 0;
			rect.y = 0;
			rect.w = pSourceCodecCtx->width;
			rect.h = pSourceCodecCtx->height;

			SDL_RenderClear(renderer);
			SDL_RenderCopy(renderer, texture, NULL, &rect);
			SDL_RenderPresent(renderer);
			SDL_Delay(1000 / 30);
			av_packet_unref(&packet);
		}

		//处理SDL事件
		SDL_PollEvent(&event);
	}
__FAIL:
	//清理分配的数据
	av_freep(&dst_data[0]);
	if (pFormatCtx)
	{
		avformat_close_input(&pFormatCtx);
	}
	if (pFrame) {
		av_frame_free(&pFrame);
	}

	if (pSourceCodecCtx)
	{
		avcodec_close(pSourceCodecCtx);
	}

	if (pSourceCodecCtx) {
		avcodec_close(pSourceCodecCtx);
	}

	if (pFormatCtx) {
		avformat_close_input(&pFormatCtx);
	}
	if (win) {
		SDL_DestroyWindow(win);
	}
	if (renderer) {
		SDL_DestroyRenderer(renderer);
	}

	if (texture) {
		SDL_DestroyTexture(texture);
	}
	SDL_Quit();
	return ret;
}

你可能感兴趣的:(音视频,音视频,ffmpeg,SDL,流媒体)