X264编码3 实时编码(网络摄像头)

    继续我们的X264编码,第一篇我们完成了对本地文件的编码 点击打开链接,第二篇我们可以实时对USB摄像头进行编码 点击打开链接,在这里,我们介绍第三个:实时对网络摄像头进行编码。

    我这里用的是海康威视网络摄像头,关于怎么调用它,可以参考博客:点击打开链接。


    编码部分都是换汤不换药,我们直接给出源代码:

#include 
#include 
#include 
#include "Windows.h"
#include "HCNetSDK.h"
#include "plaympeg4.h"
#include 
#include "cv.h"
#include "highgui.h"
#include  
#include "stdafx.h"
#include "stdint.h" 
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include 
#include 

using namespace cv;
using namespace std;

extern "C"
{
#include "x264.h"
#include "x264_config.h"
};

LONG nPort = -1; 
char filename[100];
volatile int gbHandling = 3;
int ct = 0;
int w = 640;
int h = 480;

int yuv_bufLen = w * h * 3 / 2;
unsigned char* pYuvBuf = new unsigned char[yuv_bufLen];
FILE* pFileOut = fopen("result.yuv", "w+");
Mat dst;

int MyYuvtoH264(int width, int height, string filename)
{
	int fps = 15;
	size_t yuv_size = width * height * 3 / 2;
	x264_t *encoder;
	x264_picture_t pic_in, pic_out;
	uint8_t *yuv_buffer;

	x264_param_t param;
	x264_param_default_preset(¶m, "veryfast", "zerolatency");
	param.i_threads = 1;
	param.i_width = width;
	param.i_height = height;
	param.i_fps_num = fps;
	param.i_fps_den = 1;
	param.i_keyint_max = 25;
	param.b_intra_refresh = 1;
	param.b_annexb = 1;

	x264_param_apply_profile(¶m, "baseline");
	encoder = x264_encoder_open(¶m);
	x264_picture_alloc(&pic_in, X264_CSP_I420, width, height);
	yuv_buffer = (uint8_t*)malloc(yuv_size);

	pic_in.img.plane[0] = yuv_buffer;
	pic_in.img.plane[1] = pic_in.img.plane[0] + width * height;
	pic_in.img.plane[2] = pic_in.img.plane[1] + width * height / 4;

	int64_t i_pts = 0;
	x264_nal_t *nals;
	int nnal;

	FILE *inf = fopen(filename.c_str(), "rb");
	FILE *outf = fopen("test.h264", "wb");
	if (NULL == inf)
	{
		return -1;
	}

	while (fread(yuv_buffer, 1, yuv_size, inf) > 0)
	{
		pic_in.i_pts = i_pts++;
		x264_encoder_encode(encoder, &nals, &nnal, &pic_in, &pic_out);
		x264_nal_t *nal;
		for (nal = nals; nal < nals + nnal; nal++)
		{
			fwrite(nal->p_payload, 1, nal->i_payload, outf);
		}
	}
	x264_encoder_close(encoder);

	fclose(inf);
	fclose(outf);
	free(yuv_buffer);
	return 0;
}

//解码回调 视频为YUV数据(YV12),音频为PCM数据
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)
{
	if (gbHandling)
	{
		gbHandling--;
		return;
	}
	long lFrameType = pFrameInfo->nType;
	if (lFrameType == T_YV12)
	{
		Mat pImg(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);
		Mat src(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, pBuf);
		cvtColor(src, pImg, CV_YUV2BGR_YV12);

		int resize_height = 640;
		int resize_width = 480;
		
		resize(pImg, dst, cv::Size(resize_height, resize_width), 0, 0, INTER_LINEAR);
		ct++;
		cout << "ct为:" << ct << endl;

		cv::Mat yuvImg;
		cv::cvtColor(dst, yuvImg, CV_BGR2YUV_I420);
		memcpy(pYuvBuf, yuvImg.data, yuv_bufLen*sizeof(unsigned char));
		fwrite(pYuvBuf, yuv_bufLen*sizeof(unsigned char), 1, pFileOut);
		MyYuvtoH264(w, h, "result.yuv");

		imshow("IP-Camera", dst);
		waitKey(1);
		pImg.~Mat(); //释放内存
		src.~Mat();
		dst.~Mat();
	}
	gbHandling = 3;
}

