ffmpeg解码播放

cpp 复制代码
int VideoDecodeModule::Open(std::string strUrl)
{
	AVFormatContext *pFormatCtx = nullptr;
	AVCodec *pCodec = nullptr;
	AVCodecContext* pCodecCtx = nullptr;
	AVDictionary *opt = nullptr;
	std::string decodeName = "";
	AVBufferRef* pBufferRef = nullptr;
	int ret = 0;
	int videoStream = -1;
	char errorbuf[1024] = { 0 };
	av_dict_set(&opt, "buffer_size", "1024000", 0);        // 缓冲区大小 单位字节 解决高画质模糊的问题
	av_dict_set(&opt, "max_delay", "100", 0);              // 最大延时 单位微妙
	av_dict_set(&opt, "stimeout", "3000000", 0);           // 设置超时断开连接时间 3s 单位微妙
	av_dict_set(&opt, "rtsp_transport", "tcp", 0);         // 以tcp方式打开,如果以udp方式打开将tcp替换为udp
	av_dict_set(&opt, "fflags", "nobuffer", 0);
	av_dict_set(&opt, "rtbufsize", "6", 0);
	av_dict_set(&opt, "start_time_realtime", 0, 0);

	if ((ret = avformat_open_input(&pFormatCtx, strUrl.data(), nullptr, &opt)) != 0)
	{
		av_strerror(ret, errorbuf, sizeof(errorbuf));
		return -1;
	}
	
	m_spFormatContext = std::shared_ptr<AVFormatContext>(pFormatCtx, [](AVFormatContext* ctx){
		avformat_close_input(&ctx);
	});

	/*if ((ret = avformat_find_stream_info(pFormatCtx, nullptr)) < 0)
	{
		av_strerror(ret, errorbuf, sizeof(errorbuf));
		return -1;
	}*/

	if ((ret = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &pCodec, 0)) < 0)
	{
		av_strerror(ret, errorbuf, sizeof(errorbuf));
		return -1;
	}
	videoStream = ret;
	m_video = pFormatCtx->streams[videoStream];

	if (m_decodeType == DT_CPU)
	{
		if (!(pCodec = avcodec_find_decoder(m_video->codecpar->codec_id)))
		{
			int error = AVERROR(ENOMEM);
			return -1;
		}
	}
	else if (m_decodeType == DT_INTER_QSV)
	{
		decodeName = GetCodeName(m_video->codecpar->codec_id, DT_INTER_QSV);
		if (av_hwdevice_ctx_create(&pBufferRef, AV_HWDEVICE_TYPE_QSV, "auto", nullptr, 0) != 0)
		{
			return -1;
		}
		if (!(pCodec = avcodec_find_decoder_by_name(decodeName.data())))
		{
			int error = AVERROR(ENOMEM);
			return -1;
		}
	}
	else if (m_decodeType == DT_NVIDIA_CUDA)
	{
		decodeName = GetCodeName(m_video->codecpar->codec_id, DT_NVIDIA_CUDA);
		if (av_hwdevice_ctx_create(&pBufferRef, AV_HWDEVICE_TYPE_CUDA, "auto", nullptr, 0) != 0)
		{
			return -1;
		}
		if (!(pCodec = avcodec_find_decoder_by_name(decodeName.data())))
		{
			int error = AVERROR(ENOMEM);
			return -1;
		}
	}

	if (auto frame = av_frame_alloc())
	{
		m_spSwFrame = std::shared_ptr<AVFrame>(frame, [](AVFrame* p) {av_frame_free(&p); });
	}
	
	if (!(pCodecCtx = avcodec_alloc_context3(pCodec)))
	{
		return -1;
	}

	m_spCodecContext = std::shared_ptr<AVCodecContext>(pCodecCtx, [](AVCodecContext* ctx)
	{
		avcodec_free_context(&ctx);
	});

	m_spCodecContext->codec_id = m_video->codecpar->codec_id;
	if (m_video->codecpar->extradata_size)
	{
		m_spCodecContext->extradata = (uint8_t*)av_mallocz(m_video->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
		if (!m_spCodecContext->extradata)
		{
			return -1;
		}
		memcpy(m_spCodecContext->extradata, m_video->codecpar->extradata, m_video->codecpar->extradata_size);
		m_spCodecContext->extradata_size = m_video->codecpar->extradata_size;
	}

	m_spCodecContext->flags2 |= AV_CODEC_FLAG2_FAST;    // 允许不符合规范的加速技巧。
	m_spCodecContext->thread_count = 8;                 // 使用8线程解码

	if ((ret = avcodec_parameters_to_context(m_spCodecContext.get(), m_video->codecpar)) < 0)
	{
		av_strerror(ret, errorbuf, sizeof(errorbuf));
		return -1;
	}
	
	if (m_decodeType == DT_INTER_QSV)
	{
		m_spCodecContext->opaque = pBufferRef;
		m_spCodecContext->get_format = GetHWQsvFormat;
	}
	else if (m_decodeType == DT_NVIDIA_CUDA)
	{
		m_spCodecContext->hw_device_ctx = av_buffer_ref(pBufferRef);
		m_spCodecContext->opaque = this;
		m_spCodecContext->get_format = GetHWCudaFormat;
	}

	if ((ret = avcodec_open2(m_spCodecContext.get(), pCodec, nullptr)) < 0)
	{
		av_strerror(ret, errorbuf, sizeof(errorbuf));
		return -1;
	}
	m_isConnect = true;
	return ret;
}

