C++ ffmpeg实现简单播放器之视频播放

4 篇文章 1 订阅

实现视频播放并不难,关键就是两个函数sws_getContext和sws_scale的使用,直接上代码:

int Grial(int a, int b)
{
	if(b == 0) return a;
	return Grial(b, a%b);
}

void PlayVideo(const string& strVideoPath)
{
av_register_all();
AVFormatContext* avFormatContext = avformat_alloc_context();
int ret = avformat_open_input(&avFormatContext, strVideoPath.c_str(), NULL, NULL);//读取文件头
if (ret != 0)
	return;
ret = avformat_find_stream_info(avFormatContext, NULL);
if (ret < 0)
	return;
int videoStream_index = -1;//视频流索引号
for (int i = 0; i < avFormatContext->nb_streams; i++)
{
	if (avFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
		videoStream_index = i;
		break;
	}
}
if (videoStream_index == -1)
	return;
AVCodecContext* videoCodecContext = avFormatContext->streams[videoStream_index]->codec;
AVCodec* videoCodec = avcodec_find_decoder(videoCodecContext->codec_id);//通过上下文查找编码器
if (videoCodec == NULL){
	printf("Couldn't get AVCodec\n");
	return;
}
if (avcodec_open2(videoCodecContext, videoCodec, NULL) < 0){// 打开编码器
	printf("Couldn't open AVCodec\n");
	return;
}
AVPacket *avPacket = (AVPacket*)av_malloc(sizeof(AVPacket));//分配avPacket
AVFrame *videoFrame = av_frame_alloc();//分配视频帧
int64_t start_time = av_gettime_relative();
while (av_read_frame(avFormatContext, avPacket) >= 0)
{
	if (avPacket->stream_index == videoStream_index)
	{
		int got_picture_ptr = 0;//是否被解码成功解码的标识(0标识失败,非0表示成功)
		avcodec_decode_video2(videoCodecContext, videoFrame, &got_picture_ptr, avPacket);//解码视频帧
		if (0 != got_picture_ptr)
		{
			RECT rt = video_ctrl_screen_->GetPos();
			int item_h = rt.bottom - rt.top;
			int item_w = rt.right - rt.left;
			int nGrial = Grial(videoCodecContext->width, videoCodecContext->height);//利用辗转相除法,计算得到最大公约数
			int nW = videoCodecContext->width / nGrial;
			int nH = videoCodecContext->height / nGrial;

			int nZearoWidth = 100;
			int nZearoHeight = nZearoWidth * nH / nW;
			int nWidthTemp = (item_w - nZearoWidth) / 4;
			int nHeightTemp = (item_h - nZearoHeight) / (4 * nH / nW);
			int nMin = min(nWidthTemp, nHeightTemp);
			int nOutWidth = nZearoWidth + nMin * 4;
			int nOutHeight = nZearoHeight + nMin *  (4 * nH / nW);

			AVFrame* pFrameRGB = av_frame_alloc();
			int numBytes = avpicture_get_size(AV_PIX_FMT_BGR24, nOutWidth, nOutHeight);
			uint8_t* buffer = new(std::nothrow) uint8_t[numBytes];
			avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24, nOutWidth, nOutHeight);
			SwsContext *pSWSCtx = sws_getContext(videoCodecContext->width, videoCodecContext->height, \
				videoCodecContext->pix_fmt, nOutWidth, nOutHeight, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);

			sws_scale(pSWSCtx, videoFrame->data, videoFrame->linesize, 0, videoFrame->height, pFrameRGB->data, pFrameRGB->linesize);
			if (video_ctrl_screen_)
				video_ctrl_screen_->Refresh(nOutWidth, nOutHeight, (unsigned char*)pFrameRGB->data[0], nOutWidth * nOutHeight * AV_PIX_FMT_BGR24);

			//对视频流进行同步
			AVRational time_base = avFormatContext->streams[videoStream_index]->time_base;
			AVRational time_base_q = { 1, AV_TIME_BASE };
			int64_t pts_time = av_rescale_q(avPacket->pts, time_base, time_base_q);
			int64_t now_time = av_gettime_relative() - start_time;
			if (pts_time > now_time)
				av_usleep(pts_time - now_time);

			delete[] buffer;
			buffer = nullptr;
			sws_freeContext(pSWSCtx);
			av_frame_free(&pFrameRGB);
		}
	}
	av_free_packet(avPacket);	
}
av_frame_free(&videoFrame);//释放videoFrame
av_packet_free(&avPacket);//释放avPacket
avcodec_close(videoCodecContext);//关闭视频解码器
avformat_close_input(&avFormatContext);//关闭输入文件
}

sws_scale转换得到pFrameRGB->data数据即为RGB视频数据,nOutWidth, nOutHeight分别为显示的视频宽高,接下来就是通过转成BITMAPINFO显示到dc上即可。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

简单前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值