Qt 오디오 및 비디오 개발 22- 일반 GPU 디스플레이

I. 소개

실시간 영상을 그리는 데 GPU를 사용하는 것은 항상 어려운 점이었습니다. 영상 감시 개발에 종사하는 보안 업계의 사람이라면이 장애물을 통과해야합니다. 저는 상당히 틈새 시장 인 보안 산업의 전자 울타리에 종사해 왔습니다. 비디오 감시의 개발은 주변 기술과의 놀이와 토론에 불과하며 GPU 드로잉과 관련하여 많은 우회가있었습니다.

이전에 ffmpeg로 디코딩 할 때는 하드 디코딩을 위해 qsv, dxva2, d3d11va 등을 지원하는 등 하드 디코딩이 수행되었지만, 당시 디코딩 후 드로잉을 위해 QImage로 변환되어 크게 줄었습니다. GPU 사용률을 볼 수 있지만 시간이 많이 걸리는 작업은 여전히 ​​CPU에 그려지고 표시되므로 매우 당황합니다 .Qt는 대부분의 OpenGL 작업을 캡슐화하고 ffmpeg 디코딩을 지원하는 QOPenGLWidget을 직접 만듭니다. yuyv 포맷 데이터 디스플레이는 또한 매우 훌륭하고 강력한 하드 디코딩 된 nv12 포맷 데이터 디스플레이를 지원합니다. 이렇게하면 CPU에 대한 부담이 크게 줄어들고 특별히 그리기를 위해 GPU에 넘겨집니다. 이러한 철저한 변환 후 효율성이 향상됩니다. 적어도 5 번, 너무 멋지지 마세요! OpenGL 그리기를 사용하면 해당 메모리가 많이 늘어나고 데이터 교환을 위해 OpenGL 그리기가 많은 메모리를 열어야 할 수도 있습니다.

GPU 디스플레이는 구성이 좋지 않은 일부 컴퓨터, 하드 디코딩이 중지 될 가능성이 있기 때문에 동시에 yuyv 형식과 nv12 형식을 지원해야합니다. 그러면 OpenGL을 사용하여 ffmpeg로 디코딩 된 yuyv 데이터를 부드럽게 그리면 자동으로 전환됩니다. 가능한 모든 상황과 호환됩니다. 테스트 결과 ffmpeg4의 성능은 ffmpeg3의 성능보다 좋고 64 비트의 성능은 32 비트의 성능보다 우수합니다 .64 비트 운영 체제에서는 UDP 프로토콜의 성능이 TCP의 성능보다 좋지만 패킷 손실이 발생할 수 있습니다.

프로그램 CPU GPU
없음 + 없음 12 % 147MB 0 %
dxva2 + 없음 삼% 360MB 38 %
d3d11va + 없음 2 % 277MB 62 %
없음 + 페인터 30 % 147MB 0 %
dxva2 + 페인터 30 % 360MB 38 %
d3d11va + 페인터 21 % 277MB 62 %
none + yuyv 17 % 177MB 22 %
dxva2 + yuyv 25 % 400MB 38 %
d3d11va + yuyv 18 % 30MB 65 %
qsv + nv12 22 % 970MB 40 %
dxva2 + nv12 20 % 380MB 40 %
d3d11va + nv12 15 % 320MB 62 %

