我这里用的是海康威视网络摄像头,关于怎么调用它,可以参考博客:点击打开链接。
编码部分都是换汤不换药,我们直接给出源代码:
#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;
}
运行程序:
完整代码下载:点击打开链接
在上一篇USB摄像头实时编码的内容里,提到的错误这里也会出现。首先是对每一张图片编码时都会打开编码器,编码结束后关闭编码器,没有帧间编码,编码效率太低。正确的修改方式是在主函数里打开编码器,修改编码参数,先缓存几张摄像头采集的图像,然后再开始编码,这样可以提高编码效率。