2

我构建了一个小型视频播放器,它使用 GStreamer 从电影中抓取帧(作为字符串/字节数组),然后将每个帧渲染到 OpenGL 纹理。最高可达 30 fps 1080p 电影,但当我尝试 60 fps 电影时,它跟不上,视频在音频流上滞后!当我使用“gst-launch playbin2”播放视频时,它运行良好,因此视频以足够的速度解码。

我已经做了一些测量,如果问题在于使用框架更新纹理将框架实际绘制到屏幕上,就会出现问题。我正在使用老式的 GL_BEGIN/QUADS/END 方法(因为我不知道更好)来做绘图部分,但这可能是瓶颈吗?我认为替代和更新的方法(GL_TRIANGLE_STRIP、VBOs/FBOs 或 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/FBOs/ 来提高性能顶点数组而不是使用 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)

...
4

1 回答 1

2

glTexImage2D经历一个完整的纹理对象重新初始化。您应该改用glTexSubImage2D它,它只是上传新数据,但保留现有的纹理对象。

另一个问题是,您可能会遇到交换间隔障碍 (V-Sync)。摆脱这种情况的唯一方法是SwapBuffers在显示器规定的垂直同步截止日期之前完成渲染和调用。通过避免纹理对象重新初始化来减少纹理上传延迟将帮助您。

于 2013-12-26T20:23:43.870 回答