avformat_find_stream_info 比较耗时,播放视频会比较慢,这里将他屏蔽掉,对播放视频没有影响

2:接收数据

cpp 复制代码
void VideoDecodeModule::PushFrameQueue(std::shared_ptr<AVPacket> spPacket)
{
	if (spPacket && !spPacket->size && spPacket->data)
	{
		return;
	}
	int ret = avcodec_send_packet(m_spCodecContext.get(), spPacket.get());
	if (ret < 0)
	{
		char errorbuf[1024] = { 0 };
		av_strerror(ret, errorbuf, sizeof(errorbuf));
		return;
	}
	while (true)
	{
		auto spFrame = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* p) 
		{
			av_frame_free(&p); 
		});
		if (!spFrame)
		{
			return;
		}
		ret = avcodec_receive_frame(m_spCodecContext.get(), spFrame.get());
		if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
		{
			return;
		}
		if (ret < 0)
		{
			break;
		}

		if (m_decodeType == DT_INTER_QSV || m_decodeType == DT_NVIDIA_CUDA)
		{
			HandleHWVideoFrame(spFrame);
		}
		else if (m_decodeType == DT_CPU)
		{
			HandleVideoFrame(spFrame);
		}
	}
}

3:接收视频帧

cpp 复制代码
void VideoDecodeModule::HandleVideoFrame(const std::shared_ptr<AVFrame>& spFrame)
{
	if (!spFrame)
		return;
	//if (GetFrameExtra() % 6 != 0)
	//	return;
	/// 解码的图片宽
	int width = spFrame->width;
	/// 解码的图片高
	int height = spFrame->height;
	// 计算转码后的图片裸数据需要的大小
	int nSrcbuffSize = av_image_get_buffer_size(m_pixelFormat, width, height, 1);
	if (nSrcbuffSize > m_dstBuffer.size())
	{
		m_dstBuffer.resize(nSrcbuffSize, '\0');
	}
	std::shared_ptr<SwsContext>	spSwsContext = nullptr;
	/// 判断是否需要格式转换
	if (m_pixelFormat != (AVPixelFormat)spFrame->format)
	{
		AVPixelFormat srcFmt = (AVPixelFormat)spFrame->format;
		//SwsContext* image_sws_ctx = sws_getContext(width, height, srcFmt, m_scaleWidth, m_scaleHeight, m_pixelFormat, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
		
		SwsContext* image_sws_ctx = sws_getCachedContext(NULL, width, height, srcFmt, m_scaleWidth, m_scaleHeight, m_pixelFormat, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
		
		spSwsContext = std::shared_ptr<SwsContext>(image_sws_ctx, [](SwsContext* p) {sws_freeContext(p); });
		uint8_t* data[4] = { nullptr };
		int linesize[4] = { 0 };
		av_image_fill_arrays(data, linesize, m_dstBuffer.data(), m_pixelFormat, width, height, 1);

		int ret = sws_scale(spSwsContext.get(), (uint8_t const* const*)spFrame->data, spFrame->linesize, 0, height, data, linesize);
		if (ret < 0)
			return;
	}

	if (m_HwndShow)
	{
		m_spSdlRender->DrawFrame(spFrame.get());
	}


	/*if (m_HwndShow)
	{
		BYTE** data = spFrame->data;
		int* linesize = spFrame->linesize;

		m_SdlTexture = SDL_CreateTexture(m_SdlRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
		SDL_UpdateYUVTexture(m_SdlTexture, NULL, data[0], linesize[0],
			data[1], linesize[1],
			data[2], linesize[2]);
		SDL_RenderClear(m_SdlRender);
		SDL_RenderCopy(m_SdlRender, m_SdlTexture, NULL, NULL);
		SDL_RenderPresent(m_SdlRender);
	}*/

	if (m_pFrameDataCallBack)
	{
		FrameInfo info;
		info.format = GetVideoFormatByAVPixelFormat(m_pixelFormat);
		info.width = width;
		info.height = height;
		info.pts = spFrame->pts / 1000;

		m_pFrameDataCallBack->cbFrameDataCallBack(m_dstBuffer.data(), m_dstBuffer.size(), info, m_pFrameDataCallBack->pUser);
	}
}

工程:https://download.csdn.net/download/weixin_38887743/88748651

相关推荐
问道飞鱼20 小时前
【工具介绍】Ffmpeg工具介绍与简单使用
ffmpeg·视频工具
l***77521 天前
从MySQL5.7平滑升级到MySQL8.0的最佳实践分享
ffmpeg
ZouZou老师1 天前
FFmpeg性能优化经典案例
性能优化·ffmpeg
aqi001 天前
FFmpeg开发笔记(九十)采用FFmpeg套壳的音视频转码百宝箱FFBox
ffmpeg·音视频·直播·流媒体
齐齐大魔王1 天前
FFmpeg
ffmpeg
你好音视频2 天前
FFmpeg RTSP拉流流程深度解析
ffmpeg
IFTICing2 天前
【环境配置】ffmpeg下载、安装、配置(Windows环境)
windows·ffmpeg
haiy20112 天前
FFmpeg 编译
ffmpeg
aqi002 天前
FFmpeg开发笔记(八十九)基于FFmpeg的直播视频录制工具StreamCap
ffmpeg·音视频·直播·流媒体
八月的雨季 最後的冰吻2 天前
FFmepg--28- 滤镜处理 YUV 视频帧:实现上下镜像效果
ffmpeg·音视频