void HaiKangHelper::yv12toYuv(const quint8 *yuv420, quint8 *y, quint8 *u, quint8 *v, int width, int height) { //Y分量的长度 int yLen = width * height; //U和V分量的长度 int uvLen = width / 2 * height / 2; //海康的uv是反的需要反过来否则颜色发蓝 memcpy((quint8 *)y, yuv420, yLen); memcpy((quint8 *)v, yuv420 + yLen, uvLen); memcpy((quint8 *)u, yuv420 + yLen + uvLen, uvLen); } void HaiKangHelper::yv12toYuv(const quint8 *yv12, quint8 *yuv, int width, int height, int widthStep) { int col, row; int tmp, idx; uint y, u, v; for (row = 0; row < height; row++) { idx = row * widthStep; for (col = 0; col < width; col++) { tmp = (row / 2) * (width / 2) + (col / 2); y = (uint)yv12[row * width + col]; u = (uint)yv12[width * height + width * height / 4 + tmp]; v = (uint)yv12[width * height + tmp]; yuv[idx + col * 3] = y; yuv[idx + col * 3 + 1] = u; yuv[idx + col * 3 + 2] = v; } } } void HaiKangHelper::ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser) { //具体类型含义看头文件或者手册即可 HaiKangThread *thread = (HaiKangThread *)pUser; thread->debug("异常回调", ""); switch (dwType) { case EXCEPTION_RECONNECT: thread->debug("超时重连", ""); break; default: break; } } void HaiKangHelper::RealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser) { //每个类都对应自己的port HaiKangCallbackData *callbackData = (HaiKangCallbackData *)pUser; HaiKangThread *thread = callbackData->thread; LONG nPort = thread->port; DWORD dRet; switch (dwDataType) { case NET_DVR_SYSHEAD: //获取播放库未使用的通道号 if (!PlayM4_GetPort(&nPort)) { break; } if (dwBufSize > 0) { thread->port = nPort; if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024 * 1024)) { dRet = PlayM4_GetLastError(nPort); break; } //设置解码回调函数,只解码不显示 bool result = PlayM4_SetDecCallBackMend(nPort, DecCallBack, pUser); if (!result) { dRet = PlayM4_GetLastError(nPort); break; } //打开视频解码 if (!PlayM4_Play(nPort, NULL)) { dRet = PlayM4_GetLastError(nPort); break; } //打开音频解码,需要码流是复合流 if (!PlayM4_PlaySound(nPort)) { dRet = PlayM4_GetLastError(nPort); break; } } break; case NET_DVR_STREAMDATA: //解码数据 if (dwBufSize > 0 && nPort != -1) { BOOL inData = PlayM4_InputData(nPort, pBuffer, dwBufSize); while (!inData) { //sleep(10); qApp->processEvents(); inData = PlayM4_InputData(nPort, pBuffer, dwBufSize); } } break; } } void HaiKangHelper::FileEndCallback(LONG nPort, void *pUser) { HaiKangThread *thread = (HaiKangThread *)pUser; thread->stop2(); } //void HaiKangHelper::DecCallBack(LONG nPort, char *pBuf, LONG nSize, FRAME_INFO *pFrameInfo, LONG pUser, LONG nReserved2) #ifdef Q_OS_WIN void HaiKangHelper::DecCallBack(LONG nPort, char *pBuf, LONG nSize, FRAME_INFO *pFrameInfo, void *pUser, void *nReserved2) #else void HaiKangHelper::DecCallBack(LONG nPort, char *pBuf, LONG nSize, FRAME_INFO *pFrameInfo, void *pUser, LONG nReserved2) #endif { HaiKangCallbackData *callbackData = (HaiKangCallbackData *)pUser; HaiKangThread *thread = callbackData->thread; if (thread->getIsPause()) { return; } quint8 *data = (quint8 *)pBuf; long frameType = pFrameInfo->nType; //编码时产生的图像帧率(如果是音频数据则为采样率) long frameRate = pFrameInfo->nFrameRate; //视频数据是 T_YV12 音频数据是 T_AUDIO16 if (frameType == T_YV12) { long width = pFrameInfo->nWidth; long height = pFrameInfo->nHeight; //识别尺寸发生变化 thread->checkVideoSize(width, height); //thread->debug("回调视频", QString("宽高: %1x%2").arg(width).arg(height)); //如果处于截图标志位则立即将图片保存 if (thread->getIsSnap()) { QImage image(width, height, QImage::Format_RGB888); yv12ToRgb888(data, image.bits(), width, height); image.save(thread->getSnapName(), "jpg"); QMetaObject::invokeMethod(thread, "snapFinsh"); } //如果是绘制则转成图片否则转成yuv用opengl绘制 if (thread->getVideoMode() == VideoMode_Opengl) { yv12toYuv(data, callbackData->dataY, callbackData->dataU, callbackData->dataV, width, height); thread->setYuv(width, height, callbackData->dataY, callbackData->dataU, callbackData->dataV); } else { QImage image(width, height, QImage::Format_RGB888); yv12ToRgb888(data, image.bits(), width, height); thread->setImage(image); } } else if (frameType == T_AUDIO16) { //thread->debug("回调音频", QString("采样: %1").arg(frameRate)); } }