Wander/FNA/lib/Theorafile/sdl2test/sdl2test.c

366 lines
8.8 KiB
C

/* Theorafile - Ogg Theora Video Decoder Library
*
* Copyright (c) 2017 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 */
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.596);\n"
"const vec3 Gcoeff = vec3(1.164, -0.391, -0.813);\n"
"const vec3 Bcoeff = vec3(1.164, 2.018, 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;
Uint8 run = 1;
/* Theorafile variables */
OggTheora_File fileIn;
int width, height, channels, samplerate;
double fps;
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);
frame = (char*) SDL_malloc(width * height * 2);
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,
width / 2,
height / 2,
frame + (width * height)
)
GEN_TEXTURE(
2,
width / 2,
height / 2,
frame + (width * height) + (width / 2 * height / 2)
)
#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);
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? */
thisframe = (int) (SDL_GetTicks() / (1000.0 / 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,
width / 2,
height / 2,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
frame + (width * height)
);
glActiveTexture(GL_TEXTURE2);
glTexSubImage2D(
GL_TEXTURE_2D,
0,
0,
0,
width / 2,
height / 2,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
frame + (width * height) + (width / 2 * height / 2)
);
}
}
/* 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;
}