项目中处理接入流这块 需要加入SDK方面的一些扩展支持,这里说下海康的支持吧。
从海康的SDK例子打开播放等等了解基本流程。
此处流空 后面编辑
最后实际使用代码hknet_puller.h
#pragma once
#include "mpeg_puller.h"
#include "HCNetSDK.h"
//拉流测试一路海康
class HKNetPuller :public InputElement
{
public:
using InputElement::InputElement;
//海康数据回调
void dealData(uint8_t*& buf, int buf_size);
private:
virtual void run(void) override;
virtual bool open(void) override;
virtual void close(void) override;
virtual int read_AVPacket(AVPacket*& pkt);
private:
bool hkPlay(void);
void hkStop(void);
private:
LONG lUserID; //注册设备
LONG lRealPlayHandle; //播放句柄
private:
uint8_t* avio_ctx_buffer = nullptr;
AVIOContext* pb = nullptr;
CircularQueue streamQueue; //缓存环形队列
public:
PacketList* listPkts;
virtual bool exportOpen(void)
{
if (listPkts)
listPkts->setCodecInfo(getCodecInfo());
return true;
};
inline void setPacketListDatas(PacketList* datas)
{
listPkts = datas;
};
};
//拉流后直接解码
class HKNetDecode :public HKNetPuller
{
public:
using HKNetPuller::HKNetPuller;
virtual int read_AVFrame(AVFrame*& frm)
{
Global::freeAVFrame(frm);
return 1;
}
private:
virtual int read_AVPacket(AVPacket*& pkt) override;
FFDecode mCodec;
};
//拉流解码应用
class HKNetDecodePull :public HKNetDecode
{
public:
using HKNetDecode::HKNetDecode;
public:
virtual bool exportOpen(void)
{
if (listFrms)
listFrms->setCodecInfo(getCodecInfo());
return true;
};
virtual int read_AVFrame(AVFrame*& frm);
//下面是frame数据管理 ???
FrameList* listFrms;
inline void setFrameListDatas(FrameList* datas)
{
listFrms = datas;
};
};
实现部分
#include "stdafx.h" #include "hknet_puller.h" #include#ifdef WIN32 #pragma comment(lib,"HCCore.lib") #pragma comment(lib,"HCNetSDK.lib") #endif using namespace std::chrono; const int BUF_SIZE = 1024 * 16; void HKNetPuller::run(void) { while (threadStatus) { //1-打开连接处理 if (!open()) { close(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); continue; } resetContextTimer(); //2-取pkt数据 AVPacket* pkt = av_packet_alloc(); iRet = av_read_frame(pFormatCtx, pkt); //3-检查处理视频 处理重连机制 if (iRet < 0) { Global::freeAVPacket(pkt); if (iRet == AVERROR_EOF || !pFormatCtx->pb || avio_feof(pFormatCtx->pb) || pFormatCtx->pb->error) close(); continue; } //4-回调出去 在外面处理 if (pkt->stream_index != vindex) { Global::freeAVPacket(pkt); continue; } read_AVPacket(pkt); //5-wait iFrameIndex++; //固定频率 std::this_thread::sleep_for(std::chrono::milliseconds(5)); } close(); } bool HKNetPuller::open(void) { if (isOpened) return true; if (!streamQueue.init(0, 2 * 1024 * 1024)) { ErrorLog << " init stream queue is failed"; return false; } //海康播放 if (!hkPlay()) return false; //ffmpeg------------------------------- avio_ctx_buffer = (uint8_t*)av_malloc(sizeof(uint8_t) * BUF_SIZE); if (!avio_ctx_buffer) { ErrorLog << "av_malloc ctx buffer failed!"; return false; } //ffmpeg打开流的回调 auto onReadData = [](void* pUser, uint8_t* buf, int bufSize)->int { HKNetPuller* pHkPuller = (HKNetPuller*)pUser; if (!pHkPuller) return -1; auto startTime = steady_clock::now(); do { int readLen = pHkPuller->streamQueue.popdata(buf, bufSize); if (readLen > 0) return readLen; else if (readLen < 0) return -1; else { int passed = duration_cast (steady_clock::now() - startTime).count(); if (passed > 5000) { ErrorLog << " Read No Data for " << passed << "ms"; return -1; } } } while (true); return 0; }; pb = avio_alloc_context(avio_ctx_buffer, BUF_SIZE, 0, this, onReadData, nullptr, nullptr); if (pb == nullptr) //分配空间失败 { if (avio_ctx_buffer) av_freep(&avio_ctx_buffer); ErrorLog << "avio_alloc_context failed"; return false; } //等待数据包 auto startTime = steady_clock::now(); while (1) { if (streamQueue.getsize() > 100000) break; int passed = duration_cast (steady_clock::now() - startTime).count(); if (passed > 15000) { ErrorLog << " wait source stream failed " << passed << "ms"; return false; } } AVDictionary* opts = nullptr; av_dict_set(&opts, "fflags", "nobuffer", 0); av_dict_set(&opts, "max_analyze_duration", "10", 0); av_dict_set(&opts, "max_delay", "1000", 0); av_dict_set(&opts, "stimeout", "1000000", 0); av_dict_set(&opts, "probesize", "4096", 0); //加快打开 //下面处理ffmpeg api创建 createTimeContext(&pFormatCtx); pFormatCtx->pb = pb; if ((iRet = avformat_open_input(&pFormatCtx, "", nullptr, &opts)) < 0) { ErrorLog << "Could not open input file: " << getUrl(); if (nullptr != opts) av_dict_free(&opts); return false; } if (nullptr != opts) av_dict_free(&opts); if ((iRet = avformat_find_stream_info(pFormatCtx, nullptr)) < 0) { ErrorLog << "Failed to avformat_find_stream_info: " << getUrl(); return false; } av_dump_format(pFormatCtx, 0, nullptr, 0); //分析流信息 if (!getvideoindex()) return false; tmBegin = std::chrono::steady_clock::now(); iFrameIndex = 0; isOpened = true; return true; } void HKNetPuller::close(void) { hkStop(); if (pb) { if (pb->buffer) av_freep(&pb->buffer); avio_context_free(&pb); } streamQueue.reset(true); avformat_close_input(&pFormatCtx); avformat_free_context(pFormatCtx); streamQueue.fini(); vindex = aindex = -1; isOpened = false; } int HKNetPuller::read_AVPacket(AVPacket*& pkt) { if (listPkts) { auto packet = std::make_shared (pkt); listPkts->pushData(packet); return 1; } else { DebugLog << "listPkts is null! freeAVPacket"; Global::freeAVPacket(pkt); //释放pkt return 0; } } int dumpBuff(BYTE* buff,int iSize) { std::cout << ">> 264" << std::endl; FILE* fp = nullptr; char fileName[255]; sprintf(fileName, "packet.h264"); fp = fopen(fileName, "ab"); //二进制追加写入 if (fp == nullptr) return 0; fwrite(buff, 1, iSize, fp); fclose(fp); return 1; } bool HKNetPuller::hkPlay(void) { // 初始化 NET_DVR_Init(); // 登录 NET_DVR_DEVICEINFO_V30 struDeviceInfo; lUserID = NET_DVR_Login_V30("192.168.77.113", 8000, "admin", "admin12345", &struDeviceInfo); //lUserID = NET_DVR_Login_V30("192.168.77.190", 8000, "admin", "Admin12345", &struDeviceInfo); if (lUserID < 0) { printf("Login error, %dn", NET_DVR_GetLastError()); NET_DVR_Cleanup(); return false; } //--------------------------------------- //启动预览并设置回调数据流 NET_DVR_CLIENTINFO ClientInfo = { 0 }; ClientInfo.hPlayWnd = nullptr; //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空 ClientInfo.lChannel = 1; //预览通道号 ClientInfo.lLinkMode = 0; //最高位(31)为0表示主码流,为1表示子码流0~30位表示连接方式:0-TCP方式;1-UDP方式;2-多播方式;3-RTP方式; ClientInfo.sMultiCastIP = nullptr; //多播地址,需要多播预览时配置 //海康的回调 auto fRealDataCallBack = [](LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, void* pUser) { switch (dwDataType) { case NET_DVR_SYSHEAD: break; case NET_DVR_STREAMDATA: if (dwBufSize == 0 || pUser == 0) return; static int kk = 0; if (kk++ < 500) dumpBuff(pBuffer, dwBufSize); HKNetPuller* streamSender = (HKNetPuller*)(pUser); if (streamSender) streamSender->dealData(pBuffer, dwBufSize); //丢给环形缓存 break; } }; lRealPlayHandle = NET_DVR_RealPlay_V30(lUserID, &ClientInfo, fRealDataCallBack, this, (BOOL)true); if (lRealPlayHandle < 0) { printf("NET_DVR_RealPlay_V30 error, %dn", NET_DVR_GetLastError()); NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return false; } return true; } void HKNetPuller::hkStop(void) { //关闭预览 NET_DVR_StopRealPlay(lRealPlayHandle); //注销用户 NET_DVR_Logout_V30(lUserID); NET_DVR_Cleanup(); } void HKNetPuller::dealData(uint8_t*& buf, int buf_size) { int32_t len = buf_size; int32_t total_read = 0; uint8_t* dbuf = buf; int32_t ret = 0; int emptyCount = 0; do { len = buf_size - total_read; ret = streamQueue.pushdata(dbuf, buf_size); if (ret == 0) { emptyCount++; if (emptyCount >= 10) { if (total_read > 0) return ; return; } } else if (ret < 0) return ; else emptyCount = 0; total_read += ret; dbuf += ret; } while (total_read < buf_size); } int HKNetDecode::read_AVPacket(AVPacket*& pkt) { mCodec.setAVCodecParameter(getCodecInfo()->getAVCodecParam()); //解码模块处理解码 AVFrame* pFrame = mCodec.decode(pkt); Global::freeAVPacket(pkt); if (pFrame != nullptr) { read_AVFrame(pFrame); return 1; } return 0; } int HKNetDecodePull::read_AVFrame(AVFrame*& frm) { if (listFrms) { auto frame = std::make_shared (frm); listFrms->pushData(frame); //Global::freeAVFrame(frm); //下级释放 return 1; } else { Global::freeAVFrame(frm); return 0; } return 1; }
最后代码放 http://hamiguax.gicp.net/play/hkstream.git



