学习FFmpeg简单Samples之采集屏幕并推流

实例代码

int video_capture()
{

	AVFormatContext* m_fmt_ctx = NULL;
	AVInputFormat* m_input_fmt = NULL;

	int video_stream = -1;

	avdevice_register_all();
	//注册所有的编解码器
	avcodec_register_all();

	//const char* deviceName = "video=screen-capture-recorder";
	//const char* inputformat = "dshow";

	const char* deviceName = "desktop";
	const char* inputformat = "gdigrab";

	int FPS = 15;

	m_fmt_ctx = avformat_alloc_context();
	m_input_fmt = av_find_input_format(inputformat);

	AVDictionary* deoptions = NULL;
	av_dict_set_int(&deoptions, "framerate", FPS, AV_DICT_MATCH_CASE);
	av_dict_set_int(&deoptions, "rtbufsize", 3041280 * 100 *5, 0);//默认大小3041280
	//av_dict_set(&deoptions, "video_size", "640x480", AV_DICT_MATCH_CASE);

	int ret = avformat_open_input(&m_fmt_ctx, deviceName, m_input_fmt, &deoptions);
	if (ret != 0) {
		return XError(ret);
	}

	av_dict_free(&deoptions);

	ret = avformat_find_stream_info(m_fmt_ctx, NULL);
	if (ret < 0) {
		return XError(ret);
	}

	av_dump_format(m_fmt_ctx, 0, deviceName, 0);

	video_stream = av_find_best_stream(m_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

	if (video_stream < 0) {
		return -1;
	}

	AVCodecContext * _codec_ctx = m_fmt_ctx->streams[video_stream]->codec;
	AVCodec* _codec = avcodec_find_decoder(_codec_ctx->codec_id);
	//_codec = avcodec_find_decoder_by_name("h264_qsv");
	if (_codec == NULL) {
		return -1;
	}

	ret = avcodec_open2(_codec_ctx, _codec, NULL);
	if (ret != 0) {
		return -1;
	}

	int width = m_fmt_ctx->streams[video_stream]->codec->width;
	int height = m_fmt_ctx->streams[video_stream]->codec->height;
	int fps = m_fmt_ctx->streams[video_stream]->codec->framerate.num > 0 ? m_fmt_ctx->streams[video_stream]->codec->framerate.num : 25;
	AVPixelFormat  videoType = m_fmt_ctx->streams[video_stream]->codec->pix_fmt;
	std::cout << "avstream timebase : " << m_fmt_ctx->streams[video_stream]->time_base.num << " / " << m_fmt_ctx->streams[video_stream]->time_base.den << endl;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////
	AVDictionary* enoptions = 0;

	av_dict_set(&enoptions, "preset", "superfast", 0);
	//av_dict_set(&enoptions, "preset", "slow", 0);
	av_dict_set(&enoptions, "tune", "zerolatency", 0);
	///4 初始化编码器 AV_CODEC_ID_H264
	AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
	if (!codec)
	{
		std::cout << "avcodec_find_encoder  failed!" << endl;
		return NULL;
	}
	//视频编码器上下文
	AVCodecContext* vc = avcodec_alloc_context3(codec);
	if (!vc)
	{
		std::cout << "avcodec_alloc_context3  failed!" << endl;
		return NULL;
	}
	std::cout << "avcodec_alloc_context3 success!" << endl;

	vc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
	vc->codec_id = AV_CODEC_ID_H264;
	vc->codec_type = AVMEDIA_TYPE_VIDEO;
	vc->pix_fmt = AV_PIX_FMT_YUV420P;
	vc->width = width;
	vc->height = height;
	vc->time_base.num = 1;
	vc->time_base.den = FPS;
	vc->framerate = { FPS,1 };
	vc->bit_rate = 1024*1000;
	vc->gop_size = 120;
	vc->qmin = 10;
	vc->qmax = 51;
	vc->max_b_frames = 0;//NO B Frame
	//vc->me_range = 16;
	//vc->max_qdiff = 4;
	//vc->qcompress = 0.6;
	vc->profile = FF_PROFILE_H264_MAIN;

	/*unsigned char sps_pps[23] = { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x0a, 0xf8, 0x0f, 0x00, 0x44, 0xbe, 0x8,
				  0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x38, 0x80 };
	vc->extradata_size = 23;
	vc->extradata = (uint8_t*)av_malloc(23 + AV_INPUT_BUFFER_PADDING_SIZE);
	if (vc->extradata == NULL) {
		printf("could not av_malloc the video params extradata!\n");
		return -1;
	}
	memcpy(vc->extradata, sps_pps, 23);*/

	//打开视频编码器
	ret = avcodec_open2(vc, codec, &enoptions);
	if (ret != 0)
	{
		return XError(ret);
	}
	std::cout << "avcodec_open2 success!" << endl;

	av_dict_free(&enoptions);

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///2 初始化格式转换上下文
	SwsContext* vsc = NULL;
	vsc = sws_getCachedContext(vsc,
		width, height, (AVPixelFormat)videoType,	 //源宽、高、像素格式
		width, height, AV_PIX_FMT_YUV420P,//目标宽、高、像素格式
		SWS_BICUBIC,  // 尺寸变化使用算法
		0, 0, 0
	);
	if (!vsc)
	{
		cout << "sws_getCachedContext failed!";
		return false;
	}

	///3 初始化输出的数据结构
	AVFrame* yuv = av_frame_alloc();
	yuv->format = AV_PIX_FMT_YUV420P;
	yuv->width = width;
	yuv->height = height;
	yuv->pts = 0;
	//分配yuv空间
	ret = av_frame_get_buffer(yuv, 32);
	if (ret != 0)
	{
		return XError(ret);
	}


	//////////////////////////////////////////////////////////////////////////////////////////////////////

	//打开输出 rtmp  地址 rtmp://你的流媒体服务器IP地址:端口/live/随意取啥
	const char* rtmpurl = "rtmp://你的流媒体服务器IP地址:端口/live/随意取啥";
	AVFormatContext* ic = NULL;
	ret = avformat_alloc_output_context2(&ic, 0, "flv", rtmpurl);
	if (ret < 0)
	{
		return XError(ret);
	}
	//b 添加视频流 
	AVStream* st = avformat_new_stream(ic, NULL);
	if (!st)
	{
		cout << "avformat_new_stream failed" << endl;
		return -1;
	}
	st->codecpar->codec_tag = 0;
	//从编码器复制参数
	avcodec_parameters_from_context(st->codecpar, vc);
	av_dump_format(ic, 0, rtmpurl, 1);

	///打开rtmp 的网络输出IO
	ret = avio_open(&ic->pb, rtmpurl, AVIO_FLAG_WRITE);
	if (ret != 0)
	{
		return XError(ret);
	}

	//写入封装头
	ret = avformat_write_header(ic, NULL);
	if (ret != 0)
	{
		return XError(ret);
	}


	///////////////////////////////////////////////////////////////////////////////////////////
	std::cout << "进入视频录制" << endl;

	AVPacket* packet = av_packet_alloc();

	AVPacket* Encodepacket = av_packet_alloc();

	//FILE* video_record = fopen("video_1920x1080_yuv420p.h264", "wb");

	//FILE* video_record_yuv = fopen("video_1920x1080_yuv420p.yuv", "wb");

	int frameIndex = 0;

	int EncodeIndex = 0;

	AVFrame* rgb = av_frame_alloc();

	AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");

	long long startpts = m_fmt_ctx->start_time;

	long long lastpts = 0;

	long long duration = av_rescale_q(1, { 1,FPS }, { 1,AV_TIME_BASE });

	int got_picture = 0;

	while (frameIndex < 200)
	{
		ret = av_read_frame(m_fmt_ctx, packet);
		if (ret < 0) {
			break;
		}

		if (packet->stream_index == video_stream)
		{

			//std::cout << "index: " << frameIndex << " pts:" << packet->pts<< " dts: " << packet->dts << " size: " << packet->size << endl;
			ret = avcodec_decode_video2(_codec_ctx, rgb, &got_picture, packet);
			if (ret < 0) {
				printf("Decode Error.\n");
				return ret;
			}
			if (got_picture) {

				int h = sws_scale(vsc, rgb->data, rgb->linesize, 0, height, //源数据
					yuv->data, yuv->linesize);

				/*	fwrite(yuv->data[0], 1, width* height, video_record_yuv);
					fwrite(yuv->data[1], 1, width* height / 4, video_record_yuv);
					fwrite(yuv->data[2], 1, width* height / 4, video_record_yuv);*/

					//当前 pts
					//yuv->pts = packet->pts - startpts;

					//按照帧间隔 猜测 pts
				int guesspts = frameIndex * duration;

				yuv->pts = guesspts;

				frameIndex++;

				//Encode
				ret = avcodec_encode_video2(vc, Encodepacket, yuv, &got_picture);
				if (ret < 0) {
					printf("Failed to encode! \n");
					break;
				}

				if (got_picture == 1) {

					Encodepacket->pts = av_rescale_q(EncodeIndex, vc->time_base, st->time_base);
					Encodepacket->dts = Encodepacket->pts;

					std::cout << "frameindex  : " << EncodeIndex << " pts:" << Encodepacket->pts << " dts: " << Encodepacket->dts << "  encodeSize:" << Encodepacket->size << "  curtime - lasttime " << Encodepacket->pts - lastpts << endl;

					lastpts = Encodepacket->pts;

					//av_free(Encodepacket->data);
					ret = av_interleaved_write_frame(ic, Encodepacket);
					//av_free(Encodepacket->data);
					EncodeIndex++;

					av_packet_unref(Encodepacket);
				}

			}
		}
		av_packet_unref(packet);
	}

	ret = avcodec_send_frame(vc, NULL);

	while (ret >= 0) {
		ret = avcodec_receive_packet(vc, Encodepacket);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
			break;
		}

		if (ret < 0) {
			break;
		}

		//Encodepacket->pts = av_rescale_q(av_gettime() - starttime, ac->time_base, st->time_base);
		//Encodepacket->dts = Encodepacket->pts;

		//Encodepacket->pts = av_rescale_q(packet->pts - startpts, m_fmt_ctx->streams[video_stream]->time_base, vc->time_base);
		//Encodepacket->dts = Encodepacket->pts;

		//ret = av_bitstream_filter_filter(h264bsfc, m_fmt_ctx->streams[video_stream]->codec, NULL, &Encodepacket->data, &Encodepacket->size, Encodepacket->data, Encodepacket->size, 0);

		//std::fwrite(Encodepacket->data, 1, Encodepacket->size, video_record);
		std::cout << "frameindex  : " << EncodeIndex << " pts:" << Encodepacket->pts - startpts << " dts: " << Encodepacket->dts - startpts << "  encodeSize:" << Encodepacket->size << endl;

		ret = av_interleaved_write_frame(ic, Encodepacket);
		//av_free(Encodepacket->data);

		EncodeIndex++;
	}
	///////////////////////////////////////////////////////////////////////////////////////////////

	av_write_trailer(ic);

	//std::fclose(video_record);

	//fclose(video_record_yuv);

	av_packet_free(&packet);

	av_packet_free(&Encodepacket);


	av_frame_free(&rgb);
	av_frame_free(&yuv);

	av_bitstream_filter_close(h264bsfc);
	h264bsfc = NULL;

	if (vsc)
	{
		sws_freeContext(vsc);
		vsc = NULL;
	}

	if (_codec_ctx)
		avcodec_close(_codec_ctx);

	_codec_ctx = NULL;
	_codec = NULL;

	if (vc)
		avcodec_free_context(&vc);

	if (m_fmt_ctx)
		avformat_close_input(&m_fmt_ctx);

	if (ic && !(ic->flags & AVFMT_NOFILE))
		avio_closep(&ic->pb);

	if (ic) {
		avformat_free_context(ic);
		ic = NULL;
	}

	m_input_fmt = NULL;


	std::cout << "退出视频录制" << endl;

	return 0;
}

实例运行效果

注:用拉流工具观看 vlc
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_34940879/article/details/107941480