Hoi allemaal,
Ben bezig met video speler in SDL, en hiervoor maak ik nu gebruik van een fragment shader om de vertaling van ruwe video (YUV) naar RGB te doen.
Dit werkt allemaal mooi en aardig, maar zoals ik het nu werkend heb, zal de shader elke renderloop zijn werk doen, en helaas onnodig vaak hetzelfde video frame converteren.
Daar komt de FrameBuffer om de hoek.
De FrameBuffer wil ik gebruiken om de conversie éénmalig te doen, en het resultaat in een nieuwe texture op te slaan, zodat ik deze later zoals elke willekeurige andere texture kan renderen.
Als hier andere/betere alternatieven voor zijn, hoor ik dat natuurlijk graag
(Let wel op dat ik naast de video ook andere textures wil kunnen blijven renderen)
Alvast sorry voor de ENORME lap code...
Na twee dagen proberen, debuggen, specs lezen en voorbeelden doorspitten voor iets simpels als een framebuffertje verwacht ik dat ik toch nog iets simpels over het hoofd zie...
Voor de volledigheid zal laat ik eerst eens laten zien hoe ik het spul initialiseer:
Hier de functie waarmee ik de Y, U en V data aan de fragment shader voer, die er vervolgens zijn kunstje mee doet:
En vervolgens is dit mijn (versimpelde) renderloop:
Bovenstaande code geeft 't gewenste resultaat.
Vervolgens ben ik met de FrameBuffer aan de slag gegaan...
Ik heb voor de oudere EXT functies gekozen omdat ik heb gelezen dat deze ook op OpenGL ES 2.0 zullen werken, en de nieuwe functies zonder EXT onderdeel zijn van OpenGL 3.1. But correct me if i'm wrong!
Een globale FrameBuffer aangemaakt met:
Gebruik van de FrameBuffer:
Nieuwe renderloop:
Dit geeft mij het volgende resultaat:

