项目中处理接入流这块 需要加入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 <chrono>
#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<milliseconds>(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<milliseconds>(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<PacketData>(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, %d\n", 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, %d\n", 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<FrameData>(frm);
        listFrms->pushData(frame);
        //Global::freeAVFrame(frm);      //下级释放
        return 1;
    }
    else {
        Global::freeAVFrame(frm);
        return 0;
    }
    return 1;
}


最后代码放 http://hamiguax.gicp.net/play/hkstream.git