opengl_video_widget.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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. , m_keepAspectRatio(true)
  14. , m_gray(false)
  15. , m_threshold(false)
  16. , m_thresholdValue(0.5f)
  17. , m_blur(false)
  18. , m_blurRadius(1.0f)
  19. , m_reverse(false)
  20. , m_colorReduce(false)
  21. , m_colorReduceLevel(0)
  22. , m_gamma(false)
  23. , m_gammaValue(1.0f)
  24. , m_contrastBright(false)
  25. , m_contrast(1.0f)
  26. , m_brightness(0.0f)
  27. , m_mirror(false)
  28. {
  29. // 设置顶点坐标
  30. m_vertices[0] = -1.0f; m_vertices[1] = -1.0f;
  31. m_vertices[2] = 1.0f; m_vertices[3] = -1.0f;
  32. m_vertices[4] = -1.0f; m_vertices[5] = 1.0f;
  33. m_vertices[6] = 1.0f; m_vertices[7] = 1.0f;
  34. // 设置纹理坐标
  35. m_texCoords[0] = 0.0f; m_texCoords[1] = 1.0f;
  36. m_texCoords[2] = 1.0f; m_texCoords[3] = 1.0f;
  37. m_texCoords[4] = 0.0f; m_texCoords[5] = 0.0f;
  38. m_texCoords[6] = 1.0f; m_texCoords[7] = 0.0f;
  39. }
  40. OpenGLVideoWidget::~OpenGLVideoWidget()
  41. {
  42. Close();
  43. }
  44. bool OpenGLVideoWidget::Open(unsigned int width, unsigned int height)
  45. {
  46. QMutexLocker locker(&m_mutex);
  47. m_frameWidth = width;
  48. m_frameHeight = height;
  49. // 如果已经有数据,释放它
  50. if (m_frameData) {
  51. delete[] m_frameData;
  52. }
  53. // 分配新的内存
  54. m_frameData = new unsigned char[width * height * 4]; // RGBA格式
  55. memset(m_frameData, 0, width * height * 4);
  56. return true;
  57. }
  58. void OpenGLVideoWidget::Close()
  59. {
  60. makeCurrent();
  61. if (m_textureId) {
  62. glDeleteTextures(1, &m_textureId);
  63. m_textureId = 0;
  64. }
  65. if (m_program) {
  66. delete m_program;
  67. m_program = nullptr;
  68. }
  69. doneCurrent();
  70. // 释放帧数据
  71. QMutexLocker locker(&m_mutex);
  72. if (m_frameData) {
  73. delete[] m_frameData;
  74. m_frameData = nullptr;
  75. }
  76. m_frameWidth = 0;
  77. m_frameHeight = 0;
  78. m_frameUpdated = false;
  79. m_initialized = false;
  80. }
  81. void OpenGLVideoWidget::initializeGL()
  82. {
  83. initializeOpenGLFunctions();
  84. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  85. // 创建统一shader,支持多特效
  86. if (m_program) { delete m_program; m_program = nullptr; }
  87. m_program = new QOpenGLShaderProgram();
  88. m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
  89. "attribute vec2 vertexIn;\n"
  90. "attribute vec2 textureIn;\n"
  91. "varying vec2 textureOut;\n"
  92. "void main(void)\n"
  93. "{\n"
  94. " gl_Position = vec4(vertexIn, 0.0, 1.0);\n"
  95. " textureOut = textureIn;\n"
  96. "}\n");
  97. m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
  98. R"Raw(
  99. varying vec2 textureOut;
  100. uniform sampler2D texture;
  101. uniform bool uGray;
  102. uniform bool uThreshold;
  103. uniform float uThresholdValue;
  104. uniform bool uBlur;
  105. uniform float uBlurRadius;
  106. uniform bool uReverse;
  107. uniform bool uColorReduce;
  108. uniform int uColorReduceLevel;
  109. uniform bool uGamma;
  110. uniform float uGammaValue;
  111. uniform bool uContrastBright;
  112. uniform float uContrast;
  113. uniform float uBrightness;
  114. uniform bool uMirror;
  115. void main(void)
  116. {
  117. vec2 uv = textureOut;
  118. if (uMirror) {
  119. uv.x = 1.0 - uv.x;
  120. }
  121. vec4 color = texture2D(texture, uv);
  122. // 灰度
  123. if (uGray) {
  124. float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
  125. color = vec4(gray, gray, gray, color.a);
  126. }
  127. // 二值化
  128. if (uThreshold) {
  129. float v = dot(color.rgb, vec3(0.299, 0.587, 0.114));
  130. float th = v > uThresholdValue ? 1.0 : 0.0;
  131. color = vec4(th, th, th, color.a);
  132. }
  133. // 简单3x3均值模糊
  134. if (uBlur) {
  135. vec2 tex_offset = vec2(1.0) / vec2(textureSize2D(texture, 0));
  136. vec4 sum = vec4(0.0);
  137. for (int dx = -1; dx <= 1; ++dx)
  138. for (int dy = -1; dy <= 1; ++dy)
  139. sum += texture2D(texture, uv + vec2(dx, dy) * tex_offset * uBlurRadius);
  140. color = sum / 9.0;
  141. }
  142. // 反色
  143. if (uReverse) {
  144. color.rgb = vec3(1.0) - color.rgb;
  145. }
  146. // 色彩减少
  147. if (uColorReduce) {
  148. color.rgb = floor(color.rgb * float(uColorReduceLevel)) / float(uColorReduceLevel);
  149. }
  150. // 伽马
  151. if (uGamma) {
  152. color.rgb = pow(color.rgb, vec3(1.0 / uGammaValue));
  153. }
  154. // 对比度/亮度
  155. if (uContrastBright) {
  156. color.rgb = color.rgb * uContrast + uBrightness;
  157. }
  158. gl_FragColor = color;
  159. }
  160. )Raw");
  161. m_program->bindAttributeLocation("vertexIn", 0);
  162. m_program->bindAttributeLocation("textureIn", 1);
  163. m_program->link();
  164. // 创建纹理
  165. glGenTextures(1, &m_textureId);
  166. glBindTexture(GL_TEXTURE_2D, m_textureId);
  167. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  168. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  169. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  170. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  171. glBindTexture(GL_TEXTURE_2D, 0);
  172. m_initialized = true;
  173. }
  174. void OpenGLVideoWidget::resizeGL(int width, int height)
  175. {
  176. glViewport(0, 0, width, height);
  177. }
  178. void OpenGLVideoWidget::paintGL()
  179. {
  180. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  181. QMutexLocker locker(&m_mutex);
  182. if (!m_frameData || m_frameWidth <= 0 || m_frameHeight <= 0 || !m_frameUpdated)
  183. return;
  184. // 绑定纹理并更新数据
  185. glBindTexture(GL_TEXTURE_2D, m_textureId);
  186. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_frameWidth, m_frameHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_frameData);
  187. m_program->bind();
  188. m_program->setUniformValue("texture", 0);
  189. m_program->setUniformValue("uGray", m_gray);
  190. m_program->setUniformValue("uThreshold", m_threshold);
  191. m_program->setUniformValue("uThresholdValue", m_thresholdValue);
  192. m_program->setUniformValue("uBlur", m_blur);
  193. m_program->setUniformValue("uBlurRadius", m_blurRadius);
  194. m_program->setUniformValue("uReverse", m_reverse);
  195. m_program->setUniformValue("uColorReduce", m_colorReduce);
  196. m_program->setUniformValue("uColorReduceLevel", m_colorReduceLevel);
  197. m_program->setUniformValue("uGamma", m_gamma);
  198. m_program->setUniformValue("uGammaValue", m_gammaValue);
  199. m_program->setUniformValue("uContrastBright", m_contrastBright);
  200. m_program->setUniformValue("uContrast", m_contrast);
  201. m_program->setUniformValue("uBrightness", m_brightness);
  202. m_program->setUniformValue("uMirror", m_mirror);
  203. m_program->enableAttributeArray(0);
  204. m_program->enableAttributeArray(1);
  205. m_program->setAttributeArray(0, m_vertices, 2);
  206. m_program->setAttributeArray(1, m_texCoords, 2);
  207. // 保持比例
  208. if (m_keepAspectRatio) {
  209. QSize widgetSize = size();
  210. double widgetRatio = double(widgetSize.width()) / widgetSize.height();
  211. double videoRatio = double(m_frameWidth) / m_frameHeight;
  212. int x = 0, y = 0, w = widgetSize.width(), h = widgetSize.height();
  213. if (widgetRatio > videoRatio) {
  214. w = int(h * videoRatio);
  215. x = (widgetSize.width() - w) / 2;
  216. } else {
  217. h = int(w / videoRatio);
  218. y = (widgetSize.height() - h) / 2;
  219. }
  220. glViewport(x, y, w, h);
  221. } else {
  222. glViewport(0, 0, width(), height());
  223. }
  224. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  225. m_program->disableAttributeArray(0);
  226. m_program->disableAttributeArray(1);
  227. m_program->release();
  228. glBindTexture(GL_TEXTURE_2D, 0);
  229. }
  230. void OpenGLVideoWidget::updateFrame(const VideoFrame& frame)
  231. {
  232. if (!frame.data || frame.width <= 0 || frame.height <= 0)
  233. return;
  234. QMutexLocker locker(&m_mutex);
  235. // 如果尺寸变化,重新分配内存
  236. if (m_frameWidth != frame.width || m_frameHeight != frame.height) {
  237. if (m_frameData) {
  238. delete[] m_frameData;
  239. }
  240. m_frameWidth = frame.width;
  241. m_frameHeight = frame.height;
  242. m_frameData = new unsigned char[m_frameWidth * m_frameHeight * 4]; // RGBA格式
  243. }
  244. // 复制帧数据
  245. memcpy(m_frameData, frame.data, m_frameWidth * m_frameHeight * 4);
  246. m_frameUpdated = true;
  247. // 请求重绘
  248. update();
  249. }
  250. bool OpenGLVideoWidget::convertFromAVFrame(AVFrame* frame)
  251. {
  252. if (!frame || frame->width <= 0 || frame->height <= 0)
  253. return false;
  254. QMutexLocker locker(&m_mutex);
  255. // 如果尺寸变化,重新分配内存
  256. if (m_frameWidth != frame->width || m_frameHeight != frame->height) {
  257. if (m_frameData) {
  258. delete[] m_frameData;
  259. }
  260. m_frameWidth = frame->width;
  261. m_frameHeight = frame->height;
  262. m_frameData = new unsigned char[m_frameWidth * m_frameHeight * 4]; // RGBA格式
  263. }
  264. // 根据不同的像素格式进行转换
  265. switch (frame->format) {
  266. case AV_PIX_FMT_RGBA: {
  267. // 直接复制RGBA数据
  268. for (int y = 0; y < frame->height; y++) {
  269. memcpy(m_frameData + y * m_frameWidth * 4,
  270. frame->data[0] + y * frame->linesize[0],
  271. frame->width * 4);
  272. }
  273. } break;
  274. case AV_PIX_FMT_RGB24: {
  275. // RGB24转RGBA
  276. for (int y = 0; y < frame->height; y++) {
  277. uint8_t* src = frame->data[0] + y * frame->linesize[0];
  278. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  279. for (int x = 0; x < frame->width; x++) {
  280. *dst++ = *src++; // R
  281. *dst++ = *src++; // G
  282. *dst++ = *src++; // B
  283. *dst++ = 255; // A
  284. }
  285. }
  286. } break;
  287. case AV_PIX_FMT_BGR0:
  288. case AV_PIX_FMT_BGRA: {
  289. // BGRA转RGBA
  290. for (int y = 0; y < frame->height; y++) {
  291. uint8_t* src = frame->data[0] + y * frame->linesize[0];
  292. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  293. for (int x = 0; x < frame->width; x++) {
  294. uint8_t b = *src++;
  295. uint8_t g = *src++;
  296. uint8_t r = *src++;
  297. uint8_t a = *src++;
  298. *dst++ = r;
  299. *dst++ = g;
  300. *dst++ = b;
  301. *dst++ = a;
  302. }
  303. }
  304. } break;
  305. case AV_PIX_FMT_BGR24: {
  306. // BGR24转RGBA
  307. for (int y = 0; y < frame->height; y++) {
  308. uint8_t* src = frame->data[0] + y * frame->linesize[0];
  309. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  310. for (int x = 0; x < frame->width; x++) {
  311. uint8_t b = *src++;
  312. uint8_t g = *src++;
  313. uint8_t r = *src++;
  314. *dst++ = r; // R
  315. *dst++ = g; // G
  316. *dst++ = b; // B
  317. *dst++ = 255; // A (设为不透明)
  318. }
  319. }
  320. } break;
  321. case AV_PIX_FMT_YUV420P: // 添加对YUV420P格式的支持
  322. {
  323. // YUV420P转RGBA
  324. for (int y = 0; y < frame->height; y++) {
  325. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  326. for (int x = 0; x < frame->width; x++) {
  327. int Y = frame->data[0][y * frame->linesize[0] + x];
  328. int U = frame->data[1][(y / 2) * frame->linesize[1] + (x / 2)];
  329. int V = frame->data[2][(y / 2) * frame->linesize[2] + (x / 2)];
  330. // YUV转RGB公式
  331. int C = Y - 16;
  332. int D = U - 128;
  333. int E = V - 128;
  334. int R = (298 * C + 409 * E + 128) >> 8;
  335. int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
  336. int B = (298 * C + 516 * D + 128) >> 8;
  337. // 限制RGB值在0-255范围内
  338. R = R < 0 ? 0 : (R > 255 ? 255 : R);
  339. G = G < 0 ? 0 : (G > 255 ? 255 : G);
  340. B = B < 0 ? 0 : (B > 255 ? 255 : B);
  341. *dst++ = R; // R
  342. *dst++ = G; // G
  343. *dst++ = B; // B
  344. *dst++ = 255; // A
  345. }
  346. }
  347. } break;
  348. case AV_PIX_FMT_NV12: {
  349. // NV12转RGBA
  350. for (int y = 0; y < frame->height; y++) {
  351. uint8_t* dst = m_frameData + y * m_frameWidth * 4;
  352. for (int x = 0; x < frame->width; x++) {
  353. int Y = frame->data[0][y * frame->linesize[0] + x];
  354. int U = frame->data[1][(y / 2) * frame->linesize[1] + (x / 2) * 2];
  355. int V = frame->data[1][(y / 2) * frame->linesize[1] + (x / 2) * 2 + 1];
  356. // YUV转RGB公式
  357. int C = Y - 16;
  358. int D = U - 128;
  359. int E = V - 128;
  360. int R = (298 * C + 409 * E + 128) >> 8;
  361. int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
  362. int B = (298 * C + 516 * D + 128) >> 8;
  363. // 限制RGB值在0-255范围内
  364. R = R < 0 ? 0 : (R > 255 ? 255 : R);
  365. G = G < 0 ? 0 : (G > 255 ? 255 : G);
  366. B = B < 0 ? 0 : (B > 255 ? 255 : B);
  367. *dst++ = R; // R
  368. *dst++ = G; // G
  369. *dst++ = B; // B
  370. *dst++ = 255; // A
  371. }
  372. }
  373. } break;
  374. default:
  375. // 对于其他格式,可以考虑使用FFmpeg的sws_scale函数
  376. qDebug() << "Unsupported pixel format:" << frame->format;
  377. return false;
  378. }
  379. m_frameUpdated = true;
  380. update();
  381. return true;
  382. }
  383. bool OpenGLVideoWidget::Render(AVFrame* frame)
  384. {
  385. if (!m_initialized && isValid()) {
  386. makeCurrent();
  387. initializeGL();
  388. doneCurrent();
  389. }
  390. if (!frame) {
  391. update(); // 仅刷新显示
  392. return true;
  393. }
  394. return convertFromAVFrame(frame);
  395. }
  396. void OpenGLVideoWidget::clearFrame()
  397. {
  398. QMutexLocker locker(&m_mutex);
  399. m_frameUpdated = false;
  400. update();
  401. }
  402. void OpenGLVideoWidget::setGray(bool on) {
  403. if (m_gray != on) { m_gray = on; update(); }
  404. }
  405. void OpenGLVideoWidget::setThreshold(bool on, float value) {
  406. if (m_threshold != on || m_thresholdValue != value) { m_threshold = on; m_thresholdValue = value; update(); }
  407. }
  408. void OpenGLVideoWidget::setBlur(bool on, float radius) {
  409. if (m_blur != on || m_blurRadius != radius) { m_blur = on; m_blurRadius = radius; update(); }
  410. }
  411. void OpenGLVideoWidget::setReverse(bool on) {
  412. if (m_reverse != on) { m_reverse = on; update(); }
  413. }
  414. void OpenGLVideoWidget::setColorReduce(bool on, int level) {
  415. if (m_colorReduce != on || m_colorReduceLevel != level) { m_colorReduce = on; m_colorReduceLevel = level; update(); }
  416. }
  417. void OpenGLVideoWidget::setGamma(bool on, float gamma) {
  418. if (m_gamma != on || m_gammaValue != gamma) { m_gamma = on; m_gammaValue = gamma; update(); }
  419. }
  420. void OpenGLVideoWidget::setContrastBright(bool on, float contrast, float brightness) {
  421. if (m_contrastBright != on || m_contrast != contrast || m_brightness != brightness) {
  422. m_contrastBright = on; m_contrast = contrast; m_brightness = brightness; update();
  423. }
  424. }
  425. void OpenGLVideoWidget::setMirror(bool on) {
  426. if (m_mirror != on) { m_mirror = on; update(); }
  427. }