2. 특징

  1. 1 + 4 + 6 + 8 + 9 + 13 + 16 + 25 + 36 + 64 화면 전환을 포함하여 다중 화면 전환, 전체 화면 전환 등을 지원합니다.
  2. 지원 alt + enter full screen, esc exit full screen.
  3. 사용자 지정 정보 상자 + 오류 상자 + 문의 상자 + 오른쪽 하단의 프롬프트 상자 (여러 형식 포함).
  4. 17 가지 스킨 스타일 세트를 마음대로 변경할 수 있으며 메뉴를 포함한 모든 스타일이 통합됩니다.
  5. 짐벌 대시 보드에서 마우스를 움직여 강조 표시하면 8 개의 방향이 정확하게 식별됩니다.
  6. 하단 화면 도구 모음 (화면 분할 스위치 + 스크린 샷 사운드 및 기타 설정)이 위로 이동하고 강조 표시됩니다.
  7. 로고 + 중국어 소프트웨어 이름 + 영어 소프트웨어 이름은 구성 파일에서 변경할 수 있습니다.
  8. Baidu지도,보기 전환, 모션 트랙, 장치 위치 및 마우스 클릭을 캡슐화하여 위도 및 경도 등을 가져옵니다.
  9. 지원 사진지도, 장치 버튼을 사진지도에서 자유롭게 드래그하여 위치 정보를 자동으로 저장할 수 있습니다.
  10. Baidu지도 및 사진지도에서 비디오를 두 번 클릭하여 카메라 실시간 비디오를 미리 봅니다.
  11. 스택 창, 각 창은 별도의 qwidget이며 자신의 코드를 작성하는 데 편리합니다.
  12. 상단 마우스의 오른쪽 클릭 메뉴는 CPU + 왼쪽 상단 패널 + 왼쪽 하단 패널 + 오른쪽 상단 패널 + 오른쪽 하단 패널의 시간 표시 및 숨기기를 동적으로 제어 할 수 있으며 기본 레이아웃 복원을 지원합니다.
  13. 도구 모음은 여러 개의 작은 아이콘을 배치하고 아이콘을 닫을 수 있습니다.
  14. 왼쪽과 오른쪽을 드래그하여 늘릴 수 있으며 너비와 높이 위치가 자동으로 기억되며 다시 시작하면 복원됩니다.
  15. 자동으로 비디오를 재생하려면 카메라 노드를 두 번 클릭하고, 순서대로 비디오를 자동으로 추가하려면 노드를 두 번 클릭하면 자동으로 다음 항목으로 건너 뜁니다. 상위 노드를 두 번 클릭하면 노드 아래에 모든 비디오가 자동으로 추가됩니다.
  16. 카메라 노드를 해당 창으로 드래그하여 비디오를 재생하고 로컬 파일은 직접 재생을 지원합니다.
  17. 비디오 프레임은 드래그 및 스왑을 지원하며 즉시 응답합니다.
  18. 노드를 두 번 클릭하고 노드를 드래그하고 양식을 드래그하여 위치를 바꾸면 url.txt가 자동으로 업데이트됩니다.
  19. url.txt에서 채널 비디오 재생 로딩을 지원하고, 마지막 채널에 해당하는 비디오를 자동으로 기억하고, 소프트웨어가 시작된 후 자동으로 열고 재생합니다.
  20. 오른쪽 하단 모서리에있는 볼륨 막대 컨트롤은 포커스를 잃으면 자동으로 숨겨지며 볼륨 막대에는 음소거 아이콘이 있습니다.
  21. 통합 Baidu 온라인지도 및 오프라인지도, 장치의 해당 위치를 추가하고 자동으로지도를 생성하고 확대 / 축소를 지원하고 오버레이를 추가 할 수 있습니다.
  22. 비디오를 채널 창 밖으로 드래그하면 자동으로 비디오가 삭제됩니다.
  23. 마우스 오른쪽 버튼으로 현재 + 모든 비디오를 삭제하고 현재 + 모든 비디오를 스크린 샷 할 수 있습니다.
  24. 레코더 관리, 카메라 관리, 인쇄 정보 추가, 삭제, 수정, 가져 오기 및 내보내기가 가능하며, 새 장치 정보를 즉시 적용하여 다시 시작하지 않고도 트리 목록을 생성 할 수 있습니다.
  25. pro 파일에서 맵로드 여부를 자유롭게 설정할 수 있습니다.
  26. 비디오 재생을 위해 두 종류의 커널을 자유롭게 전환 할 수 있습니다. vlc + ffmpeg, 둘 다 pro에서 설정할 수 있습니다.
  27. 1 + 4 + 9 + 16 화면 폴링 설정 가능, 폴링 간격 및 폴링 스트림 유형 설정 가능, 메인 인터페이스 하단의 툴바 오른쪽에있는 폴링 시작 버튼을 클릭 한 다음 다시 클릭하여 폴링을 중지합니다. .
  28. 기본적으로 마우스 포인터는 10 초 이상 작동하지 않고 자동으로 숨겨집니다.
  29. onvif 검색 장비 지원, Haikang Dahuayu Shitiandi Weiye Huawei 등을 포함하되 이에 국한되지 않는 모든 onvif 카메라 지원
  30. onvif PTZ 제어를 지원하고 재설정 및 초점 조정을 포함하여 PTZ 카메라를 위, 아래, 왼쪽 및 오른쪽으로 이동할 수 있습니다.
  31. 동시에 sqlite, mysql 및 postsql과 같은 데이터베이스를 지원합니다.
  32. 비디오를 저장할 수 있으며 타이밍 저장 또는 단일 파일 저장은 선택 사항이며 저장 간격은 선택 사항입니다.
  33. 비디오 스트림 통신 방법 tcp + udp를 설정할 수 있으며 비디오 디코딩을 속도 우선, 품질 우선, 이퀄라이제이션 등으로 설정할 수 있습니다.
  34. 하드 디코딩 유형을 설정하고 qsv, dxva2, d3d11va 등을 지원할 수 있습니다.
  35. 기본적으로 OpenGL은 CPU 리소스 소비가 매우 적고 yuyv 및 nv12 형식 그리기를 지원하는 비디오를 그리는 데 사용됩니다.
  36. 고도로 사용자 정의 할 수있는 사용자는 Linux 및 Mac 시스템을 지원하여이를 기반으로 자신의 기능을 쉽게 파생 할 수 있습니다.