Voor mijn ongetraind oog lijkt het wel alsof ik onbedoelt een feedback loop heb gemaakt, waar ze in menig voorbeeld al voor waarschuwen. Maar voor zover ik weet unbind ik alles netjes optijd...
Elke tip of opmerking is welkom!
(Note: mijn voorbeeld plaatje hierboven is 480x320, maar in de code staat voor video 300x225. Dit heb ik gedaan om duidelijk aan te geven dat het renderwindow en de video verschillende afmetingen hebben)
Ben bezig met video speler in SDL, en hiervoor maak ik nu gebruik van een fragment shader om de vertaling van ruwe video (YUV) naar RGB te doen.
Dit werkt allemaal mooi en aardig, maar zoals ik het nu werkend heb, zal de shader elke renderloop zijn werk doen, en helaas onnodig vaak hetzelfde video frame converteren.
Daar komt de FrameBuffer om de hoek.
De FrameBuffer wil ik gebruiken om de conversie éénmalig te doen, en het resultaat in een nieuwe texture op te slaan, zodat ik deze later zoals elke willekeurige andere texture kan renderen.
Als hier andere/betere alternatieven voor zijn, hoor ik dat natuurlijk graag
(Let wel op dat ik naast de video ook andere textures wil kunnen blijven renderen)
Alvast sorry voor de ENORME lap code...
Na twee dagen proberen, debuggen, specs lezen en voorbeelden doorspitten voor iets simpels als een framebuffertje verwacht ik dat ik toch nog iets simpels over het hoofd zie...
Voor de volledigheid zal laat ik eerst eens laten zien hoe ik het spul initialiseer:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // Initialize SDL SDL_Init( SDL_INIT_VIDEO ); // Set the default attributes SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, SCREEN_BPP ); SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); pSurface = SDL_SetVideoMode( 480, 320, SCREEN_BPP, SDL_OPENGL ); // OpenGL settings glEnable( GL_TEXTURE_2D ); glEnable( GL_LINES ); glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); glViewport( 0, 0, 480, 320 ); glClear( GL_COLOR_BUFFER_BIT ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0.0, 480, 320, 0.0, 0.0, 1.0 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); |
Hier de functie waarmee ik de Y, U en V data aan de fragment shader voer, die er vervolgens zijn kunstje mee doet:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| void RenderConverted() { // Get Textures GLuint textures[3] = { NULL }; glGenTextures( 3, textures ); if (!textures[0] || !textures[1] || !textures[2]) { return; } // Not all textures created! // Use the program. glUseProgram(pProgramHandle); int i = 0; const int videoWidth = 300; const int videoHeight = 225; // Set fragment shader variable GLint sampler1Uniform = glGetUniformLocation(pProgramHandle,"height"); glUniform1f(sampler1Uniform, GLfloat(videoHeight)); // Select texture unit 1 as the active unit and bind the U texture. glActiveTexture(GL_TEXTURE1); i=glGetUniformLocation(pProgramHandle,"Utex"); glUniform1i(i,1); // Bind Utex to texture unit 1 glBindTexture(GL_TEXTURE_RECTANGLE_NV,textures[0]); glTexParameteri(GL_TEXTURE_RECTANGLE_NV,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_NV,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); glTexImage2D(GL_TEXTURE_RECTANGLE_NV,0,GL_LUMINANCE,GetStride( Plane_U ),videoHeight /2,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,GetPlane( Plane_U )); // Select texture unit 2 as the active unit and bind the V texture. glActiveTexture(GL_TEXTURE2); i=glGetUniformLocation(pProgramHandle,"Vtex"); glBindTexture(GL_TEXTURE_RECTANGLE_NV,textures[1]); glUniform1i(i,2); // Bind Vtext to texture unit 2 glTexParameteri(GL_TEXTURE_RECTANGLE_NV,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_NV,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); glTexImage2D(GL_TEXTURE_RECTANGLE_NV,0,GL_LUMINANCE,GetStride( Plane_V ),videoHeight /2,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,GetPlane( Plane_V )); // Select texture unit 0 as the active unit and bind the Y texture. glActiveTexture(GL_TEXTURE0); i=glGetUniformLocation(pProgramHandle,"Ytex"); glUniform1i(i,0); // Bind Ytex to texture unit 0 glBindTexture(GL_TEXTURE_RECTANGLE_NV,textures[2]); glTexParameteri(GL_TEXTURE_RECTANGLE_NV,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_NV,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); glTexImage2D(GL_TEXTURE_RECTANGLE_NV,0,GL_LUMINANCE,GetStride( Plane_Y ),videoHeight ,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,GetPlane( Plane_Y )); // Note that this image is horizontally flipped glBegin(GL_QUADS); glTexCoord2i( 0, 0 ); glVertex2i( 0, videoHeight ); glTexCoord2i( videoWidth, 0 ); glVertex2i( videoWidth, videoHeight ); glTexCoord2i( videoWidth, videoHeight ); glVertex2i( videoWidth, 0 ); glTexCoord2i( 0, videoHeight ); glVertex2i( 0, 0 ); glEnd(); // Disable the program. glUseProgram(0); // Clean up if (textures[0]) { glDeleteTextures( 1, &textures[0] ); textures[0] = NULL; } if (textures[1]) { glDeleteTextures( 1, &textures[1] ); textures[1] = NULL; } if (textures[2]) { glDeleteTextures( 1, &textures[2] ); textures[2] = NULL; } } |
En vervolgens is dit mijn (versimpelde) renderloop:
C++:
1
2
3
4
5
6
7
8
| // Begin glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); glClear( GL_COLOR_BUFFER_BIT ); RenderConverted(); // Flip the surfaces SDL_GL_SwapBuffers(); |
Bovenstaande code geeft 't gewenste resultaat.
Vervolgens ben ik met de FrameBuffer aan de slag gegaan...
Ik heb voor de oudere EXT functies gekozen omdat ik heb gelezen dat deze ook op OpenGL ES 2.0 zullen werken, en de nieuwe functies zonder EXT onderdeel zijn van OpenGL 3.1. But correct me if i'm wrong!
Een globale FrameBuffer aangemaakt met:
C++:
1
2
3
| GLuint frameBufferObject = 0; glGenFramebuffersEXT(1, &frameBufferObject); if (!frameBufferObject) { return; } |
Gebruik van de FrameBuffer:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| void CreateVideoTexture() { // Prepare texture for the FrameBuffer to render on glGenTextures( 1, &texture ); if (!texture) { return false; } // No texture created const int videoWidth = 300; const int videoHeight = 225; GLenum textureFormat = (SDL_BYTEORDER == SDL_BIG_ENDIAN) ? GL_BGRA : GL_RGBA; glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, videoWidth, videoHeight, 0, textureFormat, GL_UNSIGNED_BYTE, NULL); // Reserve memory glBindTexture(GL_TEXTURE_2D, 0); // Prepare depth buffer glGenTextures(1, &depth); glBindTexture(GL_TEXTURE_2D, depth); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, videoWidth, videoHeight, 0, GL_RED, GL_BYTE, NULL); // Reserve memory glBindTexture(GL_TEXTURE_2D, 0); // Make sure no texture is bound when starting to use the Frame Buffer // Bind the framebuffer object glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameBufferObject); // Attach a texture to the Frame Buffer Object glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth, 0); // Check if the Frame Buffer Object is OK GLenum framebufferStatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (framebufferStatus != GL_FRAMEBUFFER_COMPLETE_EXT) { return; } // Render the fragment shader stuff RenderConverted(); // Unbind the Frame Buffer Object glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } |
Nieuwe renderloop:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // Begin glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); glClear( GL_COLOR_BUFFER_BIT ); if (!texture) { CreateVideoTexture(); } const int videoWidth = 300; const int videoHeight = 225; // Render the texture! glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_QUADS); glTexCoord2i( 0, 0 ); glVertex2i( 0, 0 ); glTexCoord2i( videoWidth, 0 ); glVertex2i( videoWidth, 0 ); glTexCoord2i( videoWidth, videoHeight ); glVertex2i( videoWidth, videoHeight ); glTexCoord2i( 0, videoHeight ); glVertex2i( 0, videoHeight ); glEnd(); // Flip the surfaces SDL_GL_SwapBuffers(); |
Dit geeft mij het volgende resultaat:

Voor mijn ongetraind oog lijkt het wel alsof ik onbedoelt een feedback loop heb gemaakt, waar ze in menig voorbeeld al voor waarschuwen. Maar voor zover ik weet unbind ik alles netjes optijd...
Elke tip of opmerking is welkom!
(Note: mijn voorbeeld plaatje hierboven is 480x320, maar in de code staat voor video 300x225. Dit heb ik gedaan om duidelijk aan te geven dat het renderwindow en de video verschillende afmetingen hebben)