opengl_video_widget.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. #include "opengl_video_widget.h"
  2. #include <QDebug>
  3. OpenGLVideoWidget::OpenGLVideoWidget(QWidget* parent)
  4. : QOpenGLWidget(parent)
  5. , m_program(nullptr)
  6. , m_textureId(0)
  7. , m_frameData(nullptr)
  8. , m_frameWidth(0)
  9. , m_frameHeight(0)
  10. , m_frameFormat(0)
  11. , m_frameUpdated(false)
  12. , m_initialized(false)
  13. {
  14. // 设置顶点坐标
  15. m_vertices[0] = -1.0f; m_vertices[1] = -1.0f;
  16. m_vertices[2] = 1.0f; m_vertices[3] = -1.0f;
  17. m_vertices[4] = -1.0f; m_vertices[5] = 1.0f;
  18. m_vertices[6] = 1.0f; m_vertices[7] = 1.0f;
  19. // 设置纹理坐标
  20. m_texCoords[0] = 0.0f; m_texCoords[1] = 1.0f;
  21. m_texCoords[2] = 1.0f; m_texCoords[3] = 1.0f;
  22. m_texCoords[4] = 0.0f; m_texCoords[5] = 0.0f;
  23. m_texCoords[6] = 1.0f; m_texCoords[7] = 0.0f;
  24. }
  25. OpenGLVideoWidget::~OpenGLVideoWidget()
  26. {
  27. Close();
  28. }
  29. bool OpenGLVideoWidget::Open(unsigned int width, unsigned int height)
  30. {
  31. QMutexLocker locker(&m_mutex);
  32. m_frameWidth = width;
  33. m_frameHeight = height;
  34. // 如果已经有数据,释放它
  35. if (m_frameData) {
  36. delete[] m_frameData;
  37. }
  38. // 分配新的内存
  39. m_frameData = new unsigned char[width * height * 4]; // RGBA格式
  40. memset(m_frameData, 0, width * height * 4);
  41. return true;
  42. }
  43. void OpenGLVideoWidget::Close()
  44. {
  45. makeCurrent();
  46. if (m_textureId) {
  47. glDeleteTextures(1, &m_textureId);
  48. m_textureId = 0;
  49. }
  50. if (m_program) {
  51. delete m_program;
  52. m_program = nullptr;
  53. }
  54. doneCurrent();
  55. // 释放帧数据
  56. QMutexLocker locker(&m_mutex);
  57. if (m_frameData) {
  58. delete[] m_frameData;
  59. m_frameData = nullptr;
  60. }
  61. m_frameWidth = 0;
  62. m_frameHeight = 0;
  63. m_frameUpdated = false;
  64. m_initialized = false;
  65. }
  66. void OpenGLVideoWidget::initializeGL()
  67. {
  68. initializeOpenGLFunctions();
  69. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  70. // 创建着色器程序
  71. m_program = new QOpenGLShaderProgram();
  72. m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
  73. "attribute vec2 vertexIn;\n"
  74. "attribute vec2 textureIn;\n"
  75. "varying vec2 textureOut;\n"
  76. "void main(void)\n"
  77. "{\n"
  78. " gl_Position = vec4(vertexIn, 0.0, 1.0);\n"
  79. " textureOut = textureIn;\n"
  80. "}\n");
  81. m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
  82. "varying vec2 textureOut;\n"
  83. "uniform sampler2D texture;\n"
  84. "void main(void)\n"
  85. "{\n"
  86. " gl_FragColor = texture2D(texture, textureOut);\n"
  87. "}\n");
  88. m_program->bindAttributeLocation("vertexIn", 0);
  89. m_program->bindAttributeLocation("textureIn", 1);
  90. m_program->link();
  91. // 创建纹理
  92. glGenTextures(1, &m_textureId);
  93. glBindTexture(GL_TEXTURE_2D, m_textureId);
  94. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  95. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  96. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  97. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  98. glBindTexture(GL_TEXTURE_2D, 0);
  99. m_initialized = true;
  100. }
  101. void OpenGLVideoWidget::resizeGL(int width, int height)
  102. {
  103. glViewport(0, 0, width, height);
  104. }
  105. void OpenGLVideoWidget::paintGL()
  106. {
  107. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  108. QMutexLocker locker(&m_mutex);
  109. if (!m_frameData || m_frameWidth <= 0 || m_frameHeight <= 0 || !m_frameUpdated)
  110. return;
  111. // 绑定纹理并更新数据
  112. glBindTexture(GL_TEXTURE_2D, m_textureId);
  113. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_frameWidth, m_frameHeight,
  114. 0, GL_RGBA, GL_UNSIGNED_BYTE, m_frameData);
  115. // 使用着色器程序
  116. m_program->bind();
  117. // 设置纹理单元
  118. m_program->setUniformValue("texture", 0);
  119. // 设置顶点和纹理坐标
  120. m_program->enableAttributeArray(0);
  121. m_program->enableAttributeArray(1);
  122. m_program->setAttributeArray(0, m_vertices, 2);
  123. m_program->setAttributeArray(1, m_texCoords, 2);
  124. // 绘制
  125. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  126. // 清理
  127. m_program->disableAttributeArray(0);
  128. m_program->disableAttributeArray(1);
  129. m_program->release();
  130. glBindTexture(GL_TEXTURE_2D, 0);
  131. }
  132. void OpenGLVideoWidget::updateFrame(const VideoFrame& frame)
  133. {
  134. if (!frame.data || frame.width <= 0 || frame.height <= 0)
  135. return;
  136. QMutexLocker locker(&m_mutex);
  137. // 如果尺寸变化,重新分配内存
  138. if (m_frameWidth != frame.width || m_frameHeight != frame.height) {
  139. if (m_frameData) {
  140. delete[] m_frameData;
  141. }
  142. m_frameWidth = frame.width;
  143. m_frameHeight = frame.height;
  144. m_frameData = new unsigned char[m_frameWidth * m_frameHeight * 4]; // RGBA格式
  145. }
  146. // 复制帧数据
  147. memcpy(m_frameData, frame.data, m_frameWidth * m_frameHeight * 4);
  148. m_frameUpdated = true;
  149. // 请求重绘
  150. update();
  151. }
  152. bool OpenGLVideoWidget::convertFromAVFrame(AVFrame* frame)
  153. {
  154. if (!frame || frame->width <= 0 || frame->height <= 0)
  155. return false;
  156. QMutexLocker locker(&m_mutex);
  157. // 如果尺寸变化,重新分配内存
  158. if (m_frameWidth != frame->width || m_frameHeight != frame->height) {
  159. if (m_frameData) {
  160. delete[] m_frameData;
  161. }
  162. m_frameWidth = frame->width;
  163. m_frameHeight = frame->height;
  164. m_frameData = new unsigned char[m_frameWidth * m_frameHeight * 4]; // RGBA格式
  165. }
  166. // 根据不同的像素格式进行转换
  167. switch (frame->format) {
  168. case AV_PIX_FMT_RGBA: {
  169. // 直接复制RGBA数据
  170. for (int y = 0; y < frame->height; y++) {
  171. memcpy(m_frameData + y * m_frameWidth * 4,
  172. frame->data[0] + y * frame->linesize[0],
  173. frame->width * 4);
  174. }
  175. } break;
  176. case AV_PIX_FMT_RGB24: {
  177. // RGB24转RGBA
  178. for (int y = 0; y < frame->height; y++) {
  179. uint8_t* src = frame->data[0] + y * frame->linesize[0];
  180. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  181. for (int x = 0; x < frame->width; x++) {
  182. *dst++ = *src++; // R
  183. *dst++ = *src++; // G
  184. *dst++ = *src++; // B
  185. *dst++ = 255; // A
  186. }
  187. }
  188. } break;
  189. case AV_PIX_FMT_BGR0:
  190. case AV_PIX_FMT_BGRA: {
  191. // BGRA转RGBA
  192. for (int y = 0; y < frame->height; y++) {
  193. uint8_t* src = frame->data[0] + y * frame->linesize[0];
  194. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  195. for (int x = 0; x < frame->width; x++) {
  196. uint8_t b = *src++;
  197. uint8_t g = *src++;
  198. uint8_t r = *src++;
  199. uint8_t a = *src++;
  200. *dst++ = r;
  201. *dst++ = g;
  202. *dst++ = b;
  203. *dst++ = a;
  204. }
  205. }
  206. } break;
  207. case AV_PIX_FMT_BGR24: {
  208. // BGR24转RGBA
  209. for (int y = 0; y < frame->height; y++) {
  210. uint8_t* src = frame->data[0] + y * frame->linesize[0];
  211. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  212. for (int x = 0; x < frame->width; x++) {
  213. uint8_t b = *src++;
  214. uint8_t g = *src++;
  215. uint8_t r = *src++;
  216. *dst++ = r; // R
  217. *dst++ = g; // G
  218. *dst++ = b; // B
  219. *dst++ = 255; // A (设为不透明)
  220. }
  221. }
  222. } break;
  223. case AV_PIX_FMT_YUV420P: // 添加对YUV420P格式的支持
  224. {
  225. // YUV420P转RGBA
  226. for (int y = 0; y < frame->height; y++) {
  227. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  228. for (int x = 0; x < frame->width; x++) {
  229. int Y = frame->data[0][y * frame->linesize[0] + x];
  230. int U = frame->data[1][(y / 2) * frame->linesize[1] + (x / 2)];
  231. int V = frame->data[2][(y / 2) * frame->linesize[2] + (x / 2)];
  232. // YUV转RGB公式
  233. int C = Y - 16;
  234. int D = U - 128;
  235. int E = V - 128;
  236. int R = (298 * C + 409 * E + 128) >> 8;
  237. int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
  238. int B = (298 * C + 516 * D + 128) >> 8;
  239. // 限制RGB值在0-255范围内
  240. R = R < 0 ? 0 : (R > 255 ? 255 : R);
  241. G = G < 0 ? 0 : (G > 255 ? 255 : G);
  242. B = B < 0 ? 0 : (B > 255 ? 255 : B);
  243. *dst++ = R; // R
  244. *dst++ = G; // G
  245. *dst++ = B; // B
  246. *dst++ = 255; // A
  247. }
  248. }
  249. } break;
  250. case AV_PIX_FMT_NV12: {
  251. // NV12转RGBA
  252. for (int y = 0; y < frame->height; y++) {
  253. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  254. for (int x = 0; x < frame->width; x++) {
  255. int Y = frame->data[0][y * frame->linesize[0] + x];
  256. int U = frame->data[1][(y / 2) * frame->linesize[1] + (x / 2) * 2];
  257. int V = frame->data[1][(y / 2) * frame->linesize[1] + (x / 2) * 2 + 1];
  258. // YUV转RGB公式
  259. int C = Y - 16;
  260. int D = U - 128;
  261. int E = V - 128;
  262. int R = (298 * C + 409 * E + 128) >> 8;
  263. int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
  264. int B = (298 * C + 516 * D + 128) >> 8;
  265. // 限制RGB值在0-255范围内
  266. R = R < 0 ? 0 : (R > 255 ? 255 : R);
  267. G = G < 0 ? 0 : (G > 255 ? 255 : G);
  268. B = B < 0 ? 0 : (B > 255 ? 255 : B);
  269. *dst++ = R; // R
  270. *dst++ = G; // G
  271. *dst++ = B; // B
  272. *dst++ = 255; // A
  273. }
  274. }
  275. } break;
  276. default:
  277. // 对于其他格式,可以考虑使用FFmpeg的sws_scale函数
  278. qDebug() << "Unsupported pixel format:" << frame->format;
  279. return false;
  280. }
  281. m_frameUpdated = true;
  282. update();
  283. return true;
  284. }
  285. bool OpenGLVideoWidget::Render(AVFrame* frame)
  286. {
  287. if (!m_initialized && isValid()) {
  288. makeCurrent();
  289. initializeGL();
  290. doneCurrent();
  291. }
  292. if (!frame) {
  293. update(); // 仅刷新显示
  294. return true;
  295. }
  296. return convertFromAVFrame(frame);
  297. }
  298. void OpenGLVideoWidget::clearFrame()
  299. {
  300. QMutexLocker locker(&m_mutex);
  301. m_frameUpdated = false;
  302. update();
  303. }