세, 렌더링

여기에 사진 설명 삽입

네, 관련 사이트

  1. 국내 사이트 : https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 국제 사이트 : https://github.com/feiyangqingyun/QWidgetDemo
  3. 개인 홈페이지 : https://blog.csdn.net/feiyangqingyun
  4. Zhihu 홈페이지 : https://www.zhihu.com/people/feiyangqingyun/
  5. 체험 주소 : https://blog.csdn.net/feiyangqingyun/article/details/97565652

다섯, 핵심 코드

void YUVOpenGLWidget2::initializeGL()
{
    initializeOpenGLFunctions();
    glDisable(GL_DEPTH_TEST);

    //传递顶点和纹理坐标
    static const GLfloat ver[] = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
    static const GLfloat tex[] = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f};

    //设置顶点,纹理数组并启用
    glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, ver);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, tex);
    glEnableVertexAttribArray(1);

    //初始化shader
    this->initShader();
    //初始化textures
    this->initTextures();
    //初始化颜色
    this->initColor();
}

void YUVOpenGLWidget2::paintGL()
{
    if (!dataY || !dataU || !dataV || width == 0 || height == 0) {
        this->initColor();
        return;
    }

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureY);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, linesizeY);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, dataY);
    glUniform1i(textureUniformY, 0);

    glActiveTexture(GL_TEXTURE0 + 1);
    glBindTexture(GL_TEXTURE_2D, textureU);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, linesizeU);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width >> 1, height >> 1, 0, GL_RED, GL_UNSIGNED_BYTE, dataU);
    glUniform1i(textureUniformU, 1);

    glActiveTexture(GL_TEXTURE0 + 2);
    glBindTexture(GL_TEXTURE_2D, textureV);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, linesizeV);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width >> 1, height >> 1, 0, GL_RED, GL_UNSIGNED_BYTE, dataV);
    glUniform1i(textureUniformV, 2);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

void YUVOpenGLWidget2::initColor()
{
    //取画板背景颜色
    QColor color = palette().background().color();
    //设置背景清理色
    glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
    //清理颜色背景
    glClear(GL_COLOR_BUFFER_BIT);
}

void YUVOpenGLWidget2::initShader()
{
    //加载顶点和片元脚本
    program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertShader);
    program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShader);

    //设置顶点位置
    program.bindAttributeLocation("vertexIn", 0);
    //设置纹理位置
    program.bindAttributeLocation("textureIn", 1);
    //编译shader
    program.link();
    program.bind();    

    //从shader获取地址
    textureUniformY = program.uniformLocation("textureY");
    textureUniformU = program.uniformLocation("textureU");
    textureUniformV = program.uniformLocation("textureV");
}

void YUVOpenGLWidget2::initTextures()
{
    //创建纹理
    glGenTextures(1, &textureY);
    glBindTexture(GL_TEXTURE_2D, textureY);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glGenTextures(1, &textureU);
    glBindTexture(GL_TEXTURE_2D, textureU);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glGenTextures(1, &textureV);
    glBindTexture(GL_TEXTURE_2D, textureV);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

추천

출처blog.csdn.net/feiyangqingyun/article/details/108336252