opengl_video_renderer.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. #include "opengl_video_renderer.h"
  2. // #include "../base/logger.h"
  3. #include <QPainter>
  4. #include <QResizeEvent>
  5. #include <QApplication>
  6. #include <QDebug>
  7. #include <QOpenGLContext>
  8. #include <QSurfaceFormat>
  9. #include <algorithm>
  10. extern "C" {
  11. #include <libavutil/imgutils.h>
  12. #include <libswscale/swscale.h>
  13. }
  14. namespace av {
  15. namespace player {
  16. // 顶点着色器源码
  17. static const char* vertexShaderSource = R"(
  18. #version 330 core
  19. layout(location = 0) in vec2 position;
  20. layout(location = 1) in vec2 texCoord;
  21. out vec2 TexCoord;
  22. uniform mat4 projection;
  23. void main() {
  24. gl_Position = projection * vec4(position, 0.0, 1.0);
  25. TexCoord = texCoord;
  26. }
  27. )";
  28. // 片段着色器源码 (YUV420P)
  29. static const char* fragmentShaderSource = R"(
  30. #version 330 core
  31. in vec2 TexCoord;
  32. out vec4 FragColor;
  33. uniform sampler2D yTexture;
  34. uniform sampler2D uTexture;
  35. uniform sampler2D vTexture;
  36. void main() {
  37. float y = texture(yTexture, TexCoord).r;
  38. float u = texture(uTexture, TexCoord).r - 0.5;
  39. float v = texture(vTexture, TexCoord).r - 0.5;
  40. // YUV to RGB conversion
  41. float r = y + 1.402 * v;
  42. float g = y - 0.344 * u - 0.714 * v;
  43. float b = y + 1.772 * u;
  44. FragColor = vec4(r, g, b, 1.0);
  45. }
  46. )";
  47. OpenGLVideoRenderer::OpenGLVideoRenderer(QWidget* parent)
  48. : QOpenGLWidget(parent)
  49. , m_videoWidth(0)
  50. , m_videoHeight(0)
  51. , m_inputFormat(AV_PIX_FMT_NONE)
  52. , m_swsContext(nullptr)
  53. , m_yuvBuffer(nullptr)
  54. , m_yuvBufferSize(0)
  55. , m_backgroundColor(Qt::black)
  56. , m_keepAspectRatio(true)
  57. , m_renderQuality(1.0f)
  58. , m_vSyncEnabled(true)
  59. , m_initialized(false)
  60. , m_glInitialized(false)
  61. , m_hasFrame(false)
  62. , m_updateTimer(new QTimer(this))
  63. {
  64. // 设置OpenGL格式
  65. QSurfaceFormat format;
  66. format.setVersion(3, 3);
  67. format.setProfile(QSurfaceFormat::CoreProfile);
  68. format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
  69. format.setSwapInterval(m_vSyncEnabled ? 1 : 0);
  70. setFormat(format);
  71. // 设置基本属性
  72. setMinimumSize(320, 240);
  73. setFocusPolicy(Qt::StrongFocus);
  74. // 连接更新定时器
  75. connect(m_updateTimer, &QTimer::timeout, this, &OpenGLVideoRenderer::updateDisplay);
  76. m_updateTimer->setSingleShot(true);
  77. // av::Logger::instance().info("OpenGLVideoRenderer created");
  78. }
  79. OpenGLVideoRenderer::~OpenGLVideoRenderer()
  80. {
  81. cleanupOpenGLResources();
  82. if (m_swsContext) {
  83. sws_freeContext(m_swsContext);
  84. m_swsContext = nullptr;
  85. }
  86. if (m_yuvBuffer) {
  87. av_free(m_yuvBuffer);
  88. m_yuvBuffer = nullptr;
  89. }
  90. // av::Logger::instance().info("OpenGLVideoRenderer destroyed");
  91. }
  92. void OpenGLVideoRenderer::initializeGL()
  93. {
  94. // av::Logger::instance().info("Initializing OpenGL context");
  95. // 初始化OpenGL函数
  96. initializeOpenGLFunctions();
  97. // 设置清除颜色
  98. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  99. // 启用混合
  100. glEnable(GL_BLEND);
  101. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  102. // 初始化OpenGL资源
  103. if (initializeOpenGLResources()) {
  104. m_glInitialized = true;
  105. // av::Logger::instance().info("OpenGL resources initialized successfully");
  106. } else {
  107. // av::Logger::instance().error("Failed to initialize OpenGL resources");
  108. }
  109. }
  110. void OpenGLVideoRenderer::paintGL()
  111. {
  112. if (!m_glInitialized || !m_hasFrame) {
  113. // 绘制背景
  114. glClear(GL_COLOR_BUFFER_BIT);
  115. return;
  116. }
  117. QMutexLocker locker(&m_mutex);
  118. // 清除缓冲区
  119. glClear(GL_COLOR_BUFFER_BIT);
  120. // 渲染当前帧
  121. renderCurrentFrame();
  122. // 检查OpenGL错误
  123. checkGLError("paintGL");
  124. }
  125. void OpenGLVideoRenderer::resizeGL(int width, int height)
  126. {
  127. if (!m_glInitialized) {
  128. return;
  129. }
  130. // av::Logger::instance().debugf("Resizing OpenGL viewport to %dx%d", width, height);
  131. // 设置视口
  132. glViewport(0, 0, width, height);
  133. // 更新投影矩阵
  134. setupProjectionMatrix();
  135. }
  136. void OpenGLVideoRenderer::resizeEvent(QResizeEvent* event)
  137. {
  138. QOpenGLWidget::resizeEvent(event);
  139. // 触发重绘
  140. if (m_glInitialized) {
  141. update();
  142. }
  143. }
  144. bool OpenGLVideoRenderer::initialize(int width, int height, AVPixelFormat pixelFormat, double fps)
  145. {
  146. QMutexLocker locker(&m_mutex);
  147. if (m_initialized) {
  148. // av::Logger::instance().warning("OpenGL video renderer already initialized");
  149. return true;
  150. }
  151. if (width <= 0 || height <= 0) {
  152. // av::Logger::instance().error("Invalid video dimensions");
  153. return false;
  154. }
  155. if (fps <= 0) {
  156. fps = 25.0; // 默认帧率
  157. }
  158. m_videoWidth = width;
  159. m_videoHeight = height;
  160. m_inputFormat = pixelFormat;
  161. m_fps = fps;
  162. // 初始化图像转换器
  163. if (m_inputFormat != AV_PIX_FMT_YUV420P) {
  164. m_swsContext = sws_getContext(
  165. m_videoWidth, m_videoHeight, m_inputFormat,
  166. m_videoWidth, m_videoHeight, AV_PIX_FMT_YUV420P,
  167. SWS_BILINEAR, nullptr, nullptr, nullptr
  168. );
  169. if (!m_swsContext) {
  170. // av::Logger::instance().error("Failed to create SwsContext");
  171. return false;
  172. }
  173. // 分配YUV缓冲区
  174. m_yuvBufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, m_videoWidth, m_videoHeight, 1);
  175. m_yuvBuffer = static_cast<uint8_t*>(av_malloc(m_yuvBufferSize));
  176. if (!m_yuvBuffer) {
  177. // av::Logger::instance().error("Failed to allocate YUV buffer");
  178. return false;
  179. }
  180. }
  181. // 设置控件大小提示
  182. setMinimumSize(m_videoWidth / 4, m_videoHeight / 4);
  183. m_initialized = true;
  184. // av::Logger::instance().info(
  185. // QString("OpenGL video renderer initialized: %1x%2 @ %3fps").arg(width).arg(height).arg(fps).toStdString());
  186. return true;
  187. }
  188. bool OpenGLVideoRenderer::renderFrame(const AVFramePtr& frame)
  189. {
  190. qDebug() << "------->>>>>>>>>>>>>>>>>>-" << frame.get() << m_initialized.load();
  191. if (!frame || !m_initialized) {
  192. return false;
  193. }
  194. QMutexLocker locker(&m_mutex);
  195. // 更新纹理数据
  196. updateTextures(frame);
  197. // 标记有帧数据
  198. m_hasFrame = true;
  199. // 触发更新显示
  200. if (!m_updateTimer->isActive()) {
  201. int interval = static_cast<int>(1000.0 / m_fps); // 根据帧率计算间隔
  202. m_updateTimer->start(interval);
  203. }
  204. return true;
  205. }
  206. void OpenGLVideoRenderer::clear()
  207. {
  208. QMutexLocker locker(&m_mutex);
  209. m_hasFrame = false;
  210. update();
  211. }
  212. void OpenGLVideoRenderer::setKeepAspectRatio(bool keepAspectRatio)
  213. {
  214. if (m_keepAspectRatio != keepAspectRatio) {
  215. m_keepAspectRatio = keepAspectRatio;
  216. if (m_glInitialized) {
  217. setupProjectionMatrix();
  218. update();
  219. }
  220. }
  221. }
  222. bool OpenGLVideoRenderer::getKeepAspectRatio() const
  223. {
  224. return m_keepAspectRatio;
  225. }
  226. void OpenGLVideoRenderer::setBackgroundColor(const QColor& color)
  227. {
  228. if (m_backgroundColor != color) {
  229. m_backgroundColor = color;
  230. if (m_glInitialized) {
  231. glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
  232. update();
  233. }
  234. }
  235. }
  236. QSize OpenGLVideoRenderer::getVideoSize() const
  237. {
  238. return QSize(m_videoWidth, m_videoHeight);
  239. }
  240. QSize OpenGLVideoRenderer::getDisplaySize() const
  241. {
  242. return size();
  243. }
  244. bool OpenGLVideoRenderer::isInitialized() const
  245. {
  246. return m_initialized && m_glInitialized;
  247. }
  248. void OpenGLVideoRenderer::setRenderQuality(float quality)
  249. {
  250. m_renderQuality = std::clamp(quality, 0.0f, 1.0f);
  251. if (m_glInitialized) {
  252. update();
  253. }
  254. }
  255. void OpenGLVideoRenderer::setVSync(bool enable)
  256. {
  257. m_vSyncEnabled = enable;
  258. if (m_glInitialized) {
  259. QSurfaceFormat format = context()->format();
  260. format.setSwapInterval(enable ? 1 : 0);
  261. context()->setFormat(format);
  262. }
  263. }
  264. void OpenGLVideoRenderer::updateDisplay()
  265. {
  266. if (m_glInitialized) {
  267. update();
  268. }
  269. }
  270. bool OpenGLVideoRenderer::initializeOpenGLResources()
  271. {
  272. // 初始化着色器程序
  273. if (!initializeShaders()) {
  274. return false;
  275. }
  276. // 初始化顶点数据
  277. if (!initializeVertexData()) {
  278. return false;
  279. }
  280. // 创建纹理
  281. if (!createTextures()) {
  282. return false;
  283. }
  284. return true;
  285. }
  286. void OpenGLVideoRenderer::cleanupOpenGLResources()
  287. {
  288. m_shaderProgram.reset();
  289. m_vertexBuffer.reset();
  290. m_indexBuffer.reset();
  291. m_vao.reset();
  292. m_yTexture.reset();
  293. m_uTexture.reset();
  294. m_vTexture.reset();
  295. m_glInitialized = false;
  296. }
  297. bool OpenGLVideoRenderer::initializeShaders()
  298. {
  299. m_shaderProgram = std::make_unique<QOpenGLShaderProgram>();
  300. // 添加顶点着色器
  301. if (!m_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource)) {
  302. // av::Logger::instance().error("Failed to compile vertex shader: " + m_shaderProgram->log().toStdString());
  303. return false;
  304. }
  305. // 添加片段着色器
  306. if (!m_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource)) {
  307. // av::Logger::instance().error("Failed to compile fragment shader: " + m_shaderProgram->log().toStdString());
  308. return false;
  309. }
  310. // 链接着色器程序
  311. if (!m_shaderProgram->link()) {
  312. // av::Logger::instance().error("Failed to link shader program: " + m_shaderProgram->log().toStdString());
  313. return false;
  314. }
  315. // av::Logger::instance().info("Shader program initialized successfully");
  316. return true;
  317. }
  318. bool OpenGLVideoRenderer::initializeVertexData()
  319. {
  320. // 顶点数据 (位置 + 纹理坐标)
  321. static const float vertices[] = {
  322. // 位置 // 纹理坐标
  323. -1.0f, -1.0f, 0.0f, 1.0f, // 左下
  324. 1.0f, -1.0f, 1.0f, 1.0f, // 右下
  325. 1.0f, 1.0f, 1.0f, 0.0f, // 右上
  326. -1.0f, 1.0f, 0.0f, 0.0f // 左上
  327. };
  328. // 索引数据
  329. static const unsigned int indices[] = {
  330. 0, 1, 2, // 第一个三角形
  331. 2, 3, 0 // 第二个三角形
  332. };
  333. // 创建VAO
  334. m_vao = std::make_unique<QOpenGLVertexArrayObject>();
  335. m_vao->create();
  336. m_vao->bind();
  337. // 创建顶点缓冲区
  338. m_vertexBuffer = std::make_unique<QOpenGLBuffer>(QOpenGLBuffer::VertexBuffer);
  339. m_vertexBuffer->create();
  340. m_vertexBuffer->bind();
  341. m_vertexBuffer->allocate(vertices, sizeof(vertices));
  342. // 设置顶点属性
  343. m_shaderProgram->enableAttributeArray(0);
  344. m_shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 2, 4 * sizeof(float));
  345. m_shaderProgram->enableAttributeArray(1);
  346. m_shaderProgram->setAttributeBuffer(1, GL_FLOAT, 2 * sizeof(float), 2, 4 * sizeof(float));
  347. // 创建索引缓冲区
  348. m_indexBuffer = std::make_unique<QOpenGLBuffer>(QOpenGLBuffer::IndexBuffer);
  349. m_indexBuffer->create();
  350. m_indexBuffer->bind();
  351. m_indexBuffer->allocate(indices, sizeof(indices));
  352. m_vao->release();
  353. // av::Logger::instance().info("Vertex data initialized successfully");
  354. return true;
  355. }
  356. bool OpenGLVideoRenderer::createTextures()
  357. {
  358. // 创建Y纹理
  359. m_yTexture = std::make_unique<QOpenGLTexture>(QOpenGLTexture::Target2D);
  360. m_yTexture->create();
  361. m_yTexture->bind();
  362. m_yTexture->setFormat(QOpenGLTexture::R8_UNorm);
  363. m_yTexture->setSize(m_videoWidth, m_videoHeight);
  364. m_yTexture->allocateStorage();
  365. m_yTexture->setMinificationFilter(QOpenGLTexture::Linear);
  366. m_yTexture->setMagnificationFilter(QOpenGLTexture::Linear);
  367. m_yTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
  368. // 创建U纹理
  369. m_uTexture = std::make_unique<QOpenGLTexture>(QOpenGLTexture::Target2D);
  370. m_uTexture->create();
  371. m_uTexture->bind();
  372. m_uTexture->setFormat(QOpenGLTexture::R8_UNorm);
  373. m_uTexture->setSize(m_videoWidth / 2, m_videoHeight / 2);
  374. m_uTexture->allocateStorage();
  375. m_uTexture->setMinificationFilter(QOpenGLTexture::Linear);
  376. m_uTexture->setMagnificationFilter(QOpenGLTexture::Linear);
  377. m_uTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
  378. // 创建V纹理
  379. m_vTexture = std::make_unique<QOpenGLTexture>(QOpenGLTexture::Target2D);
  380. m_vTexture->create();
  381. m_vTexture->bind();
  382. m_vTexture->setFormat(QOpenGLTexture::R8_UNorm);
  383. m_vTexture->setSize(m_videoWidth / 2, m_videoHeight / 2);
  384. m_vTexture->allocateStorage();
  385. m_vTexture->setMinificationFilter(QOpenGLTexture::Linear);
  386. m_vTexture->setMagnificationFilter(QOpenGLTexture::Linear);
  387. m_vTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
  388. // av::Logger::instance().info("Textures created successfully");
  389. return true;
  390. }
  391. void OpenGLVideoRenderer::updateTextures(const AVFramePtr& frame)
  392. {
  393. if (!m_glInitialized || !frame) {
  394. return;
  395. }
  396. // 转换帧格式(如果需要)
  397. AVFrame* yuvFrame = frame.get();
  398. if (m_inputFormat != AV_PIX_FMT_YUV420P && m_swsContext) {
  399. // 创建临时帧
  400. AVFrame* tempFrame = av_frame_alloc();
  401. tempFrame->format = AV_PIX_FMT_YUV420P;
  402. tempFrame->width = m_videoWidth;
  403. tempFrame->height = m_videoHeight;
  404. av_image_fill_arrays(tempFrame->data, tempFrame->linesize, m_yuvBuffer,
  405. AV_PIX_FMT_YUV420P, m_videoWidth, m_videoHeight, 1);
  406. // 转换格式
  407. sws_scale(m_swsContext, frame->data, frame->linesize, 0, m_videoHeight,
  408. tempFrame->data, tempFrame->linesize);
  409. yuvFrame = tempFrame;
  410. // 更新纹理数据
  411. m_yTexture->bind();
  412. m_yTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[0], nullptr);
  413. m_uTexture->bind();
  414. m_uTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[1], nullptr);
  415. m_vTexture->bind();
  416. m_vTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[2], nullptr);
  417. av_frame_free(&tempFrame);
  418. } else {
  419. // 直接更新纹理数据
  420. m_yTexture->bind();
  421. m_yTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[0], nullptr);
  422. m_uTexture->bind();
  423. m_uTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[1], nullptr);
  424. m_vTexture->bind();
  425. m_vTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[2], nullptr);
  426. }
  427. // 强制更新显示
  428. update();
  429. }
  430. QRectF OpenGLVideoRenderer::calculateDisplayRect() const
  431. {
  432. QSize widgetSize = size();
  433. QSize videoSize(m_videoWidth, m_videoHeight);
  434. if (!m_keepAspectRatio) {
  435. return QRectF(-1.0f, -1.0f, 2.0f, 2.0f);
  436. }
  437. // 计算保持宽高比的显示矩形
  438. float widgetAspect = static_cast<float>(widgetSize.width()) / widgetSize.height();
  439. float videoAspect = static_cast<float>(videoSize.width()) / videoSize.height();
  440. QRectF rect;
  441. if (widgetAspect > videoAspect) {
  442. // 控件更宽,以高度为准
  443. float width = 2.0f * videoAspect / widgetAspect;
  444. rect = QRectF(-width / 2.0f, -1.0f, width, 2.0f);
  445. } else {
  446. // 控件更高,以宽度为准
  447. float height = 2.0f * widgetAspect / videoAspect;
  448. rect = QRectF(-1.0f, -height / 2.0f, 2.0f, height);
  449. }
  450. return rect;
  451. }
  452. void OpenGLVideoRenderer::setupProjectionMatrix()
  453. {
  454. if (!m_shaderProgram) {
  455. return;
  456. }
  457. QRectF displayRect = calculateDisplayRect();
  458. // 创建投影矩阵
  459. QMatrix4x4 projection;
  460. projection.setToIdentity();
  461. projection.ortho(displayRect.left(), displayRect.right(),
  462. displayRect.bottom(), displayRect.top(), -1.0f, 1.0f);
  463. m_shaderProgram->bind();
  464. m_shaderProgram->setUniformValue("projection", projection);
  465. m_shaderProgram->release();
  466. }
  467. void OpenGLVideoRenderer::renderCurrentFrame()
  468. {
  469. if (!m_shaderProgram || !m_vao) {
  470. return;
  471. }
  472. m_shaderProgram->bind();
  473. m_vao->bind();
  474. // 绑定纹理
  475. m_yTexture->bind(0);
  476. m_uTexture->bind(1);
  477. m_vTexture->bind(2);
  478. m_shaderProgram->setUniformValue("yTexture", 0);
  479. m_shaderProgram->setUniformValue("uTexture", 1);
  480. m_shaderProgram->setUniformValue("vTexture", 2);
  481. // 绘制
  482. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
  483. m_vao->release();
  484. m_shaderProgram->release();
  485. }
  486. bool OpenGLVideoRenderer::checkGLError(const QString& operation)
  487. {
  488. GLenum error = glGetError();
  489. if (error != GL_NO_ERROR) {
  490. // av::Logger::instance().error("OpenGL error in " + operation.toStdString() + ": " + std::to_string(error));
  491. return false;
  492. }
  493. return true;
  494. }
  495. } // namespace player
  496. } // namespace av