Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
References:
File last commit:
Show/Diff file:
Action:
FNA/lib/Theorafile/sdl2test/sdl2test.c
396 lines | 9.5 KiB | text/x-c | CLexer
396 lines | 9.5 KiB | text/x-c | CLexer
r0 | /* Theorafile - Ogg Theora Video Decoder Library | |||
* | ||||
* Copyright (c) 2017-2020 Ethan Lee. | ||||
* Based on TheoraPlay, Copyright (c) 2011-2016 Ryan C. Gordon. | ||||
* | ||||
* This software is provided 'as-is', without any express or implied warranty. | ||||
* In no event will the authors be held liable for any damages arising from | ||||
* the use of this software. | ||||
* | ||||
* Permission is granted to anyone to use this software for any purpose, | ||||
* including commercial applications, and to alter it and redistribute it | ||||
* freely, subject to the following restrictions: | ||||
* | ||||
* 1. The origin of this software must not be misrepresented; you must not | ||||
* claim that you wrote the original software. If you use this software in a | ||||
* product, an acknowledgment in the product documentation would be | ||||
* appreciated but is not required. | ||||
* | ||||
* 2. Altered source versions must be plainly marked as such, and must not be | ||||
* misrepresented as being the original software. | ||||
* | ||||
* 3. This notice may not be removed or altered from any source distribution. | ||||
* | ||||
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com> | ||||
* | ||||
*/ | ||||
#include <SDL.h> | ||||
#include <SDL_main.h> | ||||
#include <SDL_opengl.h> | ||||
#include "theorafile.h" | ||||
/* GL Function typedefs */ | ||||
#define GL_PROC(ret, func, parms) \ | ||||
typedef ret (GLAPIENTRY *glfntype_##func) parms; | ||||
#include "glfuncs.h" | ||||
#undef GL_PROC | ||||
/* GL Function declarations */ | ||||
#define GL_PROC(ret, func, parms) \ | ||||
glfntype_##func INTERNAL_##func; | ||||
#include "glfuncs.h" | ||||
#undef GL_PROC | ||||
static const GLchar *GLVert = | ||||
"#version 110\n" | ||||
"attribute vec2 pos;\n" | ||||
"attribute vec2 tex;\n" | ||||
"void main() {\n" | ||||
"gl_Position = vec4(pos.xy, 0.0, 1.0);\n" | ||||
"gl_TexCoord[0].xy = tex;\n" | ||||
"}\n"; | ||||
/* This shader was originally from SDL 1.3! | ||||
* It has been modified to use color conversion for HD formats: | ||||
* http://www.equasys.de/colorconversion.html | ||||
* http://www.equasys.de/colorformat.html | ||||
*/ | ||||
static const GLchar *GLFrag = | ||||
"#version 110\n" | ||||
"uniform sampler2D samp0;\n" | ||||
"uniform sampler2D samp1;\n" | ||||
"uniform sampler2D samp2;\n" | ||||
"const vec3 offset = vec3(-0.0625, -0.5, -0.5);\n" | ||||
"const vec3 Rcoeff = vec3(1.164, 0.000, 1.793);\n" | ||||
"const vec3 Gcoeff = vec3(1.164, -0.213, -0.533);\n" | ||||
"const vec3 Bcoeff = vec3(1.164, 2.112, 0.000);\n" | ||||
"void main() {\n" | ||||
" vec2 tcoord;\n" | ||||
" vec3 yuv, rgb;\n" | ||||
" tcoord = gl_TexCoord[0].xy;\n" | ||||
" yuv.x = texture2D(samp0, tcoord).r;\n" | ||||
" yuv.y = texture2D(samp1, tcoord).r;\n" | ||||
" yuv.z = texture2D(samp2, tcoord).r;\n" | ||||
" yuv += offset;\n" | ||||
" rgb.r = dot(yuv, Rcoeff);\n" | ||||
" rgb.g = dot(yuv, Gcoeff);\n" | ||||
" rgb.b = dot(yuv, Bcoeff);\n" | ||||
" gl_FragColor = vec4(rgb, 1.0);\n" | ||||
"}\n"; | ||||
void AudioCallback(void *userdata, Uint8* stream, int len) | ||||
{ | ||||
const int samples = len / 4; | ||||
int read = tf_readaudio((OggTheora_File*) userdata, (float*) stream, samples); | ||||
if (read < samples) | ||||
{ | ||||
SDL_memset(stream + read * 4, '\0', (samples - read) * 4); | ||||
} | ||||
} | ||||
int main(int argc, char **argv) | ||||
{ | ||||
/* SDL variables */ | ||||
SDL_Window *window; | ||||
SDL_GLContext context; | ||||
SDL_AudioDeviceID audio; | ||||
SDL_AudioSpec spec; | ||||
SDL_Event evt; | ||||
Uint64 timeStart, timeCurrent; | ||||
Uint8 run = 1; | ||||
/* Theorafile variables */ | ||||
OggTheora_File fileIn; | ||||
int width, height, uvWidth, uvHeight; | ||||
int channels, samplerate; | ||||
double fps; | ||||
th_pixel_fmt fmt; | ||||
int curframe = 0, thisframe, newframe; | ||||
char *frame = NULL; | ||||
/* OpenGL variables */ | ||||
GLuint yuvTextures[3]; | ||||
GLuint vertex = 0; | ||||
GLuint fragment = 0; | ||||
GLuint program = 0; | ||||
GLint shaderlen = 0; | ||||
/* Vertex client arrays */ | ||||
static struct { float pos[2]; float tex[2]; } verts[4] = { | ||||
{ { -1.0f, 1.0f }, { 0.0f, 0.0f } }, | ||||
{ { 1.0f, 1.0f }, { 1.0f, 0.0f } }, | ||||
{ { -1.0f, -1.0f }, { 0.0f, 1.0f } }, | ||||
{ { 1.0f, -1.0f }, { 1.0f, 1.0f } } | ||||
}; | ||||
/* We need a file name! */ | ||||
if (argc < 2 || argc > 2) | ||||
{ | ||||
SDL_Log("Need a file name!\n"); | ||||
return 1; | ||||
} | ||||
/* Open the Theora file */ | ||||
if (tf_fopen(argv[1], &fileIn) < 0) | ||||
{ | ||||
SDL_Log("Failed to open file.\n"); | ||||
return 1; | ||||
} | ||||
/* This is a video test, people! */ | ||||
if (!tf_hasvideo(&fileIn)) | ||||
{ | ||||
SDL_Log("No video!\n"); | ||||
return 1; | ||||
} | ||||
/* Get the video metadata, allocate first frame */ | ||||
tf_videoinfo(&fileIn, &width, &height, &fps, &fmt); | ||||
frame = (char*) SDL_malloc(width * height * 2); | ||||
if (fmt == TH_PF_420) | ||||
{ | ||||
/* Subsampled in both dimensions */ | ||||
uvWidth = width / 2; | ||||
uvHeight = height / 2; | ||||
} | ||||
else if (fmt == TH_PF_422) | ||||
{ | ||||
/* Subsampled only horizontally */ | ||||
uvWidth = width / 2; | ||||
uvHeight = height; | ||||
} | ||||
else | ||||
{ | ||||
/* No subsampling at all... */ | ||||
uvWidth = width; | ||||
uvHeight = height; | ||||
} | ||||
while (!tf_readvideo(&fileIn, frame, 1)); | ||||
/* Create window (and audio device, if applicable) */ | ||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); | ||||
window = SDL_CreateWindow( | ||||
"Theorafile Test", | ||||
SDL_WINDOWPOS_CENTERED, | ||||
SDL_WINDOWPOS_CENTERED, | ||||
width, | ||||
height, | ||||
SDL_WINDOW_OPENGL | ||||
); | ||||
context = SDL_GL_CreateContext(window); | ||||
SDL_GL_SetSwapInterval(1); | ||||
/* GL function loading */ | ||||
#define GL_PROC(ret, func, parms) \ | ||||
INTERNAL_##func = (glfntype_##func) SDL_GL_GetProcAddress(#func); | ||||
#include "glfuncs.h" | ||||
#undef GL_PROC | ||||
/* Remap GL function names to internal entry points */ | ||||
#include "glmacros.h" | ||||
if (tf_hasaudio(&fileIn)) | ||||
{ | ||||
/* Get the audio metadata, allocate queue */ | ||||
tf_audioinfo(&fileIn, &channels, &samplerate); | ||||
SDL_zero(spec); | ||||
spec.freq = samplerate; | ||||
spec.format = AUDIO_F32; | ||||
spec.channels = channels; | ||||
spec.samples = 4096; | ||||
spec.callback = AudioCallback; | ||||
spec.userdata = &fileIn; | ||||
audio = SDL_OpenAudioDevice( | ||||
NULL, | ||||
0, | ||||
&spec, | ||||
NULL, | ||||
0 | ||||
); | ||||
SDL_PauseAudioDevice(audio, 0); | ||||
} | ||||
/* Initial GL state */ | ||||
glDepthMask(GL_FALSE); | ||||
glDisable(GL_DEPTH_TEST); | ||||
glDisable(GL_ALPHA_TEST); | ||||
glDisable(GL_BLEND); | ||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | ||||
/* YUV buffers */ | ||||
glGenTextures(3, yuvTextures); | ||||
#define GEN_TEXTURE(index, w, h, ptr) \ | ||||
glActiveTexture(GL_TEXTURE0 + index); \ | ||||
glBindTexture(GL_TEXTURE_2D, yuvTextures[index]); \ | ||||
glTexImage2D( \ | ||||
GL_TEXTURE_2D, \ | ||||
0, \ | ||||
GL_LUMINANCE8, \ | ||||
w, \ | ||||
h, \ | ||||
0, \ | ||||
GL_LUMINANCE, \ | ||||
GL_UNSIGNED_BYTE, \ | ||||
ptr \ | ||||
); \ | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); \ | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); \ | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); \ | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); \ | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); \ | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | ||||
GEN_TEXTURE( | ||||
0, | ||||
width, | ||||
height, | ||||
frame | ||||
) | ||||
GEN_TEXTURE( | ||||
1, | ||||
uvWidth, | ||||
uvHeight, | ||||
frame + (width * height) | ||||
) | ||||
GEN_TEXTURE( | ||||
2, | ||||
uvWidth, | ||||
uvHeight, | ||||
frame + (width * height) + (uvWidth * uvHeight) | ||||
) | ||||
#undef GEN_TEXTURE | ||||
/* Vertex shader... */ | ||||
shaderlen = (GLint) SDL_strlen(GLVert); | ||||
vertex = glCreateShader(GL_VERTEX_SHADER); | ||||
glShaderSource(vertex, 1, &GLVert, &shaderlen); | ||||
glCompileShader(vertex); | ||||
/* Fragment shader... */ | ||||
shaderlen = (GLint) SDL_strlen(GLFrag); | ||||
fragment = glCreateShader(GL_FRAGMENT_SHADER); | ||||
glShaderSource(fragment, 1, &GLFrag, &shaderlen); | ||||
glCompileShader(fragment); | ||||
/* Program object... */ | ||||
program = glCreateProgram(); | ||||
glAttachShader(program, vertex); | ||||
glAttachShader(program, fragment); | ||||
glBindAttribLocation(program, 0, "pos"); | ||||
glBindAttribLocation(program, 1, "tex"); | ||||
glLinkProgram(program); | ||||
glDeleteShader(vertex); | ||||
glDeleteShader(fragment); | ||||
/* ... Finally. */ | ||||
glUseProgram(program); | ||||
glUniform1i(glGetUniformLocation(program, "samp0"), 0); | ||||
glUniform1i(glGetUniformLocation(program, "samp1"), 1); | ||||
glUniform1i(glGetUniformLocation(program, "samp2"), 2); | ||||
/* Vertex buffers */ | ||||
glVertexAttribPointer(0, 2, GL_FLOAT, 0, sizeof (verts[0]), &verts[0].pos[0]); | ||||
glVertexAttribPointer(1, 2, GL_FLOAT, 0, sizeof (verts[0]), &verts[0].tex[0]); | ||||
glEnableVertexAttribArray(0); | ||||
glEnableVertexAttribArray(1); | ||||
timeStart = SDL_GetPerformanceCounter(); | ||||
while (run) | ||||
{ | ||||
while (SDL_PollEvent(&evt)) | ||||
{ | ||||
if (evt.type == SDL_QUIT) | ||||
{ | ||||
run = 0; | ||||
} | ||||
else if (evt.type == SDL_KEYDOWN) | ||||
{ | ||||
/* Slowdown simulator */ | ||||
SDL_Delay(1000); | ||||
} | ||||
} | ||||
/* Loop this video! */ | ||||
SDL_LockAudioDevice(audio); | ||||
if (tf_eos(&fileIn)) | ||||
{ | ||||
tf_reset(&fileIn); | ||||
} | ||||
SDL_UnlockAudioDevice(audio); | ||||
/* Based on when we started, what frame should we be on? */ | ||||
timeCurrent = SDL_GetPerformanceCounter(); | ||||
thisframe = (int) ( | ||||
(double) (timeCurrent - timeStart) / | ||||
(double) SDL_GetPerformanceFrequency() * | ||||
fps | ||||
); | ||||
if (thisframe > curframe) | ||||
{ | ||||
/* Keep reading frames until we're caught up */ | ||||
SDL_LockAudioDevice(audio); | ||||
newframe = tf_readvideo(&fileIn, frame, thisframe - curframe); | ||||
SDL_UnlockAudioDevice(audio); | ||||
curframe = thisframe; | ||||
/* Only update the textures if we need to! */ | ||||
if (newframe) | ||||
{ | ||||
glActiveTexture(GL_TEXTURE0); | ||||
glTexSubImage2D( | ||||
GL_TEXTURE_2D, | ||||
0, | ||||
0, | ||||
0, | ||||
width, | ||||
height, | ||||
GL_LUMINANCE, | ||||
GL_UNSIGNED_BYTE, | ||||
frame | ||||
); | ||||
glActiveTexture(GL_TEXTURE1); | ||||
glTexSubImage2D( | ||||
GL_TEXTURE_2D, | ||||
0, | ||||
0, | ||||
0, | ||||
uvWidth, | ||||
uvHeight, | ||||
GL_LUMINANCE, | ||||
GL_UNSIGNED_BYTE, | ||||
frame + (width * height) | ||||
); | ||||
glActiveTexture(GL_TEXTURE2); | ||||
glTexSubImage2D( | ||||
GL_TEXTURE_2D, | ||||
0, | ||||
0, | ||||
0, | ||||
uvWidth, | ||||
uvHeight, | ||||
GL_LUMINANCE, | ||||
GL_UNSIGNED_BYTE, | ||||
frame + (width * height) + (uvWidth * uvHeight) | ||||
); | ||||
} | ||||
} | ||||
/* Draw! */ | ||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
SDL_GL_SwapWindow(window); | ||||
} | ||||
/* Clean up. We out. */ | ||||
glDeleteProgram(program); | ||||
glDeleteTextures(3, yuvTextures); | ||||
SDL_free(frame); | ||||
if (tf_hasaudio(&fileIn)) | ||||
{ | ||||
SDL_CloseAudioDevice(audio); | ||||
} | ||||
tf_close(&fileIn); | ||||
SDL_GL_DeleteContext(context); | ||||
SDL_DestroyWindow(window); | ||||
SDL_Quit(); | ||||
SDL_Log("Test complete.\n"); | ||||
return 0; | ||||
} | ||||