我已经构建了一个小型视频播放器,它通过使用GStreamer从电影中抓取帧(作为字符串/字节数组),然后将每个帧渲染为OpenGL纹理。这可以达到30 fps 1080p电影,但是当我尝试60 fps电影时,它无法跟上,视频在音频流上落后了!当我使用“gst-launch playbin2”播放视频时,它的效果非常好,因此视频会以足够的速度进行解码。
我已经完成了一些测量,如果问题在于 用新帧更新纹理或框架的实际绘图到屏幕。我正在使用老式的GL_BEGIN / QUADS / END方法(因为我不知道更好)来做绘图部分,但这可能是瓶颈吗?我认为替代和更新的方法(GL_TRIANGLE_STRIP,VBOs / FBO或glDrawArrays / Elements)只有在处理大量纹理/多边形时才有用,而不是我想要做的事情,或者我错了吗?
有没有人知道如何在这种特定情况下提高渲染速度?
<小时/> 更新:感谢这里给出的一些好建议(使用glTexSubImage2D而不是glTexImage2D;使用显示列表),我的代码现在如下所示。虽然有一些性能提升,但电影运行速度有点太慢,无法达到60 fps(只需要一个保证金;只需要更多的优化)。
现在渲染几帧的时间测量输出如下:
Texture updated in 16.2160497479 ms
Frame drawn in 0.540967225085 ms
Texture updated in 14.7260598703 ms
Frame drawn in 0.606612686107 ms
Texture updated in 17.0613363633 ms
Frame drawn in 0.743171453788 ms
Texture updated in 12.6152746452 ms
Frame drawn in 2.45603172378 ms
Texture updated in 13.3847853272 ms
Frame drawn in 3.0869575436 ms
Texture updated in 17.7117126901 ms
Frame drawn in 0.572979517806 ms
Texture updated in 13.8203956395 ms
Frame drawn in 1.15892604026 ms
Texture updated in 16.0600404733 ms
Frame drawn in 0.563659483216 ms
Texture updated in 13.0213039782 ms
Frame drawn in 3.70653723435 ms
尽管纹理更新似乎花费了最多的时间,即使使用glTexSubImage2D(这似乎也是合理的,因为这涉及将数据从系统内存传输到gpu),我想我会尝试使用VBO来提高性能/ FBO /顶点数组,而不是使用glBegin / End
以立即模式绘制...
def __textureSetup(self):
# Setup texture in OpenGL to render video to
glEnable(GL_TEXTURE_2D)
glMatrixMode(GL_MODELVIEW)
self.textureNo = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.textureNo)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
# Fill texture with black to begin with.
img = np.zeros([self.vidsize[0],self.vidsize[1],3],dtype=np.uint8)
img.fill(0)
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, self.vidsize[0], self.vidsize[1], 0, GL_RGB, GL_UNSIGNED_BYTE, img)
# Create display list which draws to the quad to which the texture is rendered
(x,y) = self.vidPos
(w,h) = self.destsize
self.frameQuad = glGenLists(1);
glNewList(self.frameQuad, GL_COMPILE)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0); glVertex3i(x, y, 0)
glTexCoord2f(1.0, 0.0); glVertex3i(x+w, y, 0)
glTexCoord2f(1.0, 1.0); glVertex3i(x+w, y+h, 0)
glTexCoord2f(0.0, 1.0); glVertex3i(x, y+h, 0)
glEnd()
glEndList()
# Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
def __texUpdate(self, appsink):
""" Callback for GStreamer """
# Retrieve buffer from videosink
self.buffer = appsink.emit('pull-buffer')
self.texUpdated = True
def drawFrame(self):
glCallList(self.frameQuad)
# Flip the buffer to show frame to screen
pygame.display.flip()
def play(self):
# Start gst loop (which listens for events from the player)
thread.start_new_thread(self.gst_loop.run, ())
# Signal player to start video playback
self.player.set_state(gst.STATE_PLAYING)
self.paused = False
# While video is playing, render frames
while self.gst_loop.is_running():
# Only draw a frame when a new texture has been received
if self.texUpdated:
t1 = time.clock()
# Update texture
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, self.vidsize[0], self.vidsize[1], GL_RGB, GL_UNSIGNED_BYTE, self.buffer.data)
t2 = time.clock()
print "Texture updated in {0} ms".format((t2-t1)*1000)
self.drawFrame()
print "Frame drawn in {0} ms".format((time.clock()-t2)*1000)
for e in pygame.event.get():
if e.type == pygame.QUIT:
self.stop()
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_ESCAPE:
self.stop()
if e.key == pygame.K_SPACE:
self.pause()
pygame.event.pump() # Prevent freezing of screen while dragging window
def stop(self):
self.gst_loop.quit()
self.player.set_state(gst.STATE_NULL)
...
答案 0 :(得分:2)
glTexImage2D
通过完整纹理对象重新初始化。您应该使用glTexSubImage2D
代替,它只是上传新数据但保留现有纹理对象。
另一个问题是,您可能会遇到交换间隔屏障(V-Sync)。摆脱这种情况的唯一方法是在显示器强加的V-Sync截止日期之前渲染并调用SwapBuffers
。通过避免纹理对象重新初始化来减少纹理上传延迟将帮助您。