//实时流回调
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
	int j, z;
	j = z = 1;
	switch (dwDataType)
	{
	case NET_DVR_SYSHEAD: //系统头
		if (!PlayM4_GetPort(&nPort)) //获取播放库未使用的通道号
		{
			break;
		}
		if (dwBufSize > 0)
		{
			if (!PlayM4_SetStreamOpenMode(nPort, STREAME_REALTIME)) //设置实时流播放模式
			{
				break;
			}
			if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 10 * 1024 * 1024)) //打开流接口
			{
				break;
			}
			if (!PlayM4_Play(nPort, NULL)) //播放开始
			{
				break;
			}
			if (!PlayM4_SetDecCallBack(nPort, DecCBFun))
			{
				break;
			}
		}
		break;
	case NET_DVR_STREAMDATA: //码流数据
		if (dwBufSize > 0 && nPort != -1)
		{
			if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))
			{
				cout << "error" << PlayM4_GetLastError(nPort) << endl;
				break;
			}
		}
		break;
	default: //其他数据
		if (dwBufSize > 0 && nPort != -1)
		{
			if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))
			{
				break;
			}
		}
		break;
	}
}

void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
	char tempbuf[256] = { 0 };
	switch (dwType)
	{
	case EXCEPTION_RECONNECT: //预览时重连
		printf("----------reconnect--------%d\n", time(NULL));
		break;
	default:
		break;
	}
}

void capture()
{
	printf("helleo\n");

	NET_DVR_Init(); //初始化
	NET_DVR_SetConnectTime(2000, 1); //设置网络连接超时时间与连接尝试次数  
	NET_DVR_SetReconnect(10000, true);  //设置重连功能

	// 注册设备
	LONG lUserID;
	NET_DVR_DEVICEINFO_V30 struDeviceInfo;
	lUserID = NET_DVR_Login_V30("10.170.6.185", 8000, "admin", "abc20170620", &struDeviceInfo);
	if (lUserID < 0)
	{
		NET_DVR_Cleanup(); //释放SDK资源
		return;
	}

	//设置异常消息回调函数
	NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);

	//启动预览并设置回调数据流
	LONG lRealPlayHandle;
	HWND  h = (HWND)cvGetWindowHandle("IP-Camera"); 
	if (h == 0)
	{
		cout << "窗口创建失败" << endl;
	}
	NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
	struPlayInfo.hPlayWnd = h; //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空
	struPlayInfo.lChannel = 1; //预览通道号
	struPlayInfo.dwStreamType = 0; //0-主码流,1-子码流,2-码流3,3-码流4,以此类推
	struPlayInfo.dwLinkMode = 0; //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
	lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &struPlayInfo, fRealDataCallBack, NULL); //播放函数
	printf("lRealPlayHandle:%d", lRealPlayHandle);
	if (lRealPlayHandle < 0)
	{
		printf("NET_DVR_RealPlay_V40 error\n");
		printf("%d\n", NET_DVR_GetLastError());
		NET_DVR_Logout(lUserID);
		NET_DVR_Cleanup();
		return;
	}
	Sleep(-1);

	//关闭预览
	NET_DVR_StopRealPlay(lRealPlayHandle);
	//注销用户
	NET_DVR_Logout(lUserID);
	//释放SDK资源
	NET_DVR_Cleanup();
	return;
}

int main(int argc, char* argv[])
{
	if (!pFileOut)
	{
		printf("pFileOut open error \n");
		system("pause");
		exit(-1);
	}
	printf("pFileOut open ok \n");

	while (1)
	{
		capture();
		waitKey(0);
		fclose(pFileOut);
		delete[] pYuvBuf;
	}

	Sleep(100);
	return 0;
}

    运行程序:

    X264编码3 实时编码(网络摄像头)_第1张图片

    X264编码3 实时编码(网络摄像头)_第2张图片

    完整代码下载:点击打开链接


    在上一篇USB摄像头实时编码的内容里,提到的错误这里也会出现。首先是对每一张图片编码时都会打开编码器,编码结束后关闭编码器,没有帧间编码,编码效率太低。正确的修改方式是在主函数里打开编码器,修改编码参数,先缓存几张摄像头采集的图像,然后再开始编码,这样可以提高编码效率。




你可能感兴趣的:(X264)