oHanTanYanYing
_寒潭雁影
采纳率66.7%
2020-01-06 11:32

MFC框架下自定义类中定义多线程问题

80
已采纳

本人尝试在MFC框架下自定义一个类,该类负责接收和解码视频数据,因此需要在该类中定义多线程来做这件事情,以防止在对话框调用该类的时候卡死。
目前本人在自定义类中使用了static的方式开启了多线程,在win10下运行也很正常。然而在测试兼容性的时候发现该程序在win7 64位下会出错,出错的原因似乎是因为我在类内使用了static函数定义了多线程,而我又声明了该类的几个实体,因此static函数出现了调用实体出错的问题。
想问问看:

1.有没有老哥遇到过这种问题?

2.有没有比较好的解决思路?

3.是不是不应该在一个类中用static函数的形式定义线程?

4.像多路接收解码的功能除了封装成一个类然后使用多个实体来做外,还有没有更好的封装方式?

class HWdecode
{
public:
    bool changertspurl = false;//是否改变rtsp的地址标志
    bool reconnect = false;//需要断线重连标志
    bool haveconnect = false;//正常连接标志位
    CCriticalSection* mlock;//线程临界区
    bool dodecodesave = false;//像素转换开启标志位
    //参数对象
    FFmpegDemuxer* demuxer = NULL;//FFMPEG对象
    CUcontext cuContext;//Cuda设备
    NvDecoder* dec = NULL;//Cuda解码对象
    CUdeviceptr dpFrame = 0;//数据存储对象初始化
    int hwinit()//显卡设备初始化
    {
        int iGpu = 0;//选择解码播放的GPU,只有一个的话直接设置为0就行
        //检测硬件GPU设备
        ck(cuInit(0));
        int nGpu = 0;
        ck(cuDeviceGetCount(&nGpu));
        if (iGpu < 0 || iGpu >= nGpu)
        {
            std::cout << "没有相应的GPU" << std::endl;
            return 0;
        }
        //获得CUDA对象
        CUdevice cuDevice = 0;
        ck(cuDeviceGet(&cuDevice, iGpu));
        char szDeviceName[80];
        ck(cuDeviceGetName(szDeviceName, sizeof(szDeviceName), cuDevice));
        std::cout << "GPU in use: " << szDeviceName << std::endl;
        ck(cuCtxCreate(&cuContext, CU_CTX_SCHED_BLOCKING_SYNC, cuDevice));
        return 1;
    }
    int ffmpeginit(char* url)//ffmpeg对象初始化
    {
        demuxer = new FFmpegDemuxer(url);
        if (demuxer->fmtc == NULL)
        {
            return 0;
        }
        else
        {
            //RGBA帧存储显存初始化

            return 1;
        }
    }
    void cudadecoderinit()//cuda解码对象初始化
    {
        ck(cuMemAlloc(&dpFrame, demuxer->GetWidth() * demuxer->GetHeight() * 4));
        //解码器初始化
        dec = new NvDecoder(cuContext, (*demuxer).GetWidth(), (*demuxer).GetHeight(), true, FFmpeg2NvCodecId((*demuxer).GetVideoCodec()));
    }
    DWORD decAndshow()//解码和播放
    {
        int nVideoBytes = 0, nFrameReturned = 0, nFrame = 0;
        uint8_t* pVideo = NULL, ** ppFrame;
        long bt = clock();

        do
        {
            uint8_t* getdpframe = (uint8_t*)dpFrame;
            demuxer->Demux(&pVideo, &nVideoBytes);//获取一帧视频数据
            dec->Decode(pVideo, nVideoBytes, &ppFrame, &nFrameReturned);//解码这一帧数据
            //if (!nFrame && nFrameReturned)
            //  LOG(INFO) << HWD.dec->GetVideoInfo();
            if (dodecodesave)
            {
                for (int i = 0; i < nFrameReturned; i++)
                {
                    mlock->Lock();
                    //对解码出来的图像数据类型做转换,转到BGRA
                    printf("GetWidth %d\n", dec->GetWidth());
                    printf("GetHeight %d\n", dec->GetHeight());
                    if (dec->GetBitDepth() == 8)
                    {
                        if (dec->GetOutputFormat() == cudaVideoSurfaceFormat_YUV444)
                            YUV444ToColor32<RGBA32>((uint8_t*)ppFrame[i], dec->GetWidth(), getdpframe, 4 * dec->GetWidth(), dec->GetWidth(), dec->GetHeight());
                        else    // default assumed as NV12
                            Nv12ToColor32<RGBA32>((uint8_t*)ppFrame[i], dec->GetWidth(), getdpframe, 4 * dec->GetWidth(), dec->GetWidth(), dec->GetHeight());
                    }
                    mlock->Unlock();
                }
            }
            nFrame += nFrameReturned;
        } while (nVideoBytes && changertspurl == false);
        changertspurl = false;//将rtsp地址修改标志位重新设置为否
        printf("退出,尝试断线连回\n");
        reconnect = true;
        delete demuxer;
        std::cout << "Total frame decoded: " << nFrame << std::endl;
        std::cout << "花费时间:" << clock() - bt << "ms" << std::endl;
        std::cout << "FPS:" << nFrame / ((clock() - bt) / 1000.0) << std::endl;
        return 1;
    }
    HANDLE pRecvAndShowThread;//接收显示线程对象
    static DWORD ThreadRtspRecvShowFun(LPVOID lpParam)
    {
        return ((HWdecode*)lpParam)->decAndshow();//调用线程的处理函数
    }
    int Star(char* url, CCriticalSection * lock)
    {
        mlock = lock;
        if (!hwinit())//初始化硬件设备
        {
            std::cout << "硬件初始化失败!" << std::endl;
            return 0;
        }
        if (!ffmpeginit(url))//初始化硬件设备
        {
            std::cout << "无法连接码流!" << std::endl;
            return 0;
        }
        cudadecoderinit();
        pRecvAndShowThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HWdecode::ThreadRtspRecvShowFun, this, 0, NULL);
        return 1;
    }
    int ReStar(char* url, CCriticalSection* lock)//断线重连
    {

        if (!ffmpeginit(url))//初始化硬件设备
        {
            std::cout << "无法连接码流!" << std::endl;
            return 0;
        }
        if (!haveconnect)
        {
            cudadecoderinit();
        }
        pRecvAndShowThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HWdecode::ThreadRtspRecvShowFun, this, 0, NULL);
        return 1;
    }
};
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

3条回答

  • phenix2009 白色一大坨 1年前

    我看了你的描述,你应该好好看看这几个类的数据是怎么做的,为什么会串,你要是可以贴一下你类的代码,更好

    点赞 评论 复制链接分享
  • caozhy 回答这么多问题就耍赖把我的积分一笔勾销了 1年前

    按照你的描述,只有一个工作线程和一个主线程,是这样么?
    首先检查是不是会出现多个工作线程并发,如果那样的话,应该做同步。或者说你编码的代码是线程不安全的,需要修改。
    其次检查你的UI线程和工作线程的通讯代码,工作线程不能直接操作UI,而是应该同步让主线程访问。
    static函数的形式定义线程,这个应该没问题。

    点赞 3 评论 复制链接分享
  • lty2017 lty2017 1年前

    错误信息帖出来,系统日志里,报个什么错截图,单猜测可能不准。还有不可以在win7电脑上直接源码调试吗

    点赞 评论 复制链接分享