前言 使用qml製作播放器兩種架構的比較 前輩們的肩膀 載入Shader語言 Sharder簡要說明 Sharder的載入 qml渲染流程 opengl的渲染 opengl 對yuv資料的要求
前言
使用qml製作視頻播放器。在視訊框架解碼出來後,最後免不了還要交給qml還顯示。 使用qml製作播放器,兩種架構的比較
qml提供圖片傳入介面,解碼完成後,將QImage 交給qml顯示。
這種直接拋棄了。
原因是:
1,圖片傳輸太慢
2,整個動作是交個cpu來執行的,在效能弱的機子上,重新整理圖片很慢
自己實現繼承自QQuickItem的類,資料在Item內部流轉,然後像使用普通qml控制項的方式來調用相應的功能。
這是推薦使用的方式。QtAV就是這麼弄的。但是它封裝的太好了,很多時候,我們可能只需要其中一點點的功能模組,拆解起來很難。因此不得不又造了一遍輪子。 前輩們的肩膀
qt5_qml_Opengl_shader,這篇部落格已經完成的差不多了。但還是寫了一遍,有些細節可能與自己理解的不一樣。有的細節要特別注意處理一下。 載入Shader語言 Sharder簡要說明
Sharder的說明,網上汗牛充棟。
我們用到的功能:簡短點說說就是,告訴opengl你要傳入的三維物體形狀和渲染所需的顏色。 Sharder的載入
我沒有選擇將sharder內容直接包含在代碼中做法,而是單獨放到了一個資源檔中。
沒有選擇將sharder內容直接包含在代碼中做法,而是單獨放到了一個資源檔中。m_program = new QOpenGLShaderProgram();bool ok0 = m_program->addShaderFromSourceFile(QOpenGLShader::Vertex,":/os/openglResource/qmlopenglvideovertexshader.vsh");bool ok1 = false;if(QOpenGLContext::currentContext()->isOpenGLES()){//這裡opengl與opengles載入不同的Shader,不用一樣的是因為,兩者還是有那麼一點點的區別的(關鍵詞precision在opengl中沒有) ok1 = m_program->addShaderFromSourceFile(QOpenGLShader::Fragment,":/os/openglResource/qmlopenglvideofragmentshader.fsh");}else{ ok1 = m_program->addShaderFromSourceFile(QOpenGLShader::Fragment,":/os/openglResource/qmlopenglvideofragmentshader.frag");}
其中opengl es 載入了不同的fragmentshader,因為opengl和opengl es還是有那麼一點不同的,這個要取決於裝置
主要是這裡的區別:
//這裡的宏判定必須加,不然絕大部分的android tv不會顯示#ifdef GL_FRAGMENT_PRECISION_HIGHprecision highp float;#elseprecision mediump float;#endif
qml渲染流程
opengl作為組件被qml使用,存在著一個誰先渲染的問題。同一地區,後渲染的會覆蓋之前渲染的。
這個渲染的次序決定了層疊的次序
//先渲染qml,再渲染openglconnect(window(), &QQuickWindow::afterRendering, m_renderer, &ALOpenglQmlVideoShowRenderer::paint, Qt::DirectConnection);//先渲染opengl,再渲染qmlconnect(window(), &QQuickWindow::afterRendering, m_renderer, &ALOpenglQmlVideoShowRenderer::paint, Qt::DirectConnection);
opengl的渲染
其實這裡沒有什麼好說的,感覺整個流程是死的,沒有太大的發揮空間。
在整個demo中,我盡量使用Qt的opengl類,實在沒有辦法了才用opengl原生的。
但我總覺得Qt這裡做的不是很好。
m_program->bind();int vertsLocation = m_program->attributeLocation("vertexIn");int textureLocation = m_program->attributeLocation("textureIn");m_program->enableAttributeArray(vertsLocation);m_program->enableAttributeArray(textureLocation);m_program->setAttributeArray(vertsLocation, GL_FLOAT, vertexVertices,2);m_program->setAttributeArray(textureLocation, GL_FLOAT, textureVertices,2);textureUniformY = m_program->uniformLocation("tex_y");textureUniformU = m_program->uniformLocation("tex_u");textureUniformV = m_program->uniformLocation("tex_v");glViewport(m_nX, m_viewportSize.height() - m_nY - m_nHeight , m_nWidth, m_nHeight);QByteArray t_byteArray;//{QMutexLocker locker(&m_imageDataMutex);t_byteArray= m_imageData;}m_pBufYuv420p = (unsigned char*)t_byteArray.data();glActiveTexture(GL_TEXTURE0);//使用來自y資料產生紋理glBindTexture(GL_TEXTURE_2D, id_y);//使用記憶體中m_pBufYuv420p資料建立真正的y資料紋理glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW, m_nVideoH, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pBufYuv420p);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);//載入u資料紋理glActiveTexture(GL_TEXTURE1);//啟用紋理單元GL_TEXTURE1glBindTexture(GL_TEXTURE_2D, id_u);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p + m_nVideoH*m_nVideoW);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);//載入v資料紋理glActiveTexture(GL_TEXTURE2);//啟用紋理單元GL_TEXTURE2glBindTexture(GL_TEXTURE_2D, id_v);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,(char*)m_pBufYuv420p + m_nVideoH*m_nVideoW * 5 / 4);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glUniform1i(textureUniformY, 0);//指定u紋理要使用新值glUniform1i(textureUniformU, 1);//指定v紋理要使用新值glUniform1i(textureUniformV, 2);// glClearColor(0.0, 0.5, 0.0, 1.0);// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Draw the triangle !glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Starting from vertex 0; 3 vertices total -> 1 trianglem_program->disableAttributeArray(textureLocation);m_program->disableAttributeArray(vertsLocation);m_program->release();// Not strictly needed for this example, but generally useful for when// mixing with raw OpenGL.m_window->resetOpenGLState();
其中需要注意的是
// glClearColor(0.0, 0.5, 0.0, 1.0); // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
作用是清空背景,如果開啟的話。先前繪製的內容都會被清空。 opengl 對yuv資料的要求
除了格式要求,還有就是傳入的資料中沒有沒用的資料。
也就是記憶體對齊為1。
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,(char*)m_pBufYuv420p + m_nVideoH*m_nVideoW * 5 / 4);
這裡原始碼
learn_openglunderqml_5.zip