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

相关推荐
简鹿办公41 分钟前
使用 FFmpeg 进行音视频转换的相关命令行参数解释
ffmpeg·简鹿视频格式转换器·ffmpeg视频转换
EasyCVR4 小时前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频
runing_an_min4 小时前
ffmpeg 视频滤镜:屏蔽边框杂色- fillborders
ffmpeg·音视频·fillborders
岁月小龙15 小时前
如何让ffmpeg运行时从当前目录加载库,而不是从/lib64
ffmpeg·origin·ffprobe·rpath
行者记2 天前
ffmpeg命令——从wireshark包中的rtp包中分离h264
测试工具·ffmpeg·wireshark
EasyCVR2 天前
国标GB28181视频平台EasyCVR私有化视频平台工地防盗视频监控系统方案
运维·科技·ffmpeg·音视频·1024程序员节·监控视频接入
hypoqqq2 天前
使用ffmpeg播放rtsp视频流
ffmpeg
cuijiecheng20182 天前
音视频入门基础:FLV专题(24)——FFmpeg源码中,获取FLV文件视频信息的实现
ffmpeg·音视频
QMCY_jason2 天前
黑豹X2 armbian 编译rkmpp ffmpeg 实现CPU视频转码
ffmpeg
苍天饶过谁?2 天前
SDL基本使用
ffmpeg