# HG changeset patch # User John Tsiombikas # Date 1299436278 -7200 # Node ID 265a24704ff2d3aa7040a834b43458fc1a95526a stereoplay diff -r 000000000000 -r 265a24704ff2 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,32 @@ +PREFIX = /usr/local +sdrdir = $(PREFIX)/share/stereoplay +sdr = $(wildcard sdr/*.glsl) + +src = $(wildcard src/*.c) +obj = $(src:.c=.o) +bin = stereoplay + +CC = gcc +CFLAGS = -pedantic -Wall -g +LDFLAGS = $(gllibs_$(shell uname -s)) -lavformat -lavcodec -lswscale + +gllibs_Linux = -lGL -lGLU -lglut -lGLEW + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) + +.PHONY: install +install: + install -m 775 $(bin) $(PREFIX)/bin/$(bin) + install -d $(sdrdir) + install -m 664 $(sdr) $(sdrdir)/ + +.PHONY: uninstall +uninstall: + rm -f $(PREFIX)/bin/$(bin) + rm -f $(sdrdir)/* + rmdir $(sdrdir) diff -r 000000000000 -r 265a24704ff2 sdr/colorcode.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/colorcode.glsl Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,19 @@ +uniform sampler2D tex; +uniform float left_offs, right_offs; + +void main() +{ + vec2 tc_half = gl_TexCoord[0].st * vec2(0.5, 1.0); + vec2 tc_left = tc_half + vec2(left_offs, 0.0); + vec2 tc_right = tc_half + vec2(right_offs, 0.0); + + vec3 left = texture2D(tex, tc_left).rgb; + vec3 right = texture2D(tex, tc_right).rgb; + + vec3 col, coeff = vec3(0.15, 0.15, 0.7); + col.r = left.r; + col.g = left.g; + col.b = dot(right, coeff); + + gl_FragColor = vec4(col, 1.0); +} diff -r 000000000000 -r 265a24704ff2 sdr/redblue.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/redblue.glsl Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,21 @@ +uniform sampler2D tex; +uniform float left_offs, right_offs; + +void main() +{ + vec2 tc_half = gl_TexCoord[0].st * vec2(0.5, 1.0); + vec2 tc_left = tc_half + vec2(left_offs, 0.0); + vec2 tc_right = tc_half + vec2(right_offs, 0.0); + + vec3 left = texture2D(tex, tc_left).rgb; + vec3 right = texture2D(tex, tc_right).rgb; + + + + vec3 col; + col.r = (left.r + left.g + left.b) / 3.0; + col.g = 0.0; + col.b = (right.r + right.g + right.b) / 3.0; + + gl_FragColor = vec4(col, 1.0); +} diff -r 000000000000 -r 265a24704ff2 sdr/redcyan.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/redcyan.glsl Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,19 @@ +uniform sampler2D tex; +uniform float left_offs, right_offs; + +void main() +{ + vec2 tc_half = gl_TexCoord[0].st * vec2(0.5, 1.0); + vec2 tc_left = tc_half + vec2(left_offs, 0.0); + vec2 tc_right = tc_half + vec2(right_offs, 0.0); + + vec3 left = texture2D(tex, tc_left).rgb; + vec3 right = texture2D(tex, tc_right).rgb; + + vec3 col; + col.r = left.r; + col.g = right.g; + col.b = right.b; + + gl_FragColor = vec4(col, 1.0); +} diff -r 000000000000 -r 265a24704ff2 src/config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config.h Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,7 @@ +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#define PREFIX "/usr/local" +#define SDRDIR PREFIX "/share/stereoplay" + +#endif /* CONFIG_H_ */ diff -r 000000000000 -r 265a24704ff2 src/sdr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sdr.c Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,299 @@ +#include +#include +#include +#include +#include +#include + +#if defined(unix) || defined(__unix__) || defined(__APPLE__) +#include +#include +#endif /* unix */ + +#include "sdr.h" + +unsigned int create_vertex_shader(const char *src) +{ + return create_shader(src, GL_VERTEX_SHADER); +} + +unsigned int create_pixel_shader(const char *src) +{ + return create_shader(src, GL_FRAGMENT_SHADER); +} + +unsigned int create_shader(const char *src, unsigned int sdr_type) +{ + unsigned int sdr; + int success, info_len; + char *info_str = 0; + GLenum err; + + sdr = glCreateShader(sdr_type); + assert(glGetError() == GL_NO_ERROR); + glShaderSource(sdr, 1, &src, 0); + err = glGetError(); + assert(err == GL_NO_ERROR); + glCompileShader(sdr); + assert(glGetError() == GL_NO_ERROR); + + glGetShaderiv(sdr, GL_COMPILE_STATUS, &success); + assert(glGetError() == GL_NO_ERROR); + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); + assert(glGetError() == GL_NO_ERROR); + + if(info_len) { + if((info_str = malloc(info_len + 1))) { + glGetShaderInfoLog(sdr, info_len, 0, info_str); + assert(glGetError() == GL_NO_ERROR); + } + } + + if(success) { + fprintf(stderr, info_str ? "done: %s\n" : "done\n", info_str); + } else { + fprintf(stderr, info_str ? "failed: %s\n" : "failed\n", info_str); + glDeleteShader(sdr); + sdr = 0; + } + + free(info_str); + return sdr; +} + +void free_shader(unsigned int sdr) +{ + glDeleteShader(sdr); +} + +unsigned int load_vertex_shader(const char *fname) +{ + return load_shader(fname, GL_VERTEX_SHADER); +} + +unsigned int load_pixel_shader(const char *fname) +{ + return load_shader(fname, GL_FRAGMENT_SHADER); +} + +unsigned int load_shader(const char *fname, unsigned int sdr_type) +{ +#if defined(unix) || defined(__unix__) + struct stat st; +#endif + unsigned int sdr; + size_t filesize; + FILE *fp; + char *src; + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to open shader %s: %s\n", fname, strerror(errno)); + return 0; + } + +#if defined(unix) || defined(__unix__) || defined(__APPLE__) + fstat(fileno(fp), &st); + filesize = st.st_size; +#else + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); +#endif /* unix */ + + if(!(src = malloc(filesize + 1))) { + fclose(fp); + return 0; + } + fread(src, 1, filesize, fp); + src[filesize] = 0; + fclose(fp); + + fprintf(stderr, "compiling %s shader: %s... ", (sdr_type == GL_VERTEX_SHADER ? "vertex" : "pixel"), fname); + sdr = create_shader(src, sdr_type); + + free(src); + return sdr; +} + + +unsigned int get_vertex_shader(const char *fname) +{ + return get_shader(fname, GL_VERTEX_SHADER); +} + +unsigned int get_pixel_shader(const char *fname) +{ + return get_shader(fname, GL_FRAGMENT_SHADER); +} + +unsigned int get_shader(const char *fname, unsigned int sdr_type) +{ + unsigned int sdr; +#if 0 + if((res = get_resource(sdrman, fname))) { + /* TODO: validate that the shader matches sdr_type? */ + return (uintptr_t)res; + } +#endif + + if(!(sdr = load_shader(fname, sdr_type))) { + return 0; + } + add_shader(fname, sdr); + return sdr; +} + +int add_shader(const char *fname, unsigned int sdr) +{ + return 0;/*add_resource(sdrman, fname, (void*)(uintptr_t)sdr);*/ +} + +int remove_shader(const char *fname) +{ + return 0;/*remove_resource(sdrman, fname);*/ +} + + +/* ---- gpu programs ---- */ + +unsigned int create_program(void) +{ + unsigned int prog = glCreateProgram(); + assert(glGetError() == GL_NO_ERROR); + return prog; +} + +unsigned int create_program_link(unsigned int vs, unsigned int ps) +{ + unsigned int prog; + if(!vs && !ps) { + return 0; + } + + if(!(prog = create_program())) { + return 0; + } + + if(vs) { + attach_shader(prog, vs); + assert(glGetError() == GL_NO_ERROR); + } + if(ps) { + attach_shader(prog, ps); + assert(glGetError() == GL_NO_ERROR); + } + + if(link_program(prog) == -1) { + free_program(prog); + return 0; + } + return prog; +} + +unsigned int create_program_load(const char *vfile, const char *pfile) +{ + unsigned int vs = 0, ps = 0; + + if(vfile) { + if(!(vs = get_vertex_shader(vfile))) { + return 0; + } + } + if(pfile) { + if(!(ps = get_pixel_shader(pfile))) { + return 0; + } + } + return create_program_link(vs, ps); +} + +void free_program(unsigned int sdr) +{ + glDeleteProgram(sdr); +} + +void attach_shader(unsigned int prog, unsigned int sdr) +{ + glAttachShader(prog, sdr); + assert(glGetError() == GL_NO_ERROR); +} + +int link_program(unsigned int prog) +{ + int linked, info_len, retval = 0; + char *info_str = 0; + + glLinkProgram(prog); + assert(glGetError() == GL_NO_ERROR); + glGetProgramiv(prog, GL_LINK_STATUS, &linked); + assert(glGetError() == GL_NO_ERROR); + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); + assert(glGetError() == GL_NO_ERROR); + + if(info_len) { + if((info_str = malloc(info_len + 1))) { + glGetProgramInfoLog(prog, info_len, 0, info_str); + assert(glGetError() == GL_NO_ERROR); + } + } + + if(linked) { + fprintf(stderr, info_str ? "linking done: %s\n" : "linking done\n", info_str); + } else { + fprintf(stderr, info_str ? "linking failed: %s\n" : "linking failed\n", info_str); + retval = -1; + } + + free(info_str); + return retval; +} + +int bind_program(unsigned int prog) +{ + GLenum err; + + glUseProgram(prog); + if(prog && (err = glGetError()) != GL_NO_ERROR) { + /* maybe the program is not linked, try to link first */ + if(err == GL_INVALID_OPERATION) { + if(link_program(prog) == -1) { + return -1; + } + glUseProgram(prog); + return glGetError() == GL_NO_ERROR ? 0 : -1; + } + return -1; + } + return 0; +} + +/* ugly but I'm not going to write the same bloody code over and over */ +#define BEGIN_UNIFORM_CODE \ + int loc, curr_prog; \ + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); \ + if(curr_prog != prog && bind_program(prog) == -1) { \ + return -1; \ + } \ + if((loc = glGetUniformLocation(prog, name)) != -1) + +#define END_UNIFORM_CODE \ + if(curr_prog != prog) { \ + bind_program(curr_prog); \ + } \ + return loc == -1 ? -1 : 0 + +int set_uniform_int(unsigned int prog, const char *name, int val) +{ + BEGIN_UNIFORM_CODE { + glUniform1i(loc, val); + } + END_UNIFORM_CODE; +} + +int set_uniform_float(unsigned int prog, const char *name, float val) +{ + BEGIN_UNIFORM_CODE { + glUniform1f(loc, val); + } + END_UNIFORM_CODE; +} diff -r 000000000000 -r 265a24704ff2 src/sdr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sdr.h Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,34 @@ +#ifndef _SDR_H_ +#define _SDR_H_ + +/* ---- shaders ---- */ +unsigned int create_vertex_shader(const char *src); +unsigned int create_pixel_shader(const char *src); +unsigned int create_shader(const char *src, unsigned int sdr_type); +void free_shader(unsigned int sdr); + +unsigned int load_vertex_shader(const char *fname); +unsigned int load_pixel_shader(const char *fname); +unsigned int load_shader(const char *src, unsigned int sdr_type); + +unsigned int get_vertex_shader(const char *fname); +unsigned int get_pixel_shader(const char *fname); +unsigned int get_shader(const char *fname, unsigned int sdr_type); + +int add_shader(const char *fname, unsigned int sdr); +int remove_shader(const char *fname); + +/* ---- gpu programs ---- */ +unsigned int create_program(void); +unsigned int create_program_link(unsigned int vs, unsigned int ps); +unsigned int create_program_load(const char *vfile, const char *pfile); +void free_program(unsigned int sdr); + +void attach_shader(unsigned int prog, unsigned int sdr); +int link_program(unsigned int prog); +int bind_program(unsigned int prog); + +int set_uniform_int(unsigned int prog, const char *name, int val); +int set_uniform_float(unsigned int prog, const char *name, float val); + +#endif /* _SDR_H_ */ diff -r 000000000000 -r 265a24704ff2 src/stereoplay.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/stereoplay.c Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,310 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vid.h" +#include "sdr.h" +#include "config.h" + +void cleanup(void); +void decoding_loop(void); +void disp(void); +void reshape(int x, int y); +void keyb(unsigned char key, int x, int y); +void skeyb(int key, int x, int y); +void sig(int s); +void *shmalloc(size_t sz); + +struct video_file *vf; +uint32_t *img; +int vid_xsz, vid_ysz, win_xsz, win_ysz; +unsigned int tex, sdr; +int pfd[2], xsock = -1; +int upd_frame; +int fullscr; +int swap_eyes; + +Display *dpy; + +int main(int argc, char **argv) +{ + int pid; + char *stereo_combiner, *sdrfile; + + atexit(cleanup); + + if(!(vf = vid_open(argv[1]))) { + return 1; + } + vid_xsz = win_xsz = vid_frame_width(vf); + vid_ysz = win_ysz = vid_frame_height(vf); + + if(!(img = shmalloc(vid_frame_size(vf)))) { + perror("failed to allocate frame buffer"); + return 1; + } + + if(pipe(pfd) == -1) { + perror("failed to create a self-pipe"); + return 1; + } + + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + glutInitWindowSize(win_xsz, win_ysz); + glutCreateWindow(argv[1]); + + glutDisplayFunc(disp); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyb); + glutSpecialFunc(skeyb); + + glewInit(); + + /* create the frame texture */ + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + 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, 4, vid_xsz, vid_ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glEnable(GL_TEXTURE_2D); + + if(!(stereo_combiner = getenv("STEREO_METHOD"))) { + stereo_combiner = "redcyan"; + } + + sdrfile = alloca(strlen(stereo_combiner) + strlen(SDRDIR) + 7); + sprintf(sdrfile, SDRDIR "/%s.glsl", stereo_combiner); + + if(!(sdr = create_program_load(0, sdrfile))) { + return 1; + } + bind_program(sdr); + set_uniform_float(sdr, "left_offs", 0.5); + set_uniform_float(sdr, "right_offs", 0.0); + + signal(SIGCHLD, sig); + + if((pid = fork()) == -1) { + perror("failed to fork video decoding process"); + return 1; + } else if(pid) { + close(pfd[1]); + } else { + close(pfd[0]); + decoding_loop(); + _exit(0); + } + + dpy = glXGetCurrentDisplay(); + xsock = ConnectionNumber(dpy); + + for(;;) { + int res; + fd_set rdset; + + FD_ZERO(&rdset); + FD_SET(xsock, &rdset); + FD_SET(pfd[0], &rdset); + + do { + res = select((xsock > pfd[0] ? xsock : pfd[0]) + 1, &rdset, 0, 0, 0); + } while(res == -1 && errno == EINTR); + + if(FD_ISSET(pfd[0], &rdset)) { + unsigned char done; + read(pfd[0], &done, 1); + + if(done) { + exit(0); + } else { + upd_frame = 1; + glutPostRedisplay(); + } + } + + glutMainLoopEvent(); + } + + return 0; +} + +void cleanup(void) +{ + if(tex) { + glDeleteTextures(1, &tex); + } + if(img) { + munmap(img, vid_frame_size(vf)); + } + if(vf) { + vid_close(vf); + } + close(pfd[0]); +} + +/* decoding_loop() runs in a separate decoding process and communicates + * with the parent process through the pfd[1] pipe. + */ +void decoding_loop(void) +{ + unsigned char done = 0; + struct timespec ts; + struct timeval tv0, tv; + unsigned long frame_nsec = vid_frame_interval(vf) * 1000; + printf("nanosecs per frame: %lu\n", frame_nsec); + + gettimeofday(&tv0, 0); + + while(vid_get_frame(vf, img) != -1) { + write(pfd[1], &done, 1); + + gettimeofday(&tv, 0); + + ts.tv_sec = 0; + ts.tv_nsec = frame_nsec - (tv.tv_usec - tv0.tv_usec) * 1000; + nanosleep(&ts, 0); + + gettimeofday(&tv0, 0); + } + + done = 1; + write(pfd[1], &done, 1); +} + +void disp(void) +{ + if(upd_frame) { + /* frame changed, we must re-upload the texture */ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, vid_xsz, vid_ysz, GL_BGRA, GL_UNSIGNED_BYTE, img); + upd_frame = 0; + } + + glBegin(GL_QUADS); + glColor3f(1, 1, 1); + glTexCoord2f(0, 1); glVertex2f(-1, -1); + glTexCoord2f(1, 1); glVertex2f(1, -1); + glTexCoord2f(1, 0); glVertex2f(1, 1); + glTexCoord2f(0, 0); glVertex2f(-1, 1); + glEnd(); + + glutSwapBuffers(); +} + +void reshape(int x, int y) +{ + if(!fullscr) { + win_xsz = x; + win_ysz = y; + } + + glViewport(0, 0, x, y); +} + +void keyb(unsigned char key, int x, int y) +{ + switch(key) { + case 'q': + exit(0); + + case 'f': + fullscr = !fullscr; + if(fullscr) { + glutFullScreen(); + } else { + glutReshapeWindow(win_xsz, win_ysz); + } + break; + + case ' ': + /* TODO pause/resume */ + break; + + case 's': + swap_eyes = !swap_eyes; + if(swap_eyes) { + set_uniform_float(sdr, "left_offs", 0.0); + set_uniform_float(sdr, "right_offs", 0.5); + } else { + set_uniform_float(sdr, "left_offs", 0.5); + set_uniform_float(sdr, "right_offs", 0.0); + } + break; + + default: + break; + } +} + +void skeyb(int key, int x, int y) +{ + switch(key) { + case GLUT_KEY_LEFT: + /* TODO skip fwd */ + break; + + case GLUT_KEY_RIGHT: + /* TODO skip back */ + break; + + case GLUT_KEY_UP: + /* TODO skip fwd more */ + break; + + case GLUT_KEY_DOWN: + /* TODO skip back more */ + break; + + case GLUT_KEY_PAGE_UP: + /* TODO skip fwd a lot */ + break; + + case GLUT_KEY_PAGE_DOWN: + /* TODO skip back a lot */ + break; + + default: + break; + } +} + +void sig(int s) +{ + if(s == SIGCHLD) { + wait(0); + } +} + +void *shmalloc(size_t sz) +{ + int fd; + void *shm; + + if(!(fd = open("/dev/zero", O_RDWR))) { + return 0; + } + + if((shm = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == (void*)-1) { + shm = 0; + } + + close(fd); + return shm; +} diff -r 000000000000 -r 265a24704ff2 src/vid.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vid.c Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include "vid.h" + +struct video_file { + AVFormatContext *avctx; + AVCodecContext *cctx; + AVCodec *codec; + int vstream, audio_stream; + struct SwsContext *sws; + + AVFrame *frm, *rgbfrm; +}; + +struct video_file *vid_open(const char *fname) +{ + static int initialized; + struct video_file *vf; + int i; + + if(!initialized) { + av_register_all(); + initialized = 1; + } + + if(!(vf = malloc(sizeof *vf))) { + fprintf(stderr, "open_video(%s): failed to allocate memory: %s\n", fname, strerror(errno)); + return 0; + } + memset(vf, 0, sizeof *vf); + + if(av_open_input_file(&vf->avctx, fname, 0, 0, 0) != 0) { + fprintf(stderr, "open_video(%s): failed to open file\n", fname); + vid_close(vf); + return 0; + } + + if(av_find_stream_info(vf->avctx) < 0) { + fprintf(stderr, "open_video(%s): failed to find stream info\n", fname); + vid_close(vf); + return 0; + } + + vf->vstream = -1; + for(i=0; iavctx->nb_streams; i++) { + if(vf->avctx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) { + vf->vstream = i; + break; + } + } + if(vf->vstream == -1) { + fprintf(stderr, "open_video(%s): didn't find a video stream\n", fname); + vid_close(vf); + return 0; + } + vf->cctx = vf->avctx->streams[vf->vstream]->codec; + + if(!(vf->codec = avcodec_find_decoder(vf->cctx->codec_id))) { + fprintf(stderr, "open_video(%s): unsupported codec\n", fname); + vid_close(vf); + return 0; + } + + if(avcodec_open(vf->cctx, vf->codec) < 0) { + fprintf(stderr, "open_video(%s): failed to open codec\n", fname); + vid_close(vf); + return 0; + } + + if(!(vf->frm = avcodec_alloc_frame()) || !(vf->rgbfrm = avcodec_alloc_frame())) { + fprintf(stderr, "open_video(%s): failed to allocate frame\n", fname); + vid_close(vf); + return 0; + } + + vf->sws = sws_getContext(vf->cctx->width, vf->cctx->height, vf->cctx->pix_fmt, + vf->cctx->width, vf->cctx->height, PIX_FMT_RGB32, SWS_POINT, 0, 0, 0); + if(!vf->sws) { + fprintf(stderr, "open_video(%s): failed to allocate sws context\n", fname); + vid_close(vf); + return 0; + } + + + printf("using codec: %s\n", vf->codec->name); + printf("fps: %f (%u usec frame interval)\n", vid_fps(vf), vid_frame_interval(vf)); + printf("size: %dx%d\n", vid_frame_width(vf), vid_frame_height(vf)); + + return vf; +} + +void vid_close(struct video_file *vf) +{ + if(!vf) return; + + /* TODO how do we deallocate sws contexts? */ + if(vf->rgbfrm) av_free(vf->rgbfrm); + if(vf->frm) av_free(vf->frm); + if(vf->cctx) avcodec_close(vf->cctx); + if(vf->avctx) av_close_input_file(vf->avctx); + free(vf); +} + + +float vid_fps(struct video_file *vf) +{ + float inv_tb = (float)vf->cctx->time_base.den / (float)vf->cctx->time_base.num; + return inv_tb / (float)vf->cctx->ticks_per_frame; +} + +unsigned int vid_frame_interval(struct video_file *vf) +{ + float fps = vid_fps(vf); + return 1000000 / fps; +} + +int vid_frame_width(struct video_file *vf) +{ + return vf->cctx->width; +} + +int vid_frame_height(struct video_file *vf) +{ + return vf->cctx->height; +} + +size_t vid_frame_size(struct video_file *vf) +{ + return vf->cctx->width * vf->cctx->height * 4; +} + +int vid_get_frame(struct video_file *vf, uint32_t *img) +{ + AVPacket packet; + int frame_done = 0; + AVPicture *rgbfrm = (AVPicture*)vf->rgbfrm; + AVPicture *frm = (AVPicture*)vf->frm; + + avpicture_fill((AVPicture*)vf->rgbfrm, (uint8_t*)img, PIX_FMT_RGB32, vf->cctx->width, vf->cctx->height); + + while(av_read_frame(vf->avctx, &packet) >= 0) { + if(packet.stream_index == vf->vstream) { + avcodec_decode_video2(vf->cctx, vf->frm, &frame_done, &packet); + + if(frame_done) { + sws_scale(vf->sws, frm->data, frm->linesize, 0, vf->cctx->height, rgbfrm->data, rgbfrm->linesize); + av_free_packet(&packet); + return 0; + } + } + av_free_packet(&packet); + } + + return -1; +} diff -r 000000000000 -r 265a24704ff2 src/vid.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vid.h Sun Mar 06 20:31:18 2011 +0200 @@ -0,0 +1,30 @@ +#ifndef VID_H_ +#define VID_H_ + +#include + +struct video_file; + +#ifdef __cplusplus +extern "C" { +#endif + +struct video_file *vid_open(const char *fname); +void vid_close(struct video_file *vf); + +float vid_fps(struct video_file *vf); + +/* returns the interval between frames in microseconds */ +unsigned int vid_frame_interval(struct video_file *vf); + +int vid_frame_width(struct video_file *vf); +int vid_frame_height(struct video_file *vf); +size_t vid_frame_size(struct video_file *vf); + +int vid_get_frame(struct video_file *vf, uint32_t *img); + +#ifdef __cplusplus +} +#endif + +#endif /* VID_